Skip to main content
Webhooks notify your application after deposits, withdrawals, and exchange orders change. REST remains the source of truth, so use the identifiers in a webhook to retrieve current state whenever your workflow needs confirmation.

Event type guides

  • Deposit events cover required Travel Rule information and terminal deposit outcomes.
  • Withdrawal events cover terminal withdrawal outcomes, including partially settled bulk NGN withdrawals.
  • Exchange events cover order acceptance, fills, replacement, cancellation, and rejection.
Every delivery uses the same five-field top-level envelope. The linked event guides contain complete, schema-valid JSON bodies for each data shape. The body does not contain endpoint, delivery-attempt, signing-secret, or account routing metadata.

Subscribe to events

Create an HTTPS endpoint with POST /v1/partner/webhooks. An account can have at most three enabled endpoints. Subscriptions are exact event names; wildcard subscriptions are not supported.
  • deposit.action_required
  • deposit.success
  • deposit.failed
  • withdrawal.success
  • withdrawal.partial
  • withdrawal.failed
  • exchange.accepted
  • exchange.filled
  • exchange.replaced
  • exchange.cancelled
  • exchange.rejected
Create the endpoint from your server and store the returned secret immediately:
Your API key must have wallets:view for deposit and withdrawal topics, and trades:view for exchange topics, in addition to webhooks:manage to create, replace, delete, or rotate an endpoint. Listing endpoints and reading delivery history require webhooks:view. Neither webhook scope is in the default scope set, so select them when creating the key. event_types must be non-empty with no duplicates, enabled is required on both create and replace, and description is limited to 255 bytes. The URL must be HTTPS without credentials or a fragment, at most 2048 bytes, and must resolve to a public address; a URL that fails these checks returns 422 at create or replace time. This check resolves DNS but does not send a test delivery. The create response contains a generated whsec_... signing secret exactly once. Store it in your secrets manager. Rotating a secret returns webhook_id, the new secret exactly once, secret_version, and previous_secret_valid_until. Until that time, by default 24 hours, the signature header contains signatures for both the new and previous secret; accept the delivery when any one signature verifies with a secret you currently trust. Rotating again before the overlap ends returns 409 with code SECRET_ROTATION_IN_PROGRESS.

Manage an endpoint

Use the endpoint ID returned at creation:
PUT replaces the complete endpoint configuration, including the subscription list. To stop deliveries without deleting history, update the endpoint with enabled: false. Deliveries already queued for a disabled or deleted endpoint are cancelled and are not resumed if you re-enable it. Use DELETE /partner/webhooks/{webhook_id} only when the endpoint is no longer needed. There is no synthetic test-delivery endpoint. Validate your receiver in staging by completing a staging workflow that produces one of its subscribed events, then confirm the event and delivery through the history endpoints. Endpoint creation by itself does not test network delivery.

Delivery contract

Stabyl sends the exact JSON body as a POST with content-type: application/json, a user-agent of Stabyl-Webhooks/1.0, and these headers:
Your receiver must accept the connection within 2 seconds and return a 2xx within 5 seconds of the request starting. A slower response counts as a failed attempt. Commit the inbox row and respond first; do heavier processing after the response. Calculate the signature over the unmodified request bytes:
Use HMAC-SHA256 with the 32 bytes obtained by removing whsec_ and decoding the remaining standard Base64 text. Compare signatures in constant time and reject timestamps outside your chosen tolerance. The examples below use five minutes.
Read the raw body before any JSON middleware changes whitespace, key order, or encoding. Parse JSON only after signature verification succeeds.

Node.js verification

Python verification

Retries, duplicates, and ordering

A failed initial delivery can receive up to three retries. The nominal retry delays are approximately 30 seconds, 5 minutes, and 30 minutes, with jitter. Stabyl retries transient network failures and HTTP 408, 409, 425, 429, and 5xx responses. Other 4xx responses and redirects are not retried. A valid Retry-After response can delay a retry by up to 30 minutes. Network ambiguity can repeat the same logical attempt, so always deduplicate by webhook-id. No ordering is guaranteed across events, endpoints, topics, or retries. Return any 2xx response only after your durable inbox transaction is committed. Use GET /v1/partner/webhook/events and GET /v1/partner/webhook/deliveries for eventually consistent delivery history, and GET /v1/partner/webhook/events/{event_id} or GET /v1/partner/webhook/deliveries/{delivery_id} for one record. History is kept for 30 days, may lag live delivery, and does not replace REST reconciliation.