Webhook Testing Suite with Request Inspection and Replay

May 25, 2026 · 15 min read

Webhooks are the event-driven backbone of modern API integrations. When a payment completes in Stripe, a commit pushes to GitHub, or a message arrives in Slack, webhooks deliver real-time notifications to your application without polling. But testing webhooks is notoriously difficult: the payloads arrive asynchronously from external services, signature validation requires shared secrets and cryptographic verification, and debugging delivery failures requires inspecting headers and bodies that your application may have already discarded. This testing suite provides the tools you need to build, inspect, validate, and replay webhook requests entirely in your browser.

Build webhook payloads from templates for common providers (Stripe, GitHub, Slack, Shopify), generate HMAC signatures using your secret key, simulate delivery with configurable success and failure rates, and inspect a log of all simulated requests with full headers and bodies. The signature validator verifies HMAC-SHA256 signatures so you can test your endpoint's verification logic before connecting to a live provider. Every operation runs locally with no data transmitted to external servers.

Webhook Payload Builder

Select a webhook provider template or build a custom payload. Each template produces a realistic event payload matching the provider's actual format, including metadata fields like event IDs, timestamps, and API versions. Click Build Payload to generate the complete HTTP request with headers and body.

Event Configuration

Signature Generator and Validator

Generate HMAC-SHA256 signatures for webhook payloads, or validate an existing signature against a payload. This tests your signature verification logic without connecting to a live provider. Enter the payload body and signing secret to compute the signature, or enter all three to validate.

HMAC Signature Tools

Request Inspector

Simulate webhook deliveries and inspect each request in the log. Configure the delivery simulation with success and failure rates, then click Simulate Deliveries to generate a batch of webhook requests. Click any log entry to expand the full request details including headers, body, timing, and status.

Delivery Simulation

How Webhooks Work

A webhook is an HTTP POST request sent from a provider to your application when an event occurs. The lifecycle is: (1) you register a URL with the provider, (2) an event occurs in the provider's system, (3) the provider constructs a JSON payload describing the event, (4) the provider computes an HMAC signature using a shared secret, (5) the provider sends an HTTP POST to your URL with the payload and signature header, (6) your endpoint verifies the signature, processes the event, and returns a 2xx status.

If your endpoint returns a non-2xx status or times out, the provider marks the delivery as failed and queues it for retry. Most providers retry 3-5 times with exponential backoff over a period of hours to days. Some providers (Stripe, GitHub) offer a webhook dashboard where you can see delivery attempts, response codes, and response bodies for debugging. The provider eventually stops retrying after exhausting its retry budget and either sends a failure notification email or marks the endpoint as disabled.

Webhook Security Best Practices

Webhook endpoints are publicly accessible URLs that accept POST requests from the internet. Without verification, an attacker can send forged webhook payloads to your endpoint to trigger unauthorized actions like creating fake orders or granting fraudulent access. The defense is HMAC signature validation: the provider includes a signature header computed from the payload and a shared secret that only you and the provider know.

The verification process must use a constant-time comparison function (crypto.timingSafeEqual in Node.js, hmac.compare_digest in Python) to prevent timing attacks that could leak the expected signature byte by byte. Additionally, validate the timestamp header (included by Stripe, GitHub, and most providers) to reject requests older than 5 minutes, preventing replay attacks where an attacker captures a valid webhook and resends it later. Finally, use HTTPS for your webhook URL to encrypt the payload in transit and prevent man-in-the-middle inspection.

// Node.js webhook signature verification
const crypto = require('crypto');

function verifyWebhook(payload, signature, secret) {
  const timestamp = signature.split(',')[0].split('=')[1];
  const sig = signature.split(',')[1].split('=')[1];

  // Reject requests older than 5 minutes
  const age = Math.floor(Date.now() / 1000) - parseInt(timestamp);
  if (age > 300) throw new Error('Webhook too old');

  // Compute expected signature
  const signedPayload = timestamp + '.' + payload;
  const expected = crypto
    .createHmac('sha256', secret)
    .update(signedPayload)
    .digest('hex');

  // Timing-safe comparison
  if (!crypto.timingSafeEqual(
    Buffer.from(sig), Buffer.from(expected)
  )) {
    throw new Error('Invalid signature');
  }

  return JSON.parse(payload);
}

Reliable Webhook Consumption

The golden rule of webhook consumption is: acknowledge fast, process later. Your endpoint should validate the signature, enqueue the event for processing, and return 200 within 3 seconds. This decouples receipt from processing and prevents the provider's delivery timeout from interfering with your business logic. The processing happens asynchronously from the queue with proper error handling, retries, and dead letter queues for permanently failed events.

Implement idempotency using the event ID that every major provider includes in the payload. Store processed event IDs in a database with a unique constraint. Before processing, check the constraint. If the insert fails (duplicate), return success without reprocessing. This handles the two causes of duplicate deliveries: provider retries due to timeout (your endpoint processed the event but did not return 200 fast enough) and provider retries due to network errors (the 200 response was lost in transit). Both scenarios are common in production and will create duplicate side effects without idempotency.

Provider Retry Patterns

Provider Max Retries Backoff Strategy Total Window Signature Header
Stripe8Exponential~3 daysStripe-Signature
GitHub310-minute intervals~30 minX-Hub-Signature-256
Slack3Exponential~1 hourX-Slack-Signature
Shopify19Exponential~48 hoursX-Shopify-Hmac-Sha256
Twilio2Fixed 10 min~20 minX-Twilio-Signature

Local Development Testing

Testing webhooks locally requires exposing your development server to the internet. The recommended approach is a tunneling service: ngrok, Cloudflare Tunnels, or localtunnel create a public URL (e.g., https://abc123.ngrok.io) that forwards requests to localhost:3000. Register this URL as your webhook endpoint in the provider's dashboard. When the provider sends a webhook, it reaches the tunnel, forwards to your local server, and you can debug with breakpoints and console logs in your IDE.

For unit testing without external dependencies, use this tool to build realistic payloads, compute signatures, and copy the curl commands to replay against your local endpoint. Mock the signature verification in tests by either using a known secret and pre-computed signature, or by injecting a verification function that always returns true in the test environment. Integration tests should verify the full flow: receive webhook, verify signature, enqueue event, process from queue, and verify the side effect (database record created, email sent, etc.).

A robust testing strategy covers four scenarios: (1) valid payload with correct signature, (2) valid payload with incorrect signature (should reject), (3) valid payload with expired timestamp (should reject), and (4) duplicate event ID (should accept but not re-process). These four tests cover the critical verification and idempotency paths that protect your application in production.

Frequently Asked Questions

How do webhooks work and when should I use them?

Webhooks are HTTP POST callbacks sent to your endpoint when an event occurs. Instead of polling, the API pushes notifications to your URL. Use them for real-time event notifications, when polling would be wasteful, or when you need to trigger workflows from external system changes.

How do I verify that a webhook request is authentic?

Verify using HMAC signature validation. The provider computes an HMAC-SHA256 of the body using a shared secret and includes the hash in a header. Recompute the HMAC with the same secret and compare using a timing-safe function. Also validate the timestamp to prevent replay attacks.

What should a webhook endpoint return?

Return 200 OK within 3-5 seconds. Process asynchronously after returning. Most providers treat any 2xx as success and anything else as failure, triggering retries. Use a queue: receive, enqueue, return 200, then process with deduplication.

How do I handle webhook retry and deduplication?

Store the webhook event ID with a unique constraint. Before processing, check for duplicates. If the ID exists, return 200 without reprocessing. This handles both provider retries and network-level duplicate deliveries.

How do I test webhooks during local development?

Use ngrok, Cloudflare Tunnels, or localtunnel to expose your local server. Register the tunnel URL as the webhook endpoint. For unit testing, use known payloads with pre-computed signatures and test all four scenarios: valid, bad signature, expired timestamp, and duplicate event.

ML

Michael Lip

Solo developer building free tools at Zovo. Kappafy helps developers work with JSON and APIs faster. No tracking, no accounts, no data collection. Learn more.