Retries & reliability
Jeeves delivers events with at-least-once semantics. Your handler must be idempotent.
Automatic retries
If your endpoint does not return 2xx in time, Jeeves retries with exponential back-off:
| Attempt | Delay after previous failure |
|---|---|
| 1st (initial delivery) | Immediate |
| 2nd retry | 2 minutes |
| 3rd retry | 30 minutes |
After all retries are exhausted the delivery is marked exhausted. You can still trigger a manual retry.
What triggers a retry
- HTTP
5xx - HTTP
429— Jeeves honors theRetry-Afterheader if set - Network errors: connection refused, DNS failure, connection timeout
Retries are NOT triggered by: 2xx (success) or other 4xx (treated as permanent failures).
Concurrency limit
Jeeves caps each subscription at 5 concurrent in-flight requests. If you need to back off further, return 429 with a Retry-After header.
Manual retries
POST /v1/webhooks/events/{eventId}/retry- Re-delivers the event to every active subscription subscribed to its type
- One-shot — a failed manual retry does not trigger another automatic retry
- Returns
409if any active subscription still has an in-flight delivery for this event
Circuit breaker
After 50 consecutive failed deliveries, a subscription is automatically set to disabled and an alert email is sent to every address in developerEmail. Re-enable once your endpoint is healthy:
PATCH /v1/webhooks/subscriptions/{id}
{ "status": "active" }The consecutive-failure counter resets after the first successful delivery.
Idempotency
Use X-Webhook-Event-Id (or the envelope id) as a unique constraint in your database. A reliable pattern:
-- Pseudocode
INSERT INTO processed_events (event_id, processed_at)
VALUES (:event_id, NOW())
ON CONFLICT (event_id) DO NOTHING;
IF rows_affected == 0:
return -- already processed — skip
-- safe to apply business logic