POST /webhooks, passing a url and the subscriptions you want. For building the endpoint that consumes these events, see Receiving webhooks. The create response returns a one-time signingKey; store it immediately, since it is never returned again. Verify every delivery with the Webhook-Signature header before processing, and deduplicate on the envelope’s id so a redelivered event is handled once.
Choosing your subscriptions
Thesubscriptions array accepts concrete event names from the catalog below, or the * wildcard.
* does not cover the compliance review events. Those are excluded from the wildcard by design and must be named explicitly.POST /webhooks/{webhookId}/test sends a webhook.test event to your endpoint. It is delivered regardless of your subscriptions, so you can verify signing and connectivity before any real event fires.
Event envelope
A delivery carries the resource that changed underdata, so you rarely need a follow-up read. Most events use the shape below.
status lives inside data, not on the envelope: branch on data.status (or the event name) rather than expecting an envelope-level status field.
The *.statusChanged events
The five *.statusChanged events (transaction, virtualAccount, depositAddress, externalAccount, and counterparty) use a variant of this shape. It names the event type in eventType rather than event, and adds two fields:
Transactions
Transactions emit a single event on every status transition. The full transaction arrives underdata, so branch on data.status, using data.subStatus for the detail that distinguishes cases such as a payout held for review. The envelope’s previousStatus tells you which status the transaction left.
See the transaction lifecycle for the statuses this event reports and the order they occur in.
Cash-in
Virtual accounts
Lifecycle events report the account itself. Thedeposit, cryptoTransfer, and fiatTransfer events report the individual legs of money moving through it.
Deposit addresses
Lifecycle events use thedepositAddress. prefix. Per-deposit and per-payout legs use the deposit_address. prefix.
The fiat payout legs report a
returned state because a bank can reverse a settled transfer. The crypto payout leg has no returned event.
External accounts
Counterparties
Endorsements
Wallets
Compliance review events
These events fire when a transaction is held for compliance review. They are excluded from the* wildcard and must be named explicitly in subscriptions.
Delivery and verification
- Delivery is at-least-once, and ordering is best effort rather than a guarantee. Treat deliveries as unordered and possibly duplicated: deduplicate on
id, and apply an event only when itssequenceis newer than the last one you processed for that resource, so a late delivery cannot overwrite newer data. - Signature verification: the
Webhook-Signatureheader ist=<unix>,v1=<hex>, where the hex is an HMAC-SHA256 over<t>.<raw request body>keyed with your endpoint’s signing key. The timestamp and signature are regenerated on every attempt, so verify against the raw body before parsing it, and reject stale timestamps. - Manage subscriptions with the Webhooks endpoints:
POST/GET /webhooks,GET/PATCH/DELETE /webhooks/{webhookId}, plus lifecycle actions onPOST /webhooks/{webhookId}/enable,POST /webhooks/{webhookId}/disable,POST /webhooks/{webhookId}/rotate-key, andPOST /webhooks/{webhookId}/test. - Inspect delivery history with
GET /webhooks/{webhookId}/deliveries(filter bystatus,eventId,test, or acreatedAfter/createdBeforewindow). Expand a single delivery with its HTTP attempts withGET /webhooks/{webhookId}/deliveries/{deliveryId}. Requeue failed deliveries withPOST /webhooks/{webhookId}/deliveries/retry: pass explicitdeliveryIds, or astatusplus acreatedAfter/createdBeforerange to retry all matching deliveries. - A webhook is considered down after continuous failure: no successful delivery, and at least one failed attempt since the last success. Two separate thresholds apply.
notifyThresholdSecscontrols notification only (range 10 minutes to 1 day, default 1 hour), and fires once per down episode rather than per failed attempt. Auto-suspension is separate and happens after roughly 3 days of continuous failure. - A suspended webhook keeps creating delivery rows for matching events, but records them as
skippedrather thanqueued, so nothing is silently lost. Only the service setssuspended, and it never clears it: return the webhook toenabledwithPOST /webhooks/{webhookId}/enable, which resets the down clock, then re-fire the skipped deliveries withPOST /webhooks/{webhookId}/deliveries/retry. Adisabledwebhook differs: it creates no delivery rows at all. - Any non-2xx response, timeout, or network error is retried on a fixed schedule. Individual status codes are not special-cased: a
410 Goneis treated the same as a500.
That is 10 attempts over roughly 3 days, after which the delivery is marked
failed. Each attempt is bounded by the webhook’s timeoutMs (default 5000, hard cap 10000), measured from connect through response. A manual retry overrides the cap: it resets the attempt count and requeues the delivery immediately.
DELETE /webhooks/{webhookId}is a soft delete: the webhook is hidden from list and get, but delivery history is never removed with it. History is retained for the configured retention window (30 days by default), so existingwhd_links keep resolving.