Dynamic variables

Personalise prompts and tool configs per session without duplicating agents

Dynamic variables let you define named placeholders in an agent’s prompt, first message, and webhook tool configurations, then supply concrete values at session start. The same agent definition can serve different customers, tenants, or contexts - just change the values you inject, not the agent.

Defining variables on an agent

Variables are declared on an agent via PATCH /v1/agents/{id}/variables. Each variable has a key, a type, an optional default, and an optional description.

TypeSemantics
stringPlain text. Interpolated verbatim with {{key}}.
numberNumeric value. Rendered as its decimal representation.
booleantrue or false.
jsonAny valid JSON value. Use {{key|json}} to inject safely inside JSON tool bodies.
$curl -X PATCH https://api.speechify.ai/v1/agents/a_01HS.../variables \
> -H "Authorization: Bearer $SPEECHIFY_API_KEY" \
> -H "Content-Type: application/json" \
> -d '{
> "variables": [
> {
> "key": "product_name",
> "type": "string",
> "default": "Speechify",
> "description": "Name of the product this agent supports."
> },
> {
> "key": "support_tier",
> "type": "number",
> "default": 1
> },
> {
> "key": "account_metadata",
> "type": "json",
> "description": "Structured account context injected into tool bodies."
> }
> ]
> }'

The PATCH replaces the stored list wholesale. Pass "variables": [] to clear all variables.

Using variables in prompts, first messages, and tools

Reference a variable anywhere in the agent’s prompt, first message, or webhook tool config (URL, headers, body) using double-brace syntax.

Two forms:

  • {{key}} - plain interpolation. Numbers and booleans are stringified; json-typed values render as their JSON text.
  • {{key|json}} - JSON-encoded form. The value is serialised with json.Marshal, so a string is quoted and escaped. Use this when injecting inside a JSON body to prevent quote-injection.

Plain interpolation in a prompt:

You are a {{product_name}} support agent for a tier-{{support_tier}} customer.

JSON-safe injection in a tool body - before and after:

Without |json, a string like alice"malicious breaks the surrounding JSON:

{"customer_email": "{{customer_email}}"}
// renders: {"customer_email": "alice"malicious"} ← broken JSON

With |json:

{"customer_email": {{customer_email|json}}}
// renders: {"customer_email": "alice\"malicious"} ← safe

Using a system variable in a tool URL:

https://api.example.com/account?caller_id={{system__caller_id}}

Reserved system__* variables are referenced exactly like customer-scope ones - no special syntax required.

Reserved system variables

The platform auto-populates the following variables at session start. You cannot define or override them - any key beginning with system__ is rejected in the customer catalogue and in per-session overrides.

KeyDescription
system__caller_idIdentifier of the caller for this session. Empty for anonymous widget sessions.
system__called_numberDestination phone number for inbound SIP calls. Empty for non-telephony sessions.
system__languageBCP-47 language code from the agent configuration.
system__agent_idStable identifier of the agent driving this session.
system__conversation_idUnique identifier of the current conversation.
system__current_timeServer-side UTC timestamp at session start, ISO 8601.
system__memoryRendered long-term memory block for the caller (empty when memory is disabled or the caller is anonymous).

The GET /v1/agents/{id}/variables response always includes system_variables - the same catalogue above - so your editor UI can surface it without hard-coding the list client-side.

Passing variables at session start

Variables can be supplied at three points. In all cases, per-session values merge on top of agent-level defaults (see Precedence below).

API

Pass dynamic_variables in the body of POST /v1/agents/{id}/conversations (server-to-server) or POST /v1/agents/{id}/sessions (widget/browser flows).

$curl -X POST https://api.speechify.ai/v1/agents/a_01HS.../conversations \
> -H "Authorization: Bearer $SPEECHIFY_API_KEY" \
> -H "Content-Type: application/json" \
> -d '{
> "dynamic_variables": {
> "product_name": "Acme Pro",
> "support_tier": 2,
> "account_metadata": { "plan": "enterprise", "seats": 50 }
> }
> }'

Widget

Pass variables as a JSON attribute on the <speechify-agent> element. The widget forwards the attribute value to the session-create call automatically.

1<speechify-agent
2 agent-id="a_01HS..."
3 dynamic-variables='{"product_name":"Acme Pro","support_tier":2}'>
4</speechify-agent>

The dynamic-variables attribute is live today. Any JSON-serialisable object is accepted. Keys in the system__ namespace are still rejected server-side.

SDK

1const session = await client.agents.createSession("a_01HS...", {
2 dynamicVariables: {
3 product_name: "Acme Pro",
4 support_tier: 2,
5 },
6});

Precedence

When the platform resolves a variable at session start, it applies values in this order - later entries win:

  1. Agent default - the default value stored on the variable definition.
  2. Session override - values supplied in dynamic_variables on the conversation or session create call.
  3. Reserved system__* - platform-populated values always win and cannot be shadowed.

Limits

ConstraintValue
Max variables per agent20
Key pattern[a-zA-Z0-9_]+
Reserved prefixsystem__ - rejected in both the agent catalogue and session overrides
Missing variable behaviourRenders as empty string (never an error)

A typo in a template reference never breaks dispatch. The agent will simply receive an empty string for the unresolved token. Use GET /v1/agents/{id}/variables to verify the keys your prompt references are actually defined.

What’s next

Tool-response assignment (an AIS-2680 follow-up) will let variables update mid-call from data returned by a tool, enabling stateful context accumulation across conversation turns.