Webhook delivery log + replay (admin)

Last updated: April 2026

Every inbound webhook from Stripe, Stripe Connect, Clerk, Shopify, and Meta lands in a global delivery log before it touches your merchant data. Platform admins can browse the log, drill into the raw payload, and replay any failed delivery from /admin/webhooks.

What gets logged

  • Source (stripe, stripe_connect, clerk, shopify, meta)
  • Event type (checkout.session.completed, products/update, etc.)
  • External event id (used for dedupe and replay correlation)
  • Resolved merchant id when known (Clerk org webhooks may not have one yet)
  • Whether the signature verified (HMAC for Stripe/Shopify, svix for Clerk)
  • Raw request body (JSON) and a curated set of headers
  • Final HTTP status, processing duration, and error message on failure
  • Replay count and last-replayed-at timestamp

Status badges

  • received (blue) — landed but not yet finished processing
  • processed (green) — handler ran to completion with a 2xx response
  • failed (red) — signature was bad, an exception was thrown, or the handler returned 4xx/5xx
  • replayed (amber) — a manual replay completed successfully

Replay rules

Replay rebuilds the original Request from the stored payload + headers and pipes it back through the same route handler. The handler always re-verifies the signature, so:

  • A delivery with signature_valid=0 cannot be replayed — the original signature would re-fail. The Replay button is hidden for these.
  • Replay is destructive — handlers re-run side effects (DB writes, email sends, store-credit grants). Most are idempotent (we dedupe by event id), but verify before replaying twice in quick succession.
  • Replay does NOT re-deliver the webhook to its original sender — it only re-invokes our internal handler.

Common failure causes

  • Bad signature — webhook secret mismatch (rotated key, wrong env), stale clock, or genuinely malformed delivery
  • Unknown merchant — Clerk organization.created webhook fired before the org_subscriptions row existed; replay after fixing
  • D1 timeout — downstream merchant DB unavailable for >5s; replay once D1 is healthy again
  • Schema mismatch — handler tried to write a column that does not exist; deploy the migration first, then replay
  • Stripe Connect missing account — the connected account was deleted before the event arrived; cannot recover, mark resolved

Privacy + retention

WARNING

Stored payloads include PII — Stripe customer email, Shopify customer name + address, Clerk identity claims. Treat the log as restricted data. Only platform_admin users can read it.

There is no auto-purge yet — the log grows unbounded. We will add a scheduled-task purge once row count crosses ~1M. If you need an emergency wipe of old rows, run a manual DELETE against DB_GLOBAL.webhook_deliveries WHERE received_at < ? in the Cloudflare D1 console.

NOTE

Payload-redaction in the detail viewer (mask email/phone/etc.) is a v2 enhancement. For now, treat the modal as you would the Stripe Dashboard.

Was this article helpful?