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

# Give your AI agent a phone

> Connect Grok, Hermes, OpenClaw, Claude Code or any MCP agent to Assistable so it can place AI phone calls and read the results.

Assistable runs a hosted [Model Context Protocol](https://modelcontextprotocol.io)
server at `https://api.assistable.ai/mcp`. Any agent that speaks MCP over
Streamable HTTP can use it to place outbound calls with an Assistable voice
assistant, wait for the call to finish, and read the transcript and recording.

> [!NOTE]
>
> Without a key the server lists only its basic tools. If `phone_whoami` is
> missing from your agent's tool list, check that the key is sent and has the
> `subaccounts:read`, `billing:read` and `numbers:list` scopes. The
> [Tools](#tools) table lists the scopes for every phone tool.

## What you need

* An Assistable account with a voice assistant and a phone number assigned to it.
* An API key from **Settings → API keys** (see [Authentication](/v3/authentication.md)).
  Use a key limited to **one subaccount**: the server then targets that subaccount
  automatically, so the agent only needs one header.
* Calls are billed to the account at its normal usage rates.

Every request to the MCP server carries the key:

```http theme={null}
Authorization: Bearer ask_live_…
```

For a key that covers several subaccounts, also send `X-Subaccount-Id: <subaccount-id>`.

## Connect your agent

**Grok CLI**

```bash theme={null}
grok mcp add --transport http assistable https://api.assistable.ai/mcp \
  --header "Authorization: Bearer ask_live_…"
```

This writes the key to `~/.grok/config.toml`. Avoid `--scope project`, which writes it to
`./.grok/config.toml` inside the project, where it can be committed by mistake.

**Hermes**

In `~/.hermes/config.yaml`:

```yaml theme={null}
mcp_servers:
  assistable:
    url: "https://api.assistable.ai/mcp"
    headers:
      Authorization: "Bearer ask_live_…"
```

Run `/reload-mcp`. Tools appear as `mcp_assistable_<tool>`.

**OpenClaw**

```json theme={null}
{
  "mcp": {
    "servers": {
      "assistable": {
        "url": "https://api.assistable.ai/mcp",
        "transport": "streamable-http",
        "headers": { "Authorization": "Bearer ask_live_…" }
      }
    }
  }
}
```

**Claude Code**

```bash theme={null}
claude mcp add --transport http assistable https://api.assistable.ai/mcp \
  --header "Authorization: Bearer ask_live_…"
```

**Other clients**

Point any Streamable HTTP MCP client at `https://api.assistable.ai/mcp` with an
`Authorization: Bearer ask_live_…` header. Prefer a secret store or environment
variable over pasting the key into a shared config file.

## Place a call

A typical run, in the order an agent should use the tools:

**1. Check the account**

`phone_whoami` returns the subaccount, wallet balance, owned numbers, and the
default assistant when there is exactly one.

**2. Preview the call**

`calls_create` with `assistant_id` and `to` (E.164, e.g. `+15551234567`) and
no `confirm` returns a preview and places nothing.

**3. Place it**

Call `calls_create` again with `confirm: true` once the person you act for has
approved the number and the assistant. The result includes `call_id`.

**4. Wait for the result**

`calls_wait` with `call_id` blocks up to 45 seconds. If `meta.timed_out` is
`true`, call it again. It returns when the call is `COMPLETED`, `BUSY`,
`FAILED`, `NO_ANSWER` or `CANCELED`.

**5. Read the outcome**

`calls_get` returns the status, summary, transcript and recording.

Pass `variables` to `calls_create` to give the assistant context for this call,
for example the reservation time and party size. Pass `contact_id` to attach the
call to an existing contact.

## Show your own number

To make the call appear from a number the person owns, such as their cell:

1. `caller_id_add` with `number` and `confirm: true`. Assistable sends a code to
   that number by SMS (or `method: "call"` for a voice call).
2. `caller_id_confirm` with the `id` that `caller_id_add` returned as `number_id`,
   and the code the person received.
3. Pass the verified number as `from` to `calls_create`.

`caller_ids_list` shows each caller ID and whether it is verified. A number can
only be used as caller ID by the account that verified it.

## Send a text

`sms_send` with `to` (a US or Canadian number), `text` and `confirm: true` texts
from the account's number on an approved A2P 10DLC campaign. Without `confirm` it
returns a preview and sends nothing. Each text is billed \$0.07.

Texting needs a number registered for A2P 10DLC whose campaign carriers have
approved. Until then `sms_send` returns `sms_not_ready`.

## Tools

| Tool | What it does | Scope |
| - | - | - |
| `phone_whoami` | Subaccount, wallet balance, owned numbers, default assistant | `subaccounts:read`, `billing:read`, `numbers:list` |
| `numbers_list`, `numbers_get` | Owned phone numbers and their assistants | `numbers:list`, `numbers:read` |
| `numbers_assign` | Point a number at an assistant (needs `confirm: true`) | `numbers:update` |
| `voices_list` | Available voices | `voices:list` |
| `calls_create` | Place an outbound call (needs `confirm: true`) | `calls:create` |
| `calls_wait` | Wait up to 45 seconds for a call to finish | `calls:read` |
| `calls_get`, `calls_list` | Call detail, transcript and recording; call history | `calls:read`, `calls:list` |
| `messages_list` | Messages in a conversation | `messages:list` |
| `sms_send` | Text a number from an A2P-approved number (needs `confirm: true`) | `messages:create` |
| `caller_ids_list` | Caller IDs and their verification status | `numbers:list` |
| `caller_id_add`, `caller_id_confirm` | Verify a number the person owns (add needs `confirm: true`) | `numbers:create`, `numbers:update` |

Tools the key's scopes do not allow are left out of the tool list. The server
also exposes assistant, contact, conversation and appointment tools, and
`search_tools` to find a tool by intent.

## When a call is refused

Refusals come back as an error with a `details.reason` you can show the person:

| Reason | Meaning |
| - | - |
| `outside_outbound_window` | Outside the assistant's calling hours; `details.msUntilOpen` says when they open |
| `DNC_PLATFORM`, `DNC_INTERNAL` | The number is on a Do Not Contact list |
| `caller_id_unverified` | `from` is a caller ID this account has not verified |
| `from_not_owned` | `from` is not one of this account's numbers or verified caller IDs |
| `self_call` | `to` and `from` are the same number |
| `insufficient_balance` | The wallet is too low; `details.top_up_url` opens the page to add funds |

Platform numbers can only call US and Canadian (`+1`) numbers.

## When a text is refused

| Reason | Meaning |
| - | - |
| `sms_not_ready` | No number in the account is on an approved A2P campaign |
| `DNC_PLATFORM`, `DNC_INTERNAL` | The number is on a Do Not Contact list |
| `recipient_opted_out` | The person replied STOP to this account's texts |
| `outside_sms_window` | Texts go out only from 8am to 9pm in the account's time zone |
| `daily_sms_limit` | The account sent its 200 texts for today |
| `insufficient_balance` | The wallet is too low; `details.top_up_url` opens the page to add funds |
| `sms_unconfirmed` | The carrier did not confirm the text; check before sending it again |

## Safety

* `calls_create`, `sms_send`, `numbers_assign` and `caller_id_add` do nothing
  without `confirm: true`. Confirm the number, the assistant and the purpose
  with the person you act for first.
* Every call dials a real phone and every text reaches a real person, and both
  are billed to the account.
* Follow the calling and consent rules where the call lands. In the US, the TCPA
  treats AI-generated voices as artificial voices, so calls to consumers need
  their prior consent. Texts to consumers need their opt-in too.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.