How tool variables work
A tool has two different fields called parameters, and they do close to opposite things. This page explains which is which, then classifies every value that can reach a tool call by how much you can trust where it came from.
The naming distinction that matters most
Tools have two fields called “parameters.” They look similar and mean opposite things:
The decision rule:
Could a malicious caller speak a value that ends up here? If the answer is “yes if I rely on the LLM to fill it,” the field belongs in the top-level
parametersarray, not infunction.parameters.
If you find yourself adding a field under function.parameters.properties in order to “tell the LLM about” something your backend already knows, stop — you’re exposing that field to the model. Move it to the top-level parameters array instead. The LLM cannot see, name, or override values defined there.
The variable bag
Liquid templates in static parameters and other tool fields resolve against a variable bag — a key/value object the platform builds at call start and updates during the call. Not every entry in the bag is equally trustworthy. Use this table to decide which variables are safe to use as a security boundary.
Tier 1 — Server-trusted (safe for static parameters)
Populated from signaling, config, the validated API call that initiated the call, or the server clock. The LLM has no write path to any of these during the conversation.
Tier 2 — Conversation-derived (DO NOT use as a security boundary)
These are present in the bag for templating convenience but contain user speech.
Tier 3 — LLM- or conversation-derived (NEVER use as a security boundary)
Setting trusted custom data at call start
If you have server-known data that isn’t signaling-derived — for example, an account ID you looked up by reverse-lookup before initiating an outbound call — inject it once at call creation time:
These keys are now in Tier 1 of the bag for the entire call. Reference them as {{ accountId }}, {{ loyaltyTier }}, etc. in any tool’s static parameters. They are server-trusted because your backend, not the LLM, set them.