Retries and failure

Any 2xx is success; everything else is retried, up to eight attempts over about 21 hours. Endpoints that keep failing are paused, not dropped.

Any 2xx is a success. Everything else is a failure and will be retried: a 4xx, a 5xx, a timeout, a DNS failure, a TLS failure. A 3xx is a failure too, because redirects aren’t followed.

The schedule

Attempt Sent Cumulative
1 immediately 0
2 after 30 s ~30 s
3 after 2 min ~2.5 min
4 after 10 min ~12 min
5 after 30 min ~42 min
6 after 2 h ~2.7 h
7 after 6 h ~8.7 h
8 after 12 h ~21 h
then the delivery is DEAD
  • Each interval carries full jitter, so deliveries that failed together don’t retry together.
  • A Retry-After header on your response is honoured, capped at that attempt’s interval.
  • Timeouts: five seconds to connect, ten seconds for your response. Design against ten.
  • On a failure, up to 2 KB of your response body is stored and returned by the delivery API.

Delivery states

State Meaning
PENDING Queued
IN_FLIGHT Being sent
SUCCEEDED You returned 2xx
FAILED Will be retried
DEAD Eight attempts exhausted; replayable
CANCELLED The endpoint was deleted before it could be sent

When an endpoint keeps failing

After 20 consecutive failures, an endpoint moves to AUTO_PAUSED and you’re notified. A 410 Gone counts double, because that’s your server saying the URL is dead.

Nothing is lost while an endpoint is paused: events still fan out and the backlog grows. POST /webhook/endpoint/{reference}/resume resets the failure counter and sends the backlog. Fix the receiver first.

Pausing on purpose is the right move before a deploy that touches the receiver: POST /webhook/endpoint/{reference}/pause, deploy, resume.