Delivery and retries

What happens when your endpoint is slow, down, or briefly wrong.

ProposedNot yet callable

This documents a design under review. Endpoints, payloads and field names may change before release.

Guarantees

  • Delivery is at least once. A network timeout can produce a duplicate, so treat id as an idempotency key and ignore ids you have already applied.
  • Respond 2xx within 10 seconds. Queue the work and acknowledge immediately rather than processing inline.
  • Your response body is ignored. Only the status code is read.

The practical shape of a handler: verify, write the event id to a table with a unique constraint, return 200, then process asynchronously. The unique constraint is what makes a duplicate harmless.

Retry schedule

Any non-2xx or a timeout is retried with exponential backoff. Five attempts spread over roughly 8 hours:

AttemptDelay after previous
1Immediate
21 minute
35 minutes
430 minutes
52 hours
66 hours, then marked failed

A deploy that takes your handler down for ten minutes loses nothing — the retries cover it.

Ordering

Ordering is not guaranteed. A retried attendance.recorded can arrive after the attendance.updated that corrected it.

// Reject anything older than what you already hold.
if (existing && existing.updated_at >= event.created_at) return;

Compare created_at before applying a change and last-write-wins becomes correct rather than a race.

When an endpoint is disabled

After 24 hours of continuous failure the endpoint is disabled and the institution’s admins are emailed. Failed deliveries are retained for 30 days and can be replayed once you have fixed the handler.

POST /v1/webhook_endpoints/whe_8f21c0/replay

{ "from": "2026-07-19T00:00:00Z", "to": "2026-07-19T12:00:00Z" }