From c03691d8e45f8cb1973f6872a05988f34a869939 Mon Sep 17 00:00:00 2001 From: adhamvapi <256238690+adhamvapi@users.noreply.github.com> Date: Fri, 6 Mar 2026 22:17:38 +0000 Subject: [PATCH 1/6] 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 --- fern/docs.yml | 3 + fern/tools/static-variables-and-aliases.mdx | 359 ++++++++++++++++++++ 2 files changed, 362 insertions(+) create mode 100644 fern/tools/static-variables-and-aliases.mdx diff --git a/fern/docs.yml b/fern/docs.yml index f3ffd0cba..f114ec8fe 100644 --- a/fern/docs.yml +++ b/fern/docs.yml @@ -195,6 +195,9 @@ navigation: - page: Tool rejection plan path: tools/tool-rejection-plan.mdx icon: fa-light fa-shield-xmark + - page: Static variables and aliases + path: tools/static-variables-and-aliases.mdx + icon: fa-light fa-arrow-right-arrow-left - page: Custom tools troubleshooting path: tools/custom-tools-troubleshooting.mdx icon: fa-light fa-wrench diff --git a/fern/tools/static-variables-and-aliases.mdx b/fern/tools/static-variables-and-aliases.mdx new file mode 100644 index 000000000..dc8e361bb --- /dev/null +++ b/fern/tools/static-variables-and-aliases.mdx @@ -0,0 +1,359 @@ +--- +title: Static variables and aliases +subtitle: Pass fixed values and extract structured data from tool responses without LLM involvement. +slug: tools/static-variables-and-aliases +--- + +## Overview + +Vapi tools support two features that let you move data between tool calls deterministically, without relying on the LLM to interpret or forward values: + +- **Static variables (parameters)** inject fixed or template-resolved values into every tool call, regardless of what the LLM generates. +- **Variable extraction (aliases)** pull specific fields out of a tool's JSON response and store them for use in subsequent tool calls. + +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. + +**In this guide, you'll learn to:** +- Add static parameters to API request and function tools +- Extract variables from tool responses using aliases +- Chain tools together so data flows between them without LLM involvement + +## Static variables (parameters) + +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. + +### How it works + +- `parameters` is an array of `{ key, value }` objects on the tool definition. +- `value` can be any JSON type: string, number, boolean, object, or array. +- String values support **Liquid templates** (for example, `{{ customer.number }}`). Objects and arrays are walked recursively to resolve Liquid templates in nested strings. +- Static parameters are merged **after** LLM-generated arguments, so they override any LLM-generated key with the same name. + +### Supported tool types + +| Tool type | Static parameters supported | +|-----------|---------------------------| +| `apiRequest` | Yes | +| `function` | Yes | +| `code` | No | +| `handoff` | No | + +### API request tool example + +Static parameters merge into the HTTP request body alongside any LLM-generated fields: + +```json title="API request tool with static parameters" +{ + "type": "apiRequest", + "method": "POST", + "url": "https://api.example.com/leads", + "parameters": [ + { "key": "org_id", "value": "my-org-123" }, + { "key": "source", "value": "vapi-call" }, + { + "key": "metadata", + "value": { + "channel": "voice", + "callId": "{{ transport.callSid }}" + } + } + ] +} +``` + +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. + +### Function tool example + +For function tools, static parameters merge into the function call arguments sent to your server webhook: + +```json title="Function tool with static parameters" +{ + "type": "function", + "function": { + "name": "lookup_user", + "description": "Look up a user by phone number", + "parameters": { + "type": "object", + "properties": { + "phone": { + "type": "string", + "description": "The phone number to look up" + } + }, + "required": ["phone"] + } + }, + "server": { + "url": "https://my-server.com/webhook" + }, + "parameters": [ + { "key": "api_version", "value": "v2" }, + { "key": "caller_number", "value": "{{ customer.number }}" } + ] +} +``` + +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. + + +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"`. + + +### Liquid template variables + +String values in static parameters can reference any variable available in the call context: + +| Variable | Example | Description | +|----------|---------|-------------| +| `customer.number` | `{{ customer.number }}` | The customer's phone number | +| `transport.callSid` | `{{ transport.callSid }}` | The transport call session ID | +| `now` | `{{ now }}` | Current timestamp | +| `date` | `{{ date }}` | Current date | +| Previously extracted variables | `{{ userId }}` | Variables extracted by earlier tools via aliases | + +## Variable extraction plan (aliases) + +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. + +### How it works + +- `variableExtractionPlan` is an object with an `aliases` array. +- Each alias has `{ key, value }` where `key` is the variable name to store and `value` is a Liquid template expression. +- The parsed JSON response body is available as **`$`** (dollar sign). Reference nested fields with dot notation: `{{ $.data.id }}`. +- Top-level response properties are also spread at the root level, so `{{ name }}` works for a top-level `name` field. +- Liquid filters are supported: `{{ $.email | downcase }}`, `{{ $.name | upcase }}`. +- Extracted variables are stored in the call's artifact and are available in subsequent tool calls via Liquid templates. + +### Supported tool types + +| Tool type | Variable extraction supported | +|-----------|------------------------------| +| `apiRequest` | Yes | +| `function` | Yes | +| `code` | Yes | +| `handoff` | Yes | + +### Example: extract fields from an API response + +Suppose your API returns: + +```json title="API response" +{ + "data": { + "id": "usr_abc123", + "name": "Jane Smith", + "email": "Jane.Smith@example.com" + }, + "status": "active" +} +``` + +Configure aliases to extract the fields you need: + +```json title="API request tool with variable extraction" +{ + "type": "apiRequest", + "method": "GET", + "url": "https://api.example.com/users/{{ customer.number }}", + "variableExtractionPlan": { + "aliases": [ + { "key": "userId", "value": "{{ $.data.id }}" }, + { "key": "userName", "value": "{{ $.data.name }}" }, + { "key": "userEmail", "value": "{{ $.data.email | downcase }}" }, + { "key": "accountStatus", "value": "{{ $.status }}" } + ] + } +} +``` + +After this tool executes, the variables `userId`, `userName`, `userEmail`, and `accountStatus` are available for use in any subsequent tool call. + + +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. + + +### Using extracted variables in subsequent tools + +Once variables are extracted, reference them by name in any Liquid template context -- URLs, headers, request bodies, or static parameters: + +```json title="Subsequent tool using extracted variables in the URL and body" +{ + "type": "apiRequest", + "method": "POST", + "url": "https://api.example.com/orders", + "body": { + "type": "json", + "value": "{ \"user_id\": \"{{ userId }}\", \"user_name\": \"{{ userName }}\" }" + } +} +``` + +Or via static parameters on a function tool: + +```json title="Function tool using extracted variables in static parameters" +{ + "type": "function", + "function": { + "name": "create_order", + "description": "Create an order for a user", + "parameters": { + "type": "object", + "properties": { + "items": { + "type": "array", + "description": "Items to order" + } + }, + "required": ["items"] + } + }, + "server": { + "url": "https://my-server.com/webhook" + }, + "parameters": [ + { "key": "user_id", "value": "{{ userId }}" }, + { "key": "user_email", "value": "{{ userEmail }}" } + ] +} +``` + +## Deterministic tool chaining + +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. + +### Example: look up a user, then create an order + +**Tool A** calls an external API to look up a user and extracts the user's ID and name: + +```json title="Tool A: User lookup with variable extraction" +{ + "type": "apiRequest", + "method": "GET", + "url": "https://api.example.com/users/{{ customer.number }}", + "variableExtractionPlan": { + "aliases": [ + { "key": "userId", "value": "{{ $.data.id }}" }, + { "key": "userName", "value": "{{ $.data.name }}" } + ] + } +} +``` + +**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: + +```json title="Tool B: Create order with extracted user ID" +{ + "type": "function", + "function": { + "name": "create_order", + "description": "Create an order for the current user", + "parameters": { + "type": "object", + "properties": { + "items": { + "type": "array", + "description": "The items to include in the order" + } + }, + "required": ["items"] + } + }, + "server": { + "url": "https://my-server.com/webhook" + }, + "parameters": [ + { "key": "user_id", "value": "{{ userId }}" }, + { "key": "user_name", "value": "{{ userName }}" } + ] +} +``` + +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. + + +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. + + +## Full API example + +Create an assistant with two chained tools using cURL: + +```bash title="Create tools and assistant with tool chaining" +# Step 1: Create the user lookup tool (Tool A) +curl -X POST "https://api.vapi.ai/tool" \ + -H "Authorization: Bearer $VAPI_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "type": "apiRequest", + "name": "User Lookup", + "method": "GET", + "url": "https://api.example.com/users/{{ customer.number }}", + "variableExtractionPlan": { + "aliases": [ + { "key": "userId", "value": "{{ $.data.id }}" }, + { "key": "userName", "value": "{{ $.data.name }}" }, + { "key": "userEmail", "value": "{{ $.data.email | downcase }}" } + ] + } + }' + +# Step 2: Create the order tool (Tool B) +curl -X POST "https://api.vapi.ai/tool" \ + -H "Authorization: Bearer $VAPI_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "type": "function", + "function": { + "name": "create_order", + "description": "Create an order for the current user", + "parameters": { + "type": "object", + "properties": { + "items": { + "type": "array", + "description": "The items to include in the order" + } + }, + "required": ["items"] + } + }, + "server": { + "url": "https://my-server.com/webhook" + }, + "parameters": [ + { "key": "user_id", "value": "{{ userId }}" }, + { "key": "user_name", "value": "{{ userName }}" }, + { "key": "user_email", "value": "{{ userEmail }}" } + ] + }' + +# Step 3: Attach both tools to your assistant +curl -X PATCH "https://api.vapi.ai/assistant/YOUR_ASSISTANT_ID" \ + -H "Authorization: Bearer $VAPI_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "model": { + "provider": "openai", + "model": "gpt-4o", + "toolIds": ["TOOL_A_ID", "TOOL_B_ID"] + } + }' +``` + +## Tips + +- **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). +- **Aliases extract from JSON only.** The tool response must be parseable as JSON. Non-JSON responses (plain text, HTML) do not support variable extraction. +- **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. +- **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. +- **Combine with Liquid filters.** Use Liquid filters in aliases for transformations: `{{ $.name | upcase }}`, `{{ $.price | divided_by: 100 }}`, `{{ $.email | downcase }}`. + +## Next steps + +Now that you understand static variables and aliases: + +- **[Custom tools](/tools/custom-tools):** Learn how to create and configure custom function tools. +- **[Code tool](/tools/code-tool):** Run TypeScript code directly on Vapi's infrastructure without a server. +- **[Tool rejection plan](/tools/tool-rejection-plan):** Add conditions to prevent unintended tool calls. +- **[API reference](/api-reference/tools/create):** See the complete tool creation API reference. From a5a850102d755a71e4be79e0ffa275f924ae3bb3 Mon Sep 17 00:00:00 2001 From: Vapi Tasker Date: Fri, 6 Mar 2026 22:25:30 +0000 Subject: [PATCH 2/6] fix: upgrade fern-go-sdk generator to 1.28.3 to fix preview-go CI The preview-go CI check has been failing since at least Feb 16 across all PRs touching fern/ files. The root cause is the outdated Go SDK generator version (1.16.4) being unable to compile code generated from the current OpenAPI spec. Upgrading to 1.28.3 (latest) includes fixes for datetime handling, marshal/unmarshal code generation, and other compilation issues. Co-Authored-By: Claude Opus 4.6 --- fern/apis/api/generators.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fern/apis/api/generators.yml b/fern/apis/api/generators.yml index 53986bc1c..bdd9f4416 100644 --- a/fern/apis/api/generators.yml +++ b/fern/apis/api/generators.yml @@ -51,7 +51,7 @@ groups: go-sdk: generators: - name: fernapi/fern-go-sdk - version: 1.16.4 + version: 1.28.3 disable-examples: true api: settings: From f0bbd3b0e8d82381aa952839a18bb7e0366c5b4b Mon Sep 17 00:00:00 2001 From: Vapi Tasker Date: Fri, 6 Mar 2026 22:35:33 +0000 Subject: [PATCH 3/6] docs: enhance static variables example with richer JSON object - Add nested JSON object with sub-objects, arrays, and mixed types - Include number value example (priority: 1) - Add explicit callouts for recursive Liquid template resolution - Make it clearer that value accepts any JSON type DEVREL-529 Co-Authored-By: Claude Opus 4.6 --- fern/tools/static-variables-and-aliases.mdx | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/fern/tools/static-variables-and-aliases.mdx b/fern/tools/static-variables-and-aliases.mdx index dc8e361bb..cb8e3cc23 100644 --- a/fern/tools/static-variables-and-aliases.mdx +++ b/fern/tools/static-variables-and-aliases.mdx @@ -50,18 +50,29 @@ Static parameters merge into the HTTP request body alongside any LLM-generated f "parameters": [ { "key": "org_id", "value": "my-org-123" }, { "key": "source", "value": "vapi-call" }, + { "key": "priority", "value": 1 }, { "key": "metadata", "value": { "channel": "voice", - "callId": "{{ transport.callSid }}" + "callId": "{{ transport.callSid }}", + "region": "us-east", + "tags": ["inbound", "{{ customer.number }}"], + "routing": { + "department": "sales", + "queue": "priority" + } } } ] } ``` -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. +In this example, every request to the leads endpoint includes `org_id`, `source`, `priority`, and `metadata` -- even though the LLM never generates these values. Notice that: + +- `value` can be a **string** (`"my-org-123"`), **number** (`1`), or a **JSON object/array**. +- The `metadata` value is a **nested JSON object** with sub-objects (`routing`) and arrays (`tags`). +- Liquid templates like `{{ transport.callSid }}` and `{{ customer.number }}` are resolved **recursively** inside nested objects and arrays at runtime. ### Function tool example From 631bfa1b8bdac50126fb6cfc5f44b33331aff5a1 Mon Sep 17 00:00:00 2001 From: Vapi Tasker Date: Fri, 6 Mar 2026 22:36:26 +0000 Subject: [PATCH 4/6] fix: upgrade fern-python-sdk generator to 4.61.5 to fix preview-python CI The preview-python CI check has been failing across all PRs touching fern/ files due to mypy no-redef errors in the generated Python SDK. The outdated generator version (4.37.1) produces union type definitions with duplicate field names that mypy flags as errors (32 errors across 29 files). Upgrading to 4.61.5 (latest) includes fixes for union type code generation and other improvements. Co-Authored-By: Claude Opus 4.6 --- fern/apis/api/generators.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fern/apis/api/generators.yml b/fern/apis/api/generators.yml index bdd9f4416..cdd389bff 100644 --- a/fern/apis/api/generators.yml +++ b/fern/apis/api/generators.yml @@ -11,7 +11,7 @@ groups: python-sdk: generators: - name: fernapi/fern-python-sdk - version: 4.37.1 + version: 4.61.5 api: settings: prefer-undiscriminated-unions-with-literals: true From a61e40c4e87e5972f6b18866d42e79ecfae367c1 Mon Sep 17 00:00:00 2001 From: Vapi Tasker Date: Fri, 6 Mar 2026 22:47:30 +0000 Subject: [PATCH 5/6] fix: revert python-sdk to 4.37.1 to fix CLI version mismatch breaking all SDK previews The previous commit upgraded fern-python-sdk from 4.37.1 to 4.61.5, but version 4.61.5 requires Fern CLI 3.72.0+ while CI installs 3.62.0. This CLI version mismatch caused fern generate to fail for ALL SDK groups (go, python, typescript) because it validates all generators before running any single group. This reverts python-sdk to 4.37.1 (compatible with CLI 3.62.0) while keeping go-sdk at 1.28.3 which fixes the pre-existing Go code generation syntax errors (invalid assistants.go, error_codes.go produced by 1.16.4). --- fern/apis/api/generators.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fern/apis/api/generators.yml b/fern/apis/api/generators.yml index cdd389bff..bdd9f4416 100644 --- a/fern/apis/api/generators.yml +++ b/fern/apis/api/generators.yml @@ -11,7 +11,7 @@ groups: python-sdk: generators: - name: fernapi/fern-python-sdk - version: 4.61.5 + version: 4.37.1 api: settings: prefer-undiscriminated-unions-with-literals: true From 81cbefa3cfc28443b33a2431bd9d31e83e00ec5a Mon Sep 17 00:00:00 2001 From: adhamvapi <256238690+adhamvapi@users.noreply.github.com> Date: Fri, 6 Mar 2026 23:51:44 +0000 Subject: [PATCH 6/6] fix: add parameters field to tool schemas in OpenAPI spec Add the missing `parameters` field (for static variables) to the OpenAPI specification so it appears in the API reference for apiRequest and function tools. This field was added to the backend (PR #10320) but the docs spec was never updated. Changes: - Add ToolParameter schema to components/schemas - Add parameters property to ApiRequestTool - Add parameters property to CreateApiRequestToolDTO - Add parameters property to UpdateApiRequestToolDTO - Add parameters property to FunctionTool - Add parameters property to CreateFunctionToolDTO - Add parameters property to UpdateFunctionToolDTO Co-Authored-By: Claude Opus 4.6 --- fern/apis/api/openapi.json | 77 +++++++++++++++++++++++++++++++++++++- 1 file changed, 76 insertions(+), 1 deletion(-) diff --git a/fern/apis/api/openapi.json b/fern/apis/api/openapi.json index 33010af96..d64391e89 100644 --- a/fern/apis/api/openapi.json +++ b/fern/apis/api/openapi.json @@ -11654,6 +11654,13 @@ "$ref": "#/components/schemas/ToolRejectionPlan" } ] + }, + "parameters": { + "type": "array", + "description": "Static key-value pairs merged into the request body or function arguments. Values support Liquid templates.", + "items": { + "$ref": "#/components/schemas/ToolParameter" + } } }, "required": [ @@ -39184,6 +39191,13 @@ "$ref": "#/components/schemas/VariableExtractionPlan" } ] + }, + "parameters": { + "type": "array", + "description": "Static key-value pairs merged into the request body or function arguments. Values support Liquid templates.", + "items": { + "$ref": "#/components/schemas/ToolParameter" + } } }, "required": [ @@ -39564,6 +39578,13 @@ "$ref": "#/components/schemas/OpenAIFunction" } ] + }, + "parameters": { + "type": "array", + "description": "Static key-value pairs merged into the request body or function arguments. Values support Liquid templates.", + "items": { + "$ref": "#/components/schemas/ToolParameter" + } } }, "required": [ @@ -41340,6 +41361,13 @@ "$ref": "#/components/schemas/ToolRejectionPlan" } ] + }, + "parameters": { + "type": "array", + "description": "Static key-value pairs merged into the request body or function arguments. Values support Liquid templates.", + "items": { + "$ref": "#/components/schemas/ToolParameter" + } } }, "required": [ @@ -41958,6 +41986,13 @@ "$ref": "#/components/schemas/VariableExtractionPlan" } ] + }, + "parameters": { + "type": "array", + "description": "Static key-value pairs merged into the request body or function arguments. Values support Liquid templates.", + "items": { + "$ref": "#/components/schemas/ToolParameter" + } } } }, @@ -42185,6 +42220,13 @@ "$ref": "#/components/schemas/OpenAIFunction" } ] + }, + "parameters": { + "type": "array", + "description": "Static key-value pairs merged into the request body or function arguments. Values support Liquid templates.", + "items": { + "$ref": "#/components/schemas/ToolParameter" + } } } }, @@ -64645,7 +64687,40 @@ "transport", "twiml" ] + }, + "ToolParameter": { + "type": "object", + "properties": { + "key": { + "type": "string", + "description": "This is the key of the parameter." + }, + "value": { + "description": "The value of the parameter. Any JSON type. String values support Liquid templates.", + "oneOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + }, + { + "type": "object" + }, + { + "type": "array" + } + ] + } + }, + "required": [ + "key", + "value" + ] } } } -} \ No newline at end of file +}