Requests & replay
The request log is the raw truth of what hit your front door: every inbound request — including ones for unknown paths and paused endpoints — captured with its headers, body, source and outcome, and kept replayable for the retention window (30 days by default). Nothing here depends on your configuration having been right at the time: the log records what arrived, so you can always fix the config and run it again.
Two kinds of replay
Emithook has two replay verbs, and the difference matters:
| Replay a request | Replay an event | |
|---|---|---|
| Operation | POST /v1/requests/{id}/replay | POST /v1/events/{id}/replay |
| What re-runs | The whole pipeline from the top: resolve → verify → route → deliver | Delivery only — the event re-fans-out to its destinations |
| Use it when | The processing was wrong at the time: endpoint was missing or paused, secret was wrong (quarantined), no route matched | The delivery failed or the receiver lost it: your server was down, a bug ate the payload |
| In bulk | — | Replay a time range: a whole window, to the endpoint's current routes |
| Marking | Idempotent on the request id | Flagged webhook-replayed: true so receivers can distinguish re-deliveries |
Rule of thumb: fix-then-replay the request when the event never made it through the pipeline; replay the event when it went through fine and you just need it delivered again.
Inspect the log
# Everything the front door received for one endpoint, newest first
curl -s "https://api.emithook.com/v1/requests?endpoint=<endpoint-id>" \
-H "Authorization: Bearer $EMITHOOK_API_KEY"
# Only the ones that hit an unknown path (typo'd provider config, probes…)
curl -s "https://api.emithook.com/v1/requests?outcome=dropped_unknown" \
-H "Authorization: Bearer $EMITHOOK_API_KEY"
# One request in full — headers + body, claim-check resolved
curl -s "https://api.emithook.com/v1/requests/<request-id>" \
-H "Authorization: Bearer $EMITHOOK_API_KEY"Outcomes are accepted (console: stored), parked_inactive (held — endpoint paused), and dropped_unknown (rejected — no endpoint matched). Time-box any query with since / until.
Find one by id
Every inbound webhook gets one id when it arrives. The sender sees it in the webhook-id response header (and as request_id in the default JSON acknowledgement); your destination sees it as metadata.event_id in the delivery envelope and in the webhook-id delivery header. For an accepted request, the request id is the event id. Replaying a request creates a new event with a new id.
- Console: paste the id into Find an event by id on Delivery logs, or Find a request by id on Incoming requests, or press ⌘K (Ctrl-K) anywhere and paste it. Emithook opens the event or request directly, whichever exists (a dropped request has no event; an event sent through the API or received by email has no inbound request). You can paste a console link or a JSON line that contains the id; an
ep_…ordst_…id opens that endpoint or destination. - API:
GET /v1/events/{id}returns the event with its delivery attempts;GET /v1/requests/{id}returns the captured request with headers and body.
curl -s "https://api.emithook.com/v1/events/01M2XKRQ1Q2WY7HVVMGZWK3JM0" \
-H "Authorization: Bearer $EMITHOOK_API_KEY"The search is by exact id, within your organization, over the retention window (30 days by default).
In the console this is the Incoming requests screen (and the Recent requests card on each endpoint's Overview) — click any row to inspect source, headers and body, or replay it from the peek.
Replay a request
Re-enqueues the captured request onto the ingest buffer; the router re-processes it exactly as if it had just arrived — including verification and routing against your current config:
curl -s -X POST "https://api.emithook.com/v1/requests/<request-id>/replay" \
-H "Authorization: Bearer $EMITHOOK_API_KEY"
# → 202 Replay queuedTypical flows: create the missing endpoint → replay the dropped_unknown requests; fix the signing secret → replay what was quarantined; unpause and replay what was held.
Replay an event
Re-delivers a stored event to every destination it was sent to before, including destinations that already received it. A destination deleted since is skipped. The delivery keeps the event's webhook-id (HTTPS deliveries also carry webhook-replayed: true), so receivers that care can dedupe on the event id. To retry only the destinations where delivery failed, redrive the DLQ instead (POST /v1/dlq/redrive). An email that was already sent is the exception: it is not sent to its recipient again; only its failed sends retry.
curl -s -X POST "https://api.emithook.com/v1/events/<event-id>/replay" \
-H "Authorization: Bearer $EMITHOOK_API_KEY"
# → 202 Replay queued
# Find candidates: everything that failed in the last day
curl -s "https://api.emithook.com/v1/events?status=failed&since=$(date -u -v-1d +%Y-%m-%dT%H:%M:%SZ)" \
-H "Authorization: Bearer $EMITHOOK_API_KEY"Replay a time range
Re-delivers everything an endpoint received in a time range to the destinations its routes target now. Two common uses:
- You added a destination later, such as an SQS queue, and want it to receive the backlog.
- A destination lost events, for example the receiving server was down, or the consumer had a bug and dropped messages.
# 1. Preview: count what would be replayed, enqueue nothing
curl -s -X POST "https://api.emithook.com/v1/endpoints/<endpoint-id>/replay" \
-H "Authorization: Bearer $EMITHOOK_API_KEY" -H "Content-Type: application/json" \
-d '{ "since": "2026-09-18T00:00:00Z", "until": "2026-09-18T06:00:00Z", "dry_run": true }'
# → 200 { "replayed": 329954, "enqueued": 329954, "excluded": 3, "next_cursor": null, … }
# 2. Start it — one call, whatever the size. A worker replays the window.
curl -s -X POST "https://api.emithook.com/v1/endpoints/<endpoint-id>/replay" \
-H "Authorization: Bearer $EMITHOOK_API_KEY" -H "Content-Type: application/json" \
-d '{ "since": "2026-09-18T00:00:00Z", "until": "2026-09-18T06:00:00Z",
"destinations": ["<sqs-destination-id>"], "status": "failed" }'
# → 202 { "id": "rpj_…", "state": "queued", "total_estimate": 57, … }
# 3. Follow it (and stop it if you need to)
curl -s "https://api.emithook.com/v1/replays/rpj_…" -H "Authorization: Bearer $EMITHOOK_API_KEY"
# → 200 { "state": "running", "replayed": 41000, "total_estimate": 57000, "enqueued": 41000, … }
curl -s -X POST "https://api.emithook.com/v1/replays/rpj_…/stop" -H "Authorization: Bearer $EMITHOOK_API_KEY"In the console, open the endpoint and choose ⋯ → Replay a time range (it is also on the Route tab). You pick the range, which events and which destinations, then preview and start it. A replay of 10,000 events or more must be confirmed by typing its event count. The API, SDK and CLI have no such limit: a script is expected to know what it asked for.
The replay itself runs on the server. Replays in the sidebar lists every run with its progress, throughput and estimated time left, and a Stop; you can close the tab and come back to it.
How it behaves:
- Current routes decide where events go. Each event goes wherever the endpoint's routes and route filters would send it today.
destinationscan only narrow that set; it can't add a destination the endpoint doesn't route to. - Already-delivered events are sent again. That is the point of a replay. Each delivery keeps the event's
webhook-id(HTTPS deliveries also carrywebhook-replayed: true), so a receiver that dedupes on the id still can. - The stored event is what gets sent. That is the body your endpoint's transform produced when the event arrived, with its original verification verdict. Nothing is re-verified, so a Stripe or Slack event older than its provider's signature tolerance still replays. To re-run the whole pipeline instead, replay the request.
- Route filters see the same fields live traffic does: the full request path, and the inline body. A body over 128 KB (kept in object storage, not inline) is not visible to JSON-path filters, now or when it arrived.
- Two kinds of event are never replayed:
quarantinedones (failed signature verification) andparkedones (paused endpoint, or a failed fail-closed transform). Replaying them would deliver a request that failed verification, or a payload your transform never shaped. They are counted inexcluded. - A replay is a background job. Starting one is a single call whatever the size; the job carries its own progress (
replayedagainsttotal_estimate). An endpoint may have only one replay running at a time — a second is refused with409, because two would double-send every event in the overlap. A replay that stopped or failed can be resumed (POST /v1/replays/{id}/resume): it continues from where it stopped under the same run id, so what it already sent is not sent again. A preview of an endpoint whose routes carry no filter is exact and needs one call; a filtered endpoint's preview pages throughnext_cursor(the SDK'spreviewEndpointReplayAll()loops for you). - Order. Within a call, events are queued oldest first. Later calls carry older events, so a FIFO (strict-order) endpoint's strict order holds only for a range of up to 1,000 events. For a longer ordered replay, split it into consecutive ranges of up to 1,000 events each, oldest range first.
- Starting a replay isn't idempotent, but a repeat is refused while one is running, and a job that dies mid-window is resumed by another worker from where it stopped — never from the beginning.
- Stopping takes effect between pages. Everything already queued still delivers; stopping does not recall it.
- A paused endpoint is refused (
409). Resume it first: pausing means you chose to stop its flow. - Replay needs a
write-scoped key (reading a job's progress needs onlyread). In the console it needs the Admin or Developer role.
Dead-letter redrive
Events that exhausted every retry sit in the dead-letter queue — nothing is discarded. Once the receiving side is healthy again, redrive them in bulk:
curl -s -X POST "https://api.emithook.com/v1/dlq/redrive" \
-H "Authorization: Bearer $EMITHOOK_API_KEY"The console's dashboard surfaces DLQ backlogs under Needs attention, with redrive one click away.
Retention
Requests and events are replayable for the retention window — 30 days by default (deployment-configurable). The hourly archive job writes everything to object storage before the log prunes, so older events remain restorable from the archive even after they leave the live log.
Where to go next
- How requests are processed — the pipeline each replayed request re-enters.
- API reference — auth, pagination and conventions for the endpoints used above.