How to Debug Webhooks in Development

The Webhook Debugging Challenge

Debugging webhooks is harder than debugging regular API calls for several reasons. First, you do not control when webhooks arrive — they are triggered by external events, not by your code. Second, you cannot easily reproduce the exact request because it is generated by the provider with specific signatures and timestamps. Third, your development server is on localhost, which the provider cannot reach directly.

Traditional debugging tools like console.log and breakpoints still work for your handler code, but they do not help with the unique challenges of webhook debugging: "Is the webhook even being sent? What does the payload look like? Why is my signature verification failing? Did the request arrive at my server?"

The right approach combines external tooling (for receiving and inspecting webhooks) with traditional debugging (for handler logic). Tools like ReqPour address the external side, giving you visibility into what arrives before your code even processes it.

Step 1: Verify the Webhook Is Being Sent

Before debugging your handler, confirm that the provider is actually sending the webhook. Check the provider's webhook logs — most providers (Stripe, GitHub, Shopify) show delivery attempts and their results in their dashboard.

Use ReqPour to create a public endpoint and point the provider at it. Without any relay running, ReqPour captures the request in its dashboard. If the request appears in the dashboard, the provider is sending it correctly. If it does not appear, the issue is on the provider side — wrong URL, wrong event subscription, or the triggering event did not actually fire.

This eliminates a large class of debugging problems immediately. Many webhook "bugs" turn out to be configuration issues: the wrong URL, the wrong events selected, or the provider requiring URL verification that has not been completed.

Step 2: Inspect the Payload

Once you confirm the webhook is being sent, inspect the full payload in the ReqPour dashboard. Look at every component: the HTTP method (should be POST), the URL path, all headers (including provider-specific ones like signature and event type), and the complete body.

Common issues you can spot at this stage: the Content-Type does not match what your handler expects (form-encoded vs JSON), the event type is different from what you assumed, the body structure has nested objects where you expected flat fields, or a field you need is named differently than expected.

Copy the exact payload from the dashboard and use it to write test cases for your handler. This ensures your tests use realistic data, not simplified mocks that might miss edge cases in the real payload structure.

Step 3: Debug Your Handler

With a confirmed working webhook and a known payload, start the ReqPour relay to forward to your local server:

bash
npx reqpour relay --to http://localhost:3000/api/webhooks

Add logging at the entry of your handler to confirm the request arrives:

javascript
app.post('/api/webhooks', (req, res) => {
  console.log('Webhook received:', req.headers['content-type']);
  console.log('Body type:', typeof req.body);
  console.log('Body:', JSON.stringify(req.body, null, 2));
  // ... rest of handler
});

If your handler is not receiving the request, check: Is your server running? Is the port correct? Is the path correct? Is there middleware blocking the request (CSRF protection is a common culprit — webhook endpoints need to be excluded from CSRF checks)?

Use ReqPour's replay feature to re-send the exact same request while you iterate on your handler. This is much faster than triggering real events in the provider repeatedly.

Step 4: Debug Signature Verification

Signature verification failures are the most frustrating webhook debugging issue because the error is subtle — usually a mismatch between what you are hashing and what the provider hashed.

When verification fails, log these values: the raw body bytes your handler is hashing, the signature header value from the provider, and the hash your handler computed. Compare the ReqPour dashboard's view of the body with what your handler receives.

Common causes of signature mismatch: your framework parsed the body before your verification code (the re-serialized body has different bytes), you are using the wrong secret key, the algorithm is wrong (SHA1 vs SHA256), the encoding is wrong (hex vs base64), or the signed payload includes a timestamp that you are not including.

javascript
// Debug signature verification
const rawBody = req.rawBody.toString('utf8');
const expectedSig = req.headers['x-webhook-signature'];
const computedSig = crypto
  .createHmac('sha256', secret)
  .update(rawBody)
  .digest('hex');

console.log('Raw body length:', rawBody.length); console.log('Expected signature:', expectedSig); console.log('Computed signature:', computedSig); console.log('Match:', expectedSig === computedSig); ```

Compare these logged values with what you see in the ReqPour dashboard to identify the discrepancy.

Step 5: Test Edge Cases

Once your handler works for the happy path, use ReqPour's captured requests to test edge cases. Review past webhooks in the dashboard to find unusual payloads: events with missing optional fields, events with unexpected values, or events in unusual sequences.

Replay these edge case requests against your handler to verify it handles them gracefully. Common edge cases to test: empty string fields, null values where you expect objects, array fields with zero or one item, very large payloads, and events you did not subscribe to but received anyway (some providers send all events regardless of subscription filters).

Build a test suite from captured webhook payloads. Export the raw request data from ReqPour and use it as fixtures in your unit tests. This gives you realistic test data that covers the exact format and structure your handler will encounter in production.

Get started with ReqPour

Catch, inspect, and relay webhooks to localhost. Free to start, $3/mo for Pro.