API reference

Webhook retries and redelivery

The retry schedule, the delivery log, and how to debug a failing endpoint.

Retries and redelivery

The policy

A delivery that does not return 2xx within 8 seconds is retried:

Attempt Sent
1 immediately
2 1 minute later
3 5 minutes later
4 15 minutes later
5 1 hour later

After the 5th attempt the delivery is marked failed and stops. Nothing is lost — it stays in your delivery log and can be replayed by hand.

Total window from first attempt to giving up: about 1 hour 21 minutes. A deploy or a brief outage inside that window recovers on its own.

What counts as a failure

  • Any status outside 2xx — including 3xx. We do not follow redirects.
  • No response within 8 seconds
  • Connection refused, DNS failure, TLS failure
  • A TLS certificate that does not verify

Pausing an endpoint stops new deliveries. Any delivery already in flight is settled as failed with endpoint_inactive, counts against the success rate, and is not resumed when you unpause — replay those with Redeliver.

Reading the delivery log

Dashboard → Developers → Webhooks → open an endpoint.

Each delivery shows when it arrived, the event, an attempt meter, and the response. View opens the full record:

  • Payload — the exact JSON we sent, copyable
  • Request headers — including the signature we computed
  • Response — status, duration, and the receiver's own error body
  • Attempts — every attempt with its outcome and timing

That response body is usually the fastest route to a fix: your own error text, captured from the failing request. We keep it only for failures (a successful response has nothing to diagnose), only for text content types, and truncate it at 16 KB.

Redelivering

Redeliver on any failed or retrying delivery queues it again immediately with a fresh attempt budget — the full five attempts, not one leftover shot.

Attempts triggered this way are marked manual in the history, so you can tell your replay apart from the automatic schedule.

Use it after fixing a bug, after a deploy that repairs your handler, or after rotating a signing secret.

Because a manual replay restarts the budget, a delivery's total attempt count can exceed five. That is expected — do not treat five as a ceiling when reading the log.

Debugging a failing endpoint

Every delivery fails, no response recorded. We cannot reach you. Check that the URL is publicly resolvable over HTTPS with a valid certificate. Private addresses and self-signed certificates are refused.

Every delivery returns 4xx. Usually signature verification. Check you are hashing the raw body, not a re-serialised object — see Signature verification.

Deliveries time out under load. Your handler is doing work before responding. Store the event, return 200, process afterwards.

Sporadic failures. Look at the response bodies in the attempt history; a pattern usually appears immediately.

Everything worked, then stopped. Check whether the secret was rotated — the old secret stops working the moment the new one is issued.

Testing without waiting

Send test event on the endpoint page fires a synthetic message.received at your handler. It travels the ordinary path — same envelope, same signature, same retry policy — and carries "test": true inside data.

Developing locally, expose your machine with a tunnel and register the tunnel URL:

cloudflared tunnel --url http://127.0.0.1:9099

Retention

Delivery records, including payloads and captured response bodies, are kept for 30 days and then deleted. They hold your customers' message content and phone numbers, so they are a debugging aid with a deliberately short life, not an archive. Anything you need to keep, store on your side when the event arrives.

Was this guide useful? Report an issue