Retries and delivery
Timeouts, the retry schedule, what counts as success, ordering, and how to debug failing deliveries.
#What counts as success
Any 2xx response within 10 seconds. Anything else counts as a failure and is retried: a 3xx (redirects aren't followed), 4xx, 5xx, a timeout, a TLS error or a refused connection.
#Retry schedule
Each event gets up to three attempts per endpoint:
| Attempt | When |
|---|---|
| 1 | Immediately |
| 2 | About 5 seconds after attempt 1 |
| 3 | About 30 seconds after attempt 2 |
Every attempt is signed fresh, with a new webhook-timestamp, and keeps the same webhook-id. Once the third attempt fails, the delivery is marked failed and not tried again.
The retry window is short (well under a minute), so an endpoint that's down for a deploy can miss events. Make your integration self-healing: periodically reconcile against the API (for example, page through GET /conversations ordered by lastActivityAt), and treat webhooks as a fast path rather than the only source of truth. A longer retry window is on the roadmap.
Endpoints are never disabled automatically. To pause deliveries, set status: "disabled" with PATCH /webhooks/:id.
#Ordering and duplicates
- Events for different endpoints, and successive events for one endpoint, are sent concurrently. Arrival order isn't guaranteed; a
call.summary.completedcan arrive before itscall.completed. - The same event can arrive more than once (for example, your server processed it but the response timed out). Store processed
ids, for at least a day, and skip repeats.
#Debugging
| Tool | What it shows |
|---|---|
GET /webhooks/:id/deliveries | Every delivery and each attempt's statusCode or error and durationMs |
POST /webhooks/:id/test | One signed ping with the result inline. No retries |
{
"id": "d7e1…",
"webhookId": "3a8f…",
"eventId": "evt_4f9b2c0d8e1a4b7c9d2e3f4a5b6c7d8e",
"status": "success",
"attempts": [
{ "at": "2026-10-14T18:05:42.400Z", "statusCode": 503, "durationMs": 88 },
{ "at": "2026-10-14T18:05:47.493Z", "statusCode": 200, "durationMs": 121 }
],
"createdAt": "2026-10-14T18:05:47.620Z"
}#Common failures
| Symptom | Likely cause |
|---|---|
error: "The operation was aborted due to timeout" | Your handler takes over 10s. Return 200 first, then process |
statusCode: 401 or 403 | Your signature check is failing: raw body, wrong secret, or the secret was rotated |
statusCode: 404 or 405 | Wrong path, or the route doesn't accept POST |
statusCode: 301 or 308 | Redirects aren't followed. Register the final URL |
error mentioning a certificate | Invalid or self-signed TLS certificate |
| No deliveries at all | Endpoint disabled, or not subscribed to that type. Check what each type needs in Event types: for example contact.updated doesn't fire for CSV imports, and call.ringing is inbound-only |