Dynamic Variables
Personalize every call by injecting per-call data into the agent's system prompt, welcome message, and HTTP tools.
Dynamic variables let you keep one reusable agent and customize what it says on each call. They're resolved as {{variable}} placeholders in the agent's system prompt, welcome message, and HTTP tool bodies — at the moment a call is started.
Use them for:
- Lead-gen / sales —
{{first_name}},{{company}},{{product}} - Customer support callbacks —
{{ticket_id}},{{order_status}} - Appointment reminders —
{{appointment_date}},{{doctor_name}} - Verification / OTP —
{{verification_code}}(do not log!)
Quick example
Agent system prompt:
You are Priya, a service-renewal specialist at GreenLeaf Insurance.
You're calling {{first_name}} about their {{policy_type}} policy
ending on {{renewal_date}}.
Confirm if they'd like to renew. Quote the new premium: ₹{{premium}}.
Don't read the variable names — speak naturally.API call:
curl -X POST https://api.osmtalk.com/api/calls/outbound \
-H "Authorization: Bearer $OSMTALK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"agentId": "agent_xxx",
"phoneNumberId": "pn_xxx",
"destination": "+919876543210",
"dynamicVariables": {
"first_name": "Arjun",
"policy_type": "Health",
"renewal_date": "May 28, 2026",
"premium": "12,450"
}
}'The bot will speak: "Hi Arjun, I'm calling about your Health policy ending on May 28, 2026. The renewal premium is ₹12,450 …"
Variable syntax
| Pattern | Behavior | Example |
|---|---|---|
{{name}} | Direct substitution | Hi {{name}} → Hi Arjun |
{{user.email}} | Dotted-path lookup into nested objects | {{user.email}} → arjun@example.com |
{{x | upper}} | Uppercase filter | {{plan | upper}} → GOLD |
{{x | lower}} | Lowercase filter | |
{{x | trim}} | Strip whitespace | |
{{x | default:"N/A"}} | Fallback when missing or empty | {{eta | default:"shortly"}} |
{{x | trim | upper}} | Chained filters (left-to-right) |
Missing variables resolve to empty string by default, and a warning logs the missing key — so a typo doesn't crash the call, but you'll see it in the bot logs.
Where variables are rendered
Templates are rendered automatically in three places before the bot starts speaking:
systemPrompt— the agent's LLM instructionswelcomeMessage— the first thing the bot says- HTTP tool bodies — any
{{var}}inside tool URLs, headers, or body fields
You can put variables in tool definitions too. Example HTTP tool that POSTs to your CRM with the captured lead:
{
"name": "save_lead",
"url": "https://crm.example.com/api/leads",
"method": "POST",
"headers": { "Authorization": "Bearer xxx" },
"parameters": [
{ "name": "phone", "type": "string", "required": true },
{ "name": "outcome", "type": "string", "required": true }
],
"bodyTemplate": "{\"campaign\":\"{{campaign_name}}\",\"source\":\"{{lead_source}}\",\"phone\":\"{{phone}}\",\"outcome\":\"{{outcome}}\"}"
}When the bot calls save_lead(phone=..., outcome=...), the bodyTemplate is rendered with both the dynamic-variable context ({{campaign_name}}, {{lead_source}} from the call) and the LLM-supplied parameters ({{phone}}, {{outcome}}).
Limits
| Limit | Value |
|---|---|
| Max variables per call | 50 |
| Max characters per value | 4000 |
| Key length | 1–64 characters |
| Key format | letters, digits, underscores, hyphens, dots, and single inline spaces. Examples: first_name, Client Name, tracking-id, order.id. Leading/trailing/consecutive whitespace is rejected. |
| Value types accepted | string, number, boolean (coerced to string) |
Values are coerced to strings — booleans become "true"/"false", numbers stringify. Use default:"..." on the template side if you need a friendlier display.
Assistant Override (advanced)
For cases where dynamic variables aren't enough — you want to swap the whole system prompt, change the voice, or use a different model for one specific call — pass assistantOverride:
{
"agentId": "agent_xxx",
"phoneNumberId": "pn_xxx",
"destination": "+919876543210",
"assistantOverride": {
"systemPrompt": "You are a focused appointment-confirmation bot. Confirm the booking in 1 minute or less.",
"welcomeMessage": "Hi, calling to confirm your appointment tomorrow at 10am.",
"ttsVoice": "EXAVITQu4vr4xnSDxMaL",
"settings": {
"maxCallDurationSecs": 90,
"ttsSpeed": 1.05
}
}
}Every field is optional and only the ones you specify are overridden. The agent's permanent config stays unchanged — this only applies to this single call.
assistantOverride and dynamicVariables compose: assistantOverride.systemPrompt is also rendered with {{var}} substitution, so you can fully rewrite the prompt and inject per-call data.
API reference
All endpoints that start a voice call accept these two optional fields:
| Endpoint | Method |
|---|---|
/api/calls/outbound | POST |
/api/agents/:id/connect | POST |
/api/widget/:agentId/voice | POST |
/api/widget/:agentId/dial | POST |
Request body additions:
{
// ... existing fields (agentId, phoneNumberId, destination) ...
/**
* Per-call variables, resolved as {{key}} in the agent's
* systemPrompt, welcomeMessage, and HTTP tool bodies.
* Max 50 keys, 4000 chars per value.
*/
dynamicVariables?: Record<string, string | number | boolean>;
/**
* Per-call full or partial override of agent fields.
* Every field is optional.
*/
assistantOverride?: {
systemPrompt?: string;
welcomeMessage?: string;
llmProvider?: "openai" | "groq" | "anthropic";
llmModel?: string;
sttProvider?: "deepgram" | "sarvam" | "elevenlabs" | "groq";
sttModel?: string;
sttLanguage?: string;
ttsProvider?: "deepgram" | "elevenlabs" | "groq";
ttsModel?: string;
ttsVoice?: string;
settings?: Partial<AdvancedSettings>;
};
/**
* Arbitrary tags for your own use — surfaced back in webhooks
* and the call record. Not used by the bot.
*/
metadata?: Record<string, unknown>;
}The values are persisted on the call record (dynamic_variables, assistant_override, metadata columns) for later debugging and analytics.
Things to watch for
- Don't put secrets in dynamic variables you don't trust. They land in the LLM system prompt and the bot logs.
- Variable names are case-sensitive.
{{firstName}}and{{firstname}}are different. - Prefer dynamic variables over assistant override when both could work. Variables are cheaper to validate and easier to template across many calls.
- For lead-gen campaigns, see Campaigns — the campaign-dialer applies each lead's CSV row as
dynamicVariablesautomatically.