Replay & recovery
Replay is what you reach for when something went wrong and you need to re-attempt a window of deliveries. A customer's webhook was down for an hour, a destination credential rotated and a batch failed, an event-type filter was wrong and a day's worth of events skipped a destination, replays handle all of these. The replay engine respects idempotency, runs at a controlled rate, and exposes per-event progress.
When to replay
The most common triggers:
- A destination was unhealthy for a window (5xx outages, expired credentials, paused queues) and you want to resend the deliveries that failed during that window.
- A new destination was added and you want it to receive the last N hours of events.
- A routing-rule fix went out and you want events from before the fix to flow to the now-correctly-routed destination.
- A transform bug was fixed and you want previously-malformed deliveries re-sent with the corrected shape.
Replays are not a substitute for proper retry, Pushrail already retries transient failures automatically. Reach for replay when retries have been exhausted and you need a manual recovery action.
Scoping a replay
Open Observability → Replays → New. A replay scope is a combination of: time window (required), event type filter (optional), destination filter (optional), and outcome filter (failures only, or successes too).
Time: 2026-05-15T14:00Z to 2026-05-15T15:30Z
Destination: dest_webhook_acme
Outcome: failures only
The replay first counts the matching deliveries and shows you the total before you confirm. Confirm to launch; the replay runs on a separate worker pool so it does not contend with live delivery traffic.
Idempotency on replay
Replays re-send the original event payload to the original destination. The event id is preserved, so if your receiver dedups on event id (the recommended pattern, see Webhooks), a replay is safe, duplicates are dropped at the destination.
For receivers that don't dedup, replays will produce duplicate work. Decide before launching: is "deliver some events twice" worse than "miss some events"? In most operational situations, the answer is "miss is worse", but for things like billing webhooks, you may want to coordinate with the receiver's owner before replaying.
Replays are not deduplicated by Pushrail at the delivery layer, they intentionally re-create deliveries. Idempotency lives at ingest (collapsing duplicate events at intake time) and at the receiver (collapsing duplicate work).
Billing
Replayed deliveries count against your delivery quota. A 10,000-delivery replay debits 10,000 from the same monthly bucket as live traffic. The replay summary shows the projected cost before you confirm.
This is intentional, replays are real deliveries, and pricing them the same as live deliveries makes replay capacity planning easy ("I have X deliveries left this month, the replay is Y, so I have X-Y left for live traffic").
Limits
Replay scopes are capped at 1,000,000 deliveries per replay job by default. For larger backfills, slice the time window into smaller pieces and run them sequentially, this gives you operational control if something goes wrong partway through.
The replay-job retention window matches your tier's delivery-log retention. A replay launched against deliveries that have aged out of retention will fail to enumerate the source deliveries.
Step-by-step
- Identify the window. Open Observability → Deliveries, filter by destination and outcome, find the bracket of failures you want to replay.
- Open Replays → New. Pre-fill the scope with the same filters from your delivery search.
- Review the projected count. The dashboard shows total deliveries that will be re-enqueued and the projected billing impact.
- Confirm. The replay starts immediately and progress streams in.
- Watch progress. The replay job page shows live counts: pending, succeeded, failed. You can pause or cancel mid-flight.
- Verify on the destination. Spot-check the receiver to confirm replayed events arrived as expected.
See the replay jobs API reference for the same flow programmatically.