Variables & Expressions
Use {{ }} variables and expressions to pass data between nodes and your configuration.
Almost every field in a flow can contain a dynamic value instead of a fixed one. You write these dynamic values with double curly braces — {{ ... }} — and inside the braces you put an expression that the flow evaluates when it runs. This is how you take a customer's hostname from the flow's input, drop your API key from configuration into a request, reshape a response from a previous node, or build a message string out of live data.
This article explains what you can reference inside {{ }}, the difference between a placeholder that is the whole value and one embedded in a longer string, and the set of expression features and helper functions available. For where these fields live, see the Node Reference; for assembling them into a workflow, see Building Flows.
What you can reference
Inside {{ }} you can read from several sources:
| Reference | What it is |
|---|---|
input |
The data the flow was started with. For a provision flow that's the order/service details; for a webhook flow it's the incoming payload. |
config.<key> |
Your plugin's configuration values — the fields an operator fills in when they set the plugin up (e.g. config.api_url, config.default_location). |
| a node's id | The output of an earlier node, referenced by that node's id. If a node has id get_status, then {{ get_status.data.state }} reads from its output. |
item / index |
Inside a Loop, the current item and its zero-based position. (These names are configurable on the Loop node — item and index are the defaults.) |
| a variable you set | Any value you stored earlier with a Set Variable node, by the name you gave it. |
Top-level fields of the input are also reachable directly — if the input has a serviceId, both {{ input.serviceId }} and {{ serviceId }} resolve to it.
{{ input.hostname }}
{{ config.api_url }}
{{ get_status.data.state }}
{{ item.id }}
{{ index }}
{{ chosen_plan }}
Whole-value vs. embedded placeholders
How a placeholder is rendered depends on whether it makes up the entire field or sits inside a larger string.
-
Whole value. If the field is exactly one placeholder and nothing else —
{{ get_list.data.items }}— the raw value is returned, preserving its type. Objects stay objects, arrays stay arrays, numbers stay numbers. Use this when you want to pass a structured value through (for example, handing an array to a Loop node's source, or putting an object into a request body field). -
Embedded. If a placeholder appears within other text —
Bearer {{ config.token }}orHello {{ input.name }}— the resolved value is converted to text and spliced into the string. Objects and arrays become their text form, so reserve embedding for values that are meant to be strings or numbers.
{{ input.items }} → the actual array (whole value)
Order #{{ input.orderId }} ready → "Order #1234 ready" (embedded, text)
If a placeholder can't be resolved, it's left as-is rather than turning into an empty value, which makes typos easy to spot when you inspect a run in History.
How expressions are evaluated
Placeholders are resolved once. The result is not re-scanned, so if an evaluated value happens to itself contain {{ }}, that inner text is left exactly as it is — it is never evaluated a second time. This keeps data values from being mistaken for template instructions.
One naming detail: identifiers in expressions use underscores, not hyphens. If a node id or key contains a hyphen, write it with an underscore inside an expression. A node with id get-status is referenced as {{ get_status.data }}. (When naming things you'll reference in expressions, underscores are the simplest choice.)
Expression features
Expressions support a safe, focused subset of familiar syntax — enough to compute, compare, and reshape values, with no access to anything outside the flow's data.
Operators
| Feature | Example |
|---|---|
| Comparisons | {{ input.qty > 0 }}, {{ config.region == "eu" }}, {{ a !== b }} |
| Logical AND / OR | {{ input.active && input.paid }}, {{ a || b }} |
| Ternary (if/else) | {{ input.qty > 1 ? "many" : "one" }} |
| Optional chaining | {{ input.meta?.label }} (safe when meta is missing) |
| Arithmetic | {{ input.price * input.qty }}, {{ total + tax }}, {{ n % 2 }} |
Note that || is a logical OR (and a handy fallback: {{ config.name || "default" }}), not string concatenation. To build strings, embed placeholders in literal text as shown above.
Helper functions
Type conversion and inspection:
| Helper | Purpose |
|---|---|
Number(x), String(x), Boolean(x) |
Convert between types. |
parseInt(x), parseFloat(x) |
Parse numbers from strings. |
Object.keys(o), Object.values(o) |
List an object's keys or values. |
JSON.stringify(x), JSON.parse(s) |
Serialize to / parse from JSON text. |
Array.isArray(x) |
Test whether a value is an array. |
Math.round(x), Math.floor(x), Math.ceil(x), Math.abs(x) |
Common math. |
encodeURIComponent(s), decodeURIComponent(s) |
URL-encode / decode. |
btoa(s), atob(s) |
Base64 encode / decode. |
Date.now() |
Current time in milliseconds. |
String methods (called directly on a string value):
toLowerCase, toUpperCase, trim, split, replace, replaceAll, slice, substring, startsWith, endsWith, includes, indexOf, lastIndexOf, toFixed, toString, join.
Note: includes, indexOf, slice, substring, and most string methods convert the receiver to its text form before operating. So {{ input.tags.includes("priority") }} tests the array's text representation, not its elements — use a Loop or array method for true membership tests.
Array methods (accept an arrow-function callback):
map, filter, find, some, every (callbacks may be written x => expr, (x) => { return expr; }, or function(x){ ... }). Of the shared methods above, only join is array-aware (it joins the elements into a string).
Small examples
{{ input.region || "us-east" }} fallback when region is empty
{{ Number(input.amount) * 100 }} dollars → cents
{{ input.status === "active" ? "Active" : "Off" }} map a flag to a label
{{ input.servers.filter(s => s.online).length }} count the online servers
{{ input.servers.filter(s => s.online) }} keep only online servers
{{ input.servers.map(s => s.id) }} pull out just the ids
{{ Object.keys(input.meta) }} the metadata field names
{{ input.name.trim().toLowerCase() }} normalize a name
{{ Math.round(input.usage * 100) / 100 }} round to 2 decimals
{{ encodeURIComponent(input.query) }} make a value URL-safe
Related: Building Flows · Node Reference · Connections and Authentication · Configuration Settings · Notification Plugins
