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

# Phone Numbers

> Get numbers directly from Unpod or bring your own, then attach them to your agents.

## Overview

A [Number](/get-started/core-concepts#number) is the phone number callers
dial; it routes to whatever agent it is attached to. Phone numbers in
Unpod come from two sources:

* **Unpod Numbers** (default) - provision directly from the Unpod platform. No carrier account, no [trunk](#trunks), no SIP.
* **BYON (Bring Your Own Number)** - route numbers you already own from any provider over a [trunk](#trunks) you register once.

Once a number is in your account, you attach it to an `agent_id`. All inbound calls to that number are routed to that agent.

<Note>
  Support for Twilio, Telnyx, and Plivo numbers is coming soon - you will be able to import numbers from these providers directly into Unpod.
</Note>

***

## Number Sources

| Source     | Description                                        |
| ---------- | -------------------------------------------------- |
| **Unpod**  | Provision numbers directly from the Unpod platform |
| **BYON**   | Bring numbers from your existing provider          |
| **Twilio** | Coming soon                                        |
| **Telnyx** | Coming soon                                        |
| **Plivo**  | Coming soon                                        |

***

## Getting Numbers from Unpod

Numbers provisioned through Unpod are immediately available in your account. You can browse and acquire them from the dashboard under **Dev Platform -> Numbers**.

Once provisioned, list them via the SDK:

```python theme={null}
import asyncio
from unpod import AsyncClient

async def main():
    async with AsyncClient() as client:  # reads UNPOD_API_KEY
        numbers = await client.numbers.list()
        for n in numbers:
            print(n.number_id, n.number, n.status, n.pipe_id or "unattached")

asyncio.run(main())
```

***

## Trunks

A [Trunk](/get-started/core-concepts#trunk) is your carrier connection - the SIP
capacity numbers ride on. Bring your own carrier (Twilio, Tata, …), register it
once with its SIP credentials, then work with numbers, not SIP. BYON numbers
arrive this way.

<Steps>
  <Step title="Add the trunk">
    **Dev Platform -> Telephony -> Add Trunk -> BYO SIP**. Provide the carrier's
    **SIP domain** (e.g. `sip.your-carrier.com`) and **auth username / password**.
  </Step>

  <Step title="Point the carrier at Unpod">
    Set inbound routing to the Unpod SIP endpoint shown in the dashboard.
  </Step>

  <Step title="Sync">
    Click **Sync** on the trunk, or run `await client.numbers.sync()`. The trunk's
    numbers appear in your account, ready to attach to an [agent](/speech-stack/agents).
  </Step>
</Steps>

### Register a trunk via the SDK

Field names match the dashboard.

```python theme={null}
from unpod import AsyncClient
from unpod.models import TrunkCreate, ByoConfigCreate

client = AsyncClient()

trunk = await client.trunks.create(TrunkCreate(
    name="tata-byo",
    type="byo",
    byo_config=ByoConfigCreate(
        provider="tata",
        sip_domain="sip.tata.in",
        auth_username="user",
        auth_password="secret",
        transport="tls",   # default
    ),
))
```

### List and delete trunks

```python theme={null}
trunks = await client.trunks.list()
await client.trunks.delete(trunk.trunk_id)
```

### Sync numbers off a trunk

```python theme={null}
result = await client.numbers.sync()   # {"synced": int, "new": int}
```

One-time provisioning end to end (profile, agent, number, env vars):
[Provisioning checklist](/speech-stack/setup-checklist).

***

## Listing Numbers

```python theme={null}
import asyncio
from unpod import AsyncClient

async def main():
    async with AsyncClient() as client:  # reads UNPOD_API_KEY
        # All numbers
        numbers = await client.numbers.list()

        # Filter by status or country
        active = await client.numbers.list(status="active")
        us_numbers = await client.numbers.list(country="US")
        byon = await client.numbers.list(trunk_type="byo")

        for n in numbers:
            print(n.number_id, n.number, n.status, n.pipe_id or "unattached")

asyncio.run(main())
```

### Number fields

| Field        | Type          | Description                                                                                                     |
| ------------ | ------------- | --------------------------------------------------------------------------------------------------------------- |
| `number_id`  | `str`         | Unique number ID (`num_...`)                                                                                    |
| `number`     | `str`         | E.164 format, e.g. `+14155550100`                                                                               |
| `status`     | `str`         | `active`, `inactive`, `pending`                                                                                 |
| `trunk_type` | `str`         | `unpod` or `byo`                                                                                                |
| `pipe_id`    | `str \| None` | The agent-voice row this number is attached to, or `None` if free. Still spelled `pipe_id` on the number record |
| `country`    | `str`         | ISO 3166-1 alpha-2 country code                                                                                 |

***

## Attaching a Number to an Agent

A number can be attached to exactly one agent at a time. Inbound calls to that
number are routed to that agent.

```python theme={null}
import asyncio
from unpod import AsyncClient

async def main():
    async with AsyncClient() as client:  # reads UNPOD_API_KEY
        number = await client.numbers.attach("num_...", "support-bot")
        print("Attached:", number.number, "-> agent support-bot")

asyncio.run(main())
```

### `attach()` parameters

| Parameter   | Type          | Description                                                                                               |
| ----------- | ------------- | --------------------------------------------------------------------------------------------------------- |
| `number_id` | `str`         | The number's id (`num_...`)                                                                               |
| `agent_id`  | `str`         | The agent to route calls to                                                                               |
| `number`    | `str \| None` | The E.164, optional. Pass it when `number_id` is a supervoice id so telephony can resolve the row it owns |

<Warning>
  `attach()` used to take **`pipe_id=`**. It takes `agent_id` now - supervoice
  stopped storing a pipe pin and resolves the pipe from the agent instead. Old
  calls raise a `TypeError`.
</Warning>

`client.agents.numbers.attach(agent_id, number)` does the same job keyed by the
E.164 number rather than the number id - see [Agents](/speech-stack/agents).

***

## Detaching a Number

```python theme={null}
import asyncio
from unpod import AsyncClient

async def main():
    async with AsyncClient() as client:  # reads UNPOD_API_KEY
        number = await client.numbers.detach("num_...")
        print("Detached:", number.number)

asyncio.run(main())
```

***

## Common Patterns

### Find the first free number and attach it

```python theme={null}
import asyncio
from unpod import AsyncClient

async def attach_first_free(agent_id: str) -> None:
    async with AsyncClient() as client:  # reads UNPOD_API_KEY
        numbers = await client.numbers.list(status="active")
        free = [n for n in numbers if n.pipe_id is None]
        if not free:
            raise RuntimeError("No free numbers available")
        await client.numbers.attach(free[0].number_id, agent_id)
        print("Attached", free[0].number)

asyncio.run(attach_first_free("support-bot"))
```

### Rotate numbers across agents

```python theme={null}
import asyncio
from unpod import AsyncClient

async def rotate(numbers: list[str], agents: list[str]) -> None:
    async with AsyncClient() as client:  # reads UNPOD_API_KEY
        for num_id, agent_id in zip(numbers, agents):
            await client.numbers.attach(num_id, agent_id)
            print(f"{num_id} -> {agent_id}")

asyncio.run(rotate(["num_a", "num_b"], ["support-bot", "sales-bot"]))
```

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Voice Profiles" icon="audio-lines" href="/speech-stack/voice-profiles">
    Choose the voice profile your agent speaks with.
  </Card>

  <Card title="Agents" icon="robot" href="/speech-stack/agents">
    Create and configure the agent a number routes to.
  </Card>
</CardGroup>
