Skip to main content
Webhook delivery is at-least-once. Any non-2xx response, network error, or timeout (30 seconds) triggers a retry. Awardee retries with exponential backoff for up to roughly 24 hours before marking the delivery failed.

Backoff schedule

AttemptDelay before this attempt
1(immediate)
2~30 seconds
3~2 minutes
4~10 minutes
5~30 minutes
6~2 hours
Each attempt is the same delivery — same X-Awardee-Delivery id, same payload, same signature (with a fresh X-Awardee-Timestamp and matching v1 signature). Use X-Awardee-Delivery to deduplicate. See Idempotency. After the last attempt, the delivery is marked failed in the dashboard and the retry loop stops. You can replay any failed delivery manually from the deliveries view, which generates a fresh attempt with a new timestamp.

What counts as success

A 2xx response inside 30 seconds. Anything else triggers retry:
  • HTTP 3xx, 4xx, 5xx — all retried equally. The endpoint is the source of truth, so we don’t follow redirects or treat 4xx as fatal.
  • TCP timeouts, TLS errors, DNS failures.
  • Responses that take longer than 30 seconds, even if they would have been 2xx.
Return 2xx from your handler immediately, then process the payload in a queue. Synchronous heavy work risks crossing the 30-second timeout and being retried — usually creating duplicate side effects in your downstream systems.

Ordering

Best-effort. Not guaranteed. Two updates to the same resource in quick succession may arrive in either order, and a retry of an earlier event can land after a later one. Always derive ordering from the timestamps inside the payload (updatedAt, scannedAt, createdAt) — never from the order of arrival. A common pattern: store the resource’s last-seen updatedAt per id, and skip events older than it.
const seen = await db.lastSeen(payload.objectId)
if (seen && new Date(payload.updatedAt) <= seen) return
await db.upsert(payload)

Failure visibility

The dashboard’s deliveries view shows every attempt for each event, with the HTTP status, response time, and (truncated to 4 KiB) response body. Common failure modes show up here within seconds:
  • 401 / 403 from your endpoint — usually a misconfigured signature check.
  • 5xx from your endpoint — your service is down; check your own logs.
  • Network timeout — your endpoint accepted the connection but didn’t respond inside 30 seconds.
  • TLS errors — expired certificate, missing intermediate, or hostname mismatch.

Replay

Any delivery (success or failed) can be replayed from the dashboard. A replay produces a brand-new delivery id — your idempotency check on X-Awardee-Delivery will treat it as fresh. Use replay to debug a handler change, not to recover from a duplicate bug (your handler should already be idempotent).

Endpoint going dark

If an endpoint returns non-2xx on 30 consecutive deliveries across a 24-hour window, it is automatically paused. Paused endpoints don’t receive new deliveries and don’t accrue retries. You’ll see a banner in the dashboard; resume from the same view once the endpoint is healthy. Pausing protects both sides: your service stops getting hammered while it’s down, and our delivery queues don’t fill up with doomed retries.