Skip to content

Replay and Idempotency

Diego Gutierrez edited this page Sep 23, 2026 · 1 revision

Home · Previous: Getting Started · Next: API and Configuration

Reproduce 503 → 200 → deduplicated 200

This exercise uses a real HTTP receiver on your machine. Start from the repository root after installing the dependencies.

In a separate terminal, start the example:

python examples/receiver.py --port 9001 --fail-first 1

On Windows, substitute .\.venv\Scripts\python.exe for python if the virtual environment is not activated.

Stop the lab, configure a named destination, and restart it in its own terminal:

PowerShell

$env:WLAB_TARGETS = '{"local-api":"http://127.0.0.1:9001/webhook"}'
.\.venv\Scripts\python.exe -m webhook_lab

macOS / Linux (with the virtual environment activated)

export WLAB_TARGETS='{"local-api":"http://127.0.0.1:9001/webhook"}'
python -m webhook_lab

Unlock the workbench, capture one synthetic event, then replay the same event to local-api three times.

Attempt Destination response Receiver log Lab outcome
1 503 Simulated failure failed
2 200 Processed delivered
3 200 Deduplicated delivered

All three delivery attempts remain in the lab. The receiver's console distinguishes processing from deduplication. A delivered state only means an HTTP 2xx response was received.

What is preserved?

Replay sends the original body bytes and content type. It adds:

  • Idempotency-Key equal to the captured event ID.
  • X-Webhook-Lab-Event-Id equal to the same event ID.

Authorization, cookies and provider signature headers from the original request are not forwarded. A receiver that requires a provider signature needs a signing adapter, which is not implemented yet.

Where does deduplication happen?

The example receiver maintains an in-memory set of processed keys. Restarting it loses that set. The lab itself records all attempts and does not deduplicate captures.

Capturing an identical payload again creates another event ID and therefore a different replay key. Replaying one event repeatedly preserves its key. These are different scenarios.

For production receiver design, consider a durable idempotency record and an atomic boundary with the business operation. A key by itself does not guarantee exactly-once processing; timeouts can leave the sender uncertain about what the receiver did.

Failure and recovery boundaries

Replay is manual and runs within the API request lifecycle. There is no background queue or automatic backoff. An attempt left pending by a crash is marked interrupted at the next startup. Inspect the receiver before deciding to resend a possibly processed event.

Targets are fixed server configuration, not arbitrary URLs supplied in replay requests. Configure only services you own or are authorized to test. Redirects are blocked. Inside a container, 127.0.0.1 refers to that container, not your host.

How would you make the receiver durable? Add your approach or a crash scenario to the idempotency discussion.

Receiver source