Skip to navigation

Dynamic variables

Pass runtime values to personalize your agent's behavior.

Dynamic variables allow you to inject runtime values into your agent’s messages, system prompts, and tools. This enables you to personalize each conversation with user-specific data without creating multiple agents.

Overview

Dynamic variables can be integrated into multiple aspects of your agent:

  • System prompts to customize behavior and context
  • First messages to personalize greetings
  • Tool parameters and headers to pass user-specific data

Here are a few examples where dynamic variables are useful:

  • Personalizing greetings with user names
  • Including account details in responses
  • Passing data to tool calls
  • Customizing behavior based on subscription tiers
  • Accessing system information like conversation ID or call duration

Dynamic variables support three value types:

String
Text values
Number
Numeric values
Boolean
True/false values

System dynamic variables

Your agent has access to built-in system__* variables in every conversation without runtime configuration. These variables are prefixed with the reserved system__ namespace and update automatically as the conversation progresses. Custom dynamic variables cannot use the system__ prefix or override system variables.

Variables marked with † change between turns. Placing them in prompt text can prevent prompt caching; see Best practices for alternatives.

Agent and conversation identity

VariableTypeDescriptionExample
system__agent_idstringUnique identifier of the agent that started the conversation. Never changes during a conversation."agent_8401kp8bt0jdf14rzzcv5veq94yg"
system__current_agent_idstringUnique identifier of the currently active agent. Updates when the conversation transfers to another agent."agent_8401kp8bt0jdf14rzzcv5veq94yg"
system__conversation_idstringElevenLabs unique conversation identifier."conv_1801kp8qtb2begztn7zyf446v4jk"
system__user_idstring | nullClient-provided user ID for tracking and analytics. Must not be trusted for authentication."G6yhJW8MktQdhUquvXD1iKpPHql2"
system__initiator_idstring | nullIdentifier corresponding to the authentication method used to initiate the conversation."user_1801kp8qtb2begztn7zyf446v4jk"
system__channelstringChannel source through which the conversation was initiated."slack_integration"
system__is_text_onlybooleantrue when the conversation is running in text-only (chat) mode; false for voice calls.true
system__environmentstringEnvironment the conversation is running in, used to resolve environment variables."production"

Time and timezone

VariableTypeDescriptionExample
system__timezonestring | nullConfigured timezone (conversation_config.agent.prompt.timezone) or caller-provided IANA timezone. Defaults to UTC when unset."America/New_York"
system__time †stringCurrent date and time in the configured timezone (%A, %H:%M %d %B %Y). Re-evaluated on every turn (changes once per minute)."Friday, 12:33 12 December 2025"
system__time_utc †stringCurrent UTC timestamp in ISO 8601 format. Re-evaluated on every turn."2025-12-12T12:33:45.123456+00:00"
system__time_datestringCurrent date in the configured timezone (or UTC if none is set) (%A, %d %B %Y). Changes once per day."Friday, 12 December 2025"
system__time_hourintegerCurrent hour (0–23) in the configured timezone (or UTC if none is set). Changes at most once per hour.12
system__time_minute †integerCurrent minute (0–59) in the configured timezone (or UTC if none is set). Changes once per minute.33
system__time_second †integerCurrent second (0–59) in the configured timezone (or UTC if none is set). Changes on every turn.45

Telephony and messaging

These variables are populated when applicable for telephony and messaging channels:

VariableTypeDescriptionExample
system__caller_idstring | nullCaller’s phone number (voice/SMS/WhatsApp/Amazon Connect)."+14155552671"
system__called_numberstring | nullDestination phone number called."+18005551234"
system__call_sidstring | nullTwilio Call SID (Twilio calls only)."CA3498076234a8bbf65c8f09fb8ad7ac1b"
system__call_idstring | nullSIP Call-ID (SIP trunk calls only)."757f72cf-e80f-44f7-87d6-a961ffbf5b3d"

Per-turn conversation state

VariableTypeDescriptionExample
system__call_duration_secs †integerElapsed conversation time in seconds. Re-evaluated on every turn.142
system__agent_turns †integerTotal number of agent turns in the conversation so far. Increments on every turn.5
system__current_agent_turns †integerNumber of agent turns for the current agent; resets to 0 after an agent transfer.3
system__current_subagent_turns †integerNumber of agent turns in the current workflow node; resets to 0 on node transition.2
system__conversation_history †stringJSON-serialized conversation transcript up to the current turn ({"entries": [{"role": "user", "message": "..."}]}). See Conversation history format.'{"entries": [...]}'

Conversation history format

The system__conversation_history variable contains a JSON object with the following structure:

{
"x-elevenlabs-history": true,
"entries": [
{ "role": "user", "message": "Hello" },
{ "role": "agent", "message": "Hi, how can I help?" },
{
"role": "agent",
"tool_requests": [{ "tool_name": "lookup_order", "params_as_json": { "order_id": "123" } }]
},
{
"role": "tool",
"tool_results": [{ "tool_name": "lookup_order", "result_value": "{\"status\": \"shipped\"}" }]
}
]
}

Each entry includes a role ("user", "agent", or "tool") and one of:

  • message: The text content of the turn
  • tool_requests: An array of tool calls made by the agent, with resolved parameter values
  • tool_results: An array of tool responses

If a tool result or parameter contains a nested conversation history, it is redacted to a placeholder (such as [conversation_history (5 turns)]) to avoid unbounded recursive expansion.

This variable is useful for passing conversation context to tools (such as webhooks or custom LLMs) or for including conversation history in sub-agent prompts during handoffs.

Secret dynamic variables

Secret dynamic variables are populated in the same way as normal dynamic variables, but indicate to ElevenAgents that they should only be used in dynamic variable headers and never sent to an LLM provider as part of an agent’s system prompt or first message.

Use secret dynamic variables for auth tokens or private IDs that should not be sent to an LLM. To create a secret dynamic variable, prefix the dynamic variable with secret__.

Secret values are stored redacted, so the conversations API returns them as <REDACTED>. Post-call transcription webhooks deliver the original values, so make sure your webhook endpoint handles them as secrets. Don’t use the secret__ prefix for values you need to read back from the conversations API. Pass those as regular dynamic variables, or pass a non-sensitive identifier and look up the sensitive value on your own system.

Updating dynamic variables from tools

Tool calls can create or update dynamic variables if they return a valid JSON object. To specify what should be extracted, set the object paths using dot notation. If the field or path doesn’t exist, nothing is updated.

Example of a response object and dot notation:

  • Status corresponds to the path: response.status
  • The first user’s email in the users array corresponds to the path: response.users.0.email
JSON
{
"response": {
"status": 200,
"message": "Successfully found 5 users",
"users": [
"user_1": {
"user_name": "test_user_1",
"email": "test_user_1@email.com"
}
]
}
}

To update a dynamic variable to be the first user’s email, set the assignment like so.

Query parameters

Assignments are configured on each webhook tool; see tool assignments in the API reference.

Guide

Prerequisites:

1

Define dynamic variables in prompts

Add variables using double curly braces {{variable_name}} in your:

  • System prompts
  • First messages
  • Tool parameters

Dynamic variables in messages

Dynamic variables in messages

2

Define dynamic variables in tools

You can also define dynamic variables in the tool configuration. To create a new dynamic variable, set the value type to Dynamic variable and click the + button.

Setting placeholders

Setting placeholders

3

Set placeholders

Configure default values for testing without passing variables at runtime.

Set default values for each dynamic variable in the agent’s dashboard.

Setting placeholders

4

Pass variables at runtime

When starting a conversation, provide the dynamic variables in your code:

Ensure you have the latest SDK installed.

import os
import signal
from elevenlabs.client import ElevenLabs
from elevenlabs.conversational_ai.conversation import Conversation, ConversationInitiationData
from elevenlabs.conversational_ai.default_audio_interface import DefaultAudioInterface
agent_id = os.getenv("AGENT_ID")
api_key = os.getenv("ELEVENLABS_API_KEY")
elevenlabs = ElevenLabs(api_key=api_key)
dynamic_vars = {
"user_name": "Angelo",
}
config = ConversationInitiationData(
dynamic_variables=dynamic_vars
)
conversation = Conversation(
elevenlabs,
agent_id,
config=config,
# Assume auth is required when API_KEY is set.
requires_auth=bool(api_key),
# Use the default audio interface.
audio_interface=DefaultAudioInterface(),
# Callbacks that print the conversation to the console.
callback_agent_response=lambda response: print(f"Agent: {response}"),
callback_agent_response_correction=lambda original, corrected: print(f"Agent: {original} -> {corrected}"),
callback_user_transcript=lambda transcript: print(f"User: {transcript}"),
# Uncomment the below if you want to see latency measurements.
# callback_latency_measurement=lambda latency: print(f"Latency: {latency}ms"),
)
conversation.start_session()
signal.signal(signal.SIGINT, lambda sig, frame: conversation.end_session())

Public talk-to page integration

The public talk-to page supports dynamic variables through URL parameters, enabling you to personalize conversations when sharing agent links. This is particularly useful for embedding personalized agents in websites, emails, or marketing campaigns.

URL parameter methods

There are two methods to pass dynamic variables to the public talk-to page:

Method 1: Base64-encoded JSON

Pass variables as a base64-encoded JSON object using the vars parameter:

https://elevenlabs.io/app/talk-to?agent_id=agent_7101k5zvyjhmfg983brhmhkd98n6&vars=eyJ1c2VyX25hbWUiOiJKb2huIiwiYWNjb3VudF90eXBlIjoicHJlbWl1bSJ9

The vars parameter contains base64-encoded JSON:

{ "user_name": "John", "account_type": "premium" }

Method 2: Individual query parameters

Pass variables using var_ prefixed query parameters:

https://elevenlabs.io/app/talk-to?agent_id=agent_7101k5zvyjhmfg983brhmhkd98n6&var_user_name=John&var_account_type=premium

Parameter precedence

When both methods are used simultaneously, individual var_ parameters take precedence over the base64-encoded variables to prevent conflicts:

https://elevenlabs.io/app/talk-to?agent_id=agent_7101k5zvyjhmfg983brhmhkd98n6&vars=eyJ1c2VyX25hbWUiOiJKYW5lIn0=&var_user_name=John

In this example, user_name will be "John" (from var_user_name) instead of "Jane" (from the base64-encoded vars).

Implementation examples

// Method 1: Base64-encoded JSON
function generateTalkToURL(agentId, variables) {
const baseURL = 'https://elevenlabs.io/app/talk-to';
const encodedVars = btoa(JSON.stringify(variables));
return `${baseURL}?agent_id=${agentId}&vars=${encodedVars}`;
}
// Method 2: Individual parameters
function generateTalkToURLWithParams(agentId, variables) {
const baseURL = 'https://elevenlabs.io/app/talk-to';
const params = new URLSearchParams({ agent_id: agentId });
Object.entries(variables).forEach(([key, value]) => {
params.append(`var_${key}`, encodeURIComponent(value));
});
return `${baseURL}?${params.toString()}`;
}
// Usage
const variables = {
user_name: "John Doe",
account_type: "premium",
session_id: "sess_123"
};
const urlMethod1 = generateTalkToURL("agent_7101k5zvyjhmfg983brhmhkd98n6", variables);
const urlMethod2 = generateTalkToURLWithParams("agent_7101k5zvyjhmfg983brhmhkd98n6", variables);

Best practices

Variables in the system prompt, workflow nodes, and procedures are resolved on every turn. If a value changes between turns, the prompt can’t be cached, which increases cost and latency. {{system__time_utc}} and {{system__time_second}} change every turn; {{system__time}} and {{system__time_minute}} change every minute.

To give the agent the current date and time, set a timezone with the timezone button in the System Prompt editor (conversation_config.agent.prompt.timezone) instead of placing {{system__time}} or {{system__time_utc}} in prompt text. When a timezone is set, the platform adds the current date and time outside the cached prompt prefix, so your system prompt stays cached across turns. Without a timezone, the agent doesn’t receive the current date and time.

If your prompt needs the date or hour inline, combine {{system__time_date}}, {{system__time_hour}}, and {{system__timezone}} (for example, Today is {{system__time_date}}, hour {{system__time_hour}} ({{system__timezone}})). These change at most once per hour.

If a tool needs a value marked †, bind it to the tool parameter or header instead of placing it in the prompt. Tool parameters resolve only when the tool is called, so they don’t affect prompt caching. The LLM already sees the conversation, so {{system__conversation_history}} is rarely needed in prompts.

If your prompt checks turn counters such as {{system__agent_turns}}, consider these alternatives. Use first_message for static opening greetings or disclosures.

For first-reply behavior, you can phrase the instruction around the conversation history (for example, “On your first reply, when you have not yet greeted the caller…”). This relies on the LLM following the instruction, so test it with your model and keep the turn counter if results are less reliable.

If the counter only decides when to branch or escalate, a workflow edge expression condition or a structured procedure If step expression (such as system__agent_turns >= 5) can handle it outside the prompt. This doesn’t cover cases where the LLM needs the value itself.

Prefix auth tokens and API keys with secret__ so they can only be bound to tool headers and are never sent to the LLM. Because secret__ values are redacted as <REDACTED> in the conversations API (post-call transcription webhooks still receive the original values), pass values you need to read back from the conversations API as regular dynamic variables, or pass a non-sensitive identifier and look up the sensitive value on your own system.

system__user_id is provided by the client for tracking and analytics and is not verified by ElevenAgents. Verify caller identity through a conversation initiation webhook, a signed token in a secret__ header variable, or a verification tool call before exposing account data.

Set default values in dynamic_variable_placeholders for every custom variable used in your prompts, first messages, and tools. Preview conversations in the dashboard and simulation tests do not run conversation initiation webhooks, so unset variables remain as literal {{variable_name}} placeholders during testing.

Troubleshooting

Verify that:

  • Variable names match exactly (case-sensitive)
  • Variables use double curly braces: {{variable_name}}
  • Variables are included in your dynamic_variables object

Ensure that:

  • Variable values match the expected type
  • Values are strings, numbers, or booleans only