> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Manage Orbit from the terminal with the devotel CLI

> Install the first-party devotel CLI, log in with an API key, send your first SMS, tail live request logs and DLRs, forward webhooks to a local server, and script Twilio migrations — all from your terminal.

# Manage Orbit from the terminal with the `devotel` CLI

The `devotel` CLI is the first-party command-line tool for scripting sends,
listing numbers, tailing real-time request logs and delivery receipts, and
forwarding live webhooks to your machine. It ships hand-maintained types
against the platform's public API, so what you automate is the same contract
the SDKs and dashboard use.

This page walks the whole surface: install, login, first send, live logs,
local webhook forwarding, numbers and sandbox, profiles and JSON output,
Twilio migration, and troubleshooting. Every example below is copy-pasteable
once you have an API key.

The full command surface:

| Command                   | What it does                                         |
| ------------------------- | ---------------------------------------------------- |
| `devotel auth login`      | Store an API key (`--token`, `--profile`)            |
| `devotel auth logout`     | Delete a stored credential                           |
| `devotel auth whoami`     | Show the active profile (token redacted)             |
| `devotel send sms`        | Send an SMS (`--to`, `--body`, `--from`)             |
| `devotel numbers list`    | List provisioned numbers                             |
| `devotel sandbox numbers` | List sandbox numbers                                 |
| `devotel logs tail`       | Stream request logs / DLRs (Ctrl-C to stop)          |
| `devotel listen`          | Forward live webhooks to a local URL                 |
| `devotel migrate twilio`  | Port a Twilio config into Orbit (dry-run by default) |
| `devotel migrate status`  | Check a migration job's progress (`<jobId>`)         |

Run any of them with `--help`, or `devotel help` for the full usage text.

## Install

```bash theme={null}
npm install -g @devotel-orbit/cli
```

The package installs a single `devotel` binary. Check it landed:

```bash theme={null}
devotel version
# 0.1.0
```

## 1. Log in with an API key

Mint an API key from **Dashboard → Developers → API keys**, then store it:

```bash theme={null}
devotel auth login --token dv_live_sk_xxxx
# Logged in as profile "default" (dv_live…xxxx) → https://api.orbit.devotel.io
```

Credentials live at `~/.devotel/config.json` with `0600` permissions. Check
the active identity at any time:

```bash theme={null}
devotel auth whoami
# profile "default" — dv_live…xxxx → https://api.orbit.devotel.io
```

The printed token is always redacted, so the command is safe to paste into a
ticket or a chat thread.

## 2. First send

Every account has sandbox numbers to send from before you provision a real
one, so the first-send loop is three commands:

```bash theme={null}
# Pick a sandbox number to send from
devotel sandbox numbers
# +14155551234  US  active

# Send
devotel send sms --to +15551234567 --body "Hello from Orbit"
# sent msg_9f8e2d → +15551234567 (queued)
```

The `queued` status confirms the request was accepted. Outbound SMS goes
through Devotel's own wholesale network — nothing you run here touches
upstream carriers from your machine.

## 3. Watch it land in real time

`devotel logs tail` streams request logs and delivery receipts (DLRs) as
they happen:

```bash theme={null}
devotel logs tail
# Tailing logs… press Ctrl-C to stop.
# 2026-09-26T13:41:02Z INFO POST 201 /api/v1/messages/sms message queued
# 2026-09-26T13:41:03Z INFO SMS msg_9f8e2d delivered
```

Each line is one event: timestamp, level, method, status, path pattern, and
message. DLRs show up as delivery events on the message id, so you can watch
a send go `queued → delivered` without opening the dashboard. Add `--json`
for one JSON object per line if you are piping into `jq`.

Stop the stream with **Ctrl-C**.

## 4. Local webhooks

Build a webhook handler on your machine without deploying or standing up a
public tunnel. `devotel listen` tails your tenant's live events and POSTs
each one to your local server:

```bash theme={null}
devotel listen --forward-to localhost:3000/webhook
# Forwarding Orbit webhooks → http://localhost:3000/webhook
# Webhook signing secret: whsec_8f3c…
# Listening for new events… press Ctrl-C to stop.
# message.delivered  1719345600123-0 → http://localhost:3000/webhook  [200]
```

Each forwarded request carries an `X-Devotel-Signature: t=<ts>,v1=<hmac>`
header, signed with the secret the CLI printed on startup — the same scheme
the production dispatcher uses. Verify it exactly as you would a live
webhook:

```ts theme={null}
import { Orbit } from "@devotel-orbit/node";

const event = Orbit.webhooks.constructEvent(
  rawBody,
  req.headers["x-devotel-signature"],
  process.env.ORBIT_WEBHOOK_SECRET, // the whsec_… the CLI printed
);
```

Useful flags:

* `--types <csv>` — only forward these event types, e.g.
  `--types message.delivered,message.failed`.
* `--secret <whsec_…>` — reuse a fixed signing secret (or set
  `$DEVOTEL_WEBHOOK_SECRET`) instead of a fresh per-session one, so you can
  hard-code it in your local `.env`.
* `--replay` — also forward the events buffered before you started, then
  keep tailing. Use this when your handler needs to process backfill, not
  just live traffic.

Verify signatures on every request — local or production. The walkthrough on
[verifying webhook signatures](/guides/verify-webhook-signatures) covers the
per-language one-call verifiers if you find a mismatch.

## 5. Numbers and sandbox

Two read commands cover your inventory:

```bash theme={null}
devotel numbers list
# +14155551234  US  active
# +442071234567  GB  active
```

```bash theme={null}
devotel sandbox numbers
# +14155551234  US  active
```

`numbers list` shows every provisioned number; `sandbox numbers` shows only
the free-to-send sandbox pool. Use the sandbox pool when you are prototyping
so you never burn a real number on a smoke test.

## 6. Profiles and JSON output

Keep separate credentials per environment with named profiles:

```bash theme={null}
# Log in under a named profile
devotel auth login --token dv_test_sk_xxxx --profile staging

# Use it for one command
devotel numbers list --profile staging

# Or set it for a whole shell
export DEVOTEL_PROFILE=staging
devotel numbers list
```

Profiles are keyed in `~/.devotel/config.json`, so switching shells does not
clobber a colleague's setup. Clean up a profile with
`devotel auth logout --profile <name>`.

Add `--json` to any read command for machine-readable output — the CLI
emits one JSON value that you can pipe into `jq`:

```bash theme={null}
devotel numbers list --json | jq -r '.[].phone_number'
# +14155551234
# +442071234567
```

`--json` is also the right flag when you script `devotel migrate status <jobId>`
in CI and want a structured status.

## 7. Migrate from Twilio

`devotel migrate twilio` drives the same one-click migration wizard the
dashboard's **Import** screen uses, from your terminal or a CI pipeline. It
always runs a dry run first — nothing is written until you pass `--run`:

```bash theme={null}
# 1. Preview — always a dry run first
devotel migrate twilio --account-sid ACxxxxxxxx --auth-token your_auth_token
# Twilio migration preview (source: twilio)
#   phone_numbers: ~12 record(s), 0 known conflict(s)
#   messaging_services: ~2 record(s)
#   templates: ~40 record(s)
#   contacts: ~15800 record(s)
# Estimated runtime: ~190s
#
# Dry run only — nothing was written. Re-run with --run to start the migration.

# 2. Happy with the preview? Commit it
devotel migrate twilio --account-sid ACxxxxxxxx --auth-token your_auth_token --run
# Migration started: job imp_xxxxxxxx (running).

# 3. Watch it land
devotel migrate status imp_xxxxxxxx
# Import job imp_xxxxxxxx — running (source: twilio)
#   phone_numbers: 12/12 imported, 0 skipped, 0 failed
```

By default it ports `phone_numbers`, `messaging_services`, `templates`, and
`contacts`; narrow it with `--entities phone_numbers,contacts`, or shrink the
contact/message lookback window with `--conversation-days 30`. Your Twilio
Auth Token is exchanged server-side for a short-lived encrypted envelope and
is never written to `~/.devotel/config.json`.

The migration moves configuration and metadata — not live traffic. For the
full concept mapping (SMS, voice, webhooks, Verify) see the
[Twilio migration guide](/guides/migration-from-twilio).

## 8. Troubleshooting

| Symptom                                                               | Cause and fix                                                                                                                                                                                             |
| --------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `command not found: devotel`                                          | The global install did not land on `PATH`. Re-run `npm install -g @devotel-orbit/cli`, or check `npm config get prefix` and add `<prefix>/bin` to `PATH`.                                                 |
| `Not logged in (profile "default")`                                   | The profile has no stored credential. Run `devotel auth login --token <token>` first, or set `DEVOTEL_PROFILE` to the profile you actually logged in under.                                               |
| `devotel: command not found` after a `nvm` version switch             | Global npm packages are per-Node-version under `nvm`. Reinstall the CLI after switching versions.                                                                                                         |
| `API error 401` on any command                                        | The stored key was revoked or never had the right scopes. Mint a fresh key in the dashboard and re-run `devotel auth login --token <new>`.                                                                |
| `API error 403` on `send sms` or `migrate twilio`                     | The key's scopes do not include the target surface. Edit the key's scopes in **Dashboard → Developers → API keys** and retry.                                                                             |
| `devotel listen` forwards but the local handler rejects the signature | Hard-code the printed `whsec_…` via `--secret <whsec_…>` or `$DEVOTEL_WEBHOOK_SECRET`, or export it into your local `.env` each time. See [Verify webhook signatures](/guides/verify-webhook-signatures). |
| `devotel logs tail` prints nothing                                    | The stream is idle, not broken — it only shows events as they happen. Send a test SMS and watch again.                                                                                                    |

<Accordion title="Where the credential file lives, and how to move it">
  Credentials are written to `~/.devotel/config.json` with `0600` permissions —
  user-readable only, so no other account on the machine can read your tokens.
  To move the file (useful in CI) set `DEVOTEL_CONFIG_HOME` to an alternate
  directory; every command then reads from (and `auth login` writes to) that
  location instead.
</Accordion>

## See also

* [Verify webhook signatures](/guides/verify-webhook-signatures) — the one-call verifier per language, applied to both forwarded and live events.
* [Migrate from Twilio](/guides/migration-from-twilio) — the full concept mapping this page's migration section previews.
* [SDKs index](/sdks) — per-language libraries for the same public contract the CLI is typed against.
* [Tour the Developer hub](/guides/developer-hub-overview) — where the API keys page lives, and the surrounding feature map.
