Skip to content

Commit 7c1bb11

Browse files
committed
feat: add "Passing data between assistants" page to squads
Adds a focused decision guide for the three mechanisms Vapi supports for forwarding context across squad handoffs: 1. Handoff arguments (function.parameters on the handoff tool) — LLM fills inline, zero added latency 2. variableExtractionPlan.schema — separate dedicated LLM call against the transcript, structured output with JSON-schema validation 3. Liquid templating in the destination's prompt — sub-millisecond, deterministic, reads anything already in the variable bag The existing /squads/handoff page covers configuration mechanics for the handoff tool itself but doesn't compare these three approaches as alternatives. Customers consistently pick the wrong path (usually defaulting to schema extraction when handoff arguments or direct Liquid would be faster and more reliable). New page sits next to /squads/handoff in the nav. Includes: - At-a-glance comparison table - Per-approach example + when-to-use / when-to-avoid - Decision flowchart for choosing - Common patterns (forwarding extracted IDs, classifying intent, mixed) - Failure-mode notes (empty plan, LLM rejection, non-object result) reflecting the platform-hardening guarantees from PRISM-467 Validated: fern check passes, 2 JSON blocks valid, 5 internal links resolve.
1 parent 8bf7334 commit 7c1bb11

2 files changed

Lines changed: 187 additions & 13 deletions

File tree

fern/docs.yml

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,8 @@ tabs:
7676
icon: fa-light fa-square-terminal
7777
slug: cli
7878
changelog:
79-
display-name: What's New?
80-
slug: whats-new
79+
display-name: Changelog
80+
slug: changelog
8181
changelog: ./changelog
8282
icon: history
8383
mcp:
@@ -156,8 +156,6 @@ navigation:
156156
path: assistants/background-speech-denoising.mdx
157157
- page: Pronunciation dictionaries
158158
path: assistants/pronunciation-dictionaries.mdx
159-
- page: Email address reading
160-
path: assistants/email-address-reading.mdx
161159
- section: Model configurations
162160
icon: fa-light fa-waveform-lines
163161
contents:
@@ -323,12 +321,6 @@ navigation:
323321
- page: Quickstart
324322
path: observability/scorecard-quickstart.mdx
325323
icon: fa-light fa-rocket
326-
- section: Monitoring
327-
icon: fa-light fa-bell
328-
contents:
329-
- page: Quickstart
330-
path: observability/monitoring-quickstart.mdx
331-
icon: fa-light fa-rocket
332324

333325
- section: Squads
334326
contents:
@@ -341,6 +333,9 @@ navigation:
341333
- page: Handoff tool
342334
path: squads/handoff.mdx
343335
icon: fa-light fa-hand-holding-hand
336+
- page: Passing data between assistants
337+
path: squads/passing-data-between-assistants.mdx
338+
icon: fa-light fa-arrow-right-arrow-left
344339
- section: Examples
345340
icon: fa-light fa-code
346341
contents:
@@ -866,9 +861,7 @@ redirects:
866861
- source: /developer-documentation
867862
destination: /introduction
868863
- source: /documentation/general/changelog
869-
destination: /whats-new
870-
- source: /changelog
871-
destination: /whats-new
864+
destination: /changelog
872865
- source: /api-reference/assistants/create-assistant
873866
destination: /api-reference/assistants/create
874867
- source: /api-reference/assistants/get-assistant
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
---
2+
title: Passing data between assistants
3+
subtitle: Three approaches for forwarding context to the next assistant in a squad — when to use each, and what each one costs.
4+
slug: squads/passing-data-between-assistants
5+
---
6+
7+
When an assistant in a squad hands off to another assistant, you usually need to forward something — the caller's name, an extracted intent, an upstream tool's result, a session ID. Vapi gives you **three different mechanisms** to do this. Each one trades off latency, accuracy, and where the value comes from. Picking the wrong one is the single most common reason squad handoffs feel slow or unreliable.
8+
9+
This page is a decision guide. For end-to-end configuration of the handoff itself, see the [Handoff tool](/squads/handoff) page.
10+
11+
## The three approaches at a glance
12+
13+
| Approach | Where the value comes from | LLM involved? | Latency | Hallucination risk | Best for |
14+
| -------- | -------------------------- | ------------- | ------- | ------------------ | -------- |
15+
| **Handoff arguments** (`function.parameters` on the handoff tool) | The model decides, inline with the same tool call that triggers the handoff | Yes — piggybacks on the LLM call already happening | Zero added | Yes (model fills the value) | Classifications, summaries, sentiment, intent — anything the model has to derive from the live conversation |
16+
| **Variable extraction** (`variableExtractionPlan.schema` on the destination) | The model extracts from the full conversation transcript | Yes — separate dedicated LLM call | Full LLM round-trip (hundreds of ms) | Yes | Structured extraction with a dedicated prompt — e.g. pulling `dateOfBirth`, `appointmentTime` from the user's last few utterances |
17+
| **Liquid templating in the destination's prompt** | Already in the variable bag (call data, prior tool results, prior extractions) | No — pure template substitution | Sub-millisecond per render | No (deterministic) | Forwarding values that already exist — caller phone number, prior `lookupPatient` result, time variables |
18+
19+
## Approach 1: Handoff arguments
20+
21+
Define `function.parameters` on the handoff tool. The LLM that's already generating the handoff tool call also fills in your custom arguments as part of the same call — no extra round-trip.
22+
23+
```json
24+
{
25+
"type": "handoff",
26+
"function": {
27+
"name": "handoff_to_specialist",
28+
"description": "Hand off to the specialist when the customer is ready",
29+
"parameters": {
30+
"type": "object",
31+
"required": ["destination", "customerIntent", "customerSentiment"],
32+
"properties": {
33+
"destination": {
34+
"type": "string",
35+
"enum": ["specialist"]
36+
},
37+
"customerIntent": {
38+
"type": "string",
39+
"enum": ["new-customer", "existing-customer", "billing-issue"],
40+
"description": "What the customer is calling about"
41+
},
42+
"customerSentiment": {
43+
"type": "string",
44+
"enum": ["positive", "neutral", "frustrated"],
45+
"description": "Caller's overall sentiment"
46+
}
47+
}
48+
}
49+
},
50+
"destinations": [
51+
{
52+
"type": "assistant",
53+
"assistantName": "Specialist"
54+
}
55+
]
56+
}
57+
```
58+
59+
The next assistant receives `customerIntent` and `customerSentiment` in the variable bag and can reference them as `{{customerIntent}}` / `{{customerSentiment}}` in its prompts.
60+
61+
**Use this when** the value only exists "in the model's head" — it has to be derived from the live conversation, but you don't need a separate dedicated extraction call.
62+
63+
**Avoid this when** the value already exists somewhere structured (a prior tool result, the call's `customer.number`, etc.) — the model could mishear or paraphrase it. Use Approach 3 for those.
64+
65+
<Tip>
66+
The Vapi dashboard exposes this as the **Handoff Arguments** section on the handoff tool config in the squad builder.
67+
</Tip>
68+
69+
## Approach 2: Variable extraction (`variableExtractionPlan.schema`)
70+
71+
Define a `variableExtractionPlan.schema` on the handoff destination. After the handoff fires, Vapi makes a dedicated LLM call against the full conversation transcript to fill the schema, then merges the result into the variable bag for the next assistant.
72+
73+
```json
74+
{
75+
"type": "assistant",
76+
"assistantName": "Scheduler",
77+
"variableExtractionPlan": {
78+
"schema": {
79+
"type": "object",
80+
"required": ["preferredDate", "preferredTime"],
81+
"properties": {
82+
"preferredDate": {
83+
"type": "string",
84+
"description": "The date the caller asked to schedule for, in YYYY-MM-DD format"
85+
},
86+
"preferredTime": {
87+
"type": "string",
88+
"description": "The time of day the caller asked for, in 24-hour HH:MM format"
89+
}
90+
}
91+
}
92+
}
93+
}
94+
```
95+
96+
**Use this when** the value lives across several user utterances and needs a dedicated extraction prompt to get reliably. Schema validation gives you typed output and lets you constrain values via JSON-schema `enum` / `pattern`.
97+
98+
**Avoid this when** zero added latency matters — this path adds a full LLM round-trip per handoff (typically a few hundred ms). For high-traffic flows where the value is something the model can fill inline, Approach 1 is faster.
99+
100+
For full configuration details — multiple destinations, dynamic handoffs, context engineering — see the [Variable extraction section of the Handoff tool page](/squads/handoff#variable-extraction).
101+
102+
## Approach 3: Liquid templating in the destination's prompt
103+
104+
The variable bag is **shared across every assistant in the squad** for the lifetime of the call. Anything that's been put into it — by Approach 1, Approach 2, by a prior tool call returning JSON, by call-level data like `customer.number` and `phoneNumber.number`, by time variables like `now` and `year` — is reachable from any subsequent assistant's prompt via Liquid syntax. No extra wiring required.
105+
106+
```text
107+
You are the scheduling specialist. The caller is {{customer.name}}, calling
108+
from {{customer.number}}. Their patient ID is {{patientId}} (looked up earlier
109+
this call). They want a {{preferredAppointmentType}} appointment.
110+
111+
Today is {{currentDateTime}}.
112+
```
113+
114+
If `customer.name`, `patientId`, etc. are in the bag, they render. If they're not, they render as the literal token `{{patientId}}` (so the caller might hear "patientId" spoken — worth handling defensively in your prompt).
115+
116+
**Use this when** the value is already in the bag — there's no reason to re-extract via LLM what you already have structurally. Sub-millisecond, deterministic, free.
117+
118+
**Avoid this when** the value isn't in the bag yet. Liquid can't extract from the conversation; it can only forward what's already there.
119+
120+
<Note>
121+
**Sensitive fields are sanitized.** Vapi automatically redacts credential-like keys (`twilioAuthToken`, `twilioApiSecret`, `serverUrlSecret`, `accountSid`, `callToken`, `credentialId`, etc.) from the variable bag before any prompt rendering. References like `{{phoneNumber.twilioAuthToken}}` will render as `[REDACTED]` rather than leaking the actual credential.
122+
</Note>
123+
124+
## Decision flowchart
125+
126+
```text
127+
What do you want the next assistant to know?
128+
129+
├─ "Something the model just heard / classified / summarized"
130+
│ └─→ Approach 1: Handoff arguments
131+
│ Zero added latency, model fills inline.
132+
133+
├─ "Something the user explicitly said and I want a dedicated, schema-validated extraction"
134+
│ └─→ Approach 2: variableExtractionPlan.schema
135+
│ Adds an LLM round-trip, but you get structured output and a focused
136+
│ extraction prompt.
137+
138+
└─ "Something I already have — call data, prior tool result, prior extraction"
139+
└─→ Approach 3: Reference it via Liquid in the destination's prompt
140+
No extra cost. Use {{customer.number}}, {{patientId}}, etc. directly.
141+
```
142+
143+
## Common patterns
144+
145+
### Pattern: "Forward an extracted ID after a database lookup"
146+
147+
A `lookupPatient` tool returned `{patientId: "p_42", dob: "1990-01-15"}` on assistant A. Assistant B needs `patientId`.
148+
149+
Use **Approach 3** — it's already in the bag. Assistant B's prompt: `The patient ID is {{patientId}}.` Don't re-extract it via schema; the model could mishear digits.
150+
151+
### Pattern: "Categorize what the caller wants and route on it"
152+
153+
Caller spent two turns describing a problem. Assistant A needs to classify the intent and hand off to a specialist who knows about that intent.
154+
155+
Use **Approach 1** — handoff arguments with an `enum` for `intent`. The classifying assistant's tool call carries the intent inline; the destination assistant reads `{{intent}}`.
156+
157+
### Pattern: "Pull a structured booking request out of free-form speech"
158+
159+
Caller said "I want to come in next Tuesday around 2 PM, maybe earlier if there's something". Assistant A needs `{preferredDate, preferredTime, alternativesOK}` as structured fields.
160+
161+
Use **Approach 2**`variableExtractionPlan.schema` with the destination. The dedicated extraction prompt + schema validation catches the structure better than inline arguments.
162+
163+
### Pattern: "Mix and match"
164+
165+
You can combine all three on a single handoff. Common shape: handoff arguments for the LLM-classified intent, schema extraction for one structured field that needs the dedicated prompt, and the destination's system prompt directly references prior tool results via Liquid.
166+
167+
## What if extraction fails?
168+
169+
Vapi's handoff path is failure-isolated:
170+
171+
- An empty `variableExtractionPlan` (`{}`) is a graceful no-op — the handoff proceeds without extraction.
172+
- A schema-extraction LLM failure (5xx, timeout, rate limit) is logged and the handoff proceeds with no extracted variables — it does not bail the handoff.
173+
- A schema-extraction result that isn't a plain object (an array, a primitive, `null`) is dropped before merge — it does not corrupt the variable bag.
174+
175+
So extraction is best-effort; if values are critical for the next assistant to function, prefer **Approach 1** (handoff arguments — required by the function schema, blocks the LLM call until provided) or **Approach 3** (reference values you already have).
176+
177+
## Next steps
178+
179+
- [Handoff tool](/squads/handoff) — full configuration reference for the handoff tool itself.
180+
- [Static variables and aliases](/tools/static-variables-and-aliases) — how the variable bag is built and what's available in scope.
181+
- [Dynamic variables](/assistants/dynamic-variables) — set initial variables when starting a call.

0 commit comments

Comments
 (0)