> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.getdial.ai/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.getdial.ai/_mcp/server.

# Commands

> Reference for every dial command, its flags, and --json output.

## 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** — `0` 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`](#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](/documentation/reference/errors#retries-and-idempotency).
* **`DIAL_API_URL`** — environment variable to target a non-default deployment.
* **`DIAL_USER_AGENT`** — optional environment variable identifying the tool or agent runtime driving the CLI. It's **prepended** to the CLI's own `User-Agent`, which is always still sent — it never replaces it. Setting `DIAL_USER_AGENT="nanoclaw/1.1.1"` produces `nanoclaw/1.1.1 @getdial/cli/0.36.0`. Use the conventional `name/version` form. Leave it unset and the header is unchanged.

Run `dial --version` to print the installed version.

### Sandbox mode

When the CLI runs inside a pre-provisioned, ephemeral agent container — for example [NanoClaw](/integrations/nanoclaw)'s per-agent sandboxes — it enters **sandbox mode** and adapts automatically:

* **Host-management commands are turned off** — `auth`, `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_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

| Command                                         | What it does                                                               |
| ----------------------------------------------- | -------------------------------------------------------------------------- |
| [`doctor`](#doctor)                             | Report account state and what to do next.                                  |
| [`auth login`](#auth-login)                     | Email a 6-digit sign-up code to an address.                                |
| [`auth verify-otp`](#auth-verify-otp)           | Verify an email or SMS code, and optionally install the agent skill.       |
| [`auth register-number`](#auth-register-number) | Text a 6-digit code to the phone number that will own the account.         |
| [`number list`](#number-list)                   | List the phone numbers on your account.                                    |
| [`number purchase`](#number-purchase)           | Purchase an additional phone number.                                       |
| [`number set`](#number-set)                     | Update a number's inbound instruction.                                     |
| [`message`](#message)                           | Send an SMS.                                                               |
| [`message list`](#message-list)                 | List recent messages, newest first.                                        |
| [`message reply`](#message-reply)               | Reply or react to a message.                                               |
| [`typing start`](#typing-start)                 | Show a typing indicator to a recipient.                                    |
| [`typing stop`](#typing-stop)                   | Clear a typing indicator.                                                  |
| [`call`](#call)                                 | Place an outbound AI voice call.                                           |
| [`call list`](#call-list)                       | List recent calls, newest first.                                           |
| [`call get`](#call-get)                         | Fetch a single call by ID — status, duration, transcript.                  |
| [`billing`](#billing)                           | Show wallet balance, subscription, per-number mode, and recent activity.   |
| [`wait-for`](#wait-for)                         | Block until a matching account event arrives.                              |
| [`listen install`](#listen-install)             | Install the background event daemon.                                       |
| [`listen status`](#listen-status)               | Report the daemon's state and recent events.                               |
| [`listen uninstall`](#listen-uninstall)         | Stop and remove the daemon.                                                |
| [`local-target add url`](#local-target-add-url) | Register a local HTTP endpoint for event fan-out.                          |
| [`local-target add cmd`](#local-target-add-cmd) | Register an executable run once per event.                                 |
| [`local-target remove`](#local-target-remove)   | Unregister a fan-out target.                                               |
| [`local-target list`](#local-target-list)       | List the fan-out targets currently registered.                             |
| [`mcp`](#mcp)                                   | Run a local stdio MCP server exposing every command as an agent tool.      |
| [`update`](#update)                             | Update the CLI to the latest version (also runs hourly in the background). |
| [`uninstall`](#uninstall)                       | Remove the daemon, agent skills, and all local Dial state.                 |

## Commands

### doctor

```bash
dial doctor
```

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

### auth login

```bash
dial auth login <email>
```

Email a 6-digit sign-up code to the given address. The first step of both signing up and signing in.

**Flags**

| Flag      | Short | Description                                   |
| --------- | ----- | --------------------------------------------- |
| `--force` |       | Overwrite any pending sign-up for this email. |

### auth verify-otp

```bash
dial auth verify-otp --code <code> --agent claude-code
dial auth verify-otp --number --code <code>
```

Submit a one-time code. Without `--number` it verifies the **email** code from `auth login`; with `--number` it verifies the **SMS** code from `auth register-number`.

What happens next depends on the account:

* **Signing in to an existing account** — the email step finishes the job: your key is saved and you're authenticated.
* **Creating a new account** — the email step confirms your email and prints the `auth register-number` command to run next. Your key is issued only after the phone code is verified.

Pass `--agent` to install the Dial skill into that agent's config directory. Skills install on the email step in both cases, so an agent has the instructions it needs to finish signing up.

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. Omitting `--code` while *not* signed in exits non-zero and tells you to run `dial auth login <email>` first. Passing `--number` with no phone registration pending also exits non-zero, pointing you at `auth register-number`.

**Flags**

| Flag                     | Short | Description                                                                                                                                                        |
| ------------------------ | ----- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `--code <code>`          |       | The 6-digit OTP. Omit it when already signed in — the command then only installs the `--agent` skills and skips verification.                                      |
| `--number`               |       | Verify the SMS code for a registered phone number instead of the email code.                                                                                       |
| `--verification-id <id>` |       | Explicit verification ID for the email step. 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. |

### auth register-number

```bash
dial auth register-number <phone>
```

Text a 6-digit code to the phone number that will own the account. Required to create an account; never needed to sign in.

The number must be able to receive SMS and should be one you keep; a Dial number can't be used to register a Dial account. Re-run it with the same number to resend the code, or with a different number to correct a mistake before verifying.

Follow it with `dial auth verify-otp --number --code <code>`.

### number list

```bash
dial number list
```

List the phone numbers on your account.

### number purchase

```bash
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**

| Flag                                    | Short | Description                                                                                                                                                                                      |
| --------------------------------------- | ----- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `--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

```bash
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**

| Flag                                    | Short | Description                                                                                                                                                                            |
| --------------------------------------- | ----- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--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

```bash
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**

| Flag                    | Short | Description                                                                                                                                                         |
| ----------------------- | ----- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--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-file`    |       | Send an audio attachment as a regular file attachment instead of an iMessage voice message.                                                                         |

### message list

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

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

**Flags**

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

### message reply

```bash
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**

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

### typing start

```bash
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**

| Flag                  | Short | Description                                                                                                                           |
| --------------------- | ----- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `--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

```bash
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**

| Flag                  | Short | Description                                                                                          |
| --------------------- | ----- | ---------------------------------------------------------------------------------------------------- |
| `--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

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

Place an outbound voice call.

**Flags**

| Flag                            | Short | Description                                                                                                                                                                                                                              |
| ------------------------------- | ----- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--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](/documentation/reference/errors#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

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

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

**Flags**

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

### call get

```bash
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

```bash
dial billing
```

Show the account's [billing status](/api-reference/billing/get-billing): 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](https://app.getdial.ai/dashboard/billing) (embedded Stripe Checkout and one-click subscription actions), not from the CLI.

### wait-for

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

Block until a matching event arrives. Reads from the local [listen service](/documentation/cli/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**

| Flag                     | Short | Description                                                                 |
| ------------------------ | ----- | --------------------------------------------------------------------------- |
| `--field <name=value>`   | `-f`  | Exact match on a top-level event field. Repeatable.                         |
| `--regex <name=pattern>` | `-r`  | Regex match on a field. Pattern is `/re/flags` or a bare regex. Repeatable. |
| `--timeout <seconds>`    | `-t`  | How long to wait. Defaults to `30`.                                         |

### listen install

```bash
dial listen install
```

Install the background event daemon (launchd on macOS, systemd user unit on Linux). See [Listen service](/documentation/cli/listen-service).

### listen status

```bash
dial listen status
```

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

### listen uninstall

```bash
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](/integrations/methods/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](/integrations/methods/cli-command-target)).

Both variants require the listen daemon to be installed ([`dial listen install`](#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

```bash
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**

| Flag                        | Short | Description                                                                                                                                         |
| --------------------------- | ----- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--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

```bash
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**

| Flag                  | Short | Description                                                                                                                   |
| --------------------- | ----- | ----------------------------------------------------------------------------------------------------------------------------- |
| `--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

```bash
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

```bash
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

```bash
dial mcp
```

Run a local [Model Context Protocol](https://modelcontextprotocol.io) 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 (`auth`, `listen`, `local-target`) as tools.

It reuses the API key saved by [`dial auth verify-otp`](#auth-verify-otp) (`~/.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](/integrations/tools/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

```bash
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

```bash
dial uninstall
```

Remove everything Dial put on this machine, without prompting: stop and remove the [listen daemon](/documentation/cli/listen-service), 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:

```bash
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 auth login` + `dial auth verify-otp`). With `--json`, the result reports each step's outcome plus an `errors` array; the exit code is `0` when every step succeeded, `2` otherwise.