DevKit Tools

How to Test a Webhook (and Why It Fails in Production)

3 min read4 October 2026By Muhammad Abdullah

A webhook is a service calling your server when something happens: a payment succeeds, a form is submitted, a build finishes. It is simple in principle and awkward to debug, because the request comes from someone else's system at a time you do not choose. The way to make it manageable is to look at the real request first and only then write code for it.

Try it as you readWebhook TesterGet a temporary URL that captures any HTTP request sent to it. Inspect method, headers, query and body live, with no signup.

Step one: see what is actually sent

Documentation describes a webhook; the real request is what your code must handle. Before writing a handler, point the service at a temporary URL that records incoming requests, trigger the event, and read what arrives: the method, the headers, and the exact body.

You will often find something the docs did not make clear: a field that is a string rather than a number, an event wrapped one level deeper than expected, or a signature header with a slightly different name.

Step two: replay it against your own endpoint

Once you have a real payload, you can send it to your own endpoint as many times as you like with an API client, without waiting for a real payment or signup each time. Copy the headers along with the body, because many handlers check the content type or the signature header.

bash
curl -X POST https://your-app.test/webhooks/payments \
  -H "Content-Type: application/json" \
  -H "X-Signature: t=1728000000,v1=5257a8..." \
  -d @payload.json

A local server is not reachable from the internet, so a real provider cannot call it directly. For a live test from the provider, use a tunnelling tool that gives your local port a public URL, or deploy the handler to a test environment.

Verify the signature

Your webhook URL is public, so anyone can send it a request that looks like a payment. Most providers sign each request with a secret only you and they know, usually an HMAC of the raw body, and put the result in a header. Your handler must recompute it and reject the request if it does not match.

Many providers also include a timestamp in what they sign. Reject requests older than a few minutes, so a captured request cannot be replayed later.

Why it works in testing and fails in production

  • Slow responses: providers wait only a few seconds. If your handler sends emails or calls other APIs before replying, it times out and the provider marks the delivery failed. Reply with a 2xx immediately and do the slow work in a background job.
  • Retries: when a delivery fails, the provider sends it again, sometimes many times over hours or days. Your handler will see the same event more than once.
  • Duplicates and order: even successful events can arrive twice or out of order. Store each event's ID and skip any you have already processed, so a repeated payment event does not ship an order twice.
  • Wrong status codes: returning 200 for a request you could not process tells the provider all is well, and it will not retry. Return an error status when you genuinely failed.

Common questions

How can I see what a webhook sends?

Point the service at a webhook tester URL, trigger the event, and inspect the recorded method, headers and body before writing your handler.

Why is my webhook signature verification failing?

Usually because the HMAC is computed over parsed JSON rather than the raw request body, or with the wrong secret, such as a test secret against live events.

How do I stop processing the same webhook twice?

Save each event's unique ID when you process it and ignore any event whose ID you have already seen.

Tools from this guide

More guides