Guides
Local development
Receive real provider webhooks on your laptop, with no tunnel and no public URL.
Webhook handlers are awkward to develop because the provider needs a public URL and your machine does not have one. The usual answer is a tunnel, which gives the internet a door into your laptop and changes address every restart.
Catchook already receives the webhook. catchook listen opens an outbound
connection and forwards each one to a local port:
npx @catchook/cli login
npx @catchook/cli listen --forward-to http://localhost:3000/webhooks
Nothing listens on your machine from outside. The connection is outbound, so it works behind NAT, on a VPN, and on hotel wifi.
What your local server gets
The same request the provider sent — the body byte for byte, the original
headers including the provider’s signature, plus Catchook’s
catchook-* headers. Your real verification code runs against a real signed
request, rather than against a hand-written fixture that may not match.
Replaying while you iterate
The interesting part of webhook development is the second attempt. listen
keeps every payload it forwarded, so replaying is instant and needs no
network call:
↑↓ select · enter replay · a all · space pause · tab detail · ? help · q quit
Every forward is kept, not just the latest — each with its status, its latency, and what your server answered. A 500 with a stack trace in the body is the point.
To replay something from before this session, replay reads from the API
instead:
catchook replay # the most recent event
catchook replay --last 20 # the last 20, oldest first
catchook replay evt_d70ac863... # one specific event
Two replays, never implicit
catchook replay --forward-to <url>goes to your machine and is recorded nowhere.catchook replay --to-destinationsgoes through real delivery: the queue, the retries, and a row in the delivery history.
With neither, the command refuses rather than guessing. That is deliberate: accidentally re-sending production events to real destinations while debugging is not a recoverable mistake.
Signing a local replay
If your handler verifies Catchook’s own catchook-signature, a plain local
replay will fail that check — nothing signed it. Pass the destination’s
secret:
catchook listen --forward-to http://localhost:3000 --sign-with whsec_...
This signs through the same code path production deliveries use. A local replay that signed differently would “prove” your receiver works when production would reject every delivery.
Pointing at a local Catchook
Working on Catchook itself rather than against it:
CATCHOOK_API_URL=http://localhost:8787 \
CATCHOOK_EVENTS_URL=http://localhost:8788 \
CATCHOOK_API_KEY=ck_... \
catchook listen --forward-to http://localhost:3000