What happens when a webhook fails?
A failed webhook is not lost immediately. The sender records the failure and retries the same delivery on a growing backoff schedule, commonly around eight attempts spread over 24 hours. When those attempts run out, the event moves to a dead letter queue where it can be inspected and replayed, and an endpoint that keeps failing is eventually disabled.
What counts as a failure
Only a 2xx response counts as success. Everything else is a failure from the sender's point of view: 4xx, 5xx, and usually 3xx redirects too, since a sender that follows redirects would be making delivery depend on a hop it cannot verify. Failures also happen before any status code exists, when the connection is refused, DNS does not resolve, the TLS handshake breaks, or the response arrives too late.
Timeouts are the failure mode teams underestimate. Providers do not wait long: Stripe cuts a delivery off at 20 seconds, GitHub at 10, and Shopify at 5. A handler that finishes its work in 12 seconds and then returns 200 to GitHub has already been recorded as a failure, and the event will arrive again. That is why the same webhook timeout shows up in delivery logs as both a failure and a duplicate.
What the sender does next
The sender waits, then tries again, and the wait grows each time. Exponential backoff gives a receiver that is mid-deploy or mid-incident room to come back, and jitter keeps a backlog of failed deliveries from retrying in one synchronized wave. As a concrete example, Svix retries at roughly 5 seconds, 5 minutes, 30 minutes, 2 hours, 5 hours, and then twice at 10 hours, which is where the "eight attempts over about 24 hours" shape comes from. Our retry best practices walks through the schedule and the reasoning behind each interval.
Nothing about that shape is standardized, which is the practical problem. GitHub does not retry repository webhooks at all; a failed delivery stays failed until someone redelivers it by hand from the repository's webhook settings. Stripe retries for up to three days. If you are consuming webhooks from a provider, the retry window is the number that tells you how long your endpoint can be down before data starts disappearing, and it is worth looking up per provider rather than assuming.
When the retries run out
An exhausted delivery is parked rather than discarded, and it lands there along with the payload, the destination, and the history of every attempt and the response it got. That record is what turns a support ticket into a five-minute fix: you can see whether the receiver returned 401 on every attempt or timed out on all of them, and you can replay the message once the cause is fixed. Our lesson on dead letter queues covers how to size and drain one.
Sustained failure usually triggers one more thing. A webhook endpoint that has failed everything for days is disabled so it stops consuming delivery capacity, and its owner gets an email. This is failure isolation, the same idea as a circuit breaker: one customer's broken server should not slow deliveries to everyone else.
Failures that retrying will never fix
Retries only help with transient problems, and a sender that cannot tell the difference burns its whole schedule on deliveries that were never going to land. A receiver returning 400 because the payload does not match the schema it expects will return 400 on attempt eight as well. A signature check that fails because the webhook secret was rotated on one side only will keep failing until both sides agree. An endpoint whose service was decommissioned will refuse connections forever.
These are the cases that make delivery logs worth reading rather than alerting on. A rising failure rate concentrated on one endpoint and one status code is a configuration problem on the receiving side. A failure rate spread across every endpoint at once is usually yours.
What to do on the receiving side
Two habits absorb almost all of this. Return 2xx before doing the work: verify the signature, write the event to a queue or a table, respond, and process in a background worker. That removes timeouts as a failure mode entirely. Then deduplicate on the event ID, since retries mean the same event will eventually arrive twice, and idempotency is what makes the second copy harmless.
Building the sending half of this, the schedule, the dead letter queue, the delivery log, and the endpoint disabling, is more work than it looks like from the outside. Svix provides it as a service, and how reliable are webhooks covers what to ask a provider before you trust theirs.
Frequently asked questions
Does a failed webhook mean the event is lost?
Usually not. Most senders retry over a window measured in hours or days and park exhausted deliveries in a dead letter queue for replay. Loss requires your endpoint to be unreachable for the entire retry window, or a sender that does not retry at all.
How long do webhook retries last?
It varies by provider and is not standardized. A common design is about eight attempts across 24 hours. Stripe retries for up to three days, while GitHub does not retry repository webhooks automatically and expects manual redelivery.
Why did my webhook fail when my server returned 200?
Almost always a timeout: the handler finished the work but responded after the provider gave up waiting. GitHub allows 10 seconds and Shopify 5. Respond first, process asynchronously, and deduplicate on the event ID.
Should a receiver return 500 to force a retry?
Only for genuinely transient problems, such as a database being briefly unavailable. Returning 500 for a payload you will never be able to process wastes the sender retry budget and can get your endpoint disabled. Return 2xx and record the event for investigation instead.
Ready to send webhooks?
Svix handles signing, retries, rate limiting, and delivery observability for the webhooks you send to your users, so your team can stay focused on your product.
Start sending webhooks with Svix or read the build vs. buy analysis