Template expressions
A template expression passes information from one step of a workflow to the next. You write it in double curly braces inside a step’s settings, and almyty fills in the value just before the step runs. For example, a prompt that reads Write a reply about order {{input.orderId}} goes to the model as “Write a reply about order NW-10428”.
An expression can read what the run started with, what an earlier step produced, or an agent variable. It is a lookup, not a programming language: a dotted path and nothing else. No function calls, no arithmetic, no array indexing, and no comparisons inside the braces.

Syntax
{{source.path.to.value}}The first segment identifies the data source. Subsequent segments navigate into the data structure using dot notation.
Data sources
| Source | Syntax | Description |
|---|---|---|
| Input | {{input.property}} | The agent’s invocation payload |
| Node output | {{nodes.<id>.output}} | Output of any upstream node |
| Variables | {{variables.key}} | An agent-level variable |
There is no loop context. A Loop node outputs its whole array in one pass and
does not re-run the nodes after it per item, so {{loop.item}} and
{{loop.index}} name nothing and resolve to an empty string. See
Loop.
Input data
{{input.message}} // Top-level property
{{input.user.name}} // Nested propertyNode outputs
{{nodes.llm_1.output}} // Full output of node llm_1
{{nodes.tool_1.output.data}} // Nested property
{{nodes.transform_1.output}} // A transform node's rendered stringWhat a path may contain
Each segment is letters, digits, underscores and hyphens, and segments are
separated by dots. Anything else is rejected and the node throws
Template expression contains invalid characters. That rules out array
indexing ({{input.items[0].title}} is an error, not an empty value), along
with calls such as {{Date.now()}} and comparisons written inside the braces.
A segment that is exactly __proto__, constructor, prototype, process,
require, import, global, window, Function or eval is rejected as
well. A segment that merely contains one of those words is fine:
{{nodes.processor.output}} and {{input.importItems}} both resolve.
Only own properties are read, so nothing reached through a prototype resolves. An expression may be at most 500 characters, and a whole template at most 10,000.
In the builder
Template expressions are used in node configuration fields throughout the builder:
- Click a node on the canvas to open its config panel
- Type the expression into any field that takes one: prompts, mappings, parameters
- A Model call node lists the expressions it found in your prompts, so you can check them at a glance
- Use the Test panel in the builder’s top bar to run the pipeline and see what each one resolved to
Examples
Simple string interpolation
In a Model call node’s user prompt template:
You asked: {{input.message}}Resolves to: You asked: What is the weather?
Composing data from several steps
Based on the search results ({{nodes.tool_1.output.count}} found),
here is a summary: {{nodes.llm_1.output}}Composing a value in a Transform step
A Transform node renders its template and outputs the result:
Query "{{input.query}}" returned {{nodes.tool_1.output.count}} results.The output is text. A template shaped like a JSON object produces a JSON
string rather than an object, so a downstream
{{nodes.transform_1.output.count}} resolves to an empty string instead of a
number.
Type handling
Inside a template string every resolved value is rendered to text: numbers and
booleans with String(), objects and arrays with JSON.stringify. A value of
null or undefined renders as an empty string, which is also what a path
that does not exist produces.
Two fields read a raw value instead and keep its type:
| Where | Field |
|---|---|
| Output node | source, a bare dot path with no braces |
| Loop node | iterableExpression, when it is a single {{...}} reference and nothing else |
Everywhere else (prompts, transform templates, tool parameters, sub-agent
input mappings, an output node’s mapping) the result is a string.
Error handling
| Scenario | Behavior |
|---|---|
| Path does not exist | Resolves to an empty string. A warning is logged; the run does not fail |
| Node id is misspelled | The same: an empty string, silently, in the prompt or parameter |
| Path contains invalid characters or a blocked segment | The node throws and the run fails there |
| Upstream node failed | Every node downstream of it is skipped, so the expression is never resolved |
The empty resolution is the one to watch for, because nothing reports it: a prompt with a mistyped path is sent with a hole in it and the model answers anyway. Run the pipeline from the Test panel and read what each expression resolved to before activating the agent.
Good practice
- Keep expressions to a path: there is nothing else available, so shape the data in the node that produces it: ask a Model call node for JSON, or pick a tool that returns the field you want
- Validate your schema: define an Input node schema to catch bad data early
- Use meaningful node IDs:
search_apireads better thantool_1in an expression, and a renamed node breaks every expression pointing at it - Test incrementally: an expression that resolves to nothing does not fail the run, so check the Test panel rather than assuming