Skip to main content
FlowPilot has three small evaluation systems: conditions (used by dynamic values), property values (static or conditional), and expressions (the {{ }} interpolation and the assign action). Each is deliberately limited. This page documents exactly what is supported so you do not write something the runtime silently drops.

Conditions

A Condition is a pure, read-only test against a variable. It never mutates state and must be deterministic. Conditions read variables only, through a VarRef:
{ "var": "user_type" }
Each operator names a left (a VarRef) and, for most operators, a right literal.

Operators by type

TypeOperators
Stringequals, not_equals, contains, not_contains, starts_with, ends_with, is_empty, is_not_empty
Numberequals, not_equals, greater_than, less_than, greater_than_or_equals, less_than_or_equals
Booleanequals, not_equals (shown as “is ON” / “is OFF” in the editor)
Listcontains, not_contains, is_empty, is_not_empty (operates on array elements)
The empty-check operators (is_empty, is_not_empty) take no right operand.

Logical operators

Compose conditions with:
OperatorShapeNotes
and{ op: "and", conditions: Condition[] }All must hold.
or{ op: "or", conditions: Condition[] }Any must hold.
not{ op: "not", condition: Condition }Negate.

Shape examples

{ "op": "equals", "left": { "var": "user_type" }, "right": "premium" }
{
  "op": "and",
  "conditions": [
    { "op": "greater_than_or_equals", "left": { "var": "age" }, "right": 18 },
    { "op": "equals", "left": { "var": "agreed" }, "right": true }
  ]
}

Evaluation notes

  • A type mismatch fails the comparison rather than throwing. A numeric operator on a non-number returns false. A starts_with/ends_with/contains on a non-string returns false (for not_contains, an absent/non-string value returns true).
  • A missing variable is treated as empty: is_empty is true, is_not_empty is false.
  • An unknown operator fails safely to false.
  • Both the dashboard renderer (variable-runtime.ts) and the iOS SDK (ConditionEvaluator) implement the same operator set.

Property values

Component props can be a plain value or a PropertyValue<T>:
type PropertyValue<T> =
  | { type: "static"; value: T }
  | { type: "conditional"; cases: ConditionalCase<T>[]; else?: PropertyValue<T> };
Resolution rules:
  1. static: return the value directly.
  2. conditional: evaluate cases in order. The first case whose when condition is true wins.
  3. If no case matches, fall back to else.
  4. If nothing matches and there is no else, the result is undefined (the renderer uses its own default).
A ConditionalCase<T> is { when: Condition; value: PropertyValue<T>; label?: string }. Case values can themselves be conditional, so they nest. Example: a color that depends on a variable.
{
  "type": "conditional",
  "cases": [
    { "when": { "op": "equals", "left": { "var": "user_type" }, "right": "premium" },
      "value": { "type": "static", "value": "#FFD700" } }
  ],
  "else": { "type": "static", "value": "#808080" }
}

Interpolation: {{ }}

String props support {{variableName}} interpolation. The braces are replaced with the variable’s current value, converted to a string.
  • Only the variable’s value is substituted. The braces hold a bare variable name, nothing else.
  • A missing variable resolves to an empty string.
  • Interpolation re-runs when the variable changes.
{{ }} interpolation does string substitution only. There is no ternary or conditional syntax inside {{ }}. To switch a value based on a condition, use a conditional property value (case/else against a condition) instead.
To switch a value on a condition, use a conditional PropertyValue (shown above), not an expression inside {{ }}.

Expressions (the assign subset)

The assign action and node use a FlowExpression: a string in a small “JS-like” safe subset. The iOS SDK’s ExpressionEvaluator defines the supported contract: Supported
  • Literals: true, false, null, numbers (decimal, negative, leading dot), and quoted strings ("..." or '...').
  • Variable references: a bare identifier (userName) or a vars. prefix (vars.userName).
  • A single binary arithmetic operator (+, -, *, /, %) when both operands resolve to numbers.
  • String concatenation with + when either operand resolves to a string (the other operand is coerced).
Not supported
  • Parentheses.
  • Comparison operators (>, <, ==, and so on). Use a condition for comparisons.
  • Function calls.
  • Multi-operator chains (for example a + b + c). Only one top-level operator is parsed.
Failure handling An expression that cannot be evaluated returns nothing, and the runtime treats that as “skip this assignment”. The variable is left unchanged. A misconfigured expression never writes an empty, zero, or false value by accident. Division or modulo by zero also skips. Use a condition and a setVariable action when you need comparisons or branching, since expressions cannot compare.

Examples

vars.goalWeight - vars.currentWeight   // number subtraction
"Hi, " + vars.userName                 // string concat
vars.count + 1                          // increment via expression
(vars.a + vars.b) * 2                   // NOT supported: parentheses
vars.age > 30                           // NOT supported: comparison, use a condition