WebhooksDelivery and retries
Delivery and retries
When a delivery counts as delivered, how it is retried for about 24 hours, when a webhook is disabled without notice, and what to answer so it is not.
Answer
A delivery succeeds on any 2xx within 10 seconds. Otherwise it is retried up to 10 times, 11 attempts over about 24 hours with exponential backoff. A 403 or 410 disables the webhook at once; running out of retries disables it too, silently. Deliveries are not ordered, and deleting a webhook sends DELETE to its URL.
Success
A delivery is one POST of a chunk of events to the webhook's URL. It counts as delivered when
your endpoint answers any 2xx status within 10 seconds. Any other status, a timeout or a refused
connection counts as a failure. Redirects are followed and the last response's status counts, so
register the final URL.
Answer first, then process: put the events in a queue and return 200, so slow work never runs into the timeout. The webhook receiver recipe does this.
Retries
A failed delivery is retried up to 10 times, 11 attempts in all, with exponential backoff. Each
retry sends the same body, signed the same way; Webhook-Delivery-Retry says which attempt it
is.
Webhook-Delivery-Retry |
Sent after the previous attempt | Since the first attempt, about |
|---|---|---|
0 |
(first attempt) | 0 |
1 |
30 s | 30 s |
2 |
68 s | 1.6 min |
3 |
2.6 min | 4.2 min |
4 |
5.8 min | 10 min |
5 |
13.3 min | 23 min |
6 |
30.2 min | 54 min |
7 |
69 min | 2 h |
8 |
2.6 h | 4.6 h |
9 |
5.9 h | 10.6 h |
10 |
13.4 h | 24 h |
The times are the waits the platform schedules; the request time and queueing add to them.
When a webhook is disabled
| Your endpoint | What happens |
|---|---|
| Answers 403 or 410 | Disabled at once, with no retry. |
| Answers 404 on the last attempt | Disabled. A 404 on an earlier attempt is retried. |
| Fails every attempt | Disabled after the last one, unless the last failure was a timeout and the webhook delivered successfully, or was changed, in the 3 days before. |
Disabling deletes the webhook (a soft delete): it drops out of the list of webhooks (a list with
include-deleted=true still returns it, with is_deleted: true), its pending deliveries and
retries are dropped, and no one is notified. So:
- Never answer 403 or 410 by accident, for example on a signature that does not match: answer 401 (security and signatures).
- List your webhooks on a schedule, and create the webhook
again if it is gone. The events it missed meanwhile are not sent again: read the changes from
the API, filtered on
modified(querying) and page by page (pagination), as the two-way contact sync recipe does.
Order and chunks
- Deliveries are not ordered. Each chunk is retried on its own, so an older change can arrive
after a newer one. Keep the highest
entity.revisionper record. - One change to many records is sent in chunks of up to
chunk_sizeevents (100 by default). Withchunk_delay, chunk N, counted from 0, waits N ×chunk_delayseconds (filtering and options). - The record in a delivery is read when the delivery is built, after the change, not at the moment of the change. A retry hours later still carries the record as it was read then.
When you delete a webhook
Deleting a webhook through the API makes Coevera send DELETE to its URL, with a 10-second
timeout. The request has no body and no signature header. The answer is not checked, but accept
it with a 2xx so your logs stay clean. Your delete call waits for that request, so a slow
receiver makes the delete slow. A webhook disabled after failures gets no such DELETE.
Limits
- There is no delivery log, no replay and no test event in Coevera, and no way to re-send what a disabled webhook missed.
- No notification is sent when a webhook is disabled.
- The retry schedule and the 10-second timeout are not configurable.