Skip to main content
APXY traffic log showing an intercepted webhook POST request with payload details
Tutorial

Debugging Webhooks Locally with APXY

Webhooks are notoriously hard to debug locally. APXY lets you intercept incoming webhook payloads, inspect them in real time, replay them without retriggering the source, and mock the endpoint for automated testing.

APXY Team8 min read

Webhooks are one of the trickiest APIs to debug locally. The source—Stripe, GitHub, Slack, or any third-party service—pushes events to your server. That means:

  • You need a public URL (or a tunnel like ngrok) to receive them
  • Retrigger requires the external event to happen again
  • The payload only exists in memory until you log it

APXY simplifies this considerably. This tutorial covers how to capture incoming webhook payloads, inspect them, replay them without re-triggering the event, and write mock rules so your tests never need the real webhook at all.

Why webhooks are hard to debug

The standard debugging loop for webhooks looks like this:

  1. Trigger the event (complete a Stripe payment, push to GitHub, etc.)
  2. Hope the payload arrives
  3. Log the request body to a file or console
  4. Read the log and guess what went wrong
  5. Trigger the event again

This is slow. With APXY you collapse steps 1–5 into: look at the captured request.

Step 1: Install and start APXY

curl -fsSL https://apxy.dev/install.sh | bash
apxy start --port 8080

Step 2: Expose your local server

Webhooks from external services need a public URL. Use any tunnel service—ngrok is the most common:

ngrok http 3000

You will get a URL like https://abc123.ngrok.io. Use this as your webhook endpoint in the Stripe or GitHub dashboard.

Now route your local server through APXY so all incoming requests are captured. If your webhook server listens on port 3000, start it with the proxy:

HTTP_PROXY=http://localhost:8080 HTTPS_PROXY=http://localhost:8080 node server.js

Tip

For incoming webhooks specifically, you do not need the proxy to intercept the inbound POST—APXY captures the forwarded request as it reaches your server. The key is that your server processes requests while APXY watches.

Step 3: Trigger and capture

Send a test event from the webhook source. Stripe's dashboard has a "Send test event" button. GitHub's webhook settings have "Recent Deliveries" with a redeliver option.

Open APXY's Web UI at http://localhost:8081. You will see the incoming POST request in the traffic log with:

  • Full URL and method
  • All headers (including signature headers like Stripe-Signature or X-Hub-Signature-256)
  • Complete request body (JSON payload)
  • Your server's response

Click the request to expand the detail view and inspect the payload fields.

Step 4: Inspect the signature header

Webhook signature verification is the most common source of bugs. The service sends a signature header; your server recalculates it from the raw body and a secret; if they do not match, you reject the event.

APXY shows you the exact raw body bytes that went over the wire. If your server is rejecting signatures, compare:

  • The Stripe-Signature header value from APXY
  • The secret you configured in your app
  • Whether your server is reading req.body as a parsed object (loses the raw bytes) vs a raw Buffer (correct)

This is nearly impossible to debug without seeing the actual request. APXY makes it the first thing you see.

Step 5: Replay without re-triggering

APXY's Replay feature re-sends the exact captured request—same headers, same body, same signature timestamp. No need to trigger another Stripe payment or GitHub push.

Select the captured webhook request in the UI, click Replay, and your server receives it again. This is especially useful when:

  • The event is expensive to retrigger (a real payment, a production deploy)
  • You need to test your handler logic with the same payload multiple times
  • You want to confirm your fix actually worked

Note

Some webhook providers include a timestamp in the signature (Stripe does). If you replay a webhook too long after capture, the signature may fail validation. APXY lets you edit request headers before replaying, so you can update the timestamp and recalculate the signature if needed.

Step 6: Mock the webhook for automated tests

For CI and unit tests, you do not want to depend on real webhook delivery. Create a mock rule that simulates the incoming webhook so your tests work offline:

apxy rules add \
  --match "POST /webhooks/stripe" \
  --status 200 \
  --body '{"received":true}' \
  --header "Content-Type: application/json"

Then send a test payload from your test suite directly through APXY:

curl -x http://localhost:8080 \
  -X POST http://localhost:3000/webhooks/stripe \
  -H "Content-Type: application/json" \
  -H "Stripe-Signature: t=1234,v1=fakesig" \
  -d '{"type":"payment_intent.succeeded","data":{"object":{"id":"pi_test"}}}'

Your test hits your actual webhook handler, your handler processes the payload, and APXY records what happened. No Stripe account needed.

Common webhook debugging patterns

| Issue | What to check in APXY | |---|---| | 400 Bad Request | Request body shape -- compare to the API's event schema | | 401 Unauthorized | Signature header value and raw body bytes | | 500 Server Error | Your server's response body for the error message | | Event not processed | Whether the type field in the payload matches your handler's switch | | Duplicate events | Whether the id field is the same across replays |

What to do next

Once you are comfortable capturing and replaying webhooks, the natural extension is the share-pack workflow: export the captured webhook as a reproducible artifact and share it with your team. See the Stripe checkout 422 share pack for an example of this pattern applied to a real debugging scenario.

tutorialwebhooksdebuggingstripegithub

Debug your APIs with APXY

Capture, inspect, mock, and replay HTTP/HTTPS traffic. Free to install.

Install Free

Related articles