Commands
Overview
Every dial command shares three conventions:
--json— every command accepts this flag to print a machine-readable result instead of human text. Use this from agents.- Exit codes —
0on success, non-zero on failure, so scripts and agents can branch on the result. Each command makes a single attempt and doesn’t retry.callaccepts--idempotency-keyto make re-runs safe; other writes likemessagearen’t idempotent, so re-running after a failure can duplicate the action — confirm before retrying. DIAL_API_URL— environment variable to target a non-default deployment.
Run dial --version to print the installed version.
Sandbox mode
When the CLI runs inside a pre-provisioned, ephemeral agent container — for example NanoClaw’s per-agent sandboxes — it enters sandbox mode and adapts automatically:
- Host-management commands are turned off —
signup,onboard,listen,local-target,mcp,update, anduninstallare hidden from--helpand refuse to run (the container is provisioned and managed from outside, so they’d only confuse the agent). Everything you actually act with —message,call,number,typing,wait-for,billing,doctor— works normally. - Requests are keyless — the CLI sends no
Authorizationheader; a transparent proxy at the network boundary injects your credential, so there’s no API key to onboard or store inside the container.
Detection is automatic: the CLI infers sandbox mode when there’s no user service supervisor (no launchd / systemd --user) and HTTPS_PROXY is set. Override it explicitly with DIAL_SANDBOX — 1/true forces it on, 0/false forces it off. To opt a machine out permanently, create an empty .not-sandbox file in the Dial data dir (~/.local/share/dial/.not-sandbox).
All commands
Commands
doctor
Report account state and what to do next. Useful as a first check after install or when something looks off.
signup
Email a 6-digit sign-up code to the given address.
Flags
onboard
Verify the 6-digit code from signup and finish onboarding. Pass --agent to also install the Dial skill into that agent’s config directory.
Omit --code on a machine that’s already signed in and the command skips verification entirely — it just installs the requested --agent skills. This is the no-op re-run the integration pages rely on: after your first onboarding there’s no fresh OTP to supply, so drop --code and the CLI does the right thing. Omitting --code while not signed in exits non-zero and tells you to dial signup <email> first.
Flags
number list
List the phone numbers on your account.
number purchase
Purchase an additional phone number. Only US numbers can be provisioned at this time.
Flags
number set
Update a number’s properties. Takes the number in E.164 (resolved to its id for you). Pass any subset of the flags below — at least one is required. A new inbound instruction takes effect on the next inbound call.
Flags
message
Send a message, optionally with media. At least one of --body and --media is required — a media-only message sends just the attachment.
On an iMessage number, a single audio attachment is delivered as a native voice message (with --body, the text and the voice message arrive as two messages; recipients without iMessage/RCS get the audio as a link over SMS). Pass --force-audio-file to send audio as a regular file attachment instead. On standard numbers audio is always MMS media and the flag has no effect.
Flags
message list
List recent messages on your account, newest first (up to 100).
Flags
message reply
Reply or react to an existing message (get IDs from message list or a
message.received event). The reply goes out from the Dial number the target
message belongs to, to the other party of that message — there’s no --to or
--from-number-id. Exactly one of --body / --react is required.
On an iMessage number, --body is delivered as a native threaded reply and
--react as a native reaction (a Tapback); the target must be an inbound
message. When the recipient can only receive SMS — including everything sent
from standard numbers — an emoji reaction arrives as a regular text whose body
is the emoji, and reaction names are rejected. On standard numbers --body
sends a regular SMS and targets in both directions are accepted.
Flags
typing start
Show a typing indicator to the recipient, as if someone were composing a message from your number. iMessage numbers display it; standard (SMS) numbers have no typing concept, so the command succeeds and does nothing — safe to run unconditionally before a send.
The indicator is fire-and-forget and free. Delivering a message or reaction
from the number clears it natively on the recipient’s device — run
typing start again after a send if you’re still composing, and typing stop
when you stop composing without sending. (A stale indicator may also clear on
its own device-side.)
Flags
typing stop
Clear a typing indicator previously shown with typing start. Same flags and
same no-op behavior on standard numbers.
Flags
call
Place an outbound voice call.
Flags
call list
List recent calls on your account, newest first (up to 100).
Flags
call get
Fetch a single call by ID — status, duration, transcript, and the rest of the call record. Useful for inspecting a transcript after a call.ended event.
billing
Show the account’s billing status: credit-wallet balance, the current subscription (if any), each number’s billing mode (PAYG or FIXED), recent usage, recent credit deposits, and the saved payment methods (card brand, last four, and which is the default). With --json, prints the full billing object.
dial billing is read-only. Managing billing — adding credits, subscribing, cancelling, switching interval, or adding/removing payment methods — is done in the dashboard (embedded Stripe Checkout and one-click subscription actions), not from the CLI.
wait-for
Block until a matching event arrives. Reads from the local listen service log when the daemon is running; otherwise falls back to the REST API automatically. A match prints the event and exits 0; a timeout exits non-zero.
Flags
listen install
Install the background event daemon (launchd on macOS, systemd user unit on Linux). See Listen service.
listen status
Report the listen daemon’s state and its last few events.
listen uninstall
Stop and remove the listen daemon.
Register a local fan-out target on the listen daemon. The daemon delivers every account event it receives to each registered target. Two variants:
url— POST the event JSON to a local HTTP endpoint. Best for integrations that already speak HTTP webhooks (Local URL target).cmd— spawn an executable per event, passing the event JSON as the final positional argument. Best for shell scripts and one-off automations (CLI command target).
Both variants require the listen daemon to be installed (dial listen install) — fan-out runs inside that daemon. No restart needed: the daemon picks up subscribe/unsubscribe changes on the fly.
local-target add url
Register a local HTTP endpoint. The daemon POSTs the event JSON to the URL as the request body.
The URL must be http:// or https:// and resolve to a local loopback host — 127.0.0.1, 0.0.0.0, localhost, ::1, or [::1]. Public URLs are rejected; this command is for processes running on the same machine as the listen daemon.
Flags
local-target add cmd
Register an executable. For each event the daemon spawns the program and passes the event JSON as the final positional argument — anything you put after the executable path is forwarded verbatim before that argument. The two forms above invoke, respectively:
Exit code 0 is treated as delivered. On a non-zero exit or timeout the daemon retries the invocation once, then gives up; nothing is queued or retried further. Stdout/stderr are captured into the listen log so you can see what the executable printed.
The path must be absolute or resolvable on the daemon’s PATH. The daemon runs each invocation in a clean environment except for PATH and HOME.
Flags
local-target remove
Unregister a target by the same identifier you registered (URL for url, path for cmd). The daemon stops fanning out to that target immediately — no restart needed.
local-target list
List the local targets currently registered for fan-out. Each row is tagged url or cmd so you can tell them apart.
mcp
Run a local Model Context Protocol server over stdio, exposing every operational command above as an MCP tool. Point a local MCP client at dial mcp as the server command and the agent can send messages, place calls, manage numbers, wait for events, and run the local setup verbs (signup, onboard, listen, local-target) as tools.
It reuses the API key saved by dial onboard (~/.local/share/dial/auth.v1.json) — no OAuth, no separate login. This is the local counterpart to the hosted Remote MCP server: same operational tools, plus the local-machine verbs the remote server can’t offer. See the MCP integration page for client setup and a Remote-vs-Local comparison.
This command runs in the foreground and speaks JSON-RPC on stdout; it has no --json flag.
update
Update the CLI to the latest published version — a foreground npm install -g @getdial/cli@latest — and report the previous and installed versions. Only global npm installs are updatable: npx runs already resolve the latest version on every invocation, and source checkouts update through git; both exit 2 with guidance.
You rarely need this: the CLI also updates itself automatically. At most once an hour, any command (except update and uninstall) spawns the same npm update fully detached in the background. The background check never touches a command’s output — progress and failures land in ~/.local/state/dial/cli.log. Set DIAL_NO_AUTO_UPDATE=1 to disable it.
uninstall
Remove everything Dial put on this machine, without prompting: stop and remove the listen daemon, delete the dial-cli skill from every agent it was installed into, and delete Dial’s config, data (including the saved API key), and state directories. Steps are best-effort — a failure in one doesn’t stop the rest. It finishes by printing the one step it can’t perform on itself:
Uninstall is local-only: your account and numbers are untouched, and signing back in is the same email flow (dial signup + dial onboard). With --json, the result reports each step’s outcome plus an errors array; the exit code is 0 when every step succeeded, 2 otherwise.