The devotel CLI model
Thedevotel CLI exists for one loop the SDKs and curl cannot close: building a webhook handler against live events without deploying anything. An SDK types request/response calls; curl runs them; neither can push a signed message.delivered event into localhost:3000 while you edit the handler. This page explains the model behind that loop — where the credential lives, how the forwarder subscribes to temporary events, and how the four subcommand families divide the surface. For install and first-run usage, see the devotel CLI guide.
Why a dedicated CLI exists
curl and the SDKs cover the request/response plane well. The gap is the inbound plane: webhooks arrive server-to-server, so a local handler under development is unreachable. The standard workaround — a public tunnel such as ngrok — puts your work-in-progress code on a public URL and adds a credential-managed dependency. A first-party CLI closes that loop client-side: it reads the event history over the API (the same endpoint the dashboard reads) and POSTs each event to yourlocalhost, signed with the exact X-Devotel-Signature scheme the production dispatcher emits. Your handler code runs unchanged — the same verifier call, the same envelope shape — so “works locally” and “works in production” are the same claim.
The four subcommand families
Each family owns a plane; none crosses into another’s job.
Two read commands (
numbers list, sandbox numbers) and the migration surface (migrate twilio|telnyx|status) round out the surface, but the model is the four families above: auth decides who you are, send/numbers decide what you transmit, and listen/tail decide what you watch.
The auth resolution chain
Every command resolves its credential through one chain, first match wins:- An explicit flag —
--token <t>atauth logintime. - An environment variable —
DEVOTEL_TOKENas the login-time fallback (used in CI where flags would put the key in the argv list). - The workspace config file —
~/.devotel/config.json, written byauth loginwith0600permissions (owner-read-only). Move it withDEVOTEL_CONFIG_HOME; pick a named profile with--profileorDEVOTEL_PROFILE.
dv_… keys go as X-API-Key, anything else as Authorization: Bearer. auth whoami prints the resolved profile with the token redacted, so it is safe to paste into a chat.
The webhook-forwarding loop
devotel listen is the harness the CLI exists for. A session runs four steps, and all of them are client-side polling plus local POSTs — no new server surface, no public URL:
- Open the session. Mint an ephemeral signing secret (
whsec_…) unless you pinned one with--secretorDEVOTEL_WEBHOOK_SECRET. The CLI prints it once, on startup. - Subscribe. Poll the first-party
GET /eventshistory endpoint — the same Redis replay buffer the dashboard’s event stream reads — tailing forward from a monotonic stream-id cursor. A--typesallowlist narrows the subscription (mirroring the server’s filter semantics: no filter delivers everything). - Forward. Re-shape each event into the canonical webhook envelope
{ id, type, created_at, data }and POST it to your local URL withX-Devotel-Signature: t=<ts>,v1=<hmac>— byte-for-byte the header the production dispatcher emits. Your handler verifies it with the same SDK verifier call it uses in production. - Terminate on exit. Ctrl-C ends the poll loop; the ephemeral secret and the cursor die with the process. Nothing was registered server-side, so nothing needs cleanup.
--replaybackfills the buffered events first, then tails.
One session, end to end
Start the forwarder while your handler runs on the machine:500. The CLI prints the non-2xx line and keeps the session alive; the platform’s own retry schedule (attempt one of ten, on the delivery-semantics backoff) re-delivers the event, the forwarder sees it again, and this attempt resolves locally once you fix the handler. That is the loop: the retried webhook resolves on your machine, before anything deploys.
CLI planes vs. the dashboard event pages
Uselisten/tail when you are developing — you want events pushed into local code, or a raw stream piped into jq. Use the dashboard’s event log and webhook debug pages when you are operating — historical search across the team, dead-letter inspection and replay, endpoint health. The CLI is the inner loop; the dashboard is the shared record. Neither replaces the other.
Map to the guide
Install, login, first send, and the flag-by-flag walkthrough live in the devotel CLI guide. This page is the model behind it: read the guide to run the commands, read this page when you need to explain to a teammate why the credential file sits at~/.devotel, why the forwarded signature matches production, or why the listen loop was worth a first-party binary.