If your server returns a non-2xx status, times out, or is unreachable,
CallingBox retries the delivery with exponential backoff over roughly two
days. You can also replay any delivery manually from the dashboard or API.
Retry schedule
| Attempt | Delay after previous attempt |
|---|
| 1 | Immediate (at most ~2s after the triggering event). |
| 2 | +1 minute |
| 3 | +5 minutes |
| 4 | +30 minutes |
| 5 | +2 hours |
| 6 | +5 hours |
| 7 | +10 hours |
| 8 | +24 hours |
Each delay has ±20% jitter applied so failing endpoints don’t get
retried in lock-step.
After 8 attempts the delivery moves to status: "exhausted" and stops
retrying. It stays queryable from GET /v1/webhook_deliveries and can be
replayed manually.
What counts as a success
A delivery is considered successful when your server returns any 2xx
status. Everything else (3xx, 4xx, 5xx, or network error) is a retry.
Return 200 OK immediately and move work onto a background queue on your
side. Anything slower than 10 seconds times out and retries.
Idempotency
CallingBox sends the same CallingBox-Event-Id on every retry of a
given event, including manual replays. Use it as the primary key in an
idempotency table so repeated deliveries are safe.
const eventId = req.headers["callingbox-event-id"] as string;
if (await db.seen(eventId)) return res.sendStatus(200);
await process(event);
await db.markSeen(eventId);
res.sendStatus(200);
Auto-disable after repeated failures
If a single endpoint accrues 10 consecutive exhausted deliveries,
CallingBox flips the endpoint to status: "disabled" with
disabled_reason: "too_many_failures" to stop burning credits on a broken
target. The successful delivery counter resets on any 2xx response.
Re-enable from the dashboard or via:
curl -X PATCH https://api.callingbox.io/v1/webhook_endpoints/{id} \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"status": "enabled"}'
Re-enabling zeroes out the failure counter.
Manual replay
List recent deliveries:
curl "https://api.callingbox.io/v1/webhook_deliveries?status=exhausted&limit=20" \
-H "Authorization: Bearer YOUR_API_KEY"
Replay one:
curl -X POST https://api.callingbox.io/v1/webhook_deliveries/{id}/redeliver \
-H "Authorization: Bearer YOUR_API_KEY"
This resets the delivery to pending and enqueues it for the next worker
tick. Since the event id is preserved, your consumer’s idempotency check
will dedupe against the original attempt.
Inspecting failures
Each delivery row records:
attempt_count — total automatic attempts.
manual_retry_count — times you’ve clicked Resend.
last_response_status — HTTP status code of the most recent attempt.
last_error — the specific error (HTTP 502, connection reset by peer,
url resolved to a private or reserved address, etc).
See the webhook delivery object for
the full schema.