DEV Community

Michael Lip
Michael Lip

Posted on • Originally published at zovo.one

Testing Webhooks Without Deploying Anything

You are integrating with a payment processor. When a payment completes, they send a POST request to your webhook URL with the transaction details. To test this, you need a publicly accessible URL that your local machine can receive requests on. Your localhost is not publicly accessible. Deploying to staging just to test a webhook is overkill.

This is the fundamental problem of webhook development: the sender (a third-party service) needs to reach the receiver (your code), but during development, your code is on localhost.

Solutions in ascending complexity

Webhook testing services. Services like webhook.site or RequestBin generate a temporary public URL. Any request sent to that URL is captured and displayed. You can inspect headers, body, and metadata. This is the fastest way to verify that a webhook is being sent and to see its payload structure.

Tunnel services. ngrok, Cloudflare Tunnel, or localtunnel expose your localhost on a public URL. The webhook hits the public URL, which tunnels to your local machine. This lets you test your actual webhook handler code against real payloads.

ngrok http 3000
# Gives you: https://abc123.ngrok.io -> http://localhost:3000
Enter fullscreen mode Exit fullscreen mode

Local replay. Capture webhook payloads (using a testing service) and replay them locally using curl or a script. This does not require a public URL and works offline.

curl -X POST http://localhost:3000/webhook \
  -H "Content-Type: application/json" \
  -d '{"event":"payment.completed","amount":4999}'
Enter fullscreen mode Exit fullscreen mode

What to inspect in a webhook

HTTP method. Most webhooks use POST, but some use PUT or PATCH.

Headers. Common important headers:

  • Content-Type: Usually application/json or application/x-www-form-urlencoded
  • X-Webhook-Signature or X-Hub-Signature: HMAC signature for payload verification
  • X-Request-ID: Unique identifier for idempotency
  • User-Agent: Identifies the sending service

Body. The payload structure. Document it. You need to know:

  • What events are sent
  • What fields are included for each event type
  • Whether fields are always present or conditional
  • Data types (is the amount in cents or dollars? is the timestamp Unix or ISO 8601?)

Signature verification. Most webhook providers sign the payload with a shared secret. You MUST verify this signature in production to prevent forged webhook calls.

const crypto = require('crypto');

function verifySignature(payload, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}
Enter fullscreen mode Exit fullscreen mode

Common webhook pitfalls

Not returning 200 quickly. Most webhook senders expect a 200 response within 5-30 seconds. If your handler takes longer (because it processes the webhook synchronously), the sender will timeout and retry. Process the webhook asynchronously: receive, store, return 200, then process.

Not handling retries. Webhook senders retry on failure (non-2xx response or timeout). Your handler must be idempotent -- processing the same webhook twice should not create duplicate records. Use the request ID or event ID to deduplicate.

Not handling out-of-order delivery. Webhooks may arrive out of order, especially during retries. A "payment.refunded" webhook might arrive before "payment.completed." Your handler must handle this gracefully.

Not validating payloads. Trusting webhook data without validation is a security risk. Verify the signature. Validate the payload structure. Check that referenced resources exist in your system.

The tool

I built a webhook tester at zovo.one/free-tools/webhook-tester that generates a unique endpoint URL, captures all incoming requests, and displays them with full headers, body, and metadata. It supports real-time updates (requests appear instantly as they arrive), request filtering, and payload formatting. Set it as your webhook URL during development and see exactly what the provider is sending.

I'm Michael Lip. I build free developer tools at zovo.one. 500+ tools, all private, all free.

Top comments (0)