1. Your endpoint must be publicly reachable
Webhook notifications are only delivered to a publicly accessible URL. While building locally, events cannot reachlocalhost.
Fix: expose your local server with a tunnel such as ngrok or localtunnel, then update the webhook URL setting on your dashboard. Only do this in your test environment to avoid leaking data to the public.
2. Your firewall may be blocking us
If your infrastructure restricts inbound traffic, allow requests from our webhook delivery addresses:
These addresses can change; we announce changes ahead of time. An allowlist is not a substitute for verifying the x-bitnob-signature header, which stays correct even if our infrastructure moves.
3. Your endpoint rejected the event, or the retries ran out
Any2xx counts as delivered. A 400, 404, 410, 422, or 451 is treated as a permanent rejection, and the event is never retried. Anything else, including a timeout, is retried with increasing delays (by default about 1, 5, 15, and 60 minutes), up to the retry count set on your webhook: 1 to 5 attempts in total. After that, the event is not sent again.
Common ways to lose an event:
- a route mismatch returned
404, or validation in your handler returned400or422, so the first failure was final, - your endpoint was down or redeploying for longer than the retry window,
- it did heavy processing synchronously before responding, and timed out on every attempt.
2xx immediately and process asynchronously. If processing fails temporarily, return a 5xx so the event is retried. Make handling idempotent, since the same event can be delivered more than once. See Retries and duplicates.
4. Verify the signature while debugging
If events arrive but appear to be dropped by your own handler, check signature verification. Validate the x-bitnob-signature header: an HMAC SHA-512 (hex) of the raw request body, keyed with the webhook secret set in your dashboard. Signing with your API client secret, or over re-serialized JSON, never matches.Don’t rely on webhooks entirely
We recommend that you set up a service to always query transactions, in the event that webhooks keep failing.
id and track status via GET /api/payouts/{id}, so a missed webhook never leaves a transaction in an unknown state.