Catching Invalid Webhook Integrations Early
A webhook is a request one service sends to another over HTTP, the protocol used to transfer data on the web, when…
When a provider sends a webhook, it makes an HTTP request to tell your service that something happened, such as a payment update or a changed account record. If your service does not acknowledge the request, the provider may try again. A provider stops retrying after a set number of attempts or a fixed period. It may allow manual replay of an old delivery for an even shorter time.
Treat each limit as a deadline for recovering the event. Store each delivery durably before acknowledging it, make repeated deliveries safe, and define how to recover events after provider retries or replay access expire. A retry policy describes what the provider will attempt; it does not guarantee that your service will receive every event.
Each provider sets its own limits, so do not assume one provider follows another’s policy. Plaid documents retries for up to 24 hours when a webhook request times out or receives an HTTP response outside the 2xx success range. It also sets a 10-second response deadline. Supabase Platform Webhooks document five attempts, ending about six minutes and 40 seconds after the initial delivery. GitHub does not automatically redeliver failed webhooks, though it allows manual redelivery of deliveries from the past three days. Each policy applies to the named product; providers do not share a retry standard. See the Plaid webhook policy, Supabase Platform Webhooks, and GitHub’s failed-delivery guidance.
Keep three limits distinct. The retry window is how long automatic attempts continue. Replay access is how long someone can request another delivery of an earlier event. History retention is how long the provider keeps delivery records available for inspection. A provider may offer one without the others. Plaid, for example, documents a 24-hour retry period and a separate event-list endpoint that retains records for seven days, with an exclusion for Transactions webhooks. That endpoint is not an unlimited replay guarantee.
Your service must recover before the provider’s retry window closes if it needs the provider to deliver the event again. If an outage lasts longer than the retry window, the provider may stop sending the event. If history or replay access expires before you act, the provider’s documented tools may no longer let you retrieve that delivery. Set alerts and recovery deadlines based on the actual provider and product you use.
After the provider uses all its attempts, its contract determines the event’s status and your recovery options. Supabase marks a delivery failed after its documented attempts and does not continue automatic retries. Its delivery history lasts 14 days; after that, an expired delivery is no longer listed or retryable. Shopify documents up to eight retries and says persistent failure can result in removal of the webhook subscription, which then needs to be recreated to receive further deliveries. Check the Shopify troubleshooting guidance for that behavior.
Some providers document other ways to recover deliveries. GitHub’s documented manual redelivery is limited to deliveries from the past three days. Plaid’s event-list endpoint provides a separate way to inspect recent webhook events, subject to its retention limit and product exclusions. The provider’s API for retrieving a resource may also show its current state, but that state does not necessarily reveal every intermediate change or reproduce the original event. Treat each recovery method according to what the provider documents.
The underlying business data may remain available even when the provider fails to deliver a notification. The integration needs to determine whether it missed a change and, where possible, recover the relevant state. If the provider does not document a way to reconstruct missed events, record that limitation and decide what operational reconciliation your service can perform.
Keep the synchronous webhook handler small. It should verify the request and save the event to durable storage or a reliable queue first. After the event is stored, it should return the success response within the provider’s deadline. A background worker can perform slower business logic afterward. Plaid recommends writing webhooks to a queue or reliable storage, and GitHub recommends using a queue when needed to meet its response deadline.
State clearly what a success response means. A 2xx response generally tells the provider that delivery succeeded; it does not show that downstream work finished. If your handler returns success before saving the event, it can crash before the event is stored. The provider then believes delivery succeeded, but your service has nothing to process.
Assume a provider can deliver the same event more than once, including through retries or manual redelivery. Store a stable event or delivery identifier and use it to make processing idempotent, meaning that handling the same event repeatedly produces no duplicate business effect. Also tolerate out-of-order events: a delayed retry can arrive after a newer notification. Where the provider supplies timestamps, sequence information, or a resource lookup API, use those to determine the current state rather than treating arrival order as event order.
Use a recovery method the provider documents, such as polling a resource API, listing retained events, or manually redelivering eligible deliveries. Persist any recovery cursor the provider requires. Plaid’s webhook-event documentation, for example, instructs clients to save the next_cursor value when paging through events. Monitor failed deliveries and subscription status so an operator has time to respond before the relevant history or replay window closes.
An API contract should describe what customers can observe about delivery, including what happens when the provider stops trying. If your service sends webhooks, document these terms for customers. If you consume a third-party webhook, capture the same terms in the integration’s operational documentation and link to the provider’s current documentation.
| Contract term | What to specify |
|---|---|
| Success condition | Which HTTP responses count as success and how quickly the receiver must respond. State whether acknowledgment means durable receipt or completed business processing. |
| Failure handling | Which timeouts, connection failures, and HTTP responses trigger another attempt, and which count as permanent failures. |
| Retry ceiling | Maximum attempt count, maximum elapsed retry period, or both. Clarify whether the initial delivery counts as an attempt. |
| Retry timing | The documented schedule or timing bounds, including any effect of Retry-After when applicable. Label approximate schedules as approximate. |
| Delivery semantics | Whether duplicates are possible and whether the provider guarantees ordering. State what the receiver must handle when delivery is at-least-once or out of order. |
| Exhaustion behavior | Whether retries stop, the event is marked failed, a subscription is disabled or removed, and how the customer learns that this happened. |
| Replay and history | How long manual replay and delivery history remain available, who can request replay, and whether redelivery keeps the original identifier. Document each retention period separately. |
| Recovery and visibility | The supported reconciliation route, its exclusions and authorization requirements, and where customers can inspect attempts, errors, and relevant identifiers. |
Do not promise more than the provider’s policy supports. Retry duration, replay access, and history retention are separate limits, and a recovery API may cover only certain event types or time periods. Recheck the provider’s documentation when its product or API version changes.
Give Vroni a GitHub issue, bug report, spec, or rough idea. It reads the repo, plans the change, writes code, runs checks, and works toward a review-ready pull request.
Take a look at vroni.com