Overview
Webhooks let your server receive real-time notifications when deposits arrive, sweeps complete, and withdrawals broadcast / confirm / fail. They are the recommended way to drive your integration — polling is a fallback for when a delivery is missed.
How it works
- You register a webhook URL and pick which event types it should receive (one URL can subscribe to any subset of the six events).
- The gateway shows you a one-time signing secret at creation time. Copy it now — it's never displayed again.
- When a matching event fires, the delivery worker POSTs a signed JSON envelope to your URL. It expects a
2xxresponse within 10 seconds. - Non-
2xx(or timeouts / network errors) trigger an exponential-ish retry — up to 7 attempts spanning ~38 hours. See Retries & delivery log. - After the final retry fails, the row is marked
failed(dead) and stops auto-retrying. You can manually requeue it from your dashboard's delivery log.
Registering a webhook
Go to the Webhooks page in your dashboard:
- URL — must be HTTPS. The gateway only delivers to
https://URLs. - Subscribed events — tick the events you care about. Common starting set:
deposit.confirmed+withdrawal.confirmed+withdrawal.failed. - Signing secret — shown once after creation. Store it in your secrets manager immediately. If you lose it, you must delete the webhook and create a new one — there is no "reveal" or "rotate" endpoint.
A single account can register multiple webhooks. Each gets its own signing secret. Useful when you want one URL for production events and another for staging-mirror or alerting.
Lifecycle of one delivery
Every event triggers one row in tbl_webhook_deliveries per subscribed webhook, then moves through this state machine:
| Status | Meaning |
|---|---|
pending | Awaiting the next worker tick (every ~10 s). A brand-new delivery starts here, and a delivery that failed an attempt but still has retries left returns here — with attempt_count bumped and a future next_retry_at. |
delivering | Worker claimed the row and is about to POST. |
delivered | Receiver responded 2xx. Final state. |
failed | All 7 attempts exhausted. Final state — you'll need to manually requeue if needed. |
There is no separate "retrying" status — a row waiting to be retried sits in pending. The dashboard's delivery log shows the current state of every delivery, the most recent HTTP response status code, and a "retry" button for failed rows.
What you'll need to implement
Every webhook receiver should:
- Verify the signature on the raw request body — see Verifying signatures. Reject the request with
401if it doesn't match. - Respond
2xxquickly — within 10 seconds, ideally under 1 second. Persist the event to your queue or store first, process asynchronously. - Dedupe by the envelope's
id— the same delivery row'sidis constant across all retries, soidis the right primary key to dedupe on. - Tolerate unknown fields — payloads MAY gain new optional fields without bumping
api_version. Don't fail validation on extras.
What you'll find in this section
- Events — the six event types, when each fires, and the exact shape of each payload.
- Verifying signatures — canonical string, HMAC algorithm, and copy-paste verifier recipes in Python, Node.js, and PHP.
- Retries & delivery log — backoff schedule, success/failure criteria, and how to debug from the dashboard.