# AI-to-Human Handoff

> Escalate a live call from an AI agent to a human — blind, warm, and consult transfer tools, SIP REFER, sid preservation, and bot⇄human re-attach.

Escalate a live call from an AI agent to a human without a drop in audio and
without a new call. This page is the **telephony-plane view** of a handoff. It
covers what the SIP gateway does on the wire when you call `transfer(...)`, how
the `sid` survives, and how the gateway rings a human — on a browser softphone
or a real deskphone — so that the human takes over the media. For the
product-level model and the bidirectional (bot ⇄ human) framing, start at
[Handoffs](/modalities/voice/concepts/handoffs). This page is the mechanics
underneath it.

A handoff can be driven in two ways over the same gateway. In the first way,
**you** (your app, or an operator dashboard) call `transfer(...)` on the SDK.
In the second way, the **AI agent itself** decides mid-conversation and calls a
built-in telephony tool: `transfer_call`, `route_to_skill`, `warm_transfer` /
`complete_transfer`, or `consult` / `consult_transfer` / `consult_cancel`. The
tool path needs no operator config. Both ways land on the same B2BUA and
preserve the same `sid`. The
[tool path](#handoff-tools-the-ai-agent-calls-itself) only lets the model pick
the moment and the style. These tools are the same operations exposed to ISV
apps as CSTA verbs — see [CTI call control](/modalities/voice/cti/call-control).

## The one call keeps its sid

A call is a SIP dialog with two legs, **caller** and **agent**. A back-to-back
user agent (B2BUA) in the SIP gateway holds them together. The `sid` names that
dialog and is the universal key. The same value addresses the CDR, the
recording, the analytics segments, and the media tracks. A handoff **re-points
the agent leg only**. The caller leg never moves. The caller hears
uninterrupted audio, and the `sid` is unchanged before, during, and after.

```
   caller leg  ─────────┐
   (never moves)        │      ┌─── agent leg ─▶  AI agent      segment 1 (handler=ai)
                   B2BUA ┤─────┤
   same sid  ───────────┘      └─── agent leg ─▶  human agent   segment 2 (handler=human)
                              (only this leg re-points)
```

The dialog is preserved, so the call-events pipeline emits a **transfer
transition** on the existing `sid`, not an `END`. Downstream, the pipeline
folds the call into multiple segments under one `sid`. The segments are split
by handler: AI or human. This gives the "12s with the bot, 4m18s with a human"
breakdown. See
[Call lifecycle](/modalities/voice/transport-telephony/call-lifecycle) for the
full event sequence. See
[Sessions, calls, tracks & streams](/modalities/voice/concepts/sessions-calls-tracks-streams)
for the object model.

> **NOTE:**
> **When is a sid created, and when is it preserved?** The engine mints a `sid`
> once, when the call first exists: at the inbound `INVITE`, or when you call
> `calls.originate()` outbound. Every handoff after that **preserves** it. You
> never create a `sid` for a transfer. You pass the one that you already hold.

## Two mechanisms, picked by target

The SDK exposes one method, `transfer(...)`. The gateway chooses the mechanism
from the field that you set:

| You pass          | Mechanism                          | Media stays on our plane? |
|-------------------|------------------------------------|---------------------------|
| `agent: "<id>"`   | In-place **re-attach** of the agent leg (skill-queue router or a named human/AI agent) | **Yes** |
| `to: "<E.164>"`   | **SIP `REFER`** — ask the far end to re-target its leg to a phone number or PBX | No (once accepted, off-net) |

Set exactly one of `agent` / `to` per transfer. A bare string is shorthand for
`{ to }`.

### Escalate to a human (re-attach)

Route the caller into the skill-queue router (ACD). The ACD rings an available
human and bridges them in on accept. Or transfer to one named person. The
caller leg holds. The agent leg re-points to the human once they answer.

  
```ts
import { Voice } from "clutchcall_connect/voice";

const v = new Voice({
  baseUrl: "https://engine.firstbyte.fast",
  apiKey:  process.env.CLUTCHCALL_CREDENTIALS!,
  orgId:   "org_abc",
});

// A call is already live with a triage bot; you hold its sid.
const call = await v.calls.get({ sid });

// Escalate to the human support queue — the ACD picks the agent.
await call.transfer({ agent: "human-support" });
```
  
  
```python
from clutchcall.voice import Voice

v = Voice(
    base_url="https://engine.firstbyte.fast",
    api_key=os.environ["CLUTCHCALL_CREDENTIALS"],
    org_id="org_abc",
)

call = v.calls.get(sid=sid)

# Escalate to the human support queue — the ACD picks the agent.
call.transfer(agent="human-support")
```
  

The human who answers can be on a **browser softphone** or a **real SIP
deskphone/softphone**. The accept signal is unified, so your transfer code is
identical either way. When the human claims the call, the gateway dials their
endpoint: an `INVITE` to the registered contact, or a call out through a trunk
for a PSTN callback. The gateway waits for the `200 OK` and bridges the leg
into the same B2BUA. [PBX & ACD](/modalities/voice/transport-telephony/pbx-acd)
explains how agents register, get rung, and claim a call, and covers ring
strategy and glare handling.

### Transfer off-net via SIP REFER

When the target is a **phone number** (a PSTN destination or another PBX), the
gateway performs a SIP `REFER` (RFC 3515). This is the standard request that
asks the far end to re-target its leg. You do not build the `REFER` yourself.
Pass `to`, and that is enough.

  
```ts
await call.transfer({ to: "+15557654321" });  // → SIP REFER
await call.transfer("+15557654321");          // shorthand, same thing
```
  
  
```python
call.transfer(to="+15557654321")  # → SIP REFER
call.transfer("+15557654321")     # shorthand, same thing
```
  

> **WARNING:**
> A blind `REFER` to a PSTN number hands the caller to the carrier and **off our
> media plane**. Once the far end accepts, you no longer control that audio.
> Recording, barge-in, supervise, and re-attach are gone for the rest of the
> call. When you want to keep those, prefer `transfer({ agent })` to a human on a
> softphone or deskphone. That keeps the media on our plane.

## Handoff tools the AI agent calls itself

Everything above is the **operator-driven** view: your code holds the `sid`
and calls `transfer(...)`. But most escalations should be the **agent's own
decision**, made in the flow of the conversation. Every AI agent gets a
built-in telephony toolset with **no operator config**. The runtime advertises
these tools to the model automatically. When the model calls one, the gateway
runs the exact same B2BUA operation on the live `sid`. These are first-class
runtime tools, not user-declared functions. See
[Tool calling](/modalities/voice/runtime/tool-calling) for how the toolset is
advertised, and how an operator tool of the same name overrides a built-in.

There are **three transfer styles**. The right one is a caller-experience
choice. Ask two questions: does the caller *hear* the handoff, and does the AI
*stay* on the line?

| Tool | What the caller hears | AI stays or leaves | When to use |
|------|-----------------------|--------------------|-------------|
| `transfer_call` | A brief pause, then the destination. No handoff is spoken. | **Leaves** immediately | The fastest path. Use it when you know exactly where the caller should go (a queue URI, a PSTN number, another PBX) and no context needs to pass by voice. |
| `route_to_skill` | Queue treatment (ringback / hold audio) until a human answers | **Leaves** (steps out to the ACD) | "Get me a human" with no specific number. Hand to a human **skill/queue** and let the ACD pick the agent. |
| `warm_transfer` → `complete_transfer` | **Everything.** The AI and the human brief each other in a shared conference, and the caller is included. | **Stays** through the brief, then leaves on `complete_transfer` | A smooth, human-feeling handoff. The caller hears "I've got Priya here, she can take it from…". Context passes out loud. |
| `consult` → `consult_transfer` / `consult_cancel` | **Nothing.** The caller is held (hold audio) while the AI checks privately. | **Stays**; can return to the caller with `consult_cancel` | Check that a human is available, or get an answer, **before** you commit the caller. If it works out, `consult_transfer`. If not, `consult_cancel` and keep talking to the caller. |

`destination` for `transfer_call`, `warm_transfer`, and `consult` accepts a
**skill name**, an **extension**, a **SIP URI** (`sip:queue@pbx.example.com`),
or a **PSTN number** in E.164 (`+15551234567`). `transfer_call` also accepts a
`tel:` URI.

### Blind — `transfer_call`

This is the fastest exit. The AI hands the caller away and drops off the call
in one step. Under the hood, this is a SIP `REFER`, the same as an operator
`transfer({ to })`. Nothing is spoken to the receiving party first.

- **`destination`** (`string`, required) — Where to send the caller: a skill name, an extension, a SIP URI, a `tel:`   URI, or an E.164 number.
- **`referred_by`** (`string`) — Optional `Referred-By` identity to present on the `REFER`. Defaults to the   trunk identity.

### Queue to a human skill — `route_to_skill`

Send the caller to a human **skill/queue** (the ACD) without a named person or
number. The AI steps out. The [skill-queue
router](/modalities/voice/transport-telephony/pbx-acd) rings an available human
and bridges them onto the same `sid`.

- **`skill`** (`string`) — The target skill/queue. Omit it to use the agent's default escalation skill.

### Warm (attended) — `warm_transfer` then `complete_transfer`

The AI dials the destination and joins a **conference**, so that it can *speak*
the handoff. The caller **hears everything**, and the AI stays live. This is
the transfer that feels human.

  1. **AI calls warm_transfer { destination }**
The gateway dials `destination` and joins it into a conference with the
    caller. The caller hears the ring. Then all three parties are connected:
    caller, human, and AI.
  2. **The AI briefs the human, out loud, in the conference**
"This is Sam calling about order 4471, they need a refund exception." The
    caller hears the whole brief, so nothing feels like it happened behind
    their back. The human can ask the AI (or the caller) questions before they
    accept.
  3. **AI calls complete_transfer { }**
The AI leg stops. The caller and the human stay connected on the same
    `sid`. The gateway records a transfer transition, and analytics opens a new
    **human** segment.

### Private consult — `consult` then `consult_transfer` / `consult_cancel`

This is an attended, **one-to-one** check with a third party. The caller does
**not** hear it. The caller is effectively on hold while the AI confirms
availability or gets an answer. The AI then commits or backs out.

  1. **AI calls consult { destination }**
The gateway places the caller on hold (hold audio). The AI dials
    `destination` on a **private** leg. The caller hears none of the consult
    conversation.
  2. **The AI speaks privately with the consulted party**
"I have a caller who needs a refund exception on order 4471 — can you take
    it?" Only the AI and the consulted party are on this leg.
  3. **Commit — consult_transfer { }**
This connects the caller to the consulted party and drops the AI out. The
    caller comes off hold already bridged to the human.
  4. **…or back out — consult_cancel { }**
This hangs up on the consulted party and returns the AI to the caller. The
    caller comes off hold. Use this when the human cannot take the call. The AI
    keeps handling the call and can try another path.

> **NOTE:**
> **Transfer-rule policy still applies.** Whichever tool the model calls, the
> transfer passes the realm's transfer-rule policy. A denied cross-realm transfer
> returns a **structured error** that the model can react to: apologize, try
> `route_to_skill`, or offer a callback. It does not fail silently.

### Supporting in-call tools

The same built-in toolset gives the AI the rest of the mid-call controls. The
AI can shape the caller experience around a handoff:

| Tool | Params | What it does |
|------|--------|--------------|
| `hold_call` / `unhold_call` | — | Park the caller on hold audio, and bring them back. |
| `disconnect_call` | `reason?` | End the call. The gateway writes `reason` to the CDR. |
| `send_dtmf` | `digit` (`0-9*#A-D`), `duration_ms?` (default `100`), `mode` (`rfc2833`\|`inband`) | Play DTMF into the call — e.g. to navigate a downstream IVR before or after a transfer. |
| `request_supervisor` | `reason`, `urgency` (`low`\|`medium`\|`high`) | Ask a supervisor to monitor, whisper, or barge in. The caller leg does not move. |

## What happens on the wire

  1. **You call transfer(sid, …)**
The control-plane API forwards the request to the SIP gateway over its
    correlated RPC channel. The gateway looks up the live dialog by `sid`.
  2. **The agent leg is re-pointed, REFERred, or conferenced**
For `agent:` / `route_to_skill`, the gateway re-attaches the agent leg in
    the B2BUA. It dials the human endpoint, or hands the call to the
    skill-queue router. For `to:` / `transfer_call`, the gateway issues a SIP
    `REFER` toward the leg that you forward. For `warm_transfer`, the gateway
    joins the destination into a conference with the caller. For `consult`, it
    holds the caller and opens a private leg to the consult target.
  3. **A transfer lifecycle event fires on the same sid**
The gateway emits a `transferred` transition. When the gateway is the
    *receiver* of a peer `REFER`, it auto-acknowledges with `202 Accepted`. The
    gateway emits no `END` — the `sid` lives on. Recording continues.
    Analytics opens a new segment tagged with the new handler (AI or human).
  4. **The far end takes over**
For a re-attach, the human's `200 OK` completes the bridge. Audio then
    flows to the new agent leg. For an off-net `REFER`, the referred party
    accepts, and the caller now talks to the external number.

## Peer-initiated REFER (inbound)

The reverse also happens: a **remote party sends *us* a `REFER`**. This is a
carrier or upstream PBX asking us to forward a call. The gateway auto-answers
it with `202 Accepted` and records a `transferred` lifecycle event on the
`sid`. The transfer then shows up correctly in traces and CDRs.

> **NOTE:**
> **Honest status — execution of the outbound leg from a peer `REFER`.** We
> *acknowledge* an incoming `REFER` today. But the automatic **placement of the
> new outbound leg** that it asks for — following the referred-to target end to
> end — is partially in progress. Your topology may depend on
> upstream-initiated blind or attended transfer to drive a fresh outbound call.
> If so, validate it against your carrier in staging before you rely on it.
> Operator-initiated transfer (you calling `transfer(...)`) and off-net `REFER`
> from us are the fully supported paths.

## Bot ⇄ human re-attach on the media plane

For an on-plane handoff (`agent:`), the media stays on our transport the whole
time. This makes bot → human → bot bouncing possible on one `sid`:

- The caller's audio is carried as a caller-audio track keyed on the `sid`.
  The human agent takes over: they pick up that track and inject their mic into
  the caller's egress. Subscription is by `sid`, not by a fixed shard. The
  human can therefore live on a different core or host and still hear the
  caller, with no central funnel.
- To hand **back** to an AI agent, use the same `transfer({ agent })` primitive
  with an AI agent id. The bridge re-points in place. The `sid` is unchanged,
  and the call remains one continuous record.

```ts
// Human is done; hand back to an automated wrap-up agent.
await call.transfer({ agent: "post-call-summary" });
```

> **NOTE:**
> **Human-agent return audio (browser softphone).** The human agent's **mic
> uplink** into the caller is live. The **downlink** — the human hearing the
> caller over the media-over-QUIC track in the browser softphone — is still being
> hardened: the caller-audio origin publish needs to be bridged into the relay
> fan-out. If you stand up **browser** softphone agents today, validate the
> return-audio path in staging. **Real SIP deskphone/softphone agents are not
> affected.** Their media rides the normal RTP bridge.

## Related

  - **[Handoffs (concept)](/modalities/voice/concepts/handoffs)** — The product model: AI ⇄ human, warm handoff, sid preservation.
  - **[Tool calling](/modalities/voice/runtime/tool-calling)** — How the built-in telephony toolset is advertised to the AI agent.
  - **[CTI call control](/modalities/voice/cti/call-control)** — The same transfer / consult operations as CSTA verbs for ISV apps.
  - **[PBX & ACD](/modalities/voice/transport-telephony/pbx-acd)** — How the skill-queue router rings and claims human agents.
  - **[Call lifecycle](/modalities/voice/transport-telephony/call-lifecycle)** — Every event that a call emits, including transfer transitions.
  - **[SIP trunking](/modalities/voice/transport-telephony/sip-trunking)** — The trunk edge that REFER rides out over for off-net transfers.
  - **[Support agent with handoff](/modalities/voice/recipes/support-handoff)** — A full inbound bot that escalates to a human, end to end.
  - **[Transfer to a human](/modalities/voice/cookbooks/transfer-human)** — Copy-paste transfer snippet.
