BotsWorkflowsVariables & Conditions

Variables & Conditions

Workflow nodes communicate through a shared variables context. Nodes can read variables using {{variableName}} interpolation and write variables via their outputVariable field.


Variable Interpolation

Use double curly braces to insert variable values into prompts, messages, tool arguments, and conditions:

Hello, {{user_name}}! Your order {{order_id}} is {{order_status}}.

Dot Notation

Access nested properties using dot notation:

Your order total is ${{order_data.total}}
Shipped via {{order_data.shipping.carrier}}

The interpolation engine walks the object path:

// If order_data = { total: 42.50, shipping: { carrier: "FedEx" } }
"{{order_data.total}}"            // → "42.50"
"{{order_data.shipping.carrier}}" // → "FedEx"

Object Serialization

If a variable points to an object or array, it is serialized as JSON:

Order details: {{order_data}}
// → Order details: {"id":"12345","status":"shipped","total":42.50}

Unresolved Variables

If a variable path does not resolve (the variable does not exist or a nested property is missing), the placeholder is left as-is:

Hello, {{unknown_var}}!
// → Hello, {{unknown_var}}!

Built-In Variables

These variables are automatically set by the trigger node when a message arrives:

VariableTypeDescription
messagestringThe user’s message text
user_idstringPlatform-specific user identifier
user_namestringUser’s display name (empty string if unavailable)
channel_idstringPlatform-specific channel/chat identifier
platformstringtelegram, discord, whatsapp, slack, or custom

Setting Variables

Via Node Output

Most node types support an outputVariable field. The node’s result is stored in this variable:

{
  "type": "llm_call",
  "data": {
    "prompt": "Classify: {{message}}",
    "outputVariable": "classification"
  }
}

After execution, classification contains:

{
  "text": "billing",
  "tokensInput": 45,
  "tokensOutput": 3
}

Via the set_variable Action

Use the action node with actionType: "set_variable":

{
  "type": "action",
  "data": {
    "actionType": "set_variable",
    "variable": "greeting",
    "value": "Welcome back, {{user_name}}!"
  }
}

Via Code Blocks

Code blocks can directly modify the variables context:

{
  "type": "code_block",
  "data": {
    "code": "ctx.variables.full_name = ctx.variables.first_name + ' ' + ctx.variables.last_name;"
  }
}

Condition Syntax

Logic nodes (if_else type) use a simple condition syntax. The condition string is first interpolated (variables replaced), then evaluated.

Comparison Operators

OperatorDescriptionExample
==Equals{{intent}} == billing
!=Not equals{{status}} != closed
>Greater than (numeric){{total}} > 100
<Less than (numeric){{count}} < 5
>=Greater than or equal (numeric){{score}} >= 0.8
<=Less than or equal (numeric){{score}} <= 0.2
containsString contains{{message}} contains refund
startsWithString starts with{{message}} startsWith /help
endsWithString ends with{{email}} endsWith @acme.com

Syntax Rules

  • Operators must be surrounded by spaces: {{x}} == y (not {{x}}==y)
  • Values on both sides are treated as strings unless using numeric operators (>, <, >=, <=)
  • Quotes around values are optional and stripped: {{x}} == "billing" is the same as {{x}} == billing
  • For numeric comparisons, values are parsed with parseFloat()

Truthiness Fallback

If no operator is found, the condition falls back to a truthiness check:

ValueTruthy?
Non-empty stringYes
"false"No
"0"No
"null"No
"undefined"No
Empty string ""No

Example:

{
  "type": "logic",
  "data": {
    "logicType": "if_else",
    "condition": "{{has_account}}"
  }
}

This follows the true edge if has_account is set to any truthy value.


Variable Scope

All variables share a single flat scope within a workflow execution. There is no block scoping or node-level isolation.

⚠️

Be careful with variable names. If two nodes use the same outputVariable, the second one overwrites the first. Use descriptive, unique variable names like order_lookup_result instead of generic names like result.

Variable Lifetime

Variables persist for the duration of a single workflow execution (one incoming message). They are not preserved across messages. Each new message starts a fresh execution with only the built-in variables.


Examples

Condition with Variable Interpolation

{
  "type": "logic",
  "data": {
    "logicType": "if_else",
    "condition": "{{order_data.total}} > 500"
  }
}

If order_data = { total: 750 }, the condition resolves to 750 > 500, which evaluates to true.

String Matching

{
  "type": "logic",
  "data": {
    "logicType": "if_else",
    "condition": "{{message}} contains cancel"
  }
}

If the user sends “I want to cancel my order”, the condition resolves to I want to cancel my order contains cancel, which evaluates to true.

Switch with Dynamic Variable

{
  "type": "logic",
  "data": {
    "logicType": "switch",
    "variable": "platform",
    "cases": ["telegram", "discord", "slack"]
  }
}

This branches to case_telegram, case_discord, case_slack, or case_default based on which platform the message came from.