Commands

View as Markdown

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 codes0 on success, non-zero on failure, so scripts and agents can branch on the result. Each command makes a single attempt and doesn’t retry. call accepts --idempotency-key to make re-runs safe; other writes like message aren’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 offsignup, onboard, listen, local-target, mcp, update, and uninstall are hidden from --help and 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 Authorization header; 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_SANDBOX1/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

CommandWhat it does
doctorReport account state and what to do next.
signupEmail a 6-digit sign-up code to an address.
onboardVerify the code, finish onboarding, and optionally install the agent skill.
number listList the phone numbers on your account.
number purchasePurchase an additional phone number.
number setUpdate a number’s inbound instruction.
messageSend an SMS.
message listList recent messages, newest first.
message replyReply or react to a message.
typing startShow a typing indicator to a recipient.
typing stopClear a typing indicator.
callPlace an outbound AI voice call.
call listList recent calls, newest first.
call getFetch a single call by ID — status, duration, transcript.
billingShow wallet balance, subscription, per-number mode, and recent activity.
wait-forBlock until a matching account event arrives.
listen installInstall the background event daemon.
listen statusReport the daemon’s state and recent events.
listen uninstallStop and remove the daemon.
local-target add urlRegister a local HTTP endpoint for event fan-out.
local-target add cmdRegister an executable run once per event.
local-target removeUnregister a fan-out target.
local-target listList the fan-out targets currently registered.
mcpRun a local stdio MCP server exposing every command as an agent tool.
updateUpdate the CLI to the latest version (also runs hourly in the background).
uninstallRemove the daemon, agent skills, and all local Dial state.

Commands

doctor

$dial doctor

Report account state and what to do next. Useful as a first check after install or when something looks off.

signup

$dial signup <email>

Email a 6-digit sign-up code to the given address.

Flags

FlagShortDescription
--forceOverwrite any pending sign-up for this email.

onboard

$dial onboard --code <code> --inbound-instruction "You are my receptionist. Greet the caller and find out what they need." --agent claude-code

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

FlagShortDescription
--code <code>6-digit OTP from your sign-up email. Omit it when already signed in — the command then only installs the --agent skills and skips verification.
--inbound-instruction <text>System prompt for the AI voice agent on inbound calls to your auto-provisioned number. Optional — a default greeting is used if omitted; ignored when signing in to an existing account.
--verification-id <id>Explicit verification ID. Falls back to the local pending sign-up.
--agent <name>Install the Dial skill into the named agent’s config dir. One of claude-code, cursor, codex, opencode, pi, openclaw, nanoclaw, hermes. Repeatable.

number list

$dial number list

List the phone numbers on your account.

number purchase

$dial number purchase --inbound-instruction "You are my receptionist. Greet the caller and find out what they need." --area-code 415

Purchase an additional phone number. Only US numbers can be provisioned at this time.

Flags

FlagShortDescription
--inbound-instruction <text>System prompt for the AI voice agent on inbound calls to this number. Optional — a default greeting is used if omitted.
--inbound-voice-gender <male|female>Voice gender for inbound calls to this number. Optional — the default is female; pass male to override.
--inbound-language <bcp47>BCP-47 language tag for inbound calls to this number. Optional — when omitted, the language is detected from the caller’s country prefix on each call and the agent handles both it and en-US.
--area-code <code>Preferred US area code. Optional — when omitted, any available US number is provisioned.

number set

$dial number set +14155550123 --inbound-instruction "You are ACME's receptionist. Greet the caller and route them."
$dial number set +14155550123 --nickname "Support line"

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

FlagShortDescription
--inbound-instruction <text>New inbound voice-agent system prompt for this number.
--inbound-voice-gender <male|female>Voice gender for inbound calls to this number. Pass an empty string (--inbound-voice-gender "") to clear it (reverts to the default, female).
--inbound-language <bcp47>BCP-47 language tag for inbound calls to this number. Pass an empty string (--inbound-language "") to clear it (reverts to detecting the language from the caller’s country prefix).
--nickname <text>Human-readable label for the number, e.g. "Support line". Max 100 characters. Pass an empty string (--nickname "") to clear it.

message

$dial message --to +14155550123 --body "Hello" --from-number-id pn_123
$dial message --to +14155550123 --media ./note.m4a

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

FlagShortDescription
--to <e164>Destination phone number, E.164 (e.g. +14155551234). Required.
--body <text>Message body. Optional when --media is given.
--from-number <ref>Number to send from: a phone number ID, one of your numbers in E.164, or a nickname. Defaults to your onboarded number. Mutually exclusive with --from-number-id.
--from-number-id <id>Phone number ID to send from. Defaults to your onboarded number. Mutually exclusive with --from-number.
--media <path-or-url>Media attachment: a local file path (uploaded) or a public http(s) URL. Repeatable, max 10 (one for iMessage numbers).
--force-audio-fileSend an audio attachment as a regular file attachment instead of an iMessage voice message.

message list

$dial message list --direction inbound --since 2026-05-01T00:00:00Z

List recent messages on your account, newest first (up to 100).

Flags

FlagShortDescription
--number-id <id>Filter to a single phone number.
--direction <dir>inbound or outbound.
--since <iso8601>Only messages created after this timestamp.

message reply

$dial message reply msg_123 --body "On my way!"
$dial message reply msg_123 --react love
$dial message reply msg_123 --react 🔥

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

FlagShortDescription
--body <text>Reply text.
--react <reaction>Reaction to send: love, like, dislike, laugh, emphasize, question, or a single emoji.

typing start

$dial typing start --to-number +14155550123

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

FlagShortDescription
--to-number <e164>Recipient phone number, E.164 (e.g. +14155551234). Required.
--from-number <ref>Number the indicator appears from: a phone number ID, one of your numbers in E.164, or a nickname. Defaults to your onboarded number.

typing stop

$dial typing stop --to-number +14155550123

Clear a typing indicator previously shown with typing start. Same flags and same no-op behavior on standard numbers.

Flags

FlagShortDescription
--to-number <e164>Recipient phone number, E.164. Required.
--from-number <ref>Number the indicator appears from — ID, owned E.164, or nickname. Defaults to your onboarded number.

call

$dial call --to +14155550123 --outbound-instruction "You are a helpful assistant." --language en-US

Place an outbound voice call.

Flags

FlagShortDescription
--to <e164>Destination phone number, E.164 (e.g. +14155551234). Required.
--outbound-instruction <text>System prompt for the agent that will speak. Required.
--language <bcp47>BCP-47 language tag for the call. Optional — when omitted, the language is detected from the destination number’s country prefix and the agent handles both it and en-US.
--voice-gender <male|female>Voice gender for the agent. Optional — the default is female (every language); pass male to override.
--idempotency-key <key>Unique key (e.g. a UUID) making the placement idempotent: re-running with the same key returns the already-placed call instead of dialing again. See Retries and idempotency.
--from-number <ref>Number to call from: a phone number ID, one of your numbers in E.164, or a nickname. Defaults to your onboarded number. Mutually exclusive with --from-number-id.
--from-number-id <id>Phone number ID to call from. Defaults to your onboarded number. Mutually exclusive with --from-number.

call list

$dial call list --direction inbound --since 2026-05-01T00:00:00Z

List recent calls on your account, newest first (up to 100).

Flags

FlagShortDescription
--number-id <id>Filter to a single phone number.
--direction <dir>inbound or outbound.
--since <iso8601>Only calls created after this timestamp.

call get

$dial call get <call_id>

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

$dial 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

$dial wait-for message.received --field to=+14155550123 --timeout 60

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

FlagShortDescription
--field <name=value>-fExact match on a top-level event field. Repeatable.
--regex <name=pattern>-rRegex match on a field. Pattern is /re/flags or a bare regex. Repeatable.
--timeout <seconds>-tHow long to wait. Defaults to 30.

listen install

$dial listen install

Install the background event daemon (launchd on macOS, systemd user unit on Linux). See Listen service.

listen status

$dial listen status

Report the listen daemon’s state and its last few events.

listen uninstall

$dial 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

$dial local-target add url http://127.0.0.1:8787/dial-events

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

FlagShortDescription
--secret <value>HMAC-SHA256 key. The daemon signs each outgoing body and includes the hex digest in the header named by --signature-header. Omit to skip signing.
--signature-header <name>HTTP header name for the HMAC-SHA256 signature. Defaults to X-Dial-Signature. Ignored unless --secret is also set.
--bearer <token>Static bearer token. Sent as Authorization: Bearer <token> on every POST.
--timeout <seconds>Per-attempt timeout. Defaults to 5. On timeout or non-2xx, the daemon retries the request once, then gives up.

local-target add cmd

$dial local-target add cmd /usr/local/bin/handle-dial-event
$dial local-target add cmd /usr/local/bin/handle-dial-event --log-level info

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:

/usr/local/bin/handle-dial-event '<event_json>'
/usr/local/bin/handle-dial-event --log-level info '<event_json>'

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

FlagShortDescription
--timeout <seconds>Per-attempt timeout. Defaults to 5. On timeout or non-zero exit, the daemon retries the invocation once, then gives up.

local-target remove

$dial local-target remove http://127.0.0.1:8787/dial-events
$dial local-target remove /usr/local/bin/handle-dial-event

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

$dial 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

$dial 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

$dial 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

$dial 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:

$npm uninstall -g @getdial/cli

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.