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

# Phone numbers

> Search live inventory, buy a number in one call, bind it to an inbox and an agent, and release it when you no longer need it.

Every call needs a `from` — a number your account owns. You search live inventory, buy a number in one call, and it becomes the caller id your agent dials from and a texting endpoint for your inbox.

| Operation         | Method & path                     | Scope         |
| ----------------- | --------------------------------- | ------------- |
| Search inventory  | `GET /v1/phone/numbers/available` | `phone:read`  |
| List your numbers | `GET /v1/phone/numbers`           | `phone:read`  |
| Buy               | `POST /v1/phone/numbers`          | `phone:write` |
| Get one           | `GET /v1/phone/numbers/{id}`      | `phone:read`  |
| Configure         | `PATCH /v1/phone/numbers/{id}`    | `phone:write` |
| Release           | `DELETE /v1/phone/numbers/{id}`   | `phone:write` |

## Search available numbers

Filter by `country` (ISO 3166-1 alpha-2, default `US`), `areaCode`, a `contains` digit sequence, and `type` (`local`, `toll_free`, `national`, `mobile`). `limit` is 1–100 and defaults to 10.

<CodeGroup>
  ```bash title="cURL" theme={null}
  curl "https://api.dairo.app/v1/phone/numbers/available?country=US&areaCode=415&limit=5" \
    -H "Authorization: Bearer $DAIRO_API_KEY"
  ```

  ```ts title="TypeScript" theme={null}
  const available = await dairo.phoneNumbers.available({ country: "US", areaCode: "415", limit: 5 });
  for (const n of available.data) console.log(n.phoneNumber, n.monthlyCostUsd, n.purchasable);
  ```

  ```python title="Python" theme={null}
  available = dairo.phone_numbers.available(country="US", area_code="415", limit=5)
  for n in available:
      print(n.phone_number, n.monthly_cost_usd, n.purchasable)
  ```

  ```bash title="CLI" theme={null}
  dairo phone numbers search --country US --area-code 415 --limit 5
  ```

  ```text title="MCP" theme={null}
  Tool: list_phone_numbers  (scope phone:read)
  Args: { "action": "available", "country": "US", "areaCode": "415", "limit": 5 }
  ```
</CodeGroup>

```json theme={null}
{
  "object": "list",
  "data": [
    {
      "object": "phone_number.available",
      "phoneNumber": "+14155550123",
      "countryCode": "US",
      "numberType": "local",
      "locality": "SAN FRANCISCO",
      "region": "CA",
      "capabilities": ["voice", "sms"],
      "monthlyCostUsd": 1.0,
      "setupCostUsd": 1.0,
      "purchasable": true
    }
  ]
}
```

`capabilities` tells you what the number can do (`voice`, `sms`); `monthlyCostUsd` and `setupCostUsd` are the recurring and one-time cost of owning it.

<Note>
  A result with `purchasable: false` and a masked number like `+16313------` is real inventory your account can't buy yet: additional identity verification is required before those numbers unlock. It isn't an error and it isn't hidden from you — pick a `purchasable: true` number instead.
</Note>

## Buy a number

Pass the exact E.164 number from a search result. You get back a `phone_number` record; when the country requires no paperwork it arrives with `status: "active"`, immediately usable as a caller id. Buying a number you already own returns `409`, and so does a number that is no longer available.

<CodeGroup>
  ```bash title="cURL" theme={null}
  curl -X POST https://api.dairo.app/v1/phone/numbers \
    -H "Authorization: Bearer $DAIRO_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{ "phoneNumber": "+14155550123" }'
  ```

  ```ts title="TypeScript" theme={null}
  const number = await dairo.phoneNumbers.buy({ phoneNumber: "+14155550123" });
  console.log(number.id, number.status); // … active
  ```

  ```python title="Python" theme={null}
  number = dairo.phone_numbers.buy(phone_number="+14155550123")
  print(number.id, number.status)  # … active
  ```

  ```bash title="CLI" theme={null}
  dairo phone numbers buy +14155550123
  ```

  ```text title="MCP" theme={null}
  Tool: manage_phone_numbers  (scope phone:write, confirm required)
  Args: { "action": "buy", "phoneNumber": "+14155550123", "confirm": true }
  ```
</CodeGroup>

```json theme={null}
{
  "object": "phone_number",
  "id": "b4e7d9f0-2a1b-4c8e-9f3a-7d0a1f2c5b6e",
  "phoneNumber": "+14155550123",
  "countryCode": "US",
  "numberType": "local",
  "capabilities": ["voice", "sms"],
  "status": "active",
  "inboxId": null,
  "agentId": null,
  "monthlyCostUsd": 1.0,
  "setupCostUsd": 1.0,
  "requirements": { "status": null, "items": [] },
  "metadata": {},
  "purchasedAt": "2026-07-09T13:58:02Z",
  "createdAt": "2026-07-09T13:58:02Z"
}
```

With an `active` number in hand, use it as the `from` on [Making calls](/phone/making-calls).

### Regulated countries: `pending` is not an error

Some countries regulate who may hold a local number. Germany, for example, requires proof of a local address before a number activates. Buying a number there succeeds, but the order settles at `status: "pending"` with a `requirements` object listing exactly which documents are outstanding:

```json theme={null}
{
  "object": "phone_number",
  "phoneNumber": "+4930120849110",
  "countryCode": "DE",
  "status": "pending",
  "requirements": {
    "status": "pending",
    "items": [
      { "name": "proof_of_address", "description": "A utility bill or registration certificate showing a German address" }
    ]
  }
}
```

The purchase is reserved for you. Poll `GET /v1/phone/numbers/{id}` to watch the status: once the listed requirements are met, the number turns `active`. A number's `status` is one of `active`, `pending`, or `released` — only an `active` number can be a call's `from`.

## Configure a number

Two bindings turn a bare number into part of your fleet — set either, both, or clear them with an explicit `null`:

* **`inboxId`** — inbound SMS to this number lands in that [inbox](/receiving/inboxes)'s mailbox, next to your email and other channels. Texts become messages you read, search, and reply to like everything else.
* **`agentId`** — attribute the number to one of your [agents](/agents/agent-passport), so its calls and texts carry clear provenance.

<CodeGroup>
  ```bash title="cURL" theme={null}
  curl -X PATCH https://api.dairo.app/v1/phone/numbers/b4e7d9f0-2a1b-4c8e-9f3a-7d0a1f2c5b6e \
    -H "Authorization: Bearer $DAIRO_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{ "inboxId": "inbox_123", "agentId": "agt_456" }'
  ```

  ```ts title="TypeScript" theme={null}
  const number = await dairo.phoneNumbers.update("b4e7d9f0-2a1b-4c8e-9f3a-7d0a1f2c5b6e", {
    inboxId: "inbox_123",
    agentId: "agt_456",
  });
  ```

  ```python title="Python" theme={null}
  number = dairo.phone_numbers.update(
      "b4e7d9f0-2a1b-4c8e-9f3a-7d0a1f2c5b6e",
      inbox_id="inbox_123",
      agent_id="agt_456",
  )
  ```

  ```bash title="CLI" theme={null}
  dairo phone numbers update b4e7d9f0-2a1b-4c8e-9f3a-7d0a1f2c5b6e \
    --inbox inbox_123 --agent agt_456
  ```

  ```text title="MCP" theme={null}
  Tool: manage_phone_numbers  (scope phone:write)
  Args: { "action": "update", "phoneNumberId": "b4e7d9f0-2a1b-4c8e-9f3a-7d0a1f2c5b6e", "inboxId": "inbox_123", "agentId": "agt_456" }
  ```
</CodeGroup>

You can replace the number's `metadata` in the same call; the request must carry at least one of `inboxId`, `agentId`, or `metadata`. Binding an inbox or agent you don't own is rejected. List everything you own with `GET /v1/phone/numbers` — add `includeReleased=true` to include released numbers, which stay on your account for the audit trail.

## Release a number

Releasing returns the number to the public pool — anyone can buy it afterwards, and whoever does receives the calls and texts meant for you. Because that is irreversible, the API refuses to release without an explicit `confirm: true` in the body (`400` without it), and releasing an already-released number returns `409`. A successful release returns the number's final record with `status: "released"` and `releasedAt` set.

<CodeGroup>
  ```bash title="cURL" theme={null}
  curl -X DELETE https://api.dairo.app/v1/phone/numbers/b4e7d9f0-2a1b-4c8e-9f3a-7d0a1f2c5b6e \
    -H "Authorization: Bearer $DAIRO_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{ "confirm": true }'
  ```

  ```ts title="TypeScript" theme={null}
  await dairo.phoneNumbers.release("b4e7d9f0-2a1b-4c8e-9f3a-7d0a1f2c5b6e", { confirm: true });
  ```

  ```python title="Python" theme={null}
  dairo.phone_numbers.release("b4e7d9f0-2a1b-4c8e-9f3a-7d0a1f2c5b6e", confirm=True)
  ```

  ```bash title="CLI" theme={null}
  dairo phone numbers release b4e7d9f0-2a1b-4c8e-9f3a-7d0a1f2c5b6e --confirm
  ```

  ```text title="MCP" theme={null}
  Tool: manage_phone_numbers  (scope phone:write, confirm required)
  Args: { "action": "release", "phoneNumberId": "b4e7d9f0-2a1b-4c8e-9f3a-7d0a1f2c5b6e", "confirm": true }
  ```
</CodeGroup>

<Warning>
  Release is **irreversible**. A released number stops receiving calls and texts for you, monthly charges end, and the number returns to the pool for anyone to claim. If a number is printed on letterheads or saved in customers' contacts, keep it.
</Warning>
