Webhooks power modern integrations. Stripe sends one when a payment succeeds. GitHub sends one when code is pushed. Shopify sends one when an order is placed. Your application receives the HTTP request and acts on it.
The trouble starts with testing. The sender controls when and how the webhook fires. You cannot step through it in a debugger. It hits a public URL, so your local dev server never sees it. And when it breaks, the error is silent: the sender fires and forgets.
Webhook testing tools fix this. They give you visibility into the request, replay failed deliveries, and tunnel external webhooks to your local machine. The right tool saves hours of integration debugging.
Build and send test payloads with the API Request Builder before you wire up the real webhook source.
Webhook Inspection Tools
Webhook inspectors give you a temporary URL that captures and displays every request sent to it:
webhook.site: the most popular free option. Gives you a unique URL that captures all incoming requests. Shows headers, body, query parameters, and timing. Free tier retains 500 requests. No signup required.
RequestBin (Pipedream): similar to webhook.site with additional features like request transformation and forwarding. Integrates with Pipedream's workflow automation.
Hookdeck: designed specifically for webhook reliability. Provides inspection, retry logic, and delivery guarantees. The inspection dashboard shows request details, response codes, and delivery status.
Svix Play: open-source webhook inspection with a clean interface. Good for quick tests without creating accounts.
How to use an inspector: 1. Create a temporary URL on the inspection tool 2. Configure the webhook sender (Stripe, GitHub, etc.) to use this URL 3. Trigger the event (make a test payment, push code) 4. Inspect the captured request in the tool's dashboard 5. Verify the payload structure matches your expectations
Once you have captured a real webhook payload, format it with the JSON Formatter to understand the data structure before writing your handler code.
Local Tunnel Tools
To receive webhooks on your local development machine, you need a tunnel that exposes your localhost to the internet:
ngrok: the most established tunnel tool. Run ngrok http 3000 and get a public URL that forwards to your local port 3000. The free tier provides temporary URLs. Paid tiers offer custom domains and persistent URLs.
Cloudflare Tunnel: free, integrated with Cloudflare's network. More permanent than ngrok for development environments. Requires a Cloudflare account.
localtunnel: open-source alternative. lt --port 3000 gives you a temporary public URL. Simple but less reliable than ngrok.
Stripe CLI: if you are testing Stripe webhooks specifically, the Stripe CLI has a built-in stripe listen --forward-to localhost:3000/api/webhooks command that forwards Stripe events without needing a general tunnel.
GitHub CLI: similarly, gh webhook forward can forward GitHub webhooks to localhost.
Best practice workflow:
1. Start your local development server
2. Start the tunnel: ngrok http 3000
3. Copy the public URL (e.g., https://abc123.ngrok.io)
4. Configure the webhook sender to use this URL
5. Trigger events and debug with your local debugger
The tunnel URL changes every time you restart ngrok (on the free tier). Remember to update the webhook configuration in the sender's dashboard. Or use the Stripe/GitHub CLIs which handle this automatically.
Validate your webhook endpoint URLs before configuring them in external services. The URL Validator checks that URLs are properly formatted and reachable.

Webhook Security and Verification
Webhooks are HTTP requests from external services to your server. Without verification, anyone who knows your webhook URL can send fake requests.
Signature verification: most webhook senders (Stripe, GitHub, Shopify) include a cryptographic signature in the request headers. Your code should verify this signature before processing the request.
Stripe example:
`javascript
const sig = request.headers['stripe-signature'];
try {
const event = stripe.webhooks.constructEvent(
request.body,
sig,
process.env.STRIPE_WEBHOOK_SECRET
);
// Process the verified event
} catch (err) {
return response.status(400).send('Signature verification failed');
}
`
Replay protection: verify that the timestamp in the webhook is recent (within 5 minutes). This prevents replay attacks where an attacker resends a captured webhook.
HTTPS only: always use HTTPS for your webhook endpoint. HTTP endpoints expose the payload and signature to anyone on the network.
IP allowlisting: some services publish the IP ranges their webhooks originate from. Restrict your webhook endpoint to only accept requests from these IPs.
Idempotency: design your handler to be idempotent (processing the same event twice produces the same result). Webhook senders retry on failure, so your endpoint will receive duplicates. Use the event ID to detect and skip duplicates.
Debugging Common Webhook Issues
Webhook not arriving: check the sender's dashboard for delivery logs. Most services show delivery status, response codes, and retry history. Common causes: wrong URL, firewall blocking the request, DNS not resolving, or the endpoint returning an error that triggers the sender to stop retrying.
Webhook arriving but not processing: add logging at the start of your handler to verify the request reaches your code. Check that your framework is not consuming the raw body before your verification code can read it (common issue with Express.js body parsers and Stripe signature verification).
Timeout errors: webhook senders expect a response within 5 to 30 seconds. If your handler does heavy processing (database writes, external API calls, email sending), return a 200 immediately and process the event asynchronously. Use a message queue for long-running tasks.
Payload format changes: webhook payloads evolve over time. A field that was a string might become an object, or a new required field might be added. Pin to a specific API version when the sender supports it (Stripe API versions, for example) and update deliberately.
Missing events: some senders have event types that must be explicitly enabled. Check the webhook configuration to ensure all event types your handler expects are selected.
Retry storms: if your handler returns a 500 error, the sender retries. If the retry also fails, it retries again. This can create a storm of requests that overwhelms your server. Implement circuit breakers or return 200 even for errors you will handle manually.

FAQ
How do I test webhooks in CI/CD?
Use mock payloads stored as JSON fixtures. Your CI pipeline sends these fixtures to your webhook handler endpoint using curl or a test HTTP client. This tests your handler logic without depending on the external service. For integration tests, use the service's test mode (Stripe test mode, GitHub test events) to generate real webhook payloads.
Should I use a webhook management service?
For production applications receiving webhooks from multiple sources, a webhook management service (Hookdeck, Svix, Convoy) provides retry logic, delivery guarantees, event logging, and security verification out of the box. For a single webhook integration, these services add unnecessary complexity.
How do I handle webhook events out of order?
Webhook delivery is not guaranteed to be in order. A "subscription.updated" event might arrive before "subscription.created". Design your handler to be order-independent: use the event timestamp to determine the current state, or fetch the current state from the sender's API instead of relying solely on the webhook payload.
What response code should my webhook endpoint return?
Return 200 or 204 for success. Return 4xx for permanent failures (bad payload, signature mismatch). Never return 5xx unless something is genuinely broken on your server. Most senders interpret 5xx as a temporary failure and retry, which can create retry storms if the error persists.
Markdown Table Generator: Build Clean Tables Without the Pain
Markdown tables are simple until the pipes and dashes stop lining up. Learn the syntax, alignment tricks, and a free tool that formats tables for you.
CSV to JSON: Convert Spreadsheet Data for APIs and Code
Turn a CSV export into clean JSON for APIs, imports, and scripts. Learn how the conversion works, common pitfalls with types and quotes, and a free tool.
JSON Guide: Format, Validate, and Convert JSON Files
JSON guide for developers: syntax rules, common parse errors, formatting and schema validation, plus how to convert between JSON and CSV files.
