DEV Community

Adrian Orenes
Adrian Orenes

Posted on Fully Autonomous

Debug a webhook locally: capture once, fix, and replay

A webhook fails, you fix the handler, and now you need the same request again. Creating another checkout or pushing another commit gives you a new event. That makes it harder to tell whether your fix handles the original failure.

This walkthrough uses Hooklistener, which I work on, to forward requests to localhost and replay a captured delivery. Use a development or staging environment with test data.

1. Forward requests to your local handler

Start your application on port 3000, then install the CLI and sign in:

npm install -g hooklistener
hooklistener login
hooklistener tunnel --port 3000
Enter fullscreen mode Exit fullscreen mode

The tunnel prints a public HTTPS URL. If your handler is at /webhooks/stripe, append that path to the URL when configuring your provider:

https://YOUR-TUNNEL.hook.events/webhooks/stripe
    → http://localhost:3000/webhooks/stripe
Enter fullscreen mode Exit fullscreen mode

Use the actual URL printed by the CLI. Send a test event from your provider, then check both your application logs and the captured request in Hooklistener. Requests passing through the tunnel are available for inspection and replay.

If the provider already sends to a Hooklistener endpoint, you can forward that endpoint instead:

hooklistener listen --endpoint YOUR_ENDPOINT_ID --port 3000 --path-prefix /api/webhooks
Enter fullscreen mode Exit fullscreen mode

Choose the path prefix that matches your local routes. The listen reference explains the command options.

2. Inspect the delivery before changing code

Open the captured request and check its method, path, headers, and raw body. Compare those with what your handler expects. A wrong route and a signature failure need different fixes, even if both look like a failed webhook in the provider dashboard.

Keep the original event ID nearby. It helps you correlate the captured request, your logs, and any downstream job.

3. Replay the captured request

After fixing the handler:

  1. Open the original request in Hooklistener.
  2. Choose Replay.
  3. Set the destination to your tunnel URL, including the handler path.
  4. Send the request and inspect the recorded response.
  5. Repeat against the same captured request after each change.

This sends a stored delivery to your chosen destination. It does not ask the provider to create a new event. Capture must already be in the delivery path: Hooklistener cannot reconstruct a request it never received.

A successful HTTP response is only part of the check. Confirm that the expected development-side effect happened, such as a test order changing status or a job being queued.

Two reasons a replay can still fail

Signature verification. Some providers include a timestamp in the signed data. An older captured request can fail the receiver’s freshness check even when the payload is unchanged. Editing the body can also invalidate its signature. Keep signature verification enabled. Hooklistener supports re-signing Stripe, GitHub, and Slack replays through its MCP replay tool using a stored signing secret; stored secrets require a paid plan. Use the appropriate test-environment secret.

Deduplication. A replay retains the event ID. If your handler has already processed that ID, skipping it may be correct behavior. To test processing again, use isolated development state. Also test the duplicate path deliberately: processing one event twice should not create two orders or send two emails.

Turn the failure into a regression case

Once the fix works, save the captured request as a replay case. Rerun it against development or staging after future changes, checking both the response and the side effect. Keep a duplicate-delivery case alongside it.

The useful loop is small: capture a real test delivery, inspect it, change the handler, and replay the same input. You can debug the failure without repeatedly recreating the provider-side event.

References: CLI tunnel setup · Replay guide.

Top comments (0)