Webhooks & Event Handling

Webhooks power the most important parts of your system: receiving Bitcoin, settling fiat, delivering airtime, or triggering a downstream event. They are the bridge between Bitnob and your app when something changes — and they must be handled with care.

This guide teaches you how to build a robust webhook system: one that can handle retries, duplicate messages, bad payloads, delayed delivery, and even downstream service outages.


How Webhooks Work

A webhook is an HTTP request Bitnob sends to your server when something happens. Instead of you polling an endpoint to see “has this payment arrived?”, Bitnob notifies you the moment it’s confirmed. Per-product event catalogues live in the API reference: deposits and addresses, withdrawals, payouts, and virtual cards.

For example, when a user sends BTC and it clears, Bitnob sends:

Node.js
Note

This is a push notification for your backend. You must handle it like an incoming transaction.

Webhook Flow Overview

Webhook Flow Overview
Note

If your app does not return a 200 OK, Bitnob retries the webhook.


What You Get from Bitnob

Bitnob supports:

Secure webhook delivery for all asynchronous flows

A dashboard to inspect webhook logs, payloads, and retry attempts

Webhooks for deposits, withdrawals, payouts, trades, cards, and virtual account funding

Note

This makes debugging significantly easier, especially during integration or failure scenarios.

Writing a Webhook Handler

Use a backend route that accepts POST requests with a JSON payload.

Example in Node.js:

Webhook Handler

Key things to remember:

Respond with 200 OK quickly to prevent retries.

Do not block this handler with long-running DB calls or downstream logic (consider using a queue).

Log every webhook attempt with timestamp and reference.


Bitnob Webhook Retry Behavior

Delivery and retry behaviour is documented in the webhooks overview. Bitnob retries if:

Your endpoint is offline or times out

Your server returns a 5xx

Your server returns a 4xx other than 400, 404, 410, 422 or 451 (those are final and are not retried)

Note

A failed delivery is retried with increasing delays, up to the retry count set on your webhook (1 to 5 delivery attempts in total), then delivery stops. Any 2xx counts as delivered. The same event can arrive more than once and events can arrive out of order, so you must handle idempotency and must not assume ordering.


Idempotency: Why It Matters

Idempotency means processing a webhook once and only once, no matter how many times it is received.

Without idempotency, you may:

Send multiple offramps for the same crypto payment

Deliver airtime multiple times (especially dangerous if the amount is large)

Trigger double card funding or duplicate messages to a user

Corrupt internal logs by writing duplicate events with different timestamps


Real-World Failure Example

Before processing any event, check if that reference has already been marked as processed in your database.

1

Bitnob sends transfer.success

2

Your webhook server receives it but times out before responding

3

Bitnob retries

4

Your app processes it again → the same withdrawal is marked paid twice, or a customer is credited twice


How to Fix It

Node.js (Idempotency Check)

Suggested Schema

Every time you create a quote, store it alongside your transaction or UI session.

id
reference
event
status
received at
a
944a4089-6065-49ca-8f43-fb10f15c59e6
transfer.success
processed
2025-04-08T14:20

This makes auditing, debugging, and duplicate suppression easy.

Webhook Retry Behavior

Recommended Webhook Architecture (with Queue)

This decouples fast acknowledgment (step B) from slow logic (step D). Bitnob gets a 200 OK immediately. You process the event asynchronously in the background. Use queues like:

BullMQ or BeeQueue (Node.js)

Sidekiq or Sneakers (Ruby)

Celery (Python)

Redis Streams or Kafka (at scale)


Securing Your Webhooks

Signature Verification

Bitnob signs every webhook with the x-bitnob-signature header — see Verifying Events for the reference implementation, and Source IPs if you also filter inbound traffic by IP. Verify the payload:

Node.js (Signature Verification)
Fail closed

If the signature check fails, discard the request.


IP Allowlisting

Bitnob sends webhooks from the following addresses. If your firewall restricts inbound traffic, allow them so events can reach your endpoint:

ip address
use
18.171.51.218
Webhook delivery
13.134.154.133
Webhook delivery

These addresses can change, and we announce changes ahead of time. Treat the allowlist as a network convenience, not your security boundary — signature verification is what proves an event came from us.

Don't allowlist by IP alone

Any host can send a request from an allowlisted address. Verify the signature on every event, whether or not you filter by IP.

HTTPS Required

Always use https:// URLs for your webhook. Never expose webhooks over HTTP.


Testing and Debugging Webhooks

For local development:

Use ngrok, localtunnel, or cloudflared to expose your local server

Simulate a transaction (e.g., simulate deposit in sandbox) — for cards there is a dedicated Simulate Webhook endpoint

Use webhook inspection tools to replay and analyze events

You can also log webhook attempts using a simple logger:

Node.js (Simple Logging)

For production observability, log:

Event type

Reference

Timestamp

Delivery status (received, retried, failed)


What to Always Do

1

Handle webhooks in a dedicated endpoint

2

Respond with 200 OK fast

3

Always make your logic idempotent

4

Log everything (reference, type, status)

5

Use a queue if your processing takes time

6

Verify signatures to secure the source

7

Use the Bitnob dashboard to inspect payloads and retry if needed

Webhooks are your single source of truth for offchain confirmations. If you treat them like production infrastructure, your product will feel real-time, even though the underlying rails (blockchain, fiat, telcos) are asynchronous and imperfect.


Share on
Did you find this page useful?

Join our Discord