Retry & idempotency

Allison retries failed deliveries up to six times over ~32 hours. Every retry reuses the same event id so deduping is a one-line check.

What counts as success

A delivery is considered successful when your receiver returns a 2xx status within 10 seconds. Anything else is a failure:

Retry schedule

Retries are scheduled at increasing intervals from the initial attempt:

AttemptDelay from previousApprox. elapsed
1immediate0
2+ 1 minute~1 min
3+ 15 minutes~16 min
4+ 1 hour~1h 16min
5+ 6 hours~7h 16min
6+ 24 hours~31h 16min

After the sixth failure, delivery is marked permanently failed. You can still see the attempt in the dashboard (Settings → Webhooks → Deliveries) or via GET /v1/webhook-subscriptions/{id}/deliveries.

Idempotency: dedup on event.id

Every event has a stable id (evt_<32 hex>). Retries of the same event reuse the same id. Your receiver should track which event ids it has already processed and acknowledge duplicates immediately with 200.

// Pseudo-code for a minimal idempotent receiver
async function handle(req, res) {
  const eventId = req.headers['x-allison-event-id'];

  if (await seenEventIds.has(eventId)) {
    return res.status(200).end();     // dedupe
  }

  // Verify signature (see /docs/webhooks/signing) BEFORE recording, so
  // a forged request can't poison the dedup set.
  if (!verify(req)) return res.status(401).end();

  const event = JSON.parse(req.body);
  await doWork(event);
  await seenEventIds.add(eventId, { ttlDays: 7 });
  return res.status(200).end();
}

The event id is also in the envelope body (event.id), so you can read it either way. Keeping dedup entries for 7 days covers the full 32h retry window with plenty of margin.

Auto-disable

We automatically disable a subscription when it's clearly broken, so dead endpoints don't accumulate noise. Two triggers:

Disabled subscriptions stay in the dashboard with an Auto-disabled badge and auto_disabled_reason field. Re-enabling via the dashboard or PUT /v1/webhook-subscriptions/{id} with { "enabled": true } clears the auto-disable state and resumes delivery on the next matching event.

Ordering

Events are delivered in the order they're generated for a given subscription, but we don't guarantee strict ordering under retry pressure. If you need ordered processing, sort on created_at in the envelope — it's assigned when the event is generated, not when it's delivered.

Next steps