Workflow data and expressions
Step parameters can use values produced by earlier steps. Expressions are evaluated when the step runs, after its upstream dependencies have completed.
Reference another step
Use the step's name, not its id:
const persist = new Step({
name: 'Persist Reply',
type: 'core.set',
params: {
values: {
reply: "={{ $('Support Agent').json.response }}",
},
},
});
The default port is main and the default item index is 0. The expression above is equivalent
to selecting the first item from the Support Agent's main output.
When the entire parameter is an expression, the resolved value keeps its original type. An object stays an object, an array stays an array, and a number stays a number.
params: {
values: {
// number
total: "={{ $('Calculate Total').json.total }}",
// complete object
customer: "={{ $('Load Customer').json }}",
},
}
Use an inline expression to interpolate a value into text. Inline expressions always produce a string:
params: {
values: {
message: "Agent reply: {{ $('Support Agent').json.response }}",
},
}
Nested objects and arrays
Use dot or bracket notation for nested JSON:
params: {
values: {
firstEmail: "={{ $('Load Customers').json.customers[0].email }}",
secondTag: "={{ $('Load Customers').json.customers[0].tags[1] }}",
status: "={{ $('HTTP Request').json['status-message'] }}",
},
}
There are two different array indexes to keep separate:
$('Search')[2]selects item2emitted by the Search step..json.results[2]selects element2inside the selected item's JSON.
params: {
values: {
// Third emitted item, then the first nested result inside that item's JSON.
id: "={{ $('Search')[2].json.results[0].id }}",
},
}
Ports
The default main port does not need to be written:
"={{ $('Support Agent').json.response }}"
To select another port, put the port after the quoted step name and before the closing parenthesis:
"={{ $('Tool Provider':ai_tool)[0].json.name }}"
The general form is:
$('Step name':port)[itemIndex].json.path.to.value
Known engine ports include main, ai_agent, ai_chain, ai_document, ai_embedding,
ai_languageModel, ai_memory, ai_outputParser, ai_retriever, ai_reranker,
ai_textSplitter, ai_tool, and ai_vectorStore. Most workflow nodes currently expose only
main.
The port suffix is outside the quotes. $('Tool Provider:ai_tool') is not the supported syntax.
Conditional branches and output indexes
An output index is selected when steps are connected, not inside an expression. For an If step,
output index 0 is one branch and output index 1 is the other:
checkCondition.connectTo(handleSuccess, {
fromPort: 'main',
fromOutputIndex: 0,
toInputIndex: 0,
});
checkCondition.connectTo(handleFallback, {
fromPort: 'main',
fromOutputIndex: 1,
toInputIndex: 0,
});
The downstream step receives only the branch connected to it. It references that delivered data normally:
"={{ $('Check Condition').json.reason }}"
toInputIndex chooses the target input index. It also belongs to the connection, not the
expression.
Runtime metadata
Workflow parameter expressions can also read execution metadata:
params: {
values: {
executionId: '={{ $system.execId }}',
workflowId: '={{ $system.workflowId }}',
currentNodeId: '={{ $system.node.id }}',
},
}
$system is a workflow-expression root. It is not a surface data-binding root.
Expression reference
| Need | Expression |
|---|---|
First item on main | ={{ $('Node').json.value }} |
| Whole JSON object | ={{ $('Node').json }} |
| Emitted item at index 2 | ={{ $('Node')[2].json.value }} |
| Nested object | ={{ $('Node').json.customer.id }} |
| Nested array | ={{ $('Node').json.items[0].id }} |
| Key containing punctuation | ={{ $('Node').json['status-message'] }} |
| Alternate port | ={{ $('Node':ai_tool)[0].json.value }} |
| Inline text | Value: {{ $('Node').json.value }} |
Only previously executed nodes in the current lineage are available. Keep step names unique so a reference always identifies one producer.