Test webhooks locally with the CLI
The novaro CLI sends your account’s test-mode events to a server on your machine, signed as your endpoints’ are, so you can build and debug your webhook handler before it is on the internet.
Set it up
Section titled “Set it up”-
Install it: on macOS and Linux with Homebrew, or download the binary for your system from its releases.
Terminal window brew install novaro/tap/novaro -
Pair it with your account.
novaro loginshows a pairing code and opens the merchant portal’s Allow CLI access page; someone of your account with the admin or developer role checks the code is the same and confirms.Terminal window novaro loginnovaro gets a CLI key (
rk_test_…) and keeps it in your system’s keychain. It works in test mode only, for 90 days, and may only read events, resend them, and create, read and confirm test payments. The portal lists it under Developers › API keys › CLI keys with your machine’s name, where you can expire it. In CI, pass a test secret key instead:--api-key sk_test_…on any command.
Forward events to your server
Section titled “Forward events to your server”novaro listen --forward-to localhost:3000/webhooksnovaro prints this machine’s signing secret (whsec_…) and then each event as it comes, with your server’s answer:
Ready! Your webhook signing secret is whsec_… (^C to quit)2026-10-16 12:00:00 --> payment.succeeded [evt_in_…]2026-10-16 12:00:00 <-- [200] POST http://localhost:3000/webhooks [evt_in_…]- Each event is a
POSTwith the same body and headers as a delivery to an endpoint: verifyPSP-Signaturewith the secret novaro printed, as the Signatures page shows. The secret is this machine’s and stays the same when you runnovaro listenagain;novaro listen --print-secretprints it and exits, for your server’s configuration. - Your server’s answer is the delivery’s attempt: a
2xxwithin 10 seconds is received. An event sent to novaro is tried once, never retried, and an event made while novaro isn’t listening isn’t sent to it later: read those withGET /events. - While it listens, the portal’s Webhooks page (test mode) shows the machine as a Local listener. Your endpoints keep getting their events as usual.
| Option | What it does |
|---|---|
--forward-to <url> |
Where to send each event. localhost:3000/webhooks means http://localhost:3000/webhooks. Without it, novaro shows the events and answers each as received. |
--events <types> |
Only these types, comma-separated: payment.succeeded,payment.failed. All of them when left out. |
--api-version <version>, --latest |
Shape the events in this API version, or in the latest one. Your account’s default when left out. |
--headers "Key: Value, …" |
Headers to add to each forwarded request. |
--skip-verify |
Accept any certificate of an https:// --forward-to, a self-signed one included. |
--format json |
Print each event’s JSON on a line of its own instead. |
Make events happen
Section titled “Make events happen”novaro trigger creates a test payment with POST /payments and confirms it with a magic UTR, so the event, and every event before it, goes out as from your own integration: to novaro listen and to your test endpoints.
novaro trigger payment.succeedednovaro trigger payment.failed --override amount=50000 --override metadata.order=42| Event | How |
|---|---|
payment.created |
A payment waiting for the payer. |
payment.processing |
Confirmed with an ordinary UTR; it succeeds about 5 seconds later. |
payment.succeeded |
Confirmed with the magic UTR 000000000001. |
payment.failed |
Confirmed with the magic UTR 000000000003; it fails about 5 seconds later. |
--override field=value sets a field of the payment, customer.email=… and metadata.key=… included. novaro trigger --list lists what it makes.
Read and resend events
Section titled “Read and resend events”novaro events list --type payment.* --limit 5novaro events retrieve evt_in_…novaro events resend evt_in_… --webhook-endpoint whe_in_…events list and events retrieve are GET /events, events resend is POST /events/{id}/retry: one more attempt of the event’s delivery to that endpoint, as Resend in the portal. They read test mode; pass --live --api-key sk_live_… for live mode.