Flow control
Flow-control nodes shape the path through your workflow. They don't transform data; they decide what runs next, and when.
If
What: Routes each item to a true or false port based on one or more conditions.
Ports: in input; out true, false.
Configuration:
| Field | Notes |
|---|---|
conditions |
A list. Each condition is value1 operator value2. Operands may be expressions. |
combinator |
and (all must match, default) or or (at least one) |
Operators: equal, notEqual, greaterThan, lessThan, greaterThanOrEqual, lessThanOrEqual, contains, notContains, startsWith, notStartsWith, endsWith, notEndsWith, regex, isEmpty, isNotEmpty, isNull, isNotNull, isTrue, isFalse. See Operators for how each one compares values.
Example: "Is the order amount over 1000?"
value1: {{ $trigger.payload.amount }}
operator: greaterThan
value2: 1000Connect true to a high-value-order branch; connect false to the normal path.
A misconfigured condition (an unknown operator, a non-numeric value for greaterThan, an invalid regex) fails the node. It does not quietly route to false.


Switch
What: Multi-way branching. Routes each item to a named port chosen by rules.


Ports: in input; out one port per rule, plus default (no match) and error (the rule could not be evaluated, for example a missing field).
Configuration:
| Field | Notes |
|---|---|
mode |
value (default), pattern, range, type, expression or multi |
fieldToCheck |
Dot path into the incoming item, for example user.role |
rules |
Value mode: rule rows {value1, operator, value2, output} |
routingRules |
The other modes' rules; the shape depends on the mode (below) |
defaultOutput |
Port for items that match nothing. Default default. |
allowMultiple |
Send an item to every matching rule instead of only the first. Default off. |
caseSensitive |
Pattern mode. Default on. |
Value mode is what the Settings tab edits. Each row compares a field with a value using the If operators, and names the port. Add Routing Rule names new ports output1, output2 and so on, shown on the canvas as Route 1, Route 2.
mode: value
rules:
- { value1: eventType, operator: equal, value2: order.created, output: orders }
- { value1: eventType, operator: startsWith, value2: refund., output: refunds }Other modes are edited in the node's JSON tab:
| Mode | routingRules shape |
Port name comes from |
|---|---|---|
pattern |
{"*@example.com": "port"}: a */? wildcard, a /regex/ or exact text |
the value |
type |
{"string": "port"}: string, integer, double, number, boolean, array, object, null |
the value |
range |
{"port": {min, max, includeMin, includeMax}}, bounds inclusive by default |
the key |
Known limitations:
- Expression mode reads a separate
expressionslist of{condition, output}. A condition written as a$expression, such as{{ $input.isVip }}, arrives as the text"true"and never matches. Name a field of the current item without$instead, for example{{ isVip }}, and compute the flag first in a Set or Code node. - In expression and multi mode the ports drawn on the canvas do not always match the ports the node sends to. Prefer value mode, or chain If nodes.
Loop
What: Iterates over a list, an object, a numeric range, batches or pages. The loop port feeds the body once per element; done fires after the last one.
Ports: in input; out loop, done, error.
Configuration:
| Field | Notes |
|---|---|
mode |
array (default), object, range, batch or page |
fieldToLoopOn |
A field of the incoming item, or a whole-value expression that resolves to a list, such as {{ $node["Fetch"].output.response.body.items }} |
outputFieldName |
Name the current element is given. Default item. |
includeIndex |
Add the index to each element. Default off. |
batchSize |
For batch and page. Default 10. |
startIndex, endIndex, step |
For range. Defaults 0, none, 1. |
Pattern:
The loop port feeds the body once per item, and the last body node connects back into the Loop. The done port fires after the last item.
Inside the body: read the current element as {{ $input.item }} (or your outputFieldName) and its position as {{ $input._loopIteration.current }}. _loopIteration also has total, index, isFirst and isLast. Wire the last body node back into the Loop.
When you add a Loop, the editor places a Replace Me card in the body. Swap it for the node you need.
![The Loop node inspector in Array mode, with Field to Loop On set to {{ $node["List items"].json.items }}. On the canvas the Loop sends each item to Process one item, which feeds back into the Loop, and sends the finished list to Summarise.](/assets/shots/flow-control__loop-config.light.webp)
![The Loop node inspector in Array mode, with Field to Loop On set to {{ $node["List items"].json.items }}. On the canvas the Loop sends each item to Process one item, which feeds back into the Loop, and sends the finished list to Summarise.](/assets/shots/flow-control__loop-config.dark.webp)
While
What: Repeats the connected body while its conditions hold, up to a maximum number of passes. The first pass always runs.


Ports: in input; out loop, done, error.
Configuration:
| Field | Notes |
|---|---|
conditions, combinator |
Same shape and operators as the If node |
maxIterations |
Required. Default 10. |
keepHistory |
Keep each pass's result. Default off. |
historyField |
Field the history is written to. Default history. |
Use when: Polling until a job finishes, paging through an API until there are no more results.
Merge
What: Waits for its inputs and combines them into one stream.


Ports: in input1, input2 (more input* ports are accepted); out output.
Configuration:
| Field | Notes |
|---|---|
strategy |
combine (default), dedupe, intersect, union, first, last or priority |
waitForAll |
Fire only after every input has arrived. Default on. |
dedupeBy |
Field to compare for dedupe |
sortBy, sortOrder |
Optional sort. asc (default) or desc. |
limit |
Optional cap on the number of items |
Use when: Joining results from parallel branches. With waitForAll on and strategy: first, the next node runs once after both branches finish.
Split
What: Sends an identical copy of the input down every connected branch, in parallel.


Ports: in input; out branch_0, branch_1.
A plain output connection already fans out to every node connected to it, so you rarely need Split.
Approval
What: Pauses the run until a person approves or rejects. The run waits for input and the request appears in Approvals.


Ports: in input; out approved, rejected. Connect the branch you want for each decision.
Configuration:
| Field | Notes |
|---|---|
prompt |
What the approver sees |
requireComment |
Make the approver leave a comment. Default off. |
approvalType |
ui (default) or email. With email, Flero also emails the request. Every request appears in Approvals either way. |
approvers |
The email recipients, used only when the approval type is email |
Notes:
- Anyone with access to the run can decide in Approvals, not only the listed approvers.
- The first decision wins. One approve or reject resumes the run.
- There is no automatic timeout. The run waits until someone decides or someone stops it. The node shows a
timeoutport, but nothing is sent to it today. - Data from before the pause is still available to expressions after the run resumes.
See Approval basics for the full guide.
Workflow Control
What: Control operations in one node: delay, retry, parallel, conditional, gate, throttle and batch.
Ports: in input; out output. The conditional operation also sends to true and false.
Configuration: operation (required), plus a delay, retry or throttle object for those operations.
Delay
What: Waits a number of milliseconds, then passes the data on. In the palette it sits under Developer Tools.


Ports: in input; out output.
Configuration: duration in milliseconds (default 1000), passThrough (default on).
Use when: Staggering calls to a rate-limited API, or waiting for an external system to catch up.
Error Handling
What: Error-handling operations: catch, recovery, circuit_breaker, validate and sanitize. In the palette it sits under Developer Tools.
Ports: in input; out output. The catch operation also sends to success and error; validate also sends to valid and invalid.
Configuration: operation (required), includeStackTrace (default off), plus a recovery or circuitBreaker object for those operations.
For most workflows, the per-node Settings dialog (retry, fallback value, continue anyway) and a connected error port are simpler. See Settings every node has.
Tips & gotchas
- If vs Switch: If is binary; Switch handles many cases. For three or more branches, Switch in value mode is cleaner.
- Loops survive pauses. An approval inside a loop gates every iteration, and the run resumes where it stopped.
- Nested loops start fresh for each pass of the outer loop.
- A failing body node stops the loop. Its failure goes to the Loop's
errorport. - There is no workflow-wide time limit. A long Delay or a slow node only times out if you set a Timeout in its Settings.
Related
Found something out of date? This page lives in the Flero docs content set.