Destinations must be publicly reachable, so http://localhost:3000 cannot be
one. Instead the whk CLI holds an authenticated stream open and replays each
webhook against your local URL — nothing on your machine is exposed, and no
tunnel is involved. If you are weighing this against ngrok and the other
forwarding options, see
ngrok alternatives for webhooks.
1. Install the CLI
Section titled “1. Install the CLI”Download the whk binary for your platform from the
releases page, make it
executable and put it on your PATH:
chmod +x whksudo mv whk /usr/local/bin/whk --versionBuilds are published for Linux, macOS (Intel and Apple Silicon) and Windows.
2. Log in
Section titled “2. Log in”Create an API key in the dashboard, then:
whk login --server https://app.webhooker.euThe key is prompted for without echoing and saved to your user config
directory, together with the server. whk whoami prints the workspace it
resolved, which is the quickest check that the key is good.
The key can also come from the environment, which is what CI should do:
export WEBHOOKER_SERVER=https://app.webhooker.euexport WEBHOOKER_API_KEY=whk_…Both have --server and --api-key flags on every command if you would rather
pass them inline. whk logout deletes the saved credentials again.
3. Watch what arrives
Section titled “3. Watch what arrives”whk tail stripe-prodA source can be named by its name, its id or its ingest token. The stream prints
one line per event as it arrives — method, size, verification result. Add
--json for one JSON object per line, which pipes into jq.
This is metadata only: cheap, and enough to confirm a provider is really sending.
4. Forward to your handler
Section titled “4. Forward to your handler”whk listen stripe-prod --forward http://localhost:3000/hooksEach webhook is replayed against that URL with its original method and body. The command keeps running until you stop it.
Useful flags:
| Flag | Effect |
|---|---|
--forward <url> | Where to replay each webhook. Required. |
--header "Name: Value" | Extra header on the local request. Repeatable. |
--skip-verify | Also forward events whose inbound verification failed. |
--json | One JSON object per line instead of human-readable output. |
whk listen stripe-prod \ --forward http://localhost:3000/hooks \ --header "X-Env: local" \ --json5. Keep production untouched
Section titled “5. Keep production untouched”Two patterns work well:
- A separate source.
stripe-testwith the provider’s test-mode endpoint pointed at it. Production keeps its own source, gateways and history. - One source, two gateways. Your deployed endpoint stays a gateway; you listen on the same source while you develop. Both see the same events.
Because events are stored, you do not need the provider to keep sending: trigger one event, then resend it from the event log as often as you like while you iterate on the handler. To fire synthetic events with a valid signature instead of waiting on the provider, the webhook mock sender sends Stripe, GitHub and Shopify payloads from your own machine.
Troubleshooting
Section titled “Troubleshooting”| Symptom | Cause |
|---|---|
whk: command not found | The binary is not on your PATH. |
| Login fails | Wrong key, or the wrong --server. Check with whk whoami. |
tail connects but prints nothing | No events are arriving. Send a test event from the provider, or curl the ingest URL. |
| Too many open streams | Your plan caps concurrent live streams. Close other tail/listen sessions and dashboard tabs. |
Events with failed verification never appear | They are withheld by default. Add --skip-verify while you debug a secret. |
listen forwards, your app sees nothing | Check the local URL and port, and that the handler accepts the method the provider used. |