Expressions
Variables and small expressions are usable in any field that supports interpolation: message bodies, headers, URLs, request bodies, Set Variable values, and Condition rules.
Variable interpolation
Wrap a variable name in {{ }} inside any text field:
Hi {{customer_name}}, your reference is {{policy_number}}.- Names are case-sensitive.
- A bare name (no scope prefix) reads from conversation scope by default.
- Unresolved variables render as the empty string and emit an
unknown_variable_referencedevent the recipe author can spot in session telemetry. - Maximum path depth: 4 segments (
{{a.b.c.d}}is fine; deeper paths raise a parse error).
Variable scopes
Four-tier scope model:
| Scope | Syntax | Notes |
|---|---|---|
| Recipe | {{r.name}} or {{recipe_var.name}} | Scratch values inside the current recipe activation. Flushed on sub-flow return. |
| Conversation | {{name}} (default) | Shared across sub-flows within the session. Persists for the life of the conversation. |
| User (contact) | {{contact.name}} | Maps to chat_contacts.custom_attributes. Persists across conversations. |
| Config | {{config.name}} | Tenant config values; read-only at runtime. |
System variables are resolved synthetically:
| Reference | Value |
|---|---|
{{user.name}}, {{user.phone}}, {{user.email}} | Contact attributes. |
{{conversation.id}} | Current conversation id. |
{{recipe.id}}, {{recipe.version_id}} | Running recipe identifiers. |
{{language}} | Session language (e.g. en / ar). |
Filters
Filters transform the resolved value using pipe syntax {{name | filter}} or {{name | filter: 'arg'}}. Multiple filters chain left-to-right.
| Filter | Purpose | Example |
|---|---|---|
lower | Lowercase a string. | {{country | lower}} |
upper | Uppercase a string. | {{code | upper}} |
trim | Strip surrounding whitespace. | {{input | trim}} |
default: 'X' | Fall back to the argument when the value is null, undefined, or empty. | {{nickname | default: 'friend'}} |
date: 'YYYY-MM-DD' | Format a date / ISO timestamp using YYYY MM DD HH mm ss placeholders (UTC). | {{appointment_at | date: 'YYYY-MM-DD HH:mm'}} |
Unknown filters pass the value through unchanged.
Condition operators (runtime)
The Condition node compares a left-side variable to a right-side variable or literal. The runtime supports six operators:
| Operator | Meaning |
|---|---|
= | Equals (type-coercing — strings compare to numbers, booleans normalize). |
!= | Not equals. |
> | Numeric greater than. Non-numeric values evaluate to false. |
< | Numeric less than. |
contains | Substring match for strings; element membership for arrays. |
is_empty | True when the value is null, undefined, an empty string, or an empty array. |
Drift notice. The Condition node config drawer currently lists ten operators (
==,!=,>,>=,<,<=,contains,starts_with,is_empty,is_not_empty). Picking==,>=,<=,starts_with, oris_not_emptyin the drawer silently evaluates tofalseat runtime — those five are not wired. Stick to the six operators in the table above until the drift is resolved. Tracked as PF-6 in the postponed-fixes log.
Type coercion
- Equality (
=/!=) coerces strings to numbers when either side is numeric, and to booleans when either side is boolean. Otherwise the comparison is string-equal. - Numeric comparisons (
>/<) cast both sides viaNumber(...)and returnfalseif either side isNaN. containsrequires the left side to be a string or array.is_emptycovers null, undefined, empty string, and empty array.
Limits
- Expressions are evaluated synchronously at node-fire time.
- Maximum path depth on a dotted reference: 4 segments.
- No loops, no arithmetic expressions, no inline function calls — this is a value-shaping language, not a programming language. For computed values, set them upstream with a Set Variable node fed by an API or Smart Plug response.