Vex Expression Language Reference
Vex (Vesbite Expressions) is the expression language used throughout Vesbite workflows. It is based on Scriban and uses {{ }} delimiters. Vex expressions can appear in any node input that supports expressions, including text fields, code editors, and condition inputs.
Syntax Overview
Expressions are enclosed in double curly braces:
{{ expression }}Plain text outside of {{ }} is passed through literally. You can mix text and expressions:
Tag {{ output("trigger").epc }} was read on antenna {{ output("trigger").antenna }}A field containing only an expression (no surrounding text) preserves the expression’s native type – numbers, booleans, objects, and arrays are not converted to strings.
Data Types
| Type | Examples | Description |
|---|---|---|
| string | "hello", 'world' | Text values. Single or double quotes. |
| number | 42, 3.14, -7 | Integer or decimal numbers. |
| boolean | true, false | Logical values. |
| null | null | Absence of a value. |
| object | { "key": value } | Key-value collections. |
| array | [1, 2, 3] | Ordered lists. |
Operators
Arithmetic
| Operator | Description | Example | Result |
|---|---|---|---|
+ | Addition | {{ 2 + 3 }} | 5 |
- | Subtraction | {{ 10 - 4 }} | 6 |
* | Multiplication | {{ 3 * 7 }} | 21 |
/ | Division | {{ 20 / 4 }} | 5 |
% | Modulo | {{ 17 % 5 }} | 2 |
Comparison
| Operator | Description | Example | Result |
|---|---|---|---|
== | Equal | {{ 1 == 1 }} | true |
!= | Not equal | {{ 1 != 2 }} | true |
> | Greater than | {{ 5 > 3 }} | true |
< | Less than | {{ 3 < 5 }} | true |
>= | Greater or equal | {{ 5 >= 5 }} | true |
<= | Less or equal | {{ 3 <= 5 }} | true |
Logical
| Operator | Description | Example | Result |
|---|---|---|---|
&& or and | Logical AND | {{ true && false }} | false |
|| or or | Logical OR | {{ true || false }} | true |
! or not | Logical NOT | {{ !true }} | false |
Variable Access
Workflow Variables
Variables set by Set Variable nodes are accessed through the variables object:
{{ variables.name }}
{{ variables.tagCount }}For variable names containing hyphens or special characters, use bracket notation:
{{ variables["hyphenated-name"] }}
{{ variables["my variable"] }}Node Output Access
Access the output of a previous node using the output() function with the node’s activity ID:
{{ output("activityId") }}
{{ output("activityId").property }}
{{ output("activityId").nested.property }}Alternative syntax using the activities object:
{{ activities.activityId.output }}
{{ activities.activityId.output.property }}
{{ activities["activityId"].output.property }}The output() function returns the last output produced by the specified activity. If the activity has not executed, it returns null.
Pipe Syntax (Filters)
Filters transform values using the pipe (|) operator:
{{ value | filter_name }}
{{ value | filter_name: arg1, arg2 }}Filters can be chained:
{{ variables.name | string.upcase | string.truncate 10 }}String Filters
| Filter | Description | Example | Result |
|---|---|---|---|
string.upcase | Convert to uppercase | {{ "hello" | string.upcase }} | HELLO |
string.downcase | Convert to lowercase | {{ "HELLO" | string.downcase }} | hello |
string.capitalize | Capitalize first letter | {{ "hello world" | string.capitalize }} | Hello world |
string.strip | Remove leading/trailing whitespace | {{ " hi " | string.strip }} | hi |
string.truncate | Truncate to length | {{ "hello world" | string.truncate 5 }} | he... |
string.append | Append text | {{ "hello" | string.append " world" }} | hello world |
string.prepend | Prepend text | {{ "world" | string.prepend "hello " }} | hello world |
string.replace | Replace occurrences | {{ "hello" | string.replace "l" "r" }} | herro |
string.remove | Remove occurrences | {{ "hello" | string.remove "l" }} | heo |
string.split | Split into array | {{ "a,b,c" | string.split "," }} | ["a","b","c"] |
string.slice | Extract substring | {{ "hello" | string.slice 1 3 }} | ell |
string.contains | Check if contains | {{ "hello" | string.contains "ell" }} | true |
string.starts_with | Check prefix | {{ "hello" | string.starts_with "hel" }} | true |
string.ends_with | Check suffix | {{ "hello" | string.ends_with "llo" }} | true |
string.size | String length | {{ "hello" | string.size }} | 5 |
string.pad_left | Pad on the left | {{ "42" | string.pad_left 5 "0" }} | 00042 |
string.pad_right | Pad on the right | {{ "42" | string.pad_right 5 "0" }} | 42000 |
Math Filters
| Filter | Description | Example | Result |
|---|---|---|---|
math.abs | Absolute value | {{ -5 | math.abs }} | 5 |
math.ceil | Round up | {{ 3.2 | math.ceil }} | 4 |
math.floor | Round down | {{ 3.8 | math.floor }} | 3 |
math.round | Round to nearest | {{ 3.5 | math.round }} | 4 |
math.plus | Add | {{ 5 | math.plus 3 }} | 8 |
math.minus | Subtract | {{ 10 | math.minus 3 }} | 7 |
math.times | Multiply | {{ 4 | math.times 3 }} | 12 |
math.divided_by | Divide | {{ 20 | math.divided_by 4 }} | 5 |
math.modulo | Modulo | {{ 17 | math.modulo 5 }} | 2 |
math.min | Minimum of two values | {{ 5 | math.min 3 }} | 3 |
math.max | Maximum of two values | {{ 5 | math.max 3 }} | 5 |
Array Filters
| Filter | Description | Example | Result |
|---|---|---|---|
array.join | Join elements | {{ [1,2,3] | array.join ", " }} | 1, 2, 3 |
array.first | First element | {{ [1,2,3] | array.first }} | 1 |
array.last | Last element | {{ [1,2,3] | array.last }} | 3 |
array.size | Element count | {{ [1,2,3] | array.size }} | 3 |
array.reverse | Reverse order | {{ [1,2,3] | array.reverse }} | [3,2,1] |
array.sort | Sort ascending | {{ [3,1,2] | array.sort }} | [1,2,3] |
array.uniq | Remove duplicates | {{ [1,2,2,3] | array.uniq }} | [1,2,3] |
array.compact | Remove nulls | {{ [1,null,3] | array.compact }} | [1,3] |
array.concat | Concatenate arrays | {{ [1,2] | array.concat [3,4] }} | [1,2,3,4] |
array.map | Map property | {{ tags | array.map "epc" }} | EPCs array |
array.contains | Check membership | {{ [1,2,3] | array.contains 2 }} | true |
array.add | Add element | {{ [1,2] | array.add 3 }} | [1,2,3] |
array.remove_at | Remove at index | {{ [1,2,3] | array.remove_at 1 }} | [1,3] |
array.insert_at | Insert at index | {{ [1,3] | array.insert_at 1 2 }} | [1,2,3] |
array.offset | Skip elements | {{ [1,2,3,4] | array.offset 2 }} | [3,4] |
array.limit | Take first N | {{ [1,2,3,4] | array.limit 2 }} | [1,2] |
Date Filters
| Filter | Description | Example | Result |
|---|---|---|---|
date.now | Current UTC time | {{ date.now }} | 2026-03-20T10:30:00Z |
date.to_string | Format a date | {{ date.now | date.to_string "%Y-%m-%d" }} | 2026-03-20 |
date.add_days | Add days | {{ date.now | date.add_days 7 }} | (7 days from now) |
date.add_months | Add months | {{ date.now | date.add_months 1 }} | (1 month from now) |
date.add_years | Add years | {{ date.now | date.add_years 1 }} | (1 year from now) |
Date format specifiers:
| Specifier | Meaning | Example |
|---|---|---|
%Y | 4-digit year | 2026 |
%m | 2-digit month | 03 |
%d | 2-digit day | 20 |
%H | Hour (24h) | 14 |
%M | Minute | 30 |
%S | Second | 00 |
Object Filters
| Filter | Description | Example | Result |
|---|---|---|---|
object.keys | Get keys | {{ obj | object.keys }} | Array of key names |
object.values | Get values | {{ obj | object.values }} | Array of values |
object.size | Property count | {{ obj | object.size }} | Number of properties |
object.has_key | Check key exists | {{ obj | object.has_key "name" }} | true or false |
object.typeof | Get type name | {{ 42 | object.typeof }} | number |
Control Flow
if / else / end
{{ if variables.rssi > -50 }}
Strong signal
{{ else }}
Weak signal
{{ end }}With else if:
{{ if variables.count > 100 }}
High volume
{{ else if variables.count > 10 }}
Normal volume
{{ else }}
Low volume
{{ end }}for / end
Iterate over arrays:
{{ for tag in variables.tags }}
EPC: {{ tag.epc }}, RSSI: {{ tag.rssi }}
{{ end }}Access the loop index:
{{ for tag in variables.tags }}
{{ for.index }}: {{ tag.epc }}
{{ end }}Loop variables:
| Variable | Description |
|---|---|
for.index | Zero-based index |
for.index1 | One-based index |
for.first | true on first iteration |
for.last | true on last iteration |
for.even | true on even iterations |
for.odd | true on odd iterations |
while / end
{{ while variables.retries > 0 }}
Retrying...
{{ variables.retries = variables.retries - 1 }}
{{ end }}The loop limit is 100,000 iterations to prevent infinite loops.
Common Patterns and Recipes
Access a trigger’s event data
{{ output("trigger").epc }}
{{ output("trigger").rssi }}Conditional default value
{{ if output("query").rowCount > 0 }}
{{ output("query").rows | array.first }}
{{ else }}
No results found
{{ end }}Build a JSON string dynamically
{"epc": "{{ output("trigger").epc }}", "location": "{{ variables.location }}", "timestamp": "{{ date.now | date.to_string "%Y-%m-%dT%H:%M:%SZ" }}"}Check if a value exists
{{ if output("redis").exists }}
Cached: {{ output("redis").value }}
{{ else }}
Cache miss
{{ end }}Extract EPCs from a batch tag read
{{ output("trigger").tags | array.map "epc" | array.join ", " }}Count items matching a condition
{{ for tag in output("trigger").tags }}
{{ if tag.rssi > -50 }}
{{ variables.strongCount = (variables.strongCount ?? 0) + 1 }}
{{ end }}
{{ end }}String interpolation with device data
Device {{ output("action").deviceId }} reported {{ output("action").success ? "success" : "failure" }}Escaping
To output literal {{ }} delimiters without evaluation, use the raw block:
{%{ {{ this is not evaluated }} }%}Within strings, standard escape sequences apply:
| Sequence | Character |
|---|---|
\" | Double quote |
\\ | Backslash |
\n | Newline |
\t | Tab |
Error Handling
If an expression fails to parse or evaluate, Vesbite raises an ExpressionEvaluationException with the expression text, language (vex), and error details. Common errors:
| Error | Cause | Fix |
|---|---|---|
Parse error | Invalid syntax in the expression | Check for unclosed braces, missing quotes, or invalid operators |
Evaluation error | Runtime failure during evaluation | Verify that referenced variables and outputs exist |
null reference | Accessing a property on null | Add a null check with if or use the ?? operator |
See Also
- Workflow Nodes Reference – Where expressions are used in node inputs
- How to Build a Flow