Skip to main content

Delivery, Retries, and Failure Handling

When an event fires, we send it to your endpoint as an application/json POST. A delivery is considered successful when your endpoint responds with a 2xx status code within the timeout window. We allow up to 5 seconds to establish a connection and 15 seconds for a complete response. Any other outcome (a non-2xx status, a timeout, or a connection error) is treated as a failed delivery.

We recommend that your endpoint acknowledge receipt quickly with a 2xx and perform any heavy processing asynchronously, so a slow handler doesn't cause a timeout.

Delivery Rate and Rate Limiting

To avoid overwhelming your endpoint, deliveries to a single URL are paced: a short burst is sent right away, after which events to that URL are delivered at a steady rate. This applies per destination, so a large batch of events to one endpoint is smoothed out while deliveries to your other webhooks continue in parallel, unaffected.

We also honor the standard rate-limit responses if your endpoint sends them. When your endpoint replies with HTTP 429 (Too Many Requests), we treat it as a request to slow down rather than a failed delivery: we pause deliveries to that URL and retry afterward, waiting for the time you indicate.

  • Retry-After (a number of seconds, or an HTTP date) sets how long we wait before retrying.
  • If Retry-After is absent, we fall back to RateLimit-Reset / X-RateLimit-Reset.

Because a 429 is treated as a back-off and not a failure, it does not count toward the consecutive-failure thresholds described below, so being rate-limited will never cause your webhook to be paused or disabled.

Retries

A failed delivery is automatically retried up to two more times with a short, escalating delay: after 30 seconds, then after 2 minutes. This rides out brief, transient problems with no action needed on your part. Every delivery attempt for the same event carries the same event_sid, which you can use to de-duplicate events on your side.

Automatic Back-off and Disabling

If an endpoint fails consistently, we progressively back off instead of repeatedly delivering to a broken endpoint. As consecutive failed deliveries (including the automatic retries above) accumulate, the webhook is paused for an escalating period, and ultimately disabled:

Consecutive failed deliveriesAction
10Paused for 1 hour
20Paused for 6 hours
30Paused for 1 day
40Disabled. An alert is sent to your account's owners and administrators

While a webhook is paused or disabled, new events for it are not delivered. After each pause expires, delivery automatically resumes and the endpoint is tried again; if it has recovered, the webhook returns to normal.

A single successful delivery resets the failure count to zero at any point in this process. This means an endpoint only escalates while it is genuinely and continuously failing. A temporary outage will pause and recover on its own, rather than being disabled.

Re-activating a Disabled Webhook

When a webhook is disabled, a Webhook Disabled alert appears in the Portal. Once you have resolved the problem with your endpoint, re-activate the webhook from the Development ➡️ Webhooks section of the Portal. Re-activating clears the failure history and dismisses the alert, giving the webhook a clean start.