What it means
A webhook replay is the sender pushing an event to your endpoint again. The event is not regenerated: the same payload that was created the first time is sent again, byte for byte, with a fresh signature and a fresh attempt.
Replay exists because webhook delivery is at-least-once, not exactly-once, and because retry ladders are finite. A sender that gives up after 8 attempts is behaving correctly, but the event it abandoned is still an event you needed. Replay is the escape hatch: the receiver gets fixed, somebody presses the button, and the missing events arrive.
This page assumes you already know what a webhook is. It is about what happens after the first delivery does not stick.
The word means two opposite things
"Replay" in security writing means an attack: an adversary captures a valid signed request and sends it again, hoping it gets processed a second time. "Replay" in integration tooling means a feature: the sender re-delivers an event you missed. Both are the same HTTP request arriving twice. The difference is who sent it and whether you wanted it.
What resolves the tension is that the two are distinguishable by age, and only by age. A legitimate replay is signed at the moment it is sent, so its timestamp is current. A captured request has the timestamp it was born with. That is why a receiver rejects anything whose signed timestamp is more than 300 seconds from its own clock, and why the timestamp has to be inside the signed value rather than beside it. See HMAC signature for the construction.
Retry and replay are different operations
A retry is what the sender does on its own, automatically, against a published ladder. In the Pinlyx Data API, a delivery is attempted up to 8 times across 1 minute, 5 minutes, 30 minutes, 2 hours, 6 hours, 12 hours and 24 hours, and is then marked failed. It is never retried forever, because a queue that retries forever is a queue that eventually consists entirely of dead deliveries.
A replay is an operator action taken afterwards. It requeues an abandoned delivery for immediate sending with a fresh attempt budget, and the original attempt history is preserved rather than overwritten, so the incident stays auditable. Only failed and dropped deliveries can be replayed; anything else is either still in flight or already delivered.
The delivery state machine
Four states, and the distinction between two of them is the one that saves an afternoon of debugging. failed means the sender tried eight times and your receiver never returned a 2xx. dropped means the sender never tried at all, because the destination had already been disabled when the event's turn came. Those have different causes and different fixes: a failed delivery points at your handler, a dropped one points at your endpoint's health.
Endpoints are disabled automatically. Consecutive failures reset to zero on any 2xx, and an endpoint that reaches 20 consecutive failures is switched off so a dead receiver stops consuming delivery attempts for everyone else. Bringing it back means running the verification probe again, which sends one signed request and activates the endpoint on a 2xx. Only then can the dropped deliveries be replayed.
Reading an attempt log
The single most useful thing a delivery API can give you is the per-attempt record: status code, error text and duration for each try. The example further down is a real shape, and every field in it is diagnostic.
status_code: 502with a 214 ms duration means your gateway answered quickly and badly. Look at the proxy, not the handler.status_code: nullwith a 10,004 ms duration means nothing answered before the 10-second send timeout. Null is not a status code of zero: it means a timeout, a DNS failure or a TLS error, and no HTTP response was ever received.- A 200 that took 9,800 ms is a warning even though it succeeded. You are one slow query away from a timeout, and the sender will retry a delivery your handler actually processed.
That last case is exactly why receivers must be idempotent. Acknowledge as soon as the event is stored, then do the work asynchronously; a handler that finishes the work before answering is a handler that will be asked to do it twice.
Ordering: the part that bites in production
Replayed events arrive out of order relative to live ones. If you replay Monday's event on Wednesday, your receiver sees Wednesday's event first and Monday's second. A handler that writes last-received-wins will overwrite current state with stale state, and nothing in the delivery pipeline will flag it.
The fix is to order by the event's own timestamp, not by arrival. Every event payload carries a created_at and, for the daily audience events, a day field. Compare against what you already stored for that entity and ignore anything older. This is a three-line guard that turns replay from a hazard into a routine operation.
What replay cannot do
Replay re-sends events that exist. It is not a backfill. If a watch was created on the 3rd, there are no events for the 1st, and no button will conjure them. Detection also runs against an end-of-day rollup, so events are daily rather than intraday, and a day whose change could not be measured produces no event on any threshold. A missing measurement is not a change of zero, and reporting it as one would be inventing a fact.
Deleting a destination is also final: it cascades to every watch pointed at it and every event still queued for it. There is nothing left to replay afterwards.
Related concepts
- Webhook: the delivery mechanism itself, its security model and its payload anatomy.
- HMAC signature: why a replayed payload still verifies, and why the timestamp is inside the signature.
- Idempotency key: the receiver-side de-duplication that makes replay safe.
- Exponential backoff: the retry ladder that runs before anyone reaches for replay.
- AI agent: a common webhook consumer, and one that must not act twice on one event.
How Pinlyx handles it
The Data API's watch surface exposes the whole delivery lifecycle rather than hiding it. You can list every event queued for an account with its state, attempt count and last error; fetch one delivery to see the exact signed payload and the full attempt log; and requeue an abandoned delivery with a single POST once the receiver is fixed. Endpoint health is visible too: consecutive failures, last success, last failure and the reason a destination was disabled. Nothing about a missing webhook has to be guessed.