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

# Transfers on Pipecat over Twilio

> Cold transfer through your own Twilio number, with no server of yours in the call path.

This page follows the `pipecat` target of `examples/salon-concierge`, on the
`cloud-websocket` transport: your own Twilio number, and nothing of yours hosted
anywhere. Cold transfer is the only kind this route supports.

```yaml targets.yaml theme={null}
targets:
  pipecat:
    provider: pipecat
    version: "1.10.0"
    connection: twilio_voice
    deployment_region: eu-central
```

```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
```

```yaml agent.yaml theme={null}
destinations:
  billing_line: BILLING_PHONE_NUMBER
```

`billing_line` is a symbol, never a number: the right half names an environment
variable read at call time, a rule [Human transfers](/transfers/overview) states
in full.

Your number points at a small piece of static markup in the Twilio console, which
streams the call to Pipecat Cloud. No server of yours is in the path, in
production or ever.

The three names in the connection are here because this package hands calls to a
person and places test calls, and both of those speak to Twilio's API in your
name. A package that only answers calls on this route needs no `environment:`
block at all. Which keys each route accepts is in
[Connection configuration](/reference/connections-yaml#which-environment-keys-a-route-accepts),
and where each Twilio value is found is in
[Pipecat over Twilio](/telephony/pipecat-twilio).

## The transfer

```yaml agent.yaml theme={null}
escalations:
  send_to_billing:
    when: The caller asks about an invoice, a refund, or a charge they do not recognise.
    cold:
      destination: billing_line
      on_unavailable: hangup
```

The mechanism is different from every other route. One request replaces the
live call's instructions at Twilio, keyed on the call id: speak a line, dial
the destination, done. The agent's part of the call ends there.

A successful Twilio REST update means the transfer has started, not that the
destination answered. The tool result is `transfer_started`.

### Every key this transfer takes

`cold:` is the only shape this route compiles, and `on_unavailable:` is the one
key you cannot leave out. Pipecat `cloud-websocket` requires explicit
`on_unavailable: hangup`; it cannot reconnect the original media stream. The
entry's own `when:`, and the choice of shape block, work the same on every
route and are stated on [Human transfers](/transfers/overview).

<ParamField path="destination" type="a destinations entry name" required>
  Which desk to reach. The name is resolved in the `destinations:` block above,
  so the model never sees a number.
</ParamField>

<ParamField path="ring_timeout" type="a positive Go duration, e.g. 25s">
  How long the destination rings before it counts as unavailable. It becomes
  the dial timeout in the markup Twilio runs. Left out, that timeout is 25
  seconds.
</ParamField>

<ParamField path="on_unavailable" type="hangup" required>
  What happens when the destination does not take the call. `hangup` is the
  only value this route accepts, and it has to be written out: an omitted key
  resolves to `return_to_caller`, which is refused here because this route
  cannot reconnect the original media stream.
</ParamField>

There is no `briefing:` key to write. It belongs to a `warm:` block, and warm
transfer compiles on no Pipecat route.

## The transfer ends the call

After the destination leg ends, Twilio ends the original call. The same happens
after a decline or no answer. Unmute does not reconnect the caller to a fresh
agent, because that agent would have none of the conversation context.

**Unmute does not support warm transfer on any Pipecat target.** Write one and
validation refuses it, naming the connection and the transport it declares:

```text theme={null}
pipecat: telephony warm_transfer: telephony route (pipecat, cloud-websocket, twilio) does
  not emit warm transfer: a warm handoff has to act on how the destination's leg ended,
  which on this route needs a callback endpoint you host, and hosting nothing is what
  this route is for; warm transfer compiles on (livekit, sip) trunks today. Connection
  "twilio_voice" declares transport: cloud-websocket
```

If you need warm transfer, use the [LiveKit SIP route](/transfers/livekit).

## Testing it without waiting for a call

The package declares both directions:

```yaml agent.yaml theme={null}
channels:
  phone:
    kind: telephony
    inbound: true
    outbound: true
```

Outbound is there so you can call your own mobile and then ask the agent for
billing, instead of waiting for someone to ring in.

## Check the route compiles

```sh theme={null}
unmute compile examples/salon-concierge --target pipecat
```

A clean compile means the route is legal for this target and the cold transfer
is one the route supports. The emitted `README.md` carries the Twilio markup
the route needs.

## Where to go next

<Columns cols={2}>
  <Card title="Targets" icon="boxes" href="/targets/overview">
    Compare Pipecat, LiveKit, and SLNG.
  </Card>
</Columns>
