Replay failed webhook deliveries

When your endpoint was down or buggy, events queue up in webhook_deliveries with status: "failed". Replay them once you've fixed the issue.

Via the dashboard

Dashboard → Webhooks → your endpoint → Deliveries — each failed delivery has a Replay button. Replay sends a fresh attempt with the original payload and a new X-Sendoka-Delivery-Id.

Via API — one delivery

curl -X POST https://www.sendoka.com/api/v1/webhooks/whk_01HN.../deliveries/whd_01HN.../replay \
  -H "Authorization: Bearer $SENDOKA_API_KEY"

Via API — bulk replay

curl -X POST https://www.sendoka.com/api/v1/webhooks/whk_01HN.../replay-failed \
  -H "Authorization: Bearer $SENDOKA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"since": "2026-04-20T00:00:00Z"}'

since is optional — omit it to replay every failed delivery the endpoint still has. Deliveries are not kept forever, so that is a bounded window (see How far back you can replay).

Response:

{ "queued": 47, "endpoint_id": "whk_01HN..." }

Each queued delivery fires asynchronously. Watch the deliveries list — they flip to delivered or failed.

One call re-queues at most 100 deliveries (oldest first). If more are failed, call again once the first batch drains.

How far back you can replay

Delivery rows are deleted by a nightly sweep, counted from when the event fired:

  • Failed (and still-pending) deliveries are kept for 90 days, so an outage can be replayed for up to 90 days after it. Older ones are gone and cannot be replayed; their events are not re-sent.
  • Delivered deliveries are kept for 30 days, then disappear from the deliveries list too.

If you need a longer history, keep your own copy of the events you receive.

Rate limit

10/min per endpoint for manual replays (REPLAY_RATE_LIMITED on the 11th), shared by the single and the bulk replay. Prevents accidental floods after a long outage. Live and test keys each have their own 10: a test key replaying test-mode deliveries never uses up the budget a live key needs to recover the same endpoint's live backlog.

If you need to replay thousands, space the calls or contact support to raise the cap.

Test-fire synthetic events

Send a fake event to check that your endpoint is wired up correctly:

curl -X POST https://www.sendoka.com/api/v1/webhooks/whk_01HN.../test-fire \
  -H "Authorization: Bearer $SENDOKA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"event": "message.delivered"}'

Also available from the dashboard (Webhooks → your endpoint → Send test event) or the CLI:

sendoka events trigger --endpoint whk_01HN... --event message.delivered

Payload uses fixtures with data.test: true and "environment": "test" (so X-Sendoka-Environment: test) so your handler can tell it apart from a real event; a data object in the request is merged over the fixture but cannot change either marker or the synthetic message_id. Signed with your real secret so your verification path runs end-to-end. Rate limited at 20/min per endpoint (TEST_FIRE_RATE_LIMITED), shared across API, dashboard, and CLI.

Gotchas

  • Test keys replay test deliveries only. With a sok_test_* key, both replay routes (like the delivery list) reach only deliveries whose payload says "environment": "test"; a live delivery is a 404, and a bulk replay leaves live failures for a live key. Replay live events with a live key.
  • Replays get a new delivery_id. Your dedup key still works, but the new id won't match any previous seen: entry — by design.
  • Payload is unchanged. Replay does not re-fetch the underlying message state. If the bounce was later resolved, the replay still shows the original bounce payload.
  • Replays count against your 429 REPLAY_RATE_LIMITED cap, not your regular burst limit.