How to Test Webhooks — Complete Guide for Developers

Published April 4, 2026 · 8 min read

Table of Contents

What Are Webhooks?

Webhooks are HTTP callbacks — automated messages sent from one application to another when a specific event occurs. Instead of polling an API repeatedly to check for changes, webhooks push data to your endpoint in real-time.

Common webhook providers include:

Why Test Webhooks?

Webhook testing is critical because:

  1. You can't control the sender — the webhook provider sends data at unpredictable times
  2. Payloads vary — different events have different structures
  3. Failures are silent — if your endpoint fails, you might not know until a customer complains
  4. Security matters — you need to verify signatures to prevent spoofing

3 Methods to Test Webhooks

Method 1: Online Webhook Testers

The fastest way to test webhooks is with an online tool that gives you a unique URL to receive requests. Our free Webhook Tester does exactly this:

  1. Create a new webhook bin (unique URL)
  2. Configure your webhook provider to send to that URL
  3. Trigger an event and inspect the captured request
# Create a webhook bin via API
curl -X POST https://kas.storksoft.by/api/v1/webhook/create

# Send a test webhook
curl -X POST https://kas.storksoft.by/api/v1/webhook/catch/YOUR_BIN_ID \
  -H "Content-Type: application/json" \
  -d '{"event": "payment.succeeded", "amount": 4999}'

# Inspect captured requests
curl https://kas.storksoft.by/api/v1/webhook/inspect/YOUR_BIN_ID

Try Our Free Webhook Tester

No signup required. Create a URL, send requests, inspect everything.

Open Webhook Tester →

Method 2: Local Testing with Tunnels

When developing locally, your localhost isn't accessible from the internet. Use a tunnel service to expose your local server:

Using ngrok

# Install ngrok
npm install -g ngrok

# Start your local server
node server.js  # listening on port 3000

# Create a tunnel
ngrok http 3000

# Use the generated URL (e.g., https://abc123.ngrok.io)
# as your webhook endpoint

Using Cloudflare Tunnel

# Install cloudflared
brew install cloudflare/cloudflare/cloudflared

# Quick tunnel (no account needed)
cloudflared tunnel --url http://localhost:3000
💡 Tip: Most webhook providers have a "test" or "send test event" button in their dashboard. Use it together with a tunnel for rapid development.

Method 3: Mock Testing in CI/CD

For automated tests, simulate webhook deliveries with mock HTTP requests:

Node.js Example (Jest + Supertest)

const request = require('supertest');
const app = require('../app');

describe('POST /webhooks/stripe', () => {
  it('handles payment_intent.succeeded', async () => {
    const payload = {
      type: 'payment_intent.succeeded',
      data: {
        object: { id: 'pi_123', amount: 4999, currency: 'usd' }
      }
    };

    const res = await request(app)
      .post('/webhooks/stripe')
      .set('Content-Type', 'application/json')
      .set('Stripe-Signature', generateTestSignature(payload))
      .send(payload);

    expect(res.status).toBe(200);
    // Verify side effects (e.g., order created, email sent)
  });
});

Python Example (pytest + FastAPI)

from fastapi.testclient import TestClient
from app import app

client = TestClient(app)

def test_github_webhook():
    payload = {
        "action": "opened",
        "pull_request": {"number": 42, "title": "Fix bug"}
    }
    response = client.post(
        "/webhooks/github",
        json=payload,
        headers={"X-GitHub-Event": "pull_request"}
    )
    assert response.status_code == 200

Debugging Common Webhook Issues

1. 401/403 — Authentication Failed

Your endpoint is rejecting the request. Check that you're verifying the webhook signature correctly. Most providers include a signature in a header (e.g., Stripe-Signature, X-Hub-Signature-256).

2. 500 — Server Error

Your handler code is crashing. Common causes:

3. Timeout

Webhook providers typically wait 5-30 seconds for a response. If your processing takes longer, return 200 OK immediately and process the event asynchronously (e.g., with a message queue).

4. Duplicate Events

Webhook providers may retry failed deliveries, leading to duplicates. Always make your webhook handler idempotent — processing the same event twice should have the same result as processing it once.

// Idempotent webhook handler pattern
async function handleWebhook(event) {
  // Check if we've already processed this event
  const existing = await db.events.findOne({ eventId: event.id });
  if (existing) return; // Already processed, skip

  // Process the event
  await processEvent(event);

  // Mark as processed
  await db.events.insert({ eventId: event.id, processedAt: new Date() });
}

Webhook Security Best Practices

  1. Verify signatures — Always check the HMAC signature sent by the provider
  2. Use HTTPS — Never accept webhooks over plain HTTP
  3. Validate payloads — Check the structure matches what you expect
  4. Rate limit — Protect against flood attacks on your webhook endpoint
  5. Log everything — Store raw payloads for debugging and auditing

Stripe Signature Verification Example

const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);

app.post('/webhooks/stripe', express.raw({type: 'application/json'}), (req, res) => {
  const sig = req.headers['stripe-signature'];
  let event;

  try {
    event = stripe.webhooks.constructEvent(req.body, sig, process.env.WEBHOOK_SECRET);
  } catch (err) {
    console.log(`Webhook signature verification failed.`, err.message);
    return res.status(400).send(`Webhook Error: ${err.message}`);
  }

  // Handle the event
  switch (event.type) {
    case 'payment_intent.succeeded':
      // Handle successful payment
      break;
    default:
      console.log(`Unhandled event type ${event.type}`);
  }

  res.json({received: true});
});

Ready to Test Your Webhooks?

Our free tool captures and displays every detail of incoming requests.

Start Testing →
\xF0\x9F\x92\x99 Tip\xF0\x9F\x93\x9A Get Bundle \x244.99