> ## 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.

# Manage Trunks

> Create, inspect and remove SIP trunks and their numbers

## SIP trunks

A trunk holds your carrier's SIP details: its SIP URL, credentials and the addresses it
sends calls from. Map your numbers to a trunk and Unpod returns the **origin endpoint** -
where your carrier sends calls for those numbers.

```mermaid theme={null}
flowchart LR
  A["📞<br/><b>Your carrier</b>"] ==> B["📡<br/><b>Unpod SIP trunk</b>"]
  B ==> C["🔢<br/><b>Your numbers</b>"]

  classDef ep fill:#5a4fff,color:#fff,stroke:#3d34d9,stroke-width:3px;
  classDef pp fill:#796cff,color:#fff,stroke:#5a4fff,stroke-width:2px;
  class A,C ep;
  class B pp;
  linkStyle default stroke:#9a90ff,stroke-width:2.5px;
```

Setting up your first trunk? Follow the [Quick Start](https://docs.unpod.ai/telephony/quickstart.md). This page
covers each operation on its own. The samples read your
[credentials](https://docs.unpod.ai/telephony/introduction.md#credentials) (`UNPOD_BASE_URL`,
`UNPOD_PLATFORM_TOKEN`, `UNPOD_ORG_HANDLE`) from the environment.

## List trunks

```python Python theme={null}
from unpod import Client

with Client() as client:
    for t in client.telephony.trunks.list():
        print(t.id, t.name, t.sip_url, t.transport, t.active)
```

```bash cURL theme={null}
curl -s "https://unpod.ai/api/v2/platform/telephony/trunks/" \
  -H "Authorization: Token $UNPOD_PLATFORM_TOKEN" -H "Org-Handle: $UNPOD_ORG_HANDLE"
```

Returns your org's trunks, newest first. `auth_password` is masked. See
[List Trunks](https://docs.unpod.ai/telephony/trunks/list-trunks.md).

## Get a trunk

```python Python theme={null}
from unpod import Client

TRUNK_ID = 21  # your trunk id

with Client() as client:
    trunk = client.telephony.trunks.get(TRUNK_ID)
    print(trunk.name, trunk.sip_url, trunk.allowed_ips)
```

```bash cURL theme={null}
TRUNK_ID=21  # your trunk id
curl -s "https://unpod.ai/api/v2/platform/telephony/trunks/$TRUNK_ID/" \
  -H "Authorization: Token $UNPOD_PLATFORM_TOKEN" -H "Org-Handle: $UNPOD_ORG_HANDLE"
```

A trunk your org doesn't own returns `404`. See [Get Trunk](https://docs.unpod.ai/telephony/trunks/get-trunk.md).

## Create a trunk

```python Python theme={null}
from unpod import Client

with Client() as client:
    trunk = client.telephony.trunks.create(
        "my-trunk",
        "sip:carrier.example.com",
        username="<sip-username>",
        password="<sip-password>",
        transport="tcp",
        port="5060",
        source_ips=["203.0.113.10", "198.51.100.0/24"],
    )
    print(trunk.id)
```

```bash cURL theme={null}
curl -s -X POST "https://unpod.ai/api/v2/platform/telephony/trunks/" \
  -H "Authorization: Token $UNPOD_PLATFORM_TOKEN" -H "Org-Handle: $UNPOD_ORG_HANDLE" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "my-trunk",
    "sip_url": "sip:carrier.example.com",
    "auth_username": "<sip-username>",
    "auth_password": "<sip-password>",
    "transport": "tcp",
    "port": "5060",
    "source_ips": ["203.0.113.10", "198.51.100.0/24"]
  }'
```

| Python | API field | Required | Meaning |
| - | - | - | - |
| `name` | `name` | Yes | Your label for the trunk |
| `sip_url` | `sip_url` | Yes | Carrier SIP URL or host |
| `username`, `password` | `auth_username`, `auth_password` | No | SIP auth credentials. The password is masked on every read |
| `transport` | `transport` | No | `tcp` (default), `udp` or `tls` |
| `port` | `port` | No | SIP port as a string (default `5060`) |
| `source_ips` | `source_ips` | No | Addresses your carrier sends calls from. CIDR ranges allowed. Read back as `allowed_ips` |

Keep the returned `id` (`data.id` in the cURL response): you need it to map numbers, get
or delete the trunk. The API has no call to update a trunk. See
[Create Trunk](https://docs.unpod.ai/telephony/trunks/create-trunk.md).

## Map numbers to a trunk

Mapping takes number **ids**, not E.164 numbers. Only a `NOT_ASSIGNED` number can be
mapped; detach an `ASSIGNED` number first
([Detach a number](https://docs.unpod.ai/telephony/apis/numbers.md#detach-a-number)).

Read each number's `id` from your org's number list,
`GET /telephony/numbers/?include_assigned=true`. The SDK's `numbers.list()` doesn't
return the id yet - step 3 of the [Quick Start](https://docs.unpod.ai/telephony/quickstart.md) shows a Python
lookup. For numbers already on your org, `client.telephony.overview()` returns the same
id as `number_id` - a pure-SDK way to find the ids to unmap.

```python Python theme={null}
from unpod import Client

TRUNK_ID = 21        # your trunk id
NUMBER_IDS = [501]   # NOT_ASSIGNED ids from your org's number list

with Client() as client:
    mapped = client.telephony.trunks.attach_numbers(TRUNK_ID, NUMBER_IDS)
    print(mapped.origin_endpoint.ingress)
    print(mapped.origin_endpoint.accepted_source_ips)
    for r in mapped.numbers:
        print(r.number_id, r.number, r.ok, r.error)
```

```bash cURL theme={null}
TRUNK_ID=21  # your trunk id
curl -s -X POST \
  "https://unpod.ai/api/v2/platform/telephony/trunks/$TRUNK_ID/attach-numbers/" \
  -H "Authorization: Token $UNPOD_PLATFORM_TOKEN" -H "Org-Handle: $UNPOD_ORG_HANDLE" \
  -H "Content-Type: application/json" \
  -d '{"number_ids": [501]}'
```

The response has the **origin endpoint**: `ingress` is where your carrier sends calls,
`dids` lists the numbers this request mapped, and `accepted_source_ips` are the addresses
Unpod accepts them from. Each eligible number gets a row in `numbers` with its own `ok`
and `error`; ids that aren't an active `NOT_ASSIGNED` number are left out. If no id is
eligible, the request fails with `400` `No valid unassigned numbers found.` (the SDK
raises `httpx.HTTPStatusError`). See
[Attach Numbers to Trunk](https://docs.unpod.ai/telephony/trunks/attach-numbers.md).

## Unmap numbers

```python Python theme={null}
from unpod import Client

TRUNK_ID = 21  # your trunk id

with Client() as client:
    result = client.telephony.trunks.detach_numbers(TRUNK_ID, [501])
    for r in result.numbers:
        print(r.number_id, r.ok, r.error)
```

```bash cURL theme={null}
TRUNK_ID=21  # your trunk id
curl -s -X POST \
  "https://unpod.ai/api/v2/platform/telephony/trunks/$TRUNK_ID/detach-numbers/" \
  -H "Authorization: Token $UNPOD_PLATFORM_TOKEN" -H "Org-Handle: $UNPOD_ORG_HANDLE" \
  -H "Content-Type: application/json" \
  -d '{"number_ids": [501]}'
```

Unmapping releases the number back to `NOT_ASSIGNED`. A number that isn't on this trunk
reports `ok: false` with `Number is not mapped to this trunk.` If no number unmaps, the
request fails with `400` (the SDK raises `httpx.HTTPStatusError`). See
[Detach Numbers from Trunk](https://docs.unpod.ai/telephony/trunks/detach-numbers.md).

## Inbound config for a whole trunk

To send the trunk's inbound calls to your app, set the inbound config on each of its numbers
with `set_did`. It needs the Call API switched on for your org (otherwise
`403 call_api_disabled`).

```python Python theme={null}
from unpod import Client
from unpod.telephony import InboundConfig

NUMBERS = ["+15551234567"]  # the numbers mapped to the trunk (origin_endpoint.dids)

with Client() as client:
    inbound = client.telephony.inbound
    for number in NUMBERS:
        inbound.set_did(number, InboundConfig(
            answer_url="https://your-app.example.com/incoming",
        ))
        print(inbound.get_did(number))
```

```bash cURL theme={null}
# set (+ becomes %2B in the URL)
curl -s -X PUT "https://unpod.ai/api/v2/platform/sbc/v1/dids/%2B15551234567/inbound" \
  -H "Authorization: Token $UNPOD_PLATFORM_TOKEN" -H "Org-Handle: $UNPOD_ORG_HANDLE" \
  -H "Content-Type: application/json" \
  -d '{"answer_url": "https://your-app.example.com/incoming"}'

# read
curl -s "https://unpod.ai/api/v2/platform/sbc/v1/dids/%2B15551234567/inbound" \
  -H "Authorization: Token $UNPOD_PLATFORM_TOKEN" -H "Org-Handle: $UNPOD_ORG_HANDLE"
```

A number that isn't one of your org's returns `404`. `set_trunk` / `PUT /sbc/v1/trunks/{name}/inbound`
take the trunk's internal Unpod name, which this API doesn't return yet: the `name` you
gave the trunk returns `404`. For every config field and how to answer the call, see
[For a whole SIP trunk](https://docs.unpod.ai/telephony/apis/incoming-calls.md#for-a-whole-sip-trunk).

## Delete a trunk

Delete detaches every number on the trunk back to `NOT_ASSIGNED` and removes the trunk, all
or nothing: if one number fails, nothing changes. Unmapping first shows you which number
failed and why, so the sample unmaps, then deletes only once every number has unmapped. Running it again after a partial failure? Drop
the ids that already unmapped: they now report `ok: false`, and if none unmap the request
fails with `400`.

```python Python theme={null}
from unpod import Client

TRUNK_ID = 21        # your trunk id
NUMBER_IDS = [501]   # every number mapped to the trunk

with Client() as client:
    trunks = client.telephony.trunks
    result = trunks.detach_numbers(TRUNK_ID, NUMBER_IDS)
    if all(r.ok for r in result.numbers):
        trunks.delete(TRUNK_ID)
    else:
        print([(r.number_id, r.error) for r in result.numbers if not r.ok])
```

```bash cURL theme={null}
TRUNK_ID=21  # your trunk id

# 1. unmap every number on the trunk
curl -s -X POST \
  "https://unpod.ai/api/v2/platform/telephony/trunks/$TRUNK_ID/detach-numbers/" \
  -H "Authorization: Token $UNPOD_PLATFORM_TOKEN" -H "Org-Handle: $UNPOD_ORG_HANDLE" \
  -H "Content-Type: application/json" \
  -d '{"number_ids": [501]}'

# 2. delete the trunk
curl -s -X DELETE "https://unpod.ai/api/v2/platform/telephony/trunks/$TRUNK_ID/" \
  -H "Authorization: Token $UNPOD_PLATFORM_TOKEN" -H "Org-Handle: $UNPOD_ORG_HANDLE"
```

Delete returns `204`, or `404` for a trunk that isn't your org's. If a number can't be
detached it returns `400` (or `206`) with the reason in `message` and the trunk stays.
`trunks.delete()` raises on the `400` but **not** on a `206`, so check with
`trunks.get(TRUNK_ID)`: it raises `httpx.HTTPStatusError` with `404` once the trunk is gone. See
[Delete Trunk](https://docs.unpod.ai/telephony/trunks/delete-trunk.md).


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