Guide
Webhook replay and ordering
At-least-once delivery is normal. This guide covers replay windows, duplicate suppression, and ordering—after raw-body verification succeeds.
Definitions: Replay protection · Idempotent processing · Webhook replay window
Conceptual operational diagram for this guide. Not live merchant data or metrics.
01
Illustrative verification walkthrough
The following is a structural example—not a live API contract. Replace header names, algorithms, and event ids with your environment documentation.
// 1. Read raw body bytes before JSON parsing
const rawBody = await readRawBody(request)
// 2. Verify signature (algorithm per your docs)
const expected = hmacSha256(WEBHOOK_SECRET, rawBody)
if (!timingSafeEqual(expected, headerSignature)) {
return Response.json({ error: "invalid_signature" }, { status: 401 })
}
// 3. Optional replay window on provider timestamp
if (Math.abs(now - eventTimestamp) > REPLAY_WINDOW_MS) {
return Response.json({ error: "stale_event" }, { status: 400 })
}
// 4. Idempotency: provider event id + type
const key = `${payload.event_id}:${payload.type}`
if (await store.alreadyApplied(key)) {
return Response.json({ ok: true, duplicate: true }, { status: 200 })
}
// 5. Parse JSON and apply lifecycle transition rules
const event = JSON.parse(rawBody)
await applyTransition(event)
await store.markApplied(key)
return Response.json({ ok: true }, { status: 200 })Never ship webhook secrets to clients. Validate exact signing rules against your merchant environment—not this illustration alone.
02
Out-of-order delivery
Transition tables should no-op or buffer when prerequisites are missing—Confirmed before Paid should not crash-loop provider retries. Document which events are terminal per payment_id.
03
Provider retry semantics
Return 2xx only when work is durable or safely skipped as duplicate. Timeouts cause retries; ambiguous 5xx responses can amplify load during incidents.
Read: Webhook verification guide, Replay and ordering controls.
- Retries are normal. Webhook delivery is at-least-once. Design consumers to tolerate duplicates and out-of-order arrivals where possible.
- Asynchronous by design. Payers, chains, and your servers operate on different clocks. UI and finance should not assume synchronous finality.
- Eventual consistency. API reads, webhooks, and portal views may briefly diverge during transitions. Reconciliation jobs exist to converge truth.
Walkthroughs: /operations