How to debug webhooks: see what Stripe, GitHub, and others actually send
Webhooks fail in a uniquely frustrating way. Your code never runs, nothing appears in your logs, and the only error message lives in someone else's dashboard. The root cause is almost always the same: your handler expects a payload shaped one way, and the sender ships something else.
Documentation is not the payload
Every webhook provider documents their payloads, and every experienced developer has learned not to trust that documentation completely. Fields get added, nesting changes between API versions, and the example in the docs was usually written by hand years ago. The signature header might be Stripe-Signature, X-Hub-Signature-256, or something custom. The content type might be JSON or form encoded.
The only reliable reference is a real request, captured from the real sender, on the real event you care about.
Capture first, code second
The workflow that saves hours:
- Create a temporary capture URL that accepts any request.
- Register it as the webhook destination in the provider's dashboard.
- Trigger the real event: make a test payment, push a commit, whatever fires the hook.
- Read the captured request: method, headers, and the exact body.
- Write your handler against that reality, then switch the webhook to your real endpoint.
What to look at in the capture
Three things matter most. First, the signature header, because you will need to verify it and providers name it differently. Second, the content type, because parsing JSON when the body is form encoded gives you nothing. Third, the event identifier field, because routing different events to different code paths is usually the first thing your handler does.
Common webhook bugs this catches
- Verifying the signature against the parsed body instead of the raw bytes. Most signature schemes sign the raw request body; if you parse and re-serialize JSON before verifying, verification fails.
- Assuming one event per request. Some providers batch events into arrays.
- Responding too slowly. Many providers time out after a few seconds and retry, so your handler sees duplicates.
- Missing retries. Senders retry failed deliveries, sometimes for days. Handlers must be idempotent.
Once you have seen one real payload, all of these become visible instead of theoretical. Capture first.