Skip to main content
Beyond simple {{parameter}} substitution, agent prompts support Liquid templating. Liquid lets you go past dropping a value into a sentence: you can branch on conditions, loop over lists, transform values with filters, and even run randomized A/B tests across calls, all from inside the prompt editor, with no code. If you only need to insert a value (a customer name, an order ID), stick with the plain {{parameter}} syntax described in Prompting. Reach for the Liquid features below when the prompt needs to decide something.

How it works

Your prompt is re-rendered on every turn of the conversation, not once at the start. To keep a branch stable across the call, seed it from a value that’s constant for the whole call. call.started_at is fixed for the call’s lifetime, so anything derived from it resolves the same way on every turn. (now is derived from call.started_at, so it is also constant for the call rather than advancing with wall-clock time.) Times are evaluated in UTC.

Available variables

You can reference the following inside your prompt.

Call parameters

Any value you pass when creating a call, via the API or a campaign CSV, is available at the top level by its name:
You are calling {{customer_name}} about order {{order_id}}.
Their email on file is {{customer_email}}.

The call object

Metadata about the current call is always available under call:
VariableDescriptionExample
call.call_idUnique ID for this call8f3c…
call.from_numberThe number the call is from+15551234567
call.to_numberThe number the call is to+15559876543
call.directioninbound or outboundoutbound
call.started_atCall start time, ISO 8601 (UTC)2026-01-16T10:30:00.000Z
{% if call.direction == "inbound" %}
The customer called you. Greet them and ask how you can help.
{% else %}
You are calling the customer. Introduce yourself first.
{% endif %}

now

now is the call start time pre-formatted as a human-readable string, ready to speak:
For reference, the current date and time is {{now}}.
For reference, the current date and time is Friday, January 16th, 2026 10:30:00 AM UTC.
Use now when you want to tell the agent the date/time. Use call.started_at when you want a raw value to compute with, such as math, comparisons, or seeding (see A/B testing below). now is already formatted into prose, so filters like date won’t work on it.

Liquid basics

Variables: assign

Compute a value once and reuse it throughout the prompt:
{% assign company = "Downtown Medical" %}
Thanks for calling {{company}}. This is {{agent_name}} from {{company}}.
Filters can only be used inside {% assign %} and {{ output }}, not inside an {% if %} or {% case %} condition. If you need to test a transformed value, assign it first, then compare the variable:
{% assign branch = call.started_at | date: "%s" | modulo: 2 %}
{% if branch == 0 %}{% else %}{% endif %}

Conditionals: if / elsif / else

{% if account_tier == "premium" %}
Offer to connect them to a priority specialist.
{% elsif account_tier == "standard" %}
Help them directly with their request.
{% else %}
Ask which account they are calling about.
{% endif %}

Multiple branches: case / when

Cleaner than a long if/elsif chain when you are switching on one value:
{% case region %}
{% when "US" %}Quote prices in US dollars.
{% when "UK" %}Quote prices in British pounds.
{% else %}Ask the customer which currency they prefer.
{% endcase %}

Filters

Filters transform values with a |. A few useful ones:
{{customer_name | capitalize}}              → Jane
{{order_id | upcase}}                        → 7BB0A946
{{call.started_at | date: "%A"}}             → Friday
{{price | times: 1.08}}                       → apply 8% tax
All standard Liquid filters are available (upcase, downcase, capitalize, date, plus, minus, times, modulo, append, default, and more).

A/B testing prompts across calls

A powerful use of Liquid is running randomized variations of a prompt without any code or statefulness. The pattern: derive a stable number from the call start time, then branch on it.
{% assign seed = call.started_at | date: "%s" %}
{% assign variant = seed | modulo: 3 %}

{% case variant %}
{% when 0 %}Open the call warmly: "Hi there! Thanks so much for calling."
{% when 1 %}Open the call briefly: "Hi, how can I help today?"
{% when 2 %}Open the call formally: "Good day. How may I assist you?"
{% endcase %}
How it works:
  1. call.started_at | date: "%s" converts the call start time to a Unix timestamp (seconds): an ever-changing integer.
  2. | modulo: 3 reduces it to 0, 1, or 2, spreading calls roughly evenly across three variants.
  3. case/when selects the matching variant.
Because call.started_at is fixed for the life of the call, every part of the prompt that references variant resolves to the same branch: the agent stays consistent within a single call, while different calls land on different variants.
Always seed from call.started_at, not now. now is a formatted prose string and won’t run through the date filter. call.started_at is the raw timestamp the math needs.

Scaling to many variants

The same pattern extends to as many branches as you like: just raise the modulo. This is how teams test 10–30 script variations (different openers, tones, or full personas) from a single agent:
{% assign branch = call.started_at | date: "%s" | modulo: 20 %}

# Active Persona

{% case branch %}
{% when 0 %}You are warm and cheerful. Use light filler words.
{% when 1 %}You are polite and reserved. Keep responses brief.
{% when 2 %}You are tired but helpful, at the end of a long shift.
{% comment %} …branches 3 through 19… {% endcomment %}
{% endcase %}
You can reuse the same branch variable in multiple case blocks throughout the prompt (opener, acknowledgments, sign-off), so a single call presents one coherent persona end to end.

Best practices

  • Keep logic shallow. A couple of branches makes a prompt adaptive; deeply nested conditionals make it hard to read and test. If a prompt needs many distinct flows, consider flow-based agents with a node per scenario instead.
  • Provide a fallback. Always include an {% else %} (or a {% when %} default) so an unexpected value never leaves a blank section in the prompt.
  • Guard against missing values with the default filter: {{customer_name | default: "there"}}.
  • Test each variant. With A/B branches, place several test calls and confirm each branch reads correctly: a typo in one when only shows up when that branch is selected.
  • Watch for silent passthrough. If your Liquid has a syntax error, the prompt is used as written: the raw {% … %} tags will appear in the agent’s instructions instead of being evaluated. If you see template tags leaking into agent behavior, check your syntax.
  • Review the rendered prompt. The surest way to verify your logic is to make a real call and inspect what the agent actually received.

Where Liquid works

Liquid is evaluated in your agent’s prompt: both the main instructions and the per-node prompts of flow-based agents, so each node can branch on the same call data.