Forward webhooks to your app
The most common Tapeline setup: your app runs on localhost, Tapeline sits in front of it, and a tunnel gives the pair a public URL you paste into a provider dashboard. Real sandbox events then arrive on your machine, get recorded, and land in your handler — and when the handler fails, you fix the code and replay the same event instead of triggering the provider again.
This guide uses a payment provider as the example, but the flow is identical for any service that delivers webhooks.
Before you begin
Section titled “Before you begin”- Your app runs locally and has a webhook route. The examples assume
http://localhost:3000/webhooks. - The Tapeline CLI is installed.
- You have any tunnel CLI available —
cloudflared,ngrok, or whatever your team already uses. Tapeline itself never opens a public URL.
1. Start your app
Section titled “1. Start your app”Run your app as usual so the webhook route is live:
npm run dev2. Put Tapeline in front of it
Section titled “2. Put Tapeline in front of it”In a second terminal, start the listener in forward mode:
tapeline listen --forward http://localhost:3000/webhooks✔ listening on http://127.0.0.1:8787/hooks✔ forwarding to http://localhost:3000/webhooks✔ recording to .tapeline/events.ndjsonFrom now on, every request that reaches /hooks is stored first, then
re-sent to your app with the method, path, headers, and body untouched. The
provider receives whatever status your app returns.
3. Expose the listener with a tunnel
Section titled “3. Expose the listener with a tunnel”Give the listener a public URL:
cloudflared tunnel --url http://127.0.0.1:8787The tunnel prints a public hostname. In your provider’s dashboard, set the webhook endpoint to that hostname plus the listener path:
https://lucky-otter-demo.trycloudflare.com/hooks4. Trigger a sandbox event
Section titled “4. Trigger a sandbox event”Use the provider’s dashboard or test tooling to fire a sandbox event, then watch it arrive:
tapeline tail --follow12:04:11 POST /hooks payment.settled → 200 in 41 ms stored as evt_8xk2mpA 200 from your app means the whole chain works: provider → tunnel →
Tapeline → handler.
5. Debug a failing handler
Section titled “5. Debug a failing handler”Here is the loop Tapeline exists for. Suppose the next event breaks your handler:
12:05:02 POST /hooks customer.updated → 500 in 9 ms stored as evt_9f3kq2The event is already captured, so you do not need the provider anymore:
-
Read the failure in your app’s logs and fix the code.
-
Replay the exact same event:
Terminal — window 3 tapeline replay evt_9f3kq2 -
Repeat until the replay reports a
200:Output ✔ replayed customer.updated → 200 in 18 ms
Delivery behavior
Section titled “Delivery behavior”- If your app is down or times out (
forward.timeout_ms, default 5 seconds), the delivery is marked failed in the tail output — but the event is still stored, so you can replay it once your app is back. - Tapeline forwards events in the order they arrive and does not retry on
its own. Retrying is an explicit
tapeline replay, so you always know what hit your handler and when. - Signature verification keeps working on forwards and replays because the original bytes are preserved. See the FAQ note about timestamped signatures for the one caveat.