Why Webhook Deliveries Are Retried
A sender cannot always tell whether an event failed. The connection may time out after the receiver committed its work, a proxy may drop the response, or the receiver may return a temporary server error.
Retries improve eventual delivery, but they also guarantee that duplicates are normal. Build both sides around at-least-once delivery unless the provider explicitly offers a stronger contract.
Use the provider documentation for its real schedule and retention window. Your test plan should observe actual attempts rather than assuming every service retries the same response codes for the same duration.
Classify Responses Before Retrying
A 2xx response normally means the receiver accepted the event. Return it only after signature verification and a durable handoff, such as a committed inbox record or queue message.
A 429 response signals rate limiting and may include Retry-After. A 5xx response or timeout usually represents a transient failure. Most 4xx responses indicate a payload, authentication, or endpoint problem that rapid retries cannot repair.
Provider behavior varies, so treat this as a receiver design guide rather than a universal sender rule. Test the exact platform and document which responses cause another attempt.
200 or 202 accepted after durable handoff
400 or 422 invalid payload; inspect and repair
401 or 403 signature or credential failure
404 wrong or retired destination
429 rate limited; respect Retry-After if supported
500-504 transient receiver or dependency failure
timeout unknown outcome; duplicate delivery is possibleUse Exponential Backoff With Jitter
Immediate repeated attempts can turn a small outage into a larger one. Exponential backoff increases the delay after each failure, while jitter spreads requests so many senders do not retry at the same instant.
Bound the maximum delay, attempt count, and total delivery age. Store the next-attempt time durably so a sender restart does not forget pending deliveries or create a burst.
Expose attempt number, scheduled time, actual time, outcome, and response code in logs and monitoring. Operators need enough evidence to distinguish a receiver outage from a poisoned event that can never succeed.
- Increase delay after consecutive transient failures
- Add random jitter to avoid synchronized retries
- Cap delay and total delivery lifetime
- Keep a manual replay or dead-letter workflow
- Never retry forever without visibility
Make the Receiver Idempotent
Read the provider event or delivery ID and claim it in a database transaction before performing a side effect. A unique constraint is stronger than an in-memory check because concurrent duplicates can arrive at nearly the same time.
Store processing state and the relevant business result. If the same event arrives again, return success when the original result is complete or resume from a deliberate state when recovery is safe.
Choose the idempotency boundary carefully. Sending an email, charging a payment method, provisioning access, and updating inventory need their own durable keys or transactional outbox patterns when one database transaction cannot cover every system.
Run a Retry and Duplicate Test Matrix
Use a controlled endpoint with custom responses to test one failure at a time. Return 500, 429, a delayed response, and a success whose connection closes before the sender receives it.
Record when each attempt arrives and confirm that the event ID stays stable. Verify that processing happens once even when two attempts overlap or a success acknowledgement is lost.
Finish with recovery: return the endpoint to 2xx, confirm the pending event succeeds, and confirm monitoring stops alerting. A retry mechanism is incomplete if operators cannot see or safely replay exhausted deliveries.
- Transient 500 followed by success
- 429 with and without Retry-After support
- Receiver timeout after durable processing
- Two concurrent copies of the same event ID
- Maximum attempts followed by manual replay
Common questions
Frequently asked questions
- When should a webhook be retried?
- Retry network failures, timeouts, rate limits, and temporary server errors when the provider contract allows it. Permanent validation or authentication failures usually require configuration or code changes rather than repeated immediate delivery.
- What is a good webhook retry policy?
- Use a bounded number of attempts, exponential backoff with jitter, an overall delivery deadline, observable attempt history, and a dead-letter or manual replay path. Follow provider-specific Retry-After behavior when it is defined.
- How do I test webhook retries?
- Point a test sender at a controlled endpoint and return 429, 500, 502, 503, a delayed response, and a dropped connection in separate scenarios. Record attempt count, timing, event ID, response code, and whether duplicate processing occurred.
- Why must webhook handlers be idempotent?
- A sender may retry after the receiver completed work but its acknowledgement was lost. Idempotency makes the repeated event return safely without charging, sending, provisioning, or updating the same business action twice.
Primary sources
Official references
Try it with a real request
Turn the guide into a test.
Open a temporary endpoint, send your payload, and inspect exactly what arrived.
Open the tester