How to Log and Debug Webhooks (Before They Break in Production)
Webhooks fail silently. Log safe delivery metadata, preserve payloads in the right operational system, and alert before customers notice.
Webhooks are the most invisible failure point in modern apps. There's no UI watching them, the sender is outside your control, and when delivery breaks you usually find out from a customer: "my payment went through but my account didn't upgrade." Every provider retries differently - some for hours, some for minutes - so a transient bug can quietly drop events forever.
The fix is boring: treat every webhook attempt and terminal outcome as a structured operational event.
Log delivery facts, not sensitive payloads#
If you receive webhooks, preserve the raw signed request only in the operational system designed to secure and replay it. That may be an encrypted queue, restricted object store, or provider event archive. Do not copy raw bodies, headers, signing secrets, or authorization values into a general analytics collection.
In GraphJSON, log controlled metadata: provider, event type, opaque delivery ID, endpoint ID, attempt number, status family, latency, and terminal outcome. When a customer endpoint starts returning 500s at 3am, you want a queryable delivery record without broadening access to the customer payload.
Use structured JSON logging so every field is queryable later:
{
"event": "webhook_received",
"provider": "stripe",
"type": "invoice.payment_failed",
"delivery_id": "evt_1Qw2...",
"status": 200,
"processing_ms": 43,
"payload_stored": true
}
payload_stored means the restricted operational system retained the body; it does not mean the payload is present in GraphJSON.
Deduplicate by delivery id#
Providers retry, and "at least once" delivery means duplicates are normal, not exceptional. Record the provider's event or delivery id on every log line and make your handler idempotent against it. When you need to answer "did we process this event, or did it arrive twice?", the delivery id is the only reliable key.
Debugging: from alert to the operational record#
The workflow that works: get alerted on an elevated failure rate, group failures by type and status to find the pattern, then use the affected delivery IDs to retrieve authorized payloads from the restricted operational system. Replay only through the handler’s controlled replay process.
Alert on failure rate, not failures#
A single failed webhook is noise; a rising failure fraction is signal. Set an alert on the share of non-2xx deliveries over a window, per provider and per endpoint, so you catch a broken handler or a dying customer endpoint before the queue of lost events gets long.
The boring infrastructure#
GraphJSON's Stripe integration can stream provider events into a dedicated collection - the same source that can power a Stripe revenue dashboard. Review provider fields and retention before enabling a broad event stream. For your own delivery pipeline, follow the complete webhook operations recipe. If your handlers run on Vercel, the log drain integration can complement controlled delivery events with runtime diagnostics.

Written by JR
Founder and builder of GraphJSON.