Passing values between steps
Every input of a flow step gets its value from a binding: a fixed value, a field of an earlier step’s output, a short JavaScript expression, or a project variable. Bindings are how a flow moves data without glue code — and they are checked before the run, so a broken one is named instead of quietly sending null downstream.
- Pick one of the four binding modes for each input
- Read a field of an earlier step’s output by its path
- Compute a value with an expression over nodes and vars
- Use a variable, and know how secrets are kept out of the run record
Where an input gets its value
A step’s inputs are its parameters — the arguments of a script’s main, the :name placeholders of a SQL query, the fields of a built-in step, the Start inputs of a subflow. Each parameter row carries four small buttons, and the one that is lit says where the value comes from:
| Mode | The value is | Example |
|---|---|---|
| Value (pencil) | A constant you type. A number stays a number. | BRL |
| From node (arrow) | A Start input, or a field of a step above. | Fetch orders → rows.0.total |
| Expression (braces) | The result of a JavaScript expression. | nodes.fetchOrders.output.count * 2 |
| Variable (key) | A project or global variable’s current value. | BILLING_API_KEY |
A row with no value and no default shows a warning sign: the input is required for the run. The step itself says so too — Missing bindings: name on the canvas.
Bind an input
- Open the step’s parameters
For a script or SQL step, click the step and then Open code editor: its Parameters tab lists every input next to the code. Built-in steps, subflows and AI Scraper steps show their inputs on the side panel’s Parameters tab.
- Choose the mode
Click one of the four buttons on the row. Switching mode clears the other source, so a parameter never has two.
- Pick the source
From node opens a picker with Start’s inputs and every step above with its fields; Variable opens Search variables… with the project’s variables, then the global ones. Picking fills the row with a chip: the step name → the path.
- Test without touching the bindings
The Test tab holds values used only when you run the step from the editor — the bindings stay as they are.
def main(customer: str, total: float, api_key: str, currency: str = "BRL"):
# api_key arrives with its real value; the run records it as •••
return {
"line": f"{customer}: {currency} {total:.2f}",
"signed": len(api_key) > 0,
}Bound in a flow, customer could come From node (Start → customer), total from an Expression, api_key from a secret Variable, and currency left to its default.
Node outputs and paths
A From node binding names a step and a path into its output. Dots walk into objects and numbers into lists: rows.0.email is the first row’s email. A key with spaces is quoted — rows.0["First Name"]. A leading output. is optional (output.count and count read the same field); a binding to Start uses the input’s name.
The picker shows a step’s fields even before the flow has run — Not run yet — these fields are what it returns — and a field whose type does not fit the parameter cannot be picked; hovering it says why (This value is a string — this parameter expects a number). At run time, a path that does not exist fails the step with path 'rows.0.mail' has no segment 'mail', rather than passing nothing.
Expressions
An Expression is one JavaScript expression, evaluated in an isolated engine with nothing to reach outside — no network, no files, no require. It sees:
nodes.<alias>.output— every step that has already produced output. The alias is the step’s label in camelCase, accents folded: Fetch orders →nodes.fetchOrders, Preço médio →nodes.precoMedio.vars.NAME(orvars["NAME"]) — project and global variables, every value a string.- Inside a loop body,
item,index,iterandloops.<id>(see loops).
nodes.fetchOrders.output.count * 2
`Total: ${nodes.addTax.output.total}`
{ name: nodes.fetchUser.output.name, tier: "gold" } // an object literal is a value
Number(vars.RETRY_COUNT) + 1 // variables are stringsLoops and parallel branches
Inside a loop, bind a step to the loop’s current element: From node → the loop → item.email. After a Parallel branches node, read one branch by name: output["Invoice"]. Renaming a branch rewrites the bindings that read it.
Variables and secrets
A Variable binding reads the variable’s value when the step runs, never a copy saved in the flow — change the variable on the Variables page and the next run uses the new value, with no republish. The value arrives as text and is converted to the type the script declares, so RETRY_COUNT bound to retries: int arrives as a number. A project variable wins over a global one with the same name.
Secrets stay secret in the record. When an input is bound to a secret variable — directly, or by an expression that mentions vars.<SECRET> — the step receives the real value, but the run stores the input as •••, and the run view shows it with a lock: Secret value — not recorded. Non-secret variables are recorded in clear.
{
"customer": "Ana Souza",
"total": 129.9,
"api_key": "•••",
"currency": "BRL"
}A variable that no longer exists shows its chip marked missing: pick another or clear the binding. Variables made by an integration (its password or access key) appear in the picker with the provider’s icon.
Checks and error codes
unknown_variable— a binding names a variable that exists in neither scope. Refused at publish and at run; a variable deleted after publishing fails the step with the same code.invalid_binding— a variable binding also carries a node source or a typed value; a parameter has one source.invalid_parameter— a constant that does not fit the declared type is refused at publish; a value from a node or an expression is checked when the step runs.