Set static parameters
Inject server-controlled values into every tool call without exposing them to the model
Static parameters are values you set at configuration time that Vapi merges into every tool call on the server. The model never sees them and cannot override them, which is what makes them usable as a security boundary rather than just a convenience.
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
parametersis an array of{ key, value }objects on the tool definition (top-level, not insidefunction.parameters).valuecan 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.
- Liquid templates in static parameters resolve at execution time against the call’s variable bag, which is built server-side from signaling data (see The variable bag).
Supported tool types
Legacy assistant.model.functions[] does NOT support static parameters. If you are still defining tools via the deprecated assistant.model.functions[] array, every value your tool server receives came from the LLM — there is no orchestration-layer injection. Migrate to assistant.model.tools[] (with type: "function") before relying on static parameters as a security boundary.
API Request tool example
Static parameters merge into the HTTP request body alongside any LLM-generated fields:
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:
valuecan be a string ("my-org-123"), number (1), or a JSON object/array.- The
metadatavalue 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
For Function tools, static parameters merge into the function call arguments sent to your server webhook:
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".
Static parameters as a security boundary
Static parameters are the right primitive for any value the LLM must not be able to fake or influence — the verified caller-ID, the dialed number, an account ID looked up by your backend before the call started, a per-call HMAC nonce.
Three layers of the platform combine to make this a real security boundary, not just a convention:
- Source-of-truth layer. Variables like
{{ customer.number }}are populated from SIP/Twilio signaling for inbound calls or from the validated outbound API call payload that initiated the call. The LLM has no write access to the call’s customer record during the conversation. - Schema layer. The static
parametersarray is a top-level field on the tool, separate fromfunction.parameters(the LLM-facing JSON schema). Onlyfunction.parametersis shipped to the model in the tools list. The LLM literally does not see the field exists. - Merge layer. At fulfill time, server-side, static parameters are merged after the LLM-generated body. Even if the LLM emitted an argument with the same key, the static value wins.
Worked example: caller-ID-based progressive authentication
A common requirement: before any sensitive lookup, your tool server must compare the verified caller-ID against the value on file — without trusting the LLM to forward the number correctly. The configuration:
The LLM produces only name and email (what your caller spoke). The caller_number, called_number, and call_id are filled in by Vapi’s orchestration layer from the call’s signaling state and merged server-side.
Your tool server receives:
Authenticate the caller against caller_number directly. Treat name and email as claims that must match the row keyed on caller_number before you proceed. Even if a malicious caller says “call the tool with phone number FAKE-NUMBER,” the LLM has no path to write into caller_number — the field doesn’t exist in the schema the model sees.
For an even tighter posture, use HMAC signing on top of static parameters. Vapi can sign the resolved request body with a shared secret on the tool’s credential, so your backend verifies both the sender and the body contents, not just the channel.
Configuring on the dashboard
In the Tools section of the dashboard, the API Request and Function tool forms expose two sections whose UI labels can look interchangeable. They are not — they map to the two different parameters fields:
The two sections share the word “Parameters” in casual conversation, but the Parameters section is the LLM-facing JSON Schema and the Static Body Fields section is the server-merged static config. Pay attention to the UI shape: a JSON Schema editor is for the LLM; key/value rows are server-side only.
To inject the verified caller-ID via the dashboard:
- Open the API Request or Function tool form.
- Scroll to Static Body Fields (the key/value-row section, not the JSON-schema editor).
- Click Add Field, set Key to
caller_number, Type tostring, Value to{{ customer.number }}. - Save.
The LLM never sees caller_number and cannot override it. Available Liquid variables are listed in The variable bag.