Integration · Webhooks

Webhook Replay Strategy — A Field Guide

Sooner or later a bug or an outage means you processed events wrong, or not at all. Replay is how you recover — but only if you kept the raw events and stayed idempotent.

John Kihiu12 min read

Things go wrong: a deploy ships a handler bug, a downstream dependency is down for an hour, a consumer silently drops events. When that happens you need to re-process the affected events — replay them. The catch is that replay is only possible if you designed for it beforehand, by keeping the raw events and making processing safe to repeat.

Persist the raw event first

The foundation of replay is a durable record of every event exactly as it arrived — full payload, headers, received timestamp — written before you process it. If you only store the results of processing, a processing bug means the correct input is gone and there is nothing to replay. Treat the inbound event log as the source of truth and processing as a derivation from it.

SQL · an inbound event log
CREATE TABLE webhook_events (
  event_id     TEXT PRIMARY KEY,
  source       TEXT NOT NULL,
  payload      JSONB NOT NULL,
  received_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
  status       TEXT NOT NULL DEFAULT 'received',  -- received | processed | failed
  processed_at TIMESTAMPTZ
);

Replay from the DLQ or the log

There are two replay sources for two situations. The dead-letter queue holds events that failed processing — replay those once you have fixed the bug or the dependency recovers. The full event log lets you replay a broader slice — every event for a resource, or everything in a time window — which is what you need when a handler was silently wrong and the events never even reached the DLQ.

Scope and control the replay

Replay leans entirely on idempotency

A replay re-delivers events you may have already partly processed, so without idempotent handling you will double-charge and duplicate on the way to fixing the original problem. Idempotency is what makes replay a safe recovery tool instead of a second incident. Never replay into a handler you are not certain is idempotent.

A replay capability is cheap insurance you build before you need it: store raw events durably, track their processing status, keep handlers idempotent, and give yourself a scoped, controllable way to re-run them. When the inevitable bad deploy or outage arrives, recovery is a targeted replay instead of a data-repair archaeology project.

John Kihiu
Acumatica ERP Developer · Laravel Engineer

Independent software engineer in Nairobi specialising in Acumatica customisations, Laravel backends, and tax fiscalisation integrations across East and Southern Africa.