The Problem with Local Webhook Development

You're building a Stripe integration. You write your webhook handler, start your local server on localhost:3000, and… now what? Stripe can't reach your machine. You need a public URL.

The traditional solutions all have drawbacks:

ngrok / Tunnels

  • + Quick to set up
  • + Works immediately
  • - URL changes every restart (free tier)
  • - Must update provider webhook URL each time
  • - Doesn't capture when tunnel is down
  • - No replay capability

Webhook Relay (HookRelay)

  • + Permanent URL — set once, never change
  • + Captures webhooks even when offline
  • + One-click replay
  • + Real-time payload inspection
  • + Signature verification built in
  • + Free tier available

The Webhook Relay Approach

Instead of tunneling traffic directly to your machine, a webhook relay captures webhooks in the cloud and lets you forward them to localhost on demand. Here's the workflow:

Step 1: Create an Endpoint

Sign up for HookRelay and create a new endpoint. You get a unique, permanent URL like:

https://hookrelay.fly.dev/h/abc123xyz

Step 2: Configure Your Provider

Point your webhook provider (Stripe, GitHub, Shopify, etc.) to your HookRelay endpoint URL. This is a one-time setup — the URL never changes.

Step 3: Webhooks Are Captured Automatically

Every webhook sent to your endpoint is captured with full details: headers, body, query parameters, timestamp, and source IP. You can inspect them in the HookRelay dashboard in real time via WebSocket.

Step 4: Forward to Localhost

When you're actively developing, enable forwarding to send captured webhooks to your local server:

# Webhooks sent to your HookRelay endpoint
# are automatically forwarded to localhost:3000/webhook

Step 5: Replay as Needed

Found a bug? Fixed it? Replay the same webhook with one click. No need to trigger a new event from the provider. You can replay the same webhook dozens of times as you iterate on your handler code.

Real-World Example: Stripe Checkout

Let's walk through a complete example of developing a Stripe Checkout integration using HookRelay.

Set Up Your Handler

// server.js
const express = require('express');
const app = express();

app.post('/webhook', express.raw({type: 'application/json'}), (req, res) => {
  const event = JSON.parse(req.body);

  switch (event.type) {
    case 'checkout.session.completed':
      const session = event.data.object;
      console.log('Payment successful for:', session.customer_email);
      // Fulfill the order
      break;

    case 'customer.subscription.updated':
      console.log('Subscription updated:', event.data.object.id);
      break;

    default:
      console.log('Unhandled event:', event.type);
  }

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

app.listen(3000, () => console.log('Listening on :3000'));

Configure Stripe Webhook

In the Stripe Dashboard, set your webhook endpoint to your HookRelay URL. Select the events you care about (e.g., checkout.session.completed).

Trigger a Test Event

Complete a test checkout in Stripe. The webhook is captured by HookRelay. You can see the full payload in the dashboard immediately:

{
  "id": "evt_1abc123",
  "type": "checkout.session.completed",
  "data": {
    "object": {
      "id": "cs_test_xyz",
      "customer_email": "buyer@example.com",
      "amount_total": 2900,
      "currency": "usd",
      "payment_status": "paid"
    }
  }
}

Debug and Iterate

Your handler has a bug? Fix it, restart your server, and replay the captured webhook. The same payload, the same headers — but this time it hits your updated code. Repeat until it works perfectly.

No more "trigger another test payment." Every webhook you receive is saved and replayable. This alone saves hours during development.

Why Not Just Use ngrok?

ngrok is great for quick demos, but it has real limitations for webhook development:

Other Services and Alternatives

If you're evaluating webhook testing tools, here's a quick comparison:

Start testing webhooks the easy way

Get a permanent webhook URL in 30 seconds. Free tier included.

Create Free Account