Skip to navigation

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 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 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.
  • 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’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

CommandWhat it does
doctorReport account state and what to do next.
auth loginEmail a 6-digit sign-up code to an address.
auth verify-otpVerify an email or SMS code, and optionally install the agent skill.
auth register-numberText a 6-digit code to the phone number that will own the account.
number listList the phone numbers on your account.
number purchasePurchase an additional phone number.
number setUpdate a number’s inbound instruction.
number whatsappConnect WhatsApp to a number you already hold (beta).
messageSend a message to a number or a group.
message listList recent messages, newest first.
message replyReply or react to a message.
group listList the group conversations your lines are in.
contactsList every number your lines have texted or called.
lookupCheck what channels a phone number can receive on.
typing startShow a typing indicator to a recipient or group.
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.
call stopCancel a queued or ringing call, or hang up one in progress.
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.

auth login

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

FlagShortDescription
--forceOverwrite any pending sign-up for this email.
--coupon <code>An event coupon code. Gets a new sign-up past a signup pause and adds the coupon’s credit when the account is created. Ignored when signing in to an existing account.

auth verify-otp

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

FlagShortDescription
--code <code>The 6-digit OTP. Omit it when already signed in — the command then only installs the --agent skills and skips verification.
--numberVerify 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

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

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. Ignored with --include-imessage.
--explicit-programmatic-consent <text>Required. A short attestation that the account holder consented to provisioning this number programmatically, e.g. "User modal confirmation". Stored on the number for the provisioning audit trail.
--include-imessageProvision an iMessage number instead of a standard SMS/call number. Pay-as-you-go only, and provisioned asynchronously — poll number list until setupStatus is ready.
--whatsappAlso connect WhatsApp to the new line. Requires --include-imessage: WhatsApp is a channel on an iMessage line, so there’s no standard-number combination to ask for. Beta, enabled per account. The number arrives with WhatsApp already set up and warmed (ready); when none is available right now the purchase is refused and nothing is charged — retry shortly.

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.
--max-call-duration <seconds>Cap calls on this number to at most this many seconds, applied as a hard ceiling to both inbound and outbound calls. The effective cap on a call is the smallest of the per-number, account, and per-call limits.
--clear-max-call-durationRemove the per-number call duration cap.
--forward-to <e164|off>Forward inbound calls to this phone number instead of answering them with the AI voice agent. off stops forwarding.
--first-name <text>First name of the iMessage display identity — the name shown beside this number’s messages in recipients’ Messages apps. iMessage numbers only. Pass an empty string to clear it.
--last-name <text>Last name of the iMessage display identity. iMessage numbers only. Pass an empty string to clear it.
--avatar <path-or-url>iMessage display photo: a local image file (jpeg/png/gif/webp, max 5 MB) to upload, or a public image URL for the server to fetch. iMessage numbers only. Replaces the current photo — a photo can’t be removed.

number whatsapp

dial number whatsapp +14155550123
dial number whatsapp pn_123

Connect WhatsApp to an iMessage number you already hold — the second way into the same flow number purchase --whatsapp takes. Accepts a phone number ID, one of your numbers in E.164, or a nickname.

Asynchronous, and it takes hours: the number comes back with its whatsapp status at provisioning, moves to warming_up once registered, and becomes ready after about 6 hours of warm-up. Wait with a long timeout (dial wait-for number.status_changed -f status=ready -f phoneNumberId=pn_123) or poll number list sparingly before sending on the channel; that track advances independently of the number’s own setupStatus.

WhatsApp is in beta and enabled per account. Without access this answers 404 — the same as an unknown number, because to such an account the capability doesn’t exist. The CLI reports it as an access message rather than “not found”.

Flags

FlagShortDescription
--jsonMachine-readable output.

message

dial message --to +14155550123 --body "Hello" --from-number-id pn_123
dial message --to +14155550123 --media ./note.m4a
dial message --to +14155550123 --poll "Which day works?" --option Mon --option Tue
dial message --group cmtid9uww000b0flk5bq0365a --body "On my way"
dial message --to +14155550123 --channel whatsapp --body "Hello over WhatsApp"

Send a message, optionally with media. At least one of --body and --media is required — a media-only message sends just the attachment.

Give exactly one destination: --to for a phone number, or --group for a group conversation. Passing both, or neither, is refused before any request is made. A group send needs no from-number — the group already belongs to one of your lines — and a from-number naming a different line is refused rather than silently overridden.

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.

On an iMessage number, --poll <title> with two or more --option sends a native poll instead of text (one-to-one or into an iMessage group). It’s exclusive with --body, --media and --force-audio-file; see Send a poll for the limits and how votes arrive.

Flags

FlagShortDescription
--to <e164>Destination phone number, E.164 (e.g. +14155551234). Exactly one of --to or --group.
--group <id>A group conversation to send into instead (see group list). The sending line comes from the group. Exactly one of --to or --group.
--channel <imessage|whatsapp>Which channel to send on, for a line that carries more than one (an iMessage number with WhatsApp connected). Omit to use the number’s own default — a standard number sends SMS, an iMessage number sends iMessage. whatsapp needs a line whose WhatsApp channel is ready; imessage is refused on a number with no iMessage rail rather than sent as an SMS. A WhatsApp send carries at most one attachment.
--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 and WhatsApp). On WhatsApp, --body may accompany only an image or a video.
--force-audio-fileSend an audio attachment as a regular file attachment instead of an iMessage voice message or a WhatsApp voice note.
--poll <title>Send a poll with this title instead of text (iMessage numbers only). Needs at least two --option. Exclusive with --body, --media, --force-audio-file.
--option <text>One poll choice; repeat for each (2 to 10, unique). Only with --poll.

message list

dial message list --direction inbound --since 2026-05-01T00:00:00Z
dial message list --contact +14155550123
dial message list --search "invoice"
dial message list --group cmtid9uww000b0flk5bq0365a

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

A bare message list returns the newest 100 messages across every conversation, so an older thread can be missing from it entirely. Pass --contact to read one person’s conversation as itself, or --search to find a message by its text — both are applied by the API over your whole history rather than to those 100 rows.

On a message sent to a group the recipient column names the group, because a group message has no to — the destination is the group, and which of your numbers the conversation is on is its phoneNumberId.

A poll prints its title followed by each option and its vote count (poll: Which day? — Mon (1) · Tue (0)); a vote on one of your polls prints vote: <option id> or unvote: <option id> with its replyToId naming the poll.

Flags

FlagShortDescription
--number-id <id>Filter to a single phone number.
--contact <e164>One contact’s conversation: messages exchanged with this number, both directions, across every line on the account. Group messages are never included — use --group for those.
--search <text>Match message bodies containing this text, case-insensitively, anywhere in the text. Searches your whole history and returns the 100 most recent matches. Substring, not word search, so voice matches “invoiced”.
--group <id>Filter to one group conversation (see group list). Combines with the other filters. A group ID that isn’t on your account returns nothing rather than an error.
--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.

On a WhatsApp number both are native, in a one-to-one conversation or a group: --react lands on the target message itself (a reaction name maps to its emoji), and --body is a quoted reply. Targets in both directions are accepted.

A --body reply on an iMessage number needs a target that arrived over iMessage or RCS — those carry the message id a thread attaches to. A target that came in over the SMS fallback (the other party has neither) is rejected: send a new dial message instead, which reaches the same conversation.

Flags

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

contacts

dial contacts
dial contacts --number-id <id>
dial contacts --limit 50
dial contacts --json

Every number your lines have exchanged a message or a call with, newest activity first, with that contact’s message and call counts and a preview of the most recent exchange.

A contact is derived from your history, not stored. There is no address book to add anyone to: a number appears here the moment it first texts or calls one of your lines, or one of your lines first reaches it, and it can’t be created or deleted.

Counts span every line on the account. One person who has reached two of your numbers is one contact whose totals are added together — the contact is the person, not the pairing. Pass --number-id to see one line’s contacts instead: the list narrows to the numbers that line has talked to, and each contact’s counts and latest interaction cover that line only.

Group conversations are not contacts. A group message is addressed to the group rather than to a person, so it is counted under neither the group nor the participant who sent it. group list is where those live.

Read one contact’s conversation with message list --contact <e164>, and their calls with call list --contact <e164>.

By default the command follows the cursor until it has every contact. Pass --limit to take a single page instead; when more remain it prints the --starting-after value for the next one.

Flags

FlagShortDescription
--number-id <id>Only the contacts of a single phone number, with that line’s counts. An ID that isn’t on your account returns nothing rather than an error.
--limit <n>Return one page of at most n contacts (1–1000) instead of walking every page.
--starting-after <iso8601>Page cursor: the lastAt of the last contact you received. Only contacts with strictly older activity are returned.
--jsonMachine-readable output. Carries hasMore alongside contacts.

lookup

dial lookup +14155550123
dial lookup +14155550123 --json

What a phone number can receive, so you can pick a channel before you spend a send. Works on any number in the world — it doesn’t have to be one of yours, and you don’t have to have messaged it before.

+14155550123
iMessage yes
WhatsApp no

The answer describes the number you asked about, not your own line. It is a live check rather than a stored property: someone who changes device or turns the service off stops being reachable, so the same number can answer differently another day. Treat a yes as a strong signal for choosing a channel, not as a promise that the send will land.

A lookup that fails exits non-zero rather than reporting no, so a no always means “we asked, and the number isn’t reachable there” — never “we couldn’t find out”.

Flags

FlagShortDescription
--jsonMachine-readable output: { "number", "supports": { "imessage", "whatsapp" } }. Read the channels you need by name — more arrive over time.

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.

Pass --group instead of --to-number to show it in a group conversation, where every member sees it.

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). Exactly one of --to-number or --group.
--group <id>A group conversation to show the indicator in instead (see group list). The line comes from the group. Exactly one of --to-number or --group.
--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. Optional with --group, which already names its line.
--channel <imessage|whatsapp>Which channel to show it on, for a line that carries more than one. Mirrors --channel on message; omit to use the number’s own default. Leave it out with --group — the group already names its channel.

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. Clear a group’s indicator with the same --group you started it with.

Flags

FlagShortDescription
--to-number <e164>Recipient phone number, E.164. Exactly one of --to-number or --group.
--group <id>The group conversation to clear it in. Exactly one of --to-number or --group.
--from-number <ref>Number the indicator appears from — ID, owned E.164, or nickname. Defaults to your onboarded number. Optional with --group.
--channel <imessage|whatsapp>Which channel to clear it on. Pass the same channel typing start was given; leave it out with --group.

group list

dial group list

List the group conversations your lines are in. A group is a conversation that isn’t a phone number, so it carries an ID of its own — pass it as --group to message or message list.

Each row shows the group’s ID, its channel (whatsapp or imessage), and its name. Groups exist on WhatsApp lines and on iMessage numbers, and your line has to already be a member: Dial can send into a group your number was added to, but can’t create one, join one, or accept an invite link.

A group’s name may be blank in the output. It isn’t stored — participants rename groups, so it’s read live from the line holding the conversation, and a line that can’t answer in time yields no name rather than hiding the group. Run it again if you need the name.

There’s no join event: a group your line was just added to shows up here the next time you run it.

Flags

FlagShortDescription
--jsonMachine-readable output.

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.
--contact <e164>One contact’s calls: exchanged with this number, both directions, across every line on the account.
--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.

call stop

dial call stop <call_id>

End a call that hasn’t finished yet (Stop a call). A Queued or Ringing call is cancelled before it connects and ends as canceled; an In-Progress call is hung up and ends as completed. Prints the call with status.cancelPending: true — the call reaches Terminated a moment later, so follow with dial wait-for call.ended --field callId=<call_id> when you need to know it has ended. Fails if the call has already ended.

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 (auth, listen, local-target) as tools.

It reuses the API key saved by dial 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 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 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.