What an event looks like
event name, an event_id, and a data object holding the reference you supplied.
Each product documents its own events:
Deposits and addresses
deposit.success, transfer.success, transfer.failed.Payouts
payouts.initialized, payouts.processing, payouts.withdrawal.success.Withdrawals
Withdrawal lifecycle events.
Virtual cards
Card creation, top-up, and transaction events.
Verify every event
Verify the signature before you read the payload. Without this check, anyone who learns your endpoint URL can post a fake deposit to it. We sign each event with HMAC-SHA512 over the request body, keyed by your secret, and send the result in thex-bitnob-signature header.
Two rules make the check correct:
- Sign the raw request body, not a re-serialized object. Re-serializing can change key order or whitespace, and the signature will not match.
- Compare with a timing-safe function, not
==.
Restrict inbound traffic
If your firewall filters inbound traffic, allow our delivery addresses:
These addresses can change, and we announce changes in advance.
Use HTTPS
Your webhook URL must usehttps://. Do not accept events over plain HTTP.
Respond fast, process later
Return200 as soon as the signature is valid. Do the real work afterwards.
A handler that writes to a database, calls a third-party service, and sends an email before responding will eventually exceed our timeout. We then treat the delivery as failed and send the event again, even though you processed it.
Retry behavior
How we read your response:
If your own processing fails temporarily, for example your database is down, return a
5xx so the event comes back. Return 400 or 422 only when retrying would never help.
Make your handler idempotent
Redelivery means your handler will see the same event more than once. Processing it twice sends a second payout or credits a user twice. Checkevent_id before you act, and record it after. Don’t de-duplicate on reference: several events for one payout share a reference, and some events have none.
Store what you receive
Store the event name, the reference, the full payload, the delivery attempt, and the processing result. This table answers most support questions without a call to us.

Reconcile what never arrives
Signature verification and idempotency handle the events you receive. Neither helps with an event you never got. Run a scheduled job that lists recent transactions and compares them against your own records. Anything we report as complete and you still hold as pending is a missed delivery. Use Get all transactions for the sweep, and Get transaction by id or reference to resolve a single record.Do not rely on webhooks alone. Reconciliation is what makes a payments integration correct rather than usually correct.
Test locally
1
Expose your local server
Run a tunnel, such as
ngrok http 3000, and set the public URL as your webhook URL in the dashboard.2
Trigger a real event
In sandbox, simulate a deposit. See Test in sandbox.
3
Force a failure
Return
500 from your handler and watch the retries arrive.4
Force a duplicate
Process the same event twice and confirm that the second attempt changes nothing.
Checklist
- Verify the signature on every event, and fail closed.
- Return
200before you process. - Check the
event_idbefore you act. - Store every event you receive.
- Reconcile on a schedule.
- Serve the endpoint over HTTPS.