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

> ## Agent Instructions
> Unmute compiles to exactly three targets. Pipecat and LiveKit are code targets: compile writes a Python project you run. SLNG is a hosted target: compile writes a deployment body and SLNG runs the agent, so it has no `unmute dev`. Those three are the only values `provider` accepts in `targets.yaml`. Deepgram and ElevenLabs appear in these docs as model vendors, which is not the same thing as a target, and `slng` is both.
> The Go structs in `internal/spec` and `internal/ir` are the schema truth. Check a field against them, or run `unmute validate`, rather than against what you remember.

# Get your Twilio details

> Which value in the Twilio console fills which name in your connection file, per route, and the console setup each route needs.

A connection file holds environment variable **names**. This page is about the
values behind them: where each one lives in your Twilio account, and which key it
belongs to. It also covers the console setup those values assume, because a
correct connection file over a half-configured trunk still gets you nothing.

On this page:

* [Give each route its own number](#give-each-route-its-own-number) - why one number cannot serve two routes
* [Which values you need depends on the route](#which-values-you-need-depends-on-the-route) - the two credential groups, and every connection key
* [First, a voice-capable number](#first-a-voice-capable-number) - the one requirement on the number itself
* [The account trio](#the-account-trio-pipecat-cloud-websocket-and-the-livekit-connector) - for `cloud-websocket` and the LiveKit connector
* [The four SIP values](#the-four-sip-values-the-livekit-sip-route) - for the LiveKit `sip` route
* [Caller ID for a transfer target](#caller-id-for-a-transfer-target) - the trunk setting that decides whether a transfer connects
* [If your trunk is in a Twilio Region](#if-your-trunk-is-in-a-twilio-region) - what a region changes, and what it does not
* [Then declare the names](#then-declare-the-names) - `secrets:`, and where the values live

<Note>
  Unmute never buys a number, creates a trunk, or changes anything on your carrier
  account, at compile time or afterward. Every value below is one you set yourself,
  in the Twilio Console.
</Note>

## Give each route its own number

A number attached to a SIP trunk ignores its voice configuration completely, and
does so silently. So one number cannot serve a TwiML Bin and a SIP trunk at the
same time. If you want both routes live, buy a second number:

| Number     | Route                     | What it points at                                    |
| ---------- | ------------------------- | ---------------------------------------------------- |
| the first  | Pipecat `cloud-websocket` | a TwiML Bin, set in the number's voice configuration |
| the second | LiveKit `sip`             | a SIP trunk, set on the number's configuration page  |

You can also swap a single number back and forth, and the last step of the trunk
setup below covers that. Two numbers is less work after the first day.

## Which values you need depends on the route

| Target  | `transport`       | `environment` keys                                           | Where they come from                         |
| ------- | ----------------- | ------------------------------------------------------------ | -------------------------------------------- |
| Pipecat | `cloud-websocket` | `account_sid`, `auth_token`, `from_number`                   | the account dashboard, plus a number you own |
| LiveKit | `connector`       | `account_sid`, `auth_token`, `from_number`                   | the same three                               |
| LiveKit | `sip`             | `sip_address`, `sip_username`, `sip_password`, `from_number` | an Elastic SIP trunk you create              |

The two groups are different credentials, not two names for one thing. The account
trio identifies your account to Twilio's REST API. The four SIP values are one
trunk's own dial-out settings, and they live in a different part of the console.

### Every key a connection takes

Three keys. The first two pick the route, and the third names the variables that
hold its values.

<ParamField path="transport" type="sip | connector | cloud-websocket | daily-sip" required>
  The mechanism that carries the call. It decides which `environment` keys the
  file accepts, so changing it changes the group of values you need.
</ParamField>

<ParamField path="carrier" type="twilio | telnyx | plivo" required>
  The carrier account behind the route. `twilio` on every route on this page.
</ParamField>

<ParamField path="environment" type="route key to environment variable name">
  Which variable holds each value. The keys on the left are fixed by the route:
  `account_sid`, `auth_token` and `from_number` for the account trio,
  `sip_address`, `sip_username`, `sip_password` and `from_number` for the SIP
  route. A key from another route is refused, and the refusal lists the set
  this route accepts.
</ParamField>

The names on the right are yours, and each one is UPPER\_SNAKE: an uppercase
letter, then only uppercase letters, digits and underscores. The compiler never
reads a value, so a package with connections validates and compiles with no
credentials present anywhere.

## First, a voice-capable number

**Phone Numbers**, **Manage**, **Buy a number**, with the Voice capability. Skip
this if you already own the number you want the agent to use.

Whatever you buy or already own is the value of `from_number` (or `SIP_FROM_NUMBER`
on the SIP route). It is also the caller identity the person you dial sees, and
voice capability is the whole requirement on it.

## The account trio: Pipecat cloud-websocket and the LiveKit connector

These two routes talk to Twilio's REST API in your name, so they need the account
credentials.

| Connection key | The value                                                                    | Where it is                             |
| -------------- | ---------------------------------------------------------------------------- | --------------------------------------- |
| `account_sid`  | starts with `AC`                                                             | the Twilio Console account dashboard    |
| `auth_token`   | the account's auth token                                                     | the same dashboard, revealed on request |
| `from_number`  | the number in E.164 form: a plus, the country code, then the rest, no spaces | Phone Numbers, Manage                   |

```yaml connections/twilio_voice.yaml theme={null}
transport: cloud-websocket
carrier: twilio
environment:
  account_sid: TWILIO_ACCOUNT_SID
  auth_token: TWILIO_AUTH_TOKEN
  from_number: TWILIO_PHONE_NUMBER
```

The names on the right are yours to choose. Those three are what every Twilio example
in the repository uses, so one `.env` drives all of them.

### Pointing the number at the agent

This part differs by route, and it is the part to get right:

* **`cloud-websocket`**: the number points at a static piece of TwiML in the
  console, which streams the call to Pipecat Cloud. There is no URL of yours to
  configure, because nothing of yours is hosted. Your build's generated README
  dictates the exact markup, including the regional stream address. That address
  comes from the region in your `targets.yaml`. [Pipecat over
  Twilio](/telephony/pipecat-twilio) walks the whole route, including the
  organisation slug that is the most common thing to get wrong. -
  **`connector`**: the number's voice webhook points at `POST /telephony/inbound`
  on the public URL of the service you deploy.

<Warning>
  Take the number off any SIP trunk before using it on one of these routes. A number
  attached to a trunk ignores its voice configuration completely, and does so
  silently, so the markup or webhook you set would simply never be consulted.
</Warning>

## The four SIP values: the LiveKit sip route

The LiveKit `sip` route reads four values from an Elastic SIP Trunk: the
termination domain, the credential's username and password, and the attached
number. Setting the trunk up is a console walkthrough of its own, and it lives
on [LiveKit over Twilio](/telephony/livekit-twilio#set-up-the-trunk-in-twilio-console),
together with the LiveKit records the call needs afterwards.

| Connection key | The value                                       | Where it is                                       |
| -------------- | ----------------------------------------------- | ------------------------------------------------- |
| `sip_address`  | the complete domain ending in `pstn.twilio.com` | the trunk's **Termination** tab                   |
| `sip_username` | the username in the trunk's credential list     | Termination, **Authentication**, Credential Lists |
| `sip_password` | that credential's password                      | the same credential list                          |
| `from_number`  | the attached number in E.164 form               | the trunk's **Numbers** tab                       |

Two settings on that trunk decide whether a transfer works: **Call Transfer
(SIP REFER)** with **Enable PSTN Transfer**, and the caller ID below.

## Caller ID for a transfer target

A cold transfer is a SIP REFER: Twilio receives it and places a **new call** to the
destination. The trunk decides what caller ID that new call presents, and the two
choices behave very differently.

| Setting        | The transfer target sees     | Use it when                                                                                                        |
| -------------- | ---------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| **Transferee** | the original caller's number | the destination's carrier accepts a number your account does not own, which is usually the case within one country |
| **Transferor** | your Twilio number           | anything else, and always when the transfer crosses a border                                                       |

Transferee is the nicer behaviour, because the person receiving the transfer sees
the customer rather than your own line. It only works where the receiving carrier
tolerates a caller ID that belongs to nobody on your account. Present a Spanish
mobile number from a UK trunk to a Spanish carrier and it is rejected on the spot.

**How that failure looks**, so you recognise it rather than debugging your agent:

* The agent logs `cold transfer failed after 0s: SIP call failed: 486 Busy Here`.
* Twilio's call log shows a **child call** under the original call, with the
  original caller's number in `From`, status **Busy**, duration **0 sec**, cost
  blank, and no PCAP.

Zero seconds and no cost means the leg was rejected as it was offered and never
reached the destination, so nothing rang and nobody was busy. Switch the trunk to
Transferor and place the call again. A genuine busy signal costs time and money,
and the record shows both.

## If your trunk is in a Twilio Region

Twilio can hold resources in a region such as Ireland (IE1) rather than in the
default one. If you set a number's **Active Region** and create the trunk there,
three things change:

* The trunk gets a SID that the default API cannot see. A lookup returns
  `404 not found` even though the console shows the trunk.
* Regional endpoints such as `api.dublin.ie1.twilio.com` need
  [regional API credentials](https://www.twilio.com/docs/global-infrastructure/manage-regional-api-credentials).
  Your account SID and auth token get `401 Authenticate` there.
* The number's Active Region and its trunk must be in the same region, and the
  console only shows you the configuration for the region you are viewing.

None of this changes what Unmute generates. It changes what you can inspect, and
which console view tells you the truth, so note the region before you start
comparing settings against a guide.

## Then declare the names

Two files, and neither holds a value:

```yaml agent.yaml theme={null}
secrets:
  - TWILIO_ACCOUNT_SID
  - TWILIO_AUTH_TOKEN
  - TWILIO_PHONE_NUMBER
```

Every name your connection maps belongs in `secrets:` too, which is what puts it in
the generated `.env.example` and the startup check. The values go in `.env` locally,
and in your platform's secret store for a deployment.

<Warning>
  A name you declare and never set is not caught at compile time. It is caught on
  the call, and the caller hears ringing that never ends. See [the call rings and
  nobody answers](/telephony/inbound-calls#if-the-call-does-not-arrive).
</Warning>

Your build's `build/<target>/README.md` is the authority for your own route: it
was generated from your package, so it already names your region and your
number.

## Where to go next

<Columns cols={2}>
  <Card title="LiveKit over Twilio" icon="phone" href="/telephony/livekit-twilio">
    The `sip` route end to end: trunk, records, and what a rename breaks.
  </Card>

  <Card title="Pipecat over Twilio" icon="phone" href="/telephony/pipecat-twilio">
    The `cloud-websocket` route end to end.
  </Card>

  <Card title="connections/<name>.yaml" icon="braces" href="/reference/connections-yaml">
    The file these values fill.
  </Card>

  <Card title="Transfers on LiveKit" icon="phone-forwarded" href="/transfers/livekit">
    Hand the caller to a person.
  </Card>
</Columns>
