Skip to content

Commit c03691d

Browse files
adhamvapiclaude
andcommitted
feat: document static variables and aliases for tool outputs
Add new documentation page covering two recently-shipped features: - Static variables (parameters): inject fixed or Liquid-templated values into API request and function tool calls, bypassing the LLM - Variable extraction plan (aliases): deterministically extract fields from tool JSON responses using Liquid templates with the $ reference Includes a combined tool-chaining example showing data flowing from one tool's response to the next tool's request without LLM involvement. Resolves DEVREL-529 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 95a8b4f commit c03691d

2 files changed

Lines changed: 362 additions & 0 deletions

File tree

fern/docs.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,9 @@ navigation:
195195
- page: Tool rejection plan
196196
path: tools/tool-rejection-plan.mdx
197197
icon: fa-light fa-shield-xmark
198+
- page: Static variables and aliases
199+
path: tools/static-variables-and-aliases.mdx
200+
icon: fa-light fa-arrow-right-arrow-left
198201
- page: Custom tools troubleshooting
199202
path: tools/custom-tools-troubleshooting.mdx
200203
icon: fa-light fa-wrench
Lines changed: 359 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,359 @@
1+
---
2+
title: Static variables and aliases
3+
subtitle: Pass fixed values and extract structured data from tool responses without LLM involvement.
4+
slug: tools/static-variables-and-aliases
5+
---
6+
7+
## Overview
8+
9+
Vapi tools support two features that let you move data between tool calls deterministically, without relying on the LLM to interpret or forward values:
10+
11+
- **Static variables (parameters)** inject fixed or template-resolved values into every tool call, regardless of what the LLM generates.
12+
- **Variable extraction (aliases)** pull specific fields out of a tool's JSON response and store them for use in subsequent tool calls.
13+
14+
Combined, these features enable **deterministic tool chaining** -- Tool A fetches data and extracts variables, Tool B receives those variables automatically. The LLM orchestrates *when* tools run, but the data flow between them is fully controlled by you.
15+
16+
**In this guide, you'll learn to:**
17+
- Add static parameters to API request and function tools
18+
- Extract variables from tool responses using aliases
19+
- Chain tools together so data flows between them without LLM involvement
20+
21+
## Static variables (parameters)
22+
23+
The `parameters` field lets you define key-value pairs that are always merged into the tool's request body or function arguments. These values bypass the LLM entirely -- the model never sees or generates them.
24+
25+
### How it works
26+
27+
- `parameters` is an array of `{ key, value }` objects on the tool definition.
28+
- `value` can be any JSON type: string, number, boolean, object, or array.
29+
- String values support **Liquid templates** (for example, `{{ customer.number }}`). Objects and arrays are walked recursively to resolve Liquid templates in nested strings.
30+
- Static parameters are merged **after** LLM-generated arguments, so they override any LLM-generated key with the same name.
31+
32+
### Supported tool types
33+
34+
| Tool type | Static parameters supported |
35+
|-----------|---------------------------|
36+
| `apiRequest` | Yes |
37+
| `function` | Yes |
38+
| `code` | No |
39+
| `handoff` | No |
40+
41+
### API request tool example
42+
43+
Static parameters merge into the HTTP request body alongside any LLM-generated fields:
44+
45+
```json title="API request tool with static parameters"
46+
{
47+
"type": "apiRequest",
48+
"method": "POST",
49+
"url": "https://api.example.com/leads",
50+
"parameters": [
51+
{ "key": "org_id", "value": "my-org-123" },
52+
{ "key": "source", "value": "vapi-call" },
53+
{
54+
"key": "metadata",
55+
"value": {
56+
"channel": "voice",
57+
"callId": "{{ transport.callSid }}"
58+
}
59+
}
60+
]
61+
}
62+
```
63+
64+
In this example, every request to the leads endpoint includes `org_id`, `source`, and `metadata` -- even though the LLM never generates these values. The `callId` inside `metadata` is resolved from the call's transport data at runtime via Liquid.
65+
66+
### Function tool example
67+
68+
For function tools, static parameters merge into the function call arguments sent to your server webhook:
69+
70+
```json title="Function tool with static parameters"
71+
{
72+
"type": "function",
73+
"function": {
74+
"name": "lookup_user",
75+
"description": "Look up a user by phone number",
76+
"parameters": {
77+
"type": "object",
78+
"properties": {
79+
"phone": {
80+
"type": "string",
81+
"description": "The phone number to look up"
82+
}
83+
},
84+
"required": ["phone"]
85+
}
86+
},
87+
"server": {
88+
"url": "https://my-server.com/webhook"
89+
},
90+
"parameters": [
91+
{ "key": "api_version", "value": "v2" },
92+
{ "key": "caller_number", "value": "{{ customer.number }}" }
93+
]
94+
}
95+
```
96+
97+
When the LLM calls `lookup_user` with `{ "phone": "+15551234567" }`, your webhook receives `{ "phone": "+15551234567", "api_version": "v2", "caller_number": "+15559876543" }` -- the static parameters are merged in.
98+
99+
<Note>
100+
Static parameters override LLM-generated arguments with the same key. If the LLM generates `"source": "chat"` and your static parameters include `"source": "vapi-call"`, the webhook receives `"source": "vapi-call"`.
101+
</Note>
102+
103+
### Liquid template variables
104+
105+
String values in static parameters can reference any variable available in the call context:
106+
107+
| Variable | Example | Description |
108+
|----------|---------|-------------|
109+
| `customer.number` | `{{ customer.number }}` | The customer's phone number |
110+
| `transport.callSid` | `{{ transport.callSid }}` | The transport call session ID |
111+
| `now` | `{{ now }}` | Current timestamp |
112+
| `date` | `{{ date }}` | Current date |
113+
| Previously extracted variables | `{{ userId }}` | Variables extracted by earlier tools via aliases |
114+
115+
## Variable extraction plan (aliases)
116+
117+
The `variableExtractionPlan` field lets you extract specific values from a tool's JSON response and store them as named variables. These variables become available to all subsequent tool calls in the same conversation.
118+
119+
### How it works
120+
121+
- `variableExtractionPlan` is an object with an `aliases` array.
122+
- Each alias has `{ key, value }` where `key` is the variable name to store and `value` is a Liquid template expression.
123+
- The parsed JSON response body is available as **`$`** (dollar sign). Reference nested fields with dot notation: `{{ $.data.id }}`.
124+
- Top-level response properties are also spread at the root level, so `{{ name }}` works for a top-level `name` field.
125+
- Liquid filters are supported: `{{ $.email | downcase }}`, `{{ $.name | upcase }}`.
126+
- Extracted variables are stored in the call's artifact and are available in subsequent tool calls via Liquid templates.
127+
128+
### Supported tool types
129+
130+
| Tool type | Variable extraction supported |
131+
|-----------|------------------------------|
132+
| `apiRequest` | Yes |
133+
| `function` | Yes |
134+
| `code` | Yes |
135+
| `handoff` | Yes |
136+
137+
### Example: extract fields from an API response
138+
139+
Suppose your API returns:
140+
141+
```json title="API response"
142+
{
143+
"data": {
144+
"id": "usr_abc123",
145+
"name": "Jane Smith",
146+
"email": "Jane.Smith@example.com"
147+
},
148+
"status": "active"
149+
}
150+
```
151+
152+
Configure aliases to extract the fields you need:
153+
154+
```json title="API request tool with variable extraction"
155+
{
156+
"type": "apiRequest",
157+
"method": "GET",
158+
"url": "https://api.example.com/users/{{ customer.number }}",
159+
"variableExtractionPlan": {
160+
"aliases": [
161+
{ "key": "userId", "value": "{{ $.data.id }}" },
162+
{ "key": "userName", "value": "{{ $.data.name }}" },
163+
{ "key": "userEmail", "value": "{{ $.data.email | downcase }}" },
164+
{ "key": "accountStatus", "value": "{{ $.status }}" }
165+
]
166+
}
167+
}
168+
```
169+
170+
After this tool executes, the variables `userId`, `userName`, `userEmail`, and `accountStatus` are available for use in any subsequent tool call.
171+
172+
<Tip>
173+
Use the `$` reference for clarity when accessing nested fields (`{{ $.data.id }}`). For top-level fields, you can reference them directly (`{{ status }}`), but using `$` is more explicit.
174+
</Tip>
175+
176+
### Using extracted variables in subsequent tools
177+
178+
Once variables are extracted, reference them by name in any Liquid template context -- URLs, headers, request bodies, or static parameters:
179+
180+
```json title="Subsequent tool using extracted variables in the URL and body"
181+
{
182+
"type": "apiRequest",
183+
"method": "POST",
184+
"url": "https://api.example.com/orders",
185+
"body": {
186+
"type": "json",
187+
"value": "{ \"user_id\": \"{{ userId }}\", \"user_name\": \"{{ userName }}\" }"
188+
}
189+
}
190+
```
191+
192+
Or via static parameters on a function tool:
193+
194+
```json title="Function tool using extracted variables in static parameters"
195+
{
196+
"type": "function",
197+
"function": {
198+
"name": "create_order",
199+
"description": "Create an order for a user",
200+
"parameters": {
201+
"type": "object",
202+
"properties": {
203+
"items": {
204+
"type": "array",
205+
"description": "Items to order"
206+
}
207+
},
208+
"required": ["items"]
209+
}
210+
},
211+
"server": {
212+
"url": "https://my-server.com/webhook"
213+
},
214+
"parameters": [
215+
{ "key": "user_id", "value": "{{ userId }}" },
216+
{ "key": "user_email", "value": "{{ userEmail }}" }
217+
]
218+
}
219+
```
220+
221+
## Deterministic tool chaining
222+
223+
By combining static parameters and variable extraction, you can build tool chains where data flows from one tool's response to the next tool's request -- all without LLM involvement in the data transfer.
224+
225+
### Example: look up a user, then create an order
226+
227+
**Tool A** calls an external API to look up a user and extracts the user's ID and name:
228+
229+
```json title="Tool A: User lookup with variable extraction"
230+
{
231+
"type": "apiRequest",
232+
"method": "GET",
233+
"url": "https://api.example.com/users/{{ customer.number }}",
234+
"variableExtractionPlan": {
235+
"aliases": [
236+
{ "key": "userId", "value": "{{ $.data.id }}" },
237+
{ "key": "userName", "value": "{{ $.data.name }}" }
238+
]
239+
}
240+
}
241+
```
242+
243+
**Tool B** uses the extracted `userId` as a static parameter, ensuring the correct user ID reaches your webhook without the LLM needing to parse or forward it:
244+
245+
```json title="Tool B: Create order with extracted user ID"
246+
{
247+
"type": "function",
248+
"function": {
249+
"name": "create_order",
250+
"description": "Create an order for the current user",
251+
"parameters": {
252+
"type": "object",
253+
"properties": {
254+
"items": {
255+
"type": "array",
256+
"description": "The items to include in the order"
257+
}
258+
},
259+
"required": ["items"]
260+
}
261+
},
262+
"server": {
263+
"url": "https://my-server.com/webhook"
264+
},
265+
"parameters": [
266+
{ "key": "user_id", "value": "{{ userId }}" },
267+
{ "key": "user_name", "value": "{{ userName }}" }
268+
]
269+
}
270+
```
271+
272+
The LLM decides *when* to call each tool based on the conversation, but the `user_id` and `user_name` values flow directly from Tool A's response to Tool B's request through the variable system.
273+
274+
<Warning>
275+
Variable extraction depends on the tool response being valid JSON. If the response cannot be parsed as JSON, no variables are extracted. Make sure the APIs you call return JSON responses.
276+
</Warning>
277+
278+
## Full API example
279+
280+
Create an assistant with two chained tools using cURL:
281+
282+
```bash title="Create tools and assistant with tool chaining"
283+
# Step 1: Create the user lookup tool (Tool A)
284+
curl -X POST "https://api.vapi.ai/tool" \
285+
-H "Authorization: Bearer $VAPI_API_KEY" \
286+
-H "Content-Type: application/json" \
287+
-d '{
288+
"type": "apiRequest",
289+
"name": "User Lookup",
290+
"method": "GET",
291+
"url": "https://api.example.com/users/{{ customer.number }}",
292+
"variableExtractionPlan": {
293+
"aliases": [
294+
{ "key": "userId", "value": "{{ $.data.id }}" },
295+
{ "key": "userName", "value": "{{ $.data.name }}" },
296+
{ "key": "userEmail", "value": "{{ $.data.email | downcase }}" }
297+
]
298+
}
299+
}'
300+
301+
# Step 2: Create the order tool (Tool B)
302+
curl -X POST "https://api.vapi.ai/tool" \
303+
-H "Authorization: Bearer $VAPI_API_KEY" \
304+
-H "Content-Type: application/json" \
305+
-d '{
306+
"type": "function",
307+
"function": {
308+
"name": "create_order",
309+
"description": "Create an order for the current user",
310+
"parameters": {
311+
"type": "object",
312+
"properties": {
313+
"items": {
314+
"type": "array",
315+
"description": "The items to include in the order"
316+
}
317+
},
318+
"required": ["items"]
319+
}
320+
},
321+
"server": {
322+
"url": "https://my-server.com/webhook"
323+
},
324+
"parameters": [
325+
{ "key": "user_id", "value": "{{ userId }}" },
326+
{ "key": "user_name", "value": "{{ userName }}" },
327+
{ "key": "user_email", "value": "{{ userEmail }}" }
328+
]
329+
}'
330+
331+
# Step 3: Attach both tools to your assistant
332+
curl -X PATCH "https://api.vapi.ai/assistant/YOUR_ASSISTANT_ID" \
333+
-H "Authorization: Bearer $VAPI_API_KEY" \
334+
-H "Content-Type: application/json" \
335+
-d '{
336+
"model": {
337+
"provider": "openai",
338+
"model": "gpt-4o",
339+
"toolIds": ["TOOL_A_ID", "TOOL_B_ID"]
340+
}
341+
}'
342+
```
343+
344+
## Tips
345+
346+
- **Static parameters are invisible to the LLM.** The model does not see them in the tool schema and cannot override them (they are merged last).
347+
- **Aliases extract from JSON only.** The tool response must be parseable as JSON. Non-JSON responses (plain text, HTML) do not support variable extraction.
348+
- **Variable names are global to the call.** Extracted variables persist for the entire call and can be referenced by any subsequent tool. Choose unique, descriptive key names to avoid collisions.
349+
- **Liquid templates resolve at execution time.** Template expressions in static parameters and aliases are evaluated when the tool runs, not when the tool is created.
350+
- **Combine with Liquid filters.** Use Liquid filters in aliases for transformations: `{{ $.name | upcase }}`, `{{ $.price | divided_by: 100 }}`, `{{ $.email | downcase }}`.
351+
352+
## Next steps
353+
354+
Now that you understand static variables and aliases:
355+
356+
- **[Custom tools](/tools/custom-tools):** Learn how to create and configure custom function tools.
357+
- **[Code tool](/tools/code-tool):** Run TypeScript code directly on Vapi's infrastructure without a server.
358+
- **[Tool rejection plan](/tools/tool-rejection-plan):** Add conditions to prevent unintended tool calls.
359+
- **[API reference](/api-reference/tools/create):** See the complete tool creation API reference.

0 commit comments

Comments
 (0)