Webhooks
Register endpoints (scopewebhooks) to receive events as they happen.
Events
Subscribe to events by name when you register. An FX order that ends
expired sends no webhook. Treat a webhook as a signal and read the resource (GET /fx/orders/{id}, GET /transfers/{id}, GET /payment-links/{id}) for its current state.
Delivery format
Each delivery is an HTTPSPOST with a JSON body:
The body’s
id (evt_…) identifies the event; BFX-Webhook-Id identifies the delivery. Use either to de-duplicate.
Verify every delivery
Your endpoint’s signing secret (whsec_…) is returned once, in the POST /webhooks response. Store it. To verify a delivery:
- Read
tandv1fromBFX-Webhook-Signature. - Compute
HMAC-SHA256(secret, "<t>.<raw body>")as lower-case hex. Use the raw request body bytes exactly as received, before any JSON parsing. - Compare it to
v1in constant time. Reject on mismatch. - Reject the delivery if
tis more than 5 minutes from your clock. This limits replay of a captured delivery.
Retries
A delivery succeeds when your endpoint answers any2xx within 10 seconds. Anything else is retried on this schedule, measured from the previous attempt:
That is 6 attempts over about 8 hours 36 minutes. If the sixth attempt fails, the delivery is dropped and the whole endpoint is disabled: it receives no further events. There is no re-enable call; delete the endpoint and register it again. Test deliveries follow the same rules.
Managing endpoints
urlmust be HTTPS and resolve to a public address. Private, loopback and cloud-metadata addresses are refused with400 invalid_url.eventsmust list one or more ofpayment_link.paid,charge.settled,fx.order.settled,fx.order.failed,fx.order.refunded,transfer.confirmed,transfer.failed.webhook.testis not subscribable; it is sent only by the test call.- Each account can have up to 10 active endpoints (
409 endpoint_cap_reached).