CLI (evctl)

evctl is the EmailValidator command-line tool. It exists so you can develop and test a webhook handler on localhost — without a public URL, a tunnelling service, or a live validation.

On this page

Install

dotnet tool install -g evctl

Two commands:

Command What it does
evctl listen Subscribes to your account's event stream and forwards real events to a local URL, already signed
evctl trigger Fabricates one signed event and POSTs it to a local URL — no account, no job, no credits

Both send the same three headers the production worker sendsX-Signature, X-Ev-Event and X-Ev-Delivery — so code written against the CLI never reads a header production does not send.

Forward real events to your local server

evctl listen --forward-to http://localhost:5000/your-hook --api-key ev_your_key

You can also set EV_API_KEY in your environment and omit the --api-key flag:

export EV_API_KEY=ev_your_key
evctl listen --forward-to http://localhost:5000/your-hook

How it works

evctl listen connects to the API's server-sent event stream, prints a signing secret, and forwards each event to your local endpoint — already signed — so your real signature-verification code runs against real payloads.

On startup it prints a session signing secret:

Session signing secret:  whsec_example_session_secret
Set EV_WEBHOOK_SECRET=whsec_example_session_secret  to verify locally.
Ready! Forwarding to http://localhost:5000/your-hook

Use that whsec_ value as the secret in whichever verification snippet fits your stack (see Webhooks). It is ephemeral — a new one is generated each time evctl listen starts.

For each incoming event it prints the event type, the HTTP status your handler returned, and the round-trip latency:

14:02:11  verify.completed  → 200 (12ms)

No ngrok, no reverse proxy, no firewall changes. Your handler never needs to be reachable from the internet.

Fabricate an event with evctl trigger

listen still needs a real validation to happen. trigger does not: it builds a synthetic envelope of the shape the API emits, signs it, and delivers it immediately.

evctl trigger test --to http://localhost:3000/webhook
evctl trigger <event> [--to <url>] [--secret <secret>]
Argument / option Value
<event> verify.completed, batch.completed, or test
--to The local URL to POST to. Defaults to http://localhost:3000/webhook
--secret Signing secret. Falls back to EV_WEBHOOK_SECRET, then to a freshly generated one that is printed for you

An unknown event name is refused with a non-zero exit code rather than delivered, so a typo in a script fails loudly.

The output is one line, and the exit code is 0 only when your handler answered 2xx — which makes it usable directly in a test script:

test  → 200 (7ms)

The envelope is indistinguishable in shape from a real one, including an evt_-prefixed id derived the same way, so this is a genuine exercise of your handler:

evctl trigger verify.completed --to http://localhost:3000/webhook --secret whsec_example_local_secret

Reuse one secret across runs by exporting it once:

export EV_WEBHOOK_SECRET=whsec_example_local_secret
evctl trigger test
evctl trigger verify.completed
evctl trigger batch.completed

Which one do I want?

  • Writing signature verification, or a handler, from scratchevctl trigger. It is instantaneous, free, and needs no API key.
  • Checking that your handler copes with your own real dataevctl listen, then run a validation.
  • Checking that our delivery reaches a deployed endpointPOST /api/v1/webhooks/test, which signs with your account secret and returns the event_id the delivery will carry. See Webhooks.