Webhooks
Delivery guarantees
At-least-once and out-of-order delivery, and how a receiver handles both.
Webhooks are delivered at least once, and not in order. This page says why, and how a receiver handles both.
At least once
The same event can arrive more than once: for example, when your endpoint answered but the answer was lost, or a dispatch run stopped mid-request. Every retry of an event carries the same Vestiarion-Event-Id (and the same id in the body), so de-duplicate on it.
Not in order
Deliveries can arrive out of order: a retry of an older entry can land after a newer one, and several dispatch runs may be at work. Each ledger.appended event carries the entry's seq. Order by entry.seq, which ascends within a workspace, with gaps: the hash chain, not seq, proves continuity.
A receiver, step by step
- Read the raw body, and verify the signature. Answer
401or400to anything that fails, and stop. - If you have already stored this
Vestiarion-Event-Id, answer200and stop. - Store the event, keyed by its id, and for
ledger.appendedbyentry.seqtoo, so the ledger lands in order however the deliveries arrived. - Answer
2xxwithin 10 seconds. Do slow work after you answer, from your own queue, so a slow job does not turn into a timeout and a retry.
Reconciling with the ledger
Webhooks can leave gaps: a delivery fails for good after 7 attempts, an endpoint is disabled, or an entry was appended before the endpoint existed. Queueing a delivery also never blocks the ledger: if it fails, the entry is still appended, with no delivery queued for it.
The ledger itself has no gaps a cursor can miss. Now and then, resume GET /api/v1/ledger from your stored cursor and store any entry whose seq you do not have yet. Pagination explains the cursor.