escalations:, and two forms.
On this page:
- How you write it - the block, and every key it takes
- Destinations are symbolic - the model never sees a number
- The route decides what is possible - which shape compiles where
- The escalation has to be attached - the half that is enforced
- When the person does not pick up -
ring_timeoutandon_unavailable - Where to go next - the two routes that carry a transfer
How you write it
The shape you write is the shape you get. There is nomode: field, so a warm
only setting cannot be written on a cold transfer:
agent.yaml
escalations: list. That half is
enforced, not merely conventional. See the control has to be
attached below.
Escalation fields
string
The situation the model reads to decide whether to transfer. Omission supplies no
trigger guidance, so write one.
object
A cold transfer using the destination and timeout fields below. Exactly one of
cold
and warm is required; there is no default transfer form.object
A warm transfer using the fields below, including optional
briefing. Exactly one of
cold and warm is required. Supported only on LiveKit SIP.Transfer fields
string
required
A symbol declared in
destinations, whose value names an environment variable holding
the destination. No destination is inferred. Valid inside both cold and warm.string
A positive Go duration, such as
25s. Omitted means 25 seconds on Pipecat; LiveKit
leaves it unset for the platform default.Accepts
return_to_caller or hangup. Omitted means return_to_caller. Pipecat
cloud-websocket requires explicit hangup, because the original media stream cannot be
reconnected.string
Instructions for briefing the person before connecting the caller. Legal only inside
warm. Omit for the runtime’s standard briefing instructions.Destinations are symbolic
The model never sees a phone number.destination names a symbol, and the symbol
is resolved at the top level of agent.yaml:
agent.yaml
Every key a destination takes
One line per symbol, and both halves are names.UPPER_SNAKE environment variable name
required
The left half is the symbol an escalation’s
destination: names. The right
half is the environment variable holding an E.164 number or a sip: URI, read
at call time. A symbol no escalation reaches is refused.agent.yaml rather than on the target because who this agent
escalates to is the same desk whichever carrier reaches it.
The route decides what is possible
Transfers ride the platform’s own primitive. There are three different mechanisms, which is why the answer differs per route:
Warm transfer is not supported on any Pipecat target. It requires the LiveKit
sip route.
Pipecat daily-sip + Twilio is the Daily shape that transfers, and the carrier
is why: your carrier owns the number and hands Daily the SIP leg, so there is a
real phone leg to hand on. A cold transfer there needs a Twilio connection and an
active channels.phone route; a browser session has no leg and gets a named
failure before the agent announces anything.
You hear a transfer on a deployed agent. Both shapes hand a real phone leg to
a real number, so there is no local rehearsal for either: deploy, finish the
carrier setup, and place the call. The browser loop that unmute dev gives you
covers the prompt, the tools and the models, and stops exactly where the phone
leg starts.
Read the two words in that table carefully, because they are different answers:
- yes means the compiler emits it, and a deployed call performs it.
- refused means the shape does not compile. The error names the connection, the transport it declares, and a route where the shape does work.
A target with no route at all
The table above is about which route does what. A target that names no connection is a separate case, and it is now refused too:daily-sip transfer needs a Twilio connection and an active channels.phone route.
A browser session has no SIP leg and gets a named
failure before the agent announces a transfer.
The escalation has to be attached
Declaring an escalation underescalations: is half the job. Until some agent
lists its name in its own escalations: list, no agent can reach it, and that is
refused at build with the file, the line, and the agents you could attach it to:
.env.example and the generated startup check, so the agent refused to start
over a secret nothing would ever read.
When the person does not pick up
sip route, no answer, a decline, voicemail, and a failed dial
all come back as one failure, and on_unavailable decides what happens next.
On the Pipecat cloud-websocket route, when the destination leg ends, Twilio
ends the original call. A decline or no answer also ends the call after the
dial timeout. No fresh agent starts without the previous conversation context.
Pipecat cloud-websocket requires explicit on_unavailable: hangup; it cannot
reconnect the original media stream.
The agent sees only the accepted REST update as transfer_started; it does not
observe whether the person answers.
Where to go next
LiveKit route
Cold and warm over a SIP trunk.
Pipecat on Twilio
Cold, with nothing hosted by you.