Stripe, GitHub, and the rest deliver events by POSTing to a URL you give them. That URL has to be reachable from the internet, which your machine is not. A tunnel gives your local handler a public address so the real events arrive while you develop, with nothing deployed.

1. Run your handler locally

Start your app on whatever port it listens on. Here it is on 4242, with the webhook route at /webhook.

$ npm run dev   # or however you start it, listening on :4242

2. Open a tunnel with a stable name

Providers store the URL and retry against it, so you want one that survives reconnects. Give it a --name and you configure the provider once, not every time you restart.

$ viaduct http 4242 --name my-hooks

That is https://my-hooks.viaduct.sh, pinned to your machine for as long as the tunnel is live.

3. Point the provider at it

Paste the full endpoint into the provider's webhook settings, for example https://my-hooks.viaduct.sh/webhook in the Stripe or GitHub dashboard, and send a test event.

4. Watch them land

Press i on the running tunnel (or start it with --inspect) and each delivery prints a line: method, path, status, and timing. No dashboard, no redeploy.

viaduct http 4242 --name my-hooks
โœ“ tunnel live

  https://my-hooks.viaduct.sh
  โ†’ http://localhost:4242

โ— traffic inspector ON ยท press i to turn off
14:02:01  POST /webhook            200 8ms
14:02:04  POST /webhook            200 6ms
14:03:10  POST /webhook            400 2ms

Every delivery, method, path, status and timing, as it happens.

A few things worth knowing

viaduct opens the tunnel even before your app is listening, so a delivery that arrives before your handler is ready gets a clean 502. Providers retry, so this sorts itself out; if it does not, check the handler is up on the right port.

The tunnel is a raw byte splice: it passes the request body through untouched, so signature checks like Stripe-Signature or GitHub's HMAC verify exactly as they would in production. Keep verifying signatures, that is the real check on a webhook, rather than trusting the tunnel. If you also want to fence the endpoint off while testing, add a --bearer token.

← More on the inspector