# Handoffs

> Hand a live call between AI and humans with transfer, warm handoff, and SIP REFER. The audio continues and the sid does not change.

You can hand a live call between an AI agent and a human, in either direction.
The audio does not drop. The identifier that addresses the call does not
change. A handoff swaps who is on the far end of the media bridge. It does
**not** stop and re-create the call. The same `sid` addresses the call
before, during, and after the handoff. Every recording, transcript, and
analytics row keyed on that `sid` stays one continuous story.

You will make three moves in practice:

| Handoff                | What happens                                                        | Mechanism                                      |
|------------------------|--------------------------------------------------------------------|------------------------------------------------|
| **AI → human**         | The bot escalates; a human takes over the live audio.              | Re-attach the bridge to a human agent or queue |
| **Human → AI**         | A human hands back to an AI agent (deflection, after-hours, ACW).  | Re-attach the bridge to an AI agent            |
| **Transfer off-net**   | Forward the caller to an external number or PBX.                   | SIP `REFER` from the gateway                   |

## Everything hangs off one sid

A call is a SIP dialog with two legs: caller and agent. A B2BUA (back-to-back
user agent) in the SIP gateway holds the two legs together. The `sid` names
that dialog. When you hand off, only the **agent leg** re-points. The caller
leg never moves. From the caller's perspective, the audio path continues
without interruption.

```
                        ┌──────────────  same sid  ──────────────┐
                        │                                          │
   caller leg  ─────────┤  B2BUA  ├─── agent leg ──▶  AI agent      │  segment 1 (handler=ai)
   (never moves)        │  (SIP gateway)                            │
                        │         └─── agent leg ──▶  human agent   │  segment 2 (handler=human)
                        └───────────────────────────────────────────┘
```

Because the `sid` survives, the call events pipeline emits a
`TRANSFER_INITIATED` (or `REFER_RECEIVED`) transition, not an `END`.
Downstream systems fold the call into **multiple segments under one `sid`**.
The segments split by handler: AI or human. This gives you a correct "12s
with the bot, 4m18s with a human" breakdown, not two unrelated calls. See
[Sessions, calls, tracks & streams](/modalities/voice/concepts/sessions-calls-tracks-streams)
for the object model and
[Call traces](/modalities/voice/observability/call-traces) for the segment
rows.

## AI → human

The bot decides that it needs a person. Examples: an intent that it cannot
satisfy, a compliance trigger, or an explicit "let me talk to someone". You
transfer the live call to a human. There are two options:

- **To a skill queue** — route the caller into the ACD (skill-queue router).
  The ACD rings an available human agent and bridges them in on accept. Use
  this option when *any* qualified agent is acceptable.
- **To a specific human** — transfer to a named agent, a browser softphone,
  or a phone number. Use this option for warm escalations to a known owner.

  
```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" });

// …or hand straight to one on-call person's number.
await call.transfer({ to: "+15557654321" });
```
  
  
```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")

# …or hand straight to one on-call person's number.
call.transfer(to="+15557654321")
```
  

The human who answers can be a **browser softphone** or a **real SIP
deskphone/softphone**. The accept path is unified, so your transfer code is
identical in both cases. [PBX & ACD](/modalities/voice/transport-telephony/pbx-acd)
covers how the system rings agents and how agents claim a call.

> **NOTE:**
> **Warm handoff / screen-pop.** The product thesis for regulated verticals is
> this sequence: the AI does intent, qualification, auth, and compliance capture
> *first*. Then a context bundle screen-pops to the human before audio cuts in.
> The routing and accept legs are shipped today. Your application layer
> assembles the rich context payload that travels with the transfer. You build
> the context bundle. There is no turnkey field for it yet.

## Human → AI

The reverse move re-attaches an **AI** agent to a live call. Use it to
deflect a queued caller to a self-service bot, to drop into an after-hours
agent, or to run automated wrap-up. It is the same `transfer({ agent })`
call. You name an AI agent instead of a human queue. The bridge re-points in
place, and the `sid` does not change. A call can bounce bot → human → bot and
stay one record.

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

## Transfer off-net via SIP REFER

When you transfer to a **phone number** (a PSTN destination or another PBX),
the gateway performs a SIP `REFER`. This is the standard way to ask the far
end to re-target its call leg. You do not construct the `REFER` yourself.
Pass `to`, and that is enough. A bare string is shorthand for `{ to }`.

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

The gateway records the transfer as a `REFER_RECEIVED` lifecycle transition
on the same `sid`. Rule of thumb: **`to` → SIP REFER (off-net); `agent` →
in-place re-attach (on our media plane).** In both cases, the original `sid`
continues to address the call.

> **WARNING:**
> A blind `REFER` to a PSTN number hands the caller to the carrier and off our
> media plane. After the far end accepts, you no longer control that audio.
> Prefer `transfer({ agent })` to a human on a softphone or deskphone when you
> want to keep recording, barge-in, and re-attach available for the rest of the
> call.

## Bot ⇄ human re-attach, step by step

  1. **Hold the sid**
You get a `sid` from `calls.originate()` (outbound) or from the inbound
    call webhook. It is the only identifier that you need for every later
    step: transfer, hang up, fetch recordings, and read analytics.
  2. **Decide the target**
The target is a human queue (`agent: "human-support"`), a specific human,
    an AI agent, or an off-net number (`to`). Give exactly one of `agent` /
    `to` per transfer.
  3. **Transfer**
Call `transfer(...)`. The caller leg stays in place. The agent leg
    re-points. The caller hears continuous audio. You can optionally play
    hold media while the queue rings.
  4. **It re-attaches, it does not restart**
A `TRANSFER_INITIATED` / `REFER_RECEIVED` event fires on the same `sid`.
    Recording continues. Analytics opens a new segment tagged with the new
    handler (AI vs human). No new call is created.

## Honest status

> **NOTE:**
> - **AI → human transfer** — shipped. Routing into the queue and human accept
>   work end to end (a prior transfer-teardown crash is fixed).
> - **Human → AI re-attach** — shipped, same `transfer({ agent })` primitive.
> - **sid preservation across handoffs** — shipped; segments fold under one
>   `sid` by handler.
> - **Human-agent two-way audio (browser softphone).** The agent's mic uplink
>   is live. The downlink leg (the human hears the caller over the
>   media-over-QUIC track) is still being hardened. The caller-audio origin
>   publish must be bridged into the relay fan-out. If you stand up browser
>   softphone agents now, validate the return audio path in staging.
>   Real SIP deskphone agents are not affected.
> - **Warm screen-pop context payload** — application-level today (see the Note
>   above), not a turnkey feature.

## Related

  - **[AI-to-Human Handoff](/modalities/voice/transport-telephony/ai-human-handoff)** — See the telephony-plane view of escalation and accept.
  - **[PBX & ACD](/modalities/voice/transport-telephony/pbx-acd)** — Learn how the skill-queue router rings and claims human agents.
  - **[Support agent with handoff](/modalities/voice/recipes/support-handoff)** — Build a full inbound bot that escalates to a human, end to end.
  - **[Transfer to a human](/modalities/voice/cookbooks/transfer-human)** — Copy a ready transfer snippet.
  - **[Call lifecycle](/modalities/voice/transport-telephony/call-lifecycle)** — See every event that a call emits, including transfers.
  - **[Sessions, calls, tracks & streams](/modalities/voice/concepts/sessions-calls-tracks-streams)** — Learn why the sid is the one identifier that matters.
