Webhooks

Receive push notifications when a payout changes state so you do not have to poll, signed using the Standard Webhooks scheme.

We tell you when a payout changes state, so you do not have to poll. Webhooks are the nudge; the API is the truth. Build reconciliation against GET /payments/organizations/{orgId}/orders/{payoutId} and treat a delivery as a reason to look, never as the record itself.

Signing: Standard Webhooks

We sign with Standard Webhooks, the same scheme Svix popularised — so you can verify with an off-the-shelf library in your language instead of hand-rolling against a format only we speak.

Three headers arrive with every delivery:

HeaderMeaning
svix-idThe delivery id. Idempotency key — dedupe on this.
svix-timestampUnix seconds. Reject anything far from your own clock.
svix-signaturev1,<base64> — a space-separated list, so a rotation can ship two valid signatures on one request

The signed content is the delivery id, the timestamp and the raw body, joined by dots:

signedContent = `${svix-id}.${svix-timestamp}.${rawBody}`
signature     = base64(HMAC_SHA256(base64decode(secret minus "whsec_"), signedContent))

Verify over the bytes you received, before any JSON parsing. Re-serializing the body changes it — a reordered key or a different float formatting produces a signature mismatch on a delivery that was perfectly valid.

Your secret is issued with the endpoint, in the conventional whsec_ form. It is shown once.

Events

Six event types, and they are the canonical payout states:

EventMeaningTerminal
payout.pendingAccepted, not yet moving
payout.processingOn its way
payout.completedThe beneficiary has been paid; a later bank return is still possible
payout.failedIt failed; inspect fundsReturned before changing your ledger
payout.returnedIt settled and the receiving bank later returned it
payout.canceledStopped before it moved
📘

Rail Independence

The rail is never named. No provider's own status words appear in any payload, which is what lets us re-route your organization to a different payment network without your ledger changing. If you find yourself branching on something rail-shaped, you are reading a field you should not depend on.

New event types are a backwards-compatible change. Ignore ones you do not recognize rather than throwing — see Versioning.

Delivery

We consider a delivery successful on any 2xx. Anything else — including a timeout — is retried on a fixed ladder:

AttemptSent
1Immediately
2+1 minute
3+5 minutes
4+30 minutes
5+2 hours
6+6 hours

Six attempts over roughly 8.6 hours, after which the delivery is left failed and we stop. It is not lost: it stays in the delivery log, and you can replay it.

Return 2xx fast, and do the work afterwards. A receiver that finishes its own processing before responding is a receiver that times out under load and gets retried, which is how one slow database write becomes a duplicate.

Ordering, duplicates, and the two rules that follow

Deliveries are at-least-once and not ordered. Both are consequences of retrying, and both are ordinary; every webhook system worth trusting has them.

  1. Dedupe on svix-id. The same event may arrive twice — a retry after a timeout that actually succeeded is the common case.
  2. Never move a payout backwards. A delayed payout.processing can land after payout.completed; ignore it. The one forward transition from completed is payout.returned, whose body carries status: failed and failureCode: returned_by_bank. Only failed, returned, and canceled are terminal.

Managing endpoints

GET /organizations/{orgId}/webhook-endpointsList
POST /organizations/{orgId}/webhook-endpointsRegister; returns the signing secret once
DELETE /organizations/{orgId}/webhook-endpoints/{id}Remove
POST /organizations/{orgId}/webhook-endpoints/{id}/enabledPause or resume without losing the secret
GET /organizations/{orgId}/webhook-endpoints/{id}/deliveriesRecent attempts, with status and next retry
POST /organizations/{orgId}/webhook-endpoints/{id}/deliveries/{deliveryId}/replayRe-fire one, with a fresh retry ladder

The delivery log is there for the integration you are debugging at 2am: it shows what we sent, what came back, how many attempts remain and when the next one is due. Reach for it before you reach for us.

These management routes use a human dashboard session and require a signer role. Partner API keys can receive and verify deliveries, but cannot create, pause, replay, or repoint live webhook endpoints.

Before you go live

  • You verify signatures over the raw bytes, and reject on mismatch.
  • You dedupe on svix-id.
  • You ignore events older than the state you hold.
  • You return 2xx before doing your own work.
  • Your endpoint is reachable from the public internet over HTTPS.
  • You reconcile against the API on a schedule anyway, because a webhook you never received is indistinguishable from one that never fired.

Did this page help you?