Variable substitution
Dynamic placeholder syntax for injecting runtime values into templates.
Overview
Variable substitution allows templates to reference dynamic values using {'{placeholder}'} syntax. At call time, placeholders are resolved from multiple sources and injected into messages, URLs, headers, and request bodies.
Syntax
Variables use single curly braces: {'{variable_name}'}. Names are case-sensitive and must match a key in one of the resolution sources.
Resolution order
The three sources are merged sequentially — last writer wins. In practice that means lead-payload values always override template secrets, which always override credential secrets. The same ordering in priority terms:
| Priority | Source | Example Keys | Description |
|---|---|---|---|
| 1 (highest) | Lead Payload | customer_name, appointment_date | Per-call data pushed when creating the lead |
| 2 | Template Secrets | api_token, webhook_url | Defined in the template’s secrets object |
| 3 (lowest) | Credential Secrets | api_secret, auth_key | Merchant-level credentials shared across templates |
Last-writer-wins via merge
Internally all three dicts are merged in order credentials → secrets → payload. Later writes overwrite earlier ones, so payload fields always win. This also means a duplicate key between credentials and secrets silently resolves to the secret — credentials cannot shadow secrets.
Variable sources
Lead Payload
Per-call data pushed via the API:
/agent/voice/breeze-buddy/leads {
"template_id": "abc-123",
"payload": {
"customer_name": "Priya Sharma",
"appointment_date": "2026-04-15",
"appointment_time": "10:30 AM",
"doctor_name": "Dr. Mehta",
"clinic_phone": "+91-9876543210"
}
}Template Secrets
Secrets shared across all calls on this template:
{
"secrets": {
"api_token": "sk-live-abc123xyz",
"webhook_url": "https://hooks.example.com/breeze"
}
}Credential Secrets
Merchant-level credentials (lowest priority fallback):
{
"api_secret": "sk-merchant-global-key",
"auth_key": "merchant-auth-abc"
}Where variables work
| Location | Example |
|---|---|
task_messages content | "You are calling {customer_name} about order {order_id}" |
role_messages content | "Address the customer as {customer_name}" |
pre_actions tts_say text | "Hello {customer_name}, thank you for your patience." |
HTTP request url | "https://api.example.com/orders/{order_id}" |
HTTP request headers | "Authorization": "Bearer {api_secret}" |
HTTP request body | "customer_id": "{customer_id}" |
| Hook field values (static) | { "source": "static", "value": "{campaign_id}" } |
Global function pre_tts_message | "Looking up your order, {customer_name}" |
Variables in task messages
{
"task_messages": [
{
"role": "system",
"content": "You are calling {customer_name} to remind them about their appointment with {doctor_name} on {appointment_date} at {appointment_time}."
}
]
}At call time, this resolves to:
{
"task_messages": [
{
"role": "system",
"content": "You are calling Priya Sharma to remind them about their appointment with Dr. Mehta on 2026-04-15 at 10:30 AM."
}
]
}Variables in HTTP requests
Note the difference between {'{variable}'} (template variable) and <<field>> (hook field reference):
{
"http_request": {
"url": "{webhook_url}/appointments",
"method": "POST",
"headers": {
"Authorization": "{callback_auth}",
"Content-Type": "application/json"
},
"body": {
"name": "{customer_name}",
"date": "{appointment_date}",
"status": "confirmed"
}
}
}Built-in & computed variables
The system provides computed values through the computed field source in hooks:
| Expression | Description | Example Output |
|---|---|---|
utc_now | Current UTC timestamp | 2026-04-15T10:30:00Z |
utc_now_minus_hours:0 | Current UTC timestamp (equivalent to utc_now) | 2026-04-15T10:30:00Z |
utc_now_minus_hours:1 | UTC minus 1 hour | 2026-04-15T09:30:00Z |
utc_now_minus_hours:24 | UTC minus 24 hours | 2026-04-14T10:30:00Z |
ist_now | Current IST (UTC+5:30) timestamp | 2026-04-15T16:00:00+05:30 |
utc_today_start | Start of today in UTC (00:00:00Z) | 2026-04-15T00:00:00Z |
utc_today_end | End of today in UTC (23:59:59Z) | 2026-04-15T23:59:59Z |
Payload transformations
expected_payload_schema entries can carry a function key that transforms the payload value before injection. Single transforms use a string; chains use a list (applied left to right).
{
"expected_payload_schema": {
"type": "object",
"properties": {
"customer_name": { "type": "string", "function": "string_trim" },
"order_id": {
"type": "string",
"function": ["string_trim", "string_to_uppercase"]
},
"phone_number": { "type": "string", "function": "extract_10_digit_mobile" }
}
}
}Registered transformation functions:
| Function | Description |
|---|---|
string_trim | Strip leading/trailing whitespace. |
string_to_lowercase | Lowercase the string. |
string_to_uppercase | Uppercase the string. |
indian_number_to_speech | Render Indian-format numbers as spoken words. |
digits_to_speech | Render a digit string as spoken digits. |
date_to_speech | Render an ISO date as spoken English. |
extract_10_digit_mobile | Extract the trailing 10-digit mobile number from a longer string. |
expand_shorthand | Expand common shorthand (e.g. “Dr” → “Doctor”). |
Payload validation
Use expected_payload_schema to validate required variables are present:
{
"expected_payload_schema": {
"type": "object",
"properties": {
"customer_name": { "type": "string" },
"appointment_date": { "type": "string" },
"doctor_name": { "type": "string" }
},
"required": ["customer_name", "appointment_date", "doctor_name"]
}
}Two different failure modes
A placeholder for a key outside expected_payload_schema stays as literal text (e.g. {unknown_var}). But a placeholder for a schema-declared field that the lead payload didn’t include resolves to the empty string "" — which can silently corrupt prompts (“Hello , your order is ready”). Always send values for every schema-declared field.
Escaping & edge cases
No Escape Syntax
There is currently no escape syntax for literal curly braces. Use distinctive variable names to prevent accidental resolution.
- Case sensitivity:
{'{Customer_Name}'}and{'{customer_name}'}are different - Missing variables: Placeholder remains as literal text
- Empty values: Replaced with empty string (not removed)
- Nested resolution: Not recursive — resolved values containing
{'{placeholders}'}are not resolved again - JSON values: Variables always resolve to strings
Best practices
- Use
expected_payload_schemato validate required variables - Keep sensitive values in template secrets or credential secrets — never in the lead payload
- Use descriptive names:
{'{customer_name}'}over{'{name}'} - Test substitution by reviewing resolved templates in logs
- Document your payload schema for API consumers