Skip to main content

What are Webhooks?

Imagine you ordered a package and you want to know when it arrives. You have two options:
  1. Polling: Call the courier every 5 minutes to ask “is my package here yet?” Exhausting, slow, and wasteful.
  2. Push notification: The courier texts you the moment the package lands at your door.
Webhooks are push notifications for your server. Instead of your application repeatedly calling our API to check whether money has arrived (polling), we call your server the moment it happens. This means your customers see their wallet credited within seconds of a transfer, rather than waiting for your next poll cycle.

Events You Can Subscribe To

We may add new event types over time. Handle unknown event types gracefully: log them and return 200 OK without taking action. This way, new events we add won’t cause your endpoint to error.

Setting Up a Webhook

1

Build an HTTPS endpoint on your server

Create a route in your web application that accepts POST requests. This is where we send notifications.Requirements for the endpoint:
  • Must use HTTPS (not plain HTTP). We reject HTTP endpoints for security
  • Must respond within 30 seconds. Requests that take longer are treated as failures
  • Must return HTTP 200 to acknowledge receipt. Any other status code (including redirects) is treated as a failure
  • Must be idempotent. We may deliver the same event more than once in rare circumstances (see Retry Behaviour below)
A minimal endpoint in Node.js:
2

Register your endpoint via the API

Tell us where to send notifications:
Response:
Copy and securely store the secret value immediately. It’s only shown once at registration. You’ll use it to verify all incoming webhook requests. If you lose it, you’ll have to delete the webhook and register a new one.
3

We test-ping your endpoint

During registration, we send a test POST request to your URL with a ping event. If your endpoint doesn’t respond with 200 OK within 30 seconds, registration fails and you receive an error.Example ping payload:
Make sure your endpoint handles the ping event and returns 200. You don’t need to do anything with it other than respond.
4

Receive and verify notifications

Once registered, events start arriving. Always verify the X-Signature header before processing any payload. See the full verification guide below.

Verifying Webhook Authenticity

This is the most important part of your webhook integration. Without signature verification, your endpoint is open to fake notifications. An attacker could send a forged collection.credit event and trick your system into crediting a customer without real money arriving. We compute the X-Signature header by running an HMAC-SHA256 hash over the raw request body, using your webhook secret as the key.
Always use timingSafeEqual (JavaScript) or hmac.compare_digest (Python) for the final comparison. Never use === or ==. Standard equality checks can leak information about the signature through timing differences, giving attackers a way to forge valid signatures over many attempts.

Handling Duplicate Deliveries

In rare cases (such as a network timeout between our servers and yours), we may deliver the same event more than once. Your handler must be idempotent: processing the same event twice should produce the same result as processing it once. The simplest way: before processing a webhook, check whether you’ve already seen this transactionRef. If yes, return 200 OK immediately without re-processing.

Retry Behaviour

If your endpoint returns anything other than 200 OK, or doesn’t respond within 30 seconds, we retry delivery on this schedule: After all 7 retries are exhausted, we stop. The event is marked as FAILED in your delivery history. You can manually replay failed events from the sandbox dashboard or via the API.
Return 200 OK as quickly as possible, ideally before any database writes or external API calls. Acknowledge receipt first, then do the work asynchronously. This prevents timeouts on our end from triggering unnecessary retries.

View Delivery History

Check the status of recent webhook deliveries. Useful for debugging:
Response:
To manually replay a failed delivery:

Remove a Webhook

Deleting a webhook is immediate and permanent. Any in-flight events that haven’t yet been delivered will be dropped. If you’re rotating your webhook (e.g. changing URLs), register the new one first and confirm it’s working before deleting the old one.

Testing Webhooks in the Sandbox

During local development, your server isn’t publicly accessible, so we can’t reach your localhost:3000. Two tools solve this:
webhook.site gives you a public HTTPS URL instantly, with no setup. Visit the site, copy the unique URL it gives you, and register that as your webhook URL in the sandbox. Every request we send to it appears in the browser in real time.Best for: Quickly inspecting what the webhook payload looks like. Not suitable for testing your actual application logic.
ngrok creates a secure public HTTPS tunnel to your local development server. Install it, run ngrok http 3000 (replacing 3000 with your local port), and it gives you a public URL like https://a1b2c3d4.ngrok.io. Register that URL as your webhook.Now when we send a webhook to that URL, it tunnels through ngrok to your local server. Your actual application code handles it, exactly as it would in production.
Best for: End-to-end testing your full webhook handling logic locally before deploying.
The free tier of ngrok gives you a new URL every time you restart it. If you’re doing extended testing, note down your webhook ID and update the URL each session, or sign up for a paid ngrok plan that gives you a fixed domain.