# Agent Phone: give an AI agent a phone line

> Provision a real business phone line end to end through the API: buy a number, register 10DLC, configure and publish the AI agent, route calls to it, and place calls.

Source: https://dev.ody.co/guides/agent-phone

**Agent Phone** is the name for the part of the API that lets software (your backend, or an AI agent over MCP) set up a complete business phone line on its own: a real number, texting registration, an AI receptionist answering calls, and outbound AI calling.

The rule that makes it safe: **everything an agent provisions appears in the customer's Ody account exactly as if they'd done it in the app.** It's billed on the same subscription, held to the same gates, and shown on the same timeline. A human can take over at any time.

```text
1. key ─► 2. buy number ─► 3. register 10DLC ─► 4. configure agent ─► 5. route number ─► 6. call & text
          numbers:manage    numbers:manage        agent:manage           agent:manage        calls:place / messages:send
```

> **Rehearse with a test key:**
Run the whole guide with an `ody_test_…` key first. The purchase, 10DLC filing (mock mode), publishing and calls are all simulated, and the responses have the same shape as live ones.

## 1. Get a key

An owner or admin creates it in **Settings → Developer API** with these scopes: `numbers:read`, `numbers:manage`, `agent:manage`, `calls:place`, `messages:send`, `conversations:read`. The workspace must already have a plan, and have completed identity verification where the app requires it. The API can't skip those steps, by design.

## 2. Buy a number

Search local inventory by area code, then buy one:

cURL:

```bash
curl "https://api.ody.co/v1/api/numbers/available?startsWith=512&limit=5" \
  -H "Authorization: Bearer $ODY_API_KEY"

curl https://api.ody.co/v1/api/numbers \
  -H "Authorization: Bearer $ODY_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: acme-main-line" \
  -d '{"phoneNumber":"+15125550188"}'
```
TypeScript:

```ts
const [pick] = await ody.numbers.searchAvailable({ startsWith: "512", limit: 5 });
const { number } = await ody.numbers.buy({ phoneNumber: pick.phone_number, idempotencyKey: "acme-main-line" });
```
Python:

```python
pick = client.numbers.search_available(starts_with="512", limit=5)[0]
res = client.numbers.buy(phone_number=pick["phone_number"], idempotency_key="acme-main-line")
```

The first local number is included in the plan; extra local numbers are $5/mo. Toll-free numbers (`type=toll_free`) are $10/mo and **calling-only** for now, so skip step 3 for them.

## 3. Register for texting (10DLC)

US carriers require this before a local number can text. It's a **purchase**: on a free trial it ends the trial and charges the plan plus $20 for the number right away. Tell the customer first, then pass `acknowledge_trial_end`.

```bash
curl https://api.ody.co/v1/api/messaging/registration \
  -H "Authorization: Bearer $ODY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "business": {
      "displayName": "Acme Plumbing", "companyName": "Acme Plumbing LLC", "ein": "12-3456789",
      "email": "owner@acmeplumbing.com", "vertical": "PROFESSIONAL",
      "street": "100 Congress Ave", "city": "Austin", "state": "TX", "postalCode": "78701"
    },
    "campaign": {
      "useCase": "CUSTOMER_CARE",
      "optIn": { "method": "web_form", "url": "https://acmeplumbing.com/contact",
                 "location": "the quote form at the bottom of the contact page", "formConfirmed": true }
    },
    "acknowledge_trial_end": true
  }'
```

Carrier review takes minutes to days. Poll until `canText` is `true`:

```ts
let status = await ody.messaging.refresh();
while (!status.canText && !status.step.endsWith("_failed")) {
  await new Promise((r) => setTimeout(r, 15 * 60_000)); // every 15 min
  status = await ody.messaging.refresh();
}
```

Sole proprietors also get a PIN by text: submit it with [`POST /messaging/otp/verify`](https://dev.ody.co/api/messaging#verify-a-sole-proprietor-pin). All the fields are in the [10DLC reference](https://dev.ody.co/api/messaging).

## 4. Configure and publish the agent

```ts
await ody.agent.update({
  name: "Astra",
  greeting: "Thanks for calling Acme Plumbing, this is Astra. How can I help?",
  voice: "luna",
  timezone: "America/Chicago",
  inHoursAction: "agent",
  offHoursAction: "agent",
  jobs: [
    { name: "Book appointments", instructions: "Collect name, address and the problem; offer the next two slots." },
    { name: "Emergencies", instructions: "For burst pipes or flooding, take the address and promise a callback within 15 minutes." },
  ],
});
await ody.agent.publish();
```

## 5. Route the number to the agent

```bash
curl -X POST https://api.ody.co/v1/api/numbers/%2B15125550188/connect \
  -H "Authorization: Bearer $ODY_API_KEY"
```

Inbound calls to that number are now answered by the agent. Recordings, transcripts and summaries land in the inbox, and you get them as [`call.completed`](https://dev.ody.co/webhooks/events#call-completed) and [`call.summary.completed`](https://dev.ody.co/webhooks/events#call-summary-completed) webhooks.

## 6. Call and text

Outbound AI call:

```ts
await ody.calls.place({ to: "+15125550123" }); // the agent dials, talks, and logs the call
```
Text (after 10DLC):

```ts
await ody.messages.send({ to: "+15125550123", body: "Hi! This is Acme Plumbing, confirming tomorrow's 2pm visit." });
```

## Doing it from an AI agent (MCP)

Every step above has an MCP tool: `search_numbers`, `buy_number`, `submit_10dlc_registration`, `get_messaging_status`, `configure_agent` (with `publish: true`), `connect_agent_to_number`, `place_call`, `send_message`. Connect a client ([setup](https://dev.ody.co/mcp/connect)) and ask:

> *"Set up a phone line for Acme Plumbing in Austin: get a 512 number, have the AI answer calls, book appointments and flag emergencies."*

Keep approvals on for `buy_number`, `submit_10dlc_registration` and `place_call`. See [Safety](https://dev.ody.co/mcp/safety).

## Handing over to a human

Nothing here is hidden from the customer. They see the number in **Settings → Numbers**, the agent under **Astra**, and every call and text in the inbox. They can re-route the number, edit the agent or take over a conversation at any time, and your integration sees those changes on its next read.
