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.

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