Flow Control
Conditionals, branching, loops, parallel execution, subprocess invocation, and error boundaries.
Flow control nodes let you branch, loop, and pause execution within a process.
Conditional Node
The conditional node evaluates expressions and routes data to different output handles based on the result. It supports two modes: Conditions and Switch.
Type: conditionalNode
Conditions Mode
Evaluates one or more boolean conditions, combines them with AND/OR logic, and routes to a "true" or "false" output handle.
Config
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
mode | string | Yes | -- | "conditions" |
operator | string | No | "and" | "and" or "or" to combine conditions |
conditions | array | Yes | -- | Array of condition objects |
passthroughFiles | bool | No | false | Forward files to output |
Condition Object
| Field | Type | Description |
|---|---|---|
id | int | Condition identifier |
field | string | Left-hand value (node reference or literal) |
operator | string | Comparison operator |
value | string | Right-hand value (node reference or literal) |
Comparison Operators
| Operator | Type | Description |
|---|---|---|
== | Equality | Equal (with type coercion for numbers) |
!= | Equality | Not equal |
> | Numeric | Greater than |
>= | Numeric | Greater than or equal |
< | Numeric | Less than |
<= | Numeric | Less than or equal |
contains | String | Field contains value as substring |
startsWith | String | Field starts with value |
endsWith | String | Field ends with value |
matches | String | Field matches regex pattern |
isEmpty | Unary | Field is empty, nil, or zero-length (no value needed) |
isNotEmpty | Unary | Field is not empty (no value needed) |
Logical Combination
| Operator | Behavior |
|---|---|
and | All conditions must be true |
or | At least one condition must be true |
Reference Syntax
Within condition field and value strings, you can use:
Dot notation:
ExtractOrder.Order Request.weight_lbs
Bracket notation:
input["Extract Order"]["Order Request"]["weight_lbs"]
Quoted literals:
"5000" "rush"
Interpolated strings (single curly braces):
"Hello {ExtractOrder.customer_name}"Example: Route by Weight
{
"mode": "conditions",
"operator": "and",
"conditions": [
{
"id": 1,
"field": "input[\"Extract Order\"][\"Order Request\"][\"weight_lbs\"]",
"operator": ">",
"value": "10000"
}
]
}Graph:
[Extract Order] → [Conditional] ──true──→ [Large Order Handler]
──false──→ [Standard Handler]If weight > 10000, data flows to Large Order Handler. Otherwise, to Standard Handler.
Example: Multiple Conditions with OR
{
"mode": "conditions",
"operator": "or",
"conditions": [
{
"id": 1,
"field": "input[\"Extract Order\"][\"Order Request\"][\"urgency\"]",
"operator": "==",
"value": "\"rush\""
},
{
"id": 2,
"field": "input[\"Extract Order\"][\"Order Request\"][\"urgency\"]",
"operator": "==",
"value": "\"emergency\""
}
]
}Routes to true if urgency is either "rush" or "emergency".
Output Handles
"true"-- condition(s) evaluated to true"false"-- condition(s) evaluated to false
Nodes connected to the inactive handle are skipped. Their descendants are also skipped unless they have another active path.
Switch Mode
Evaluates a single field and routes to one of several named output handles based on the value.
Config
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
mode | string | Yes | -- | "switch" |
switchField | string | Yes | -- | Field reference to evaluate |
cases | array | Yes | -- | Array of case objects |
defaultCase | string | No | -- | Default handle label if no case matches |
passthroughFiles | bool | No | false | Forward files |
Case Object
| Field | Type | Description |
|---|---|---|
id | int | Case identifier |
value | string | Value to match against (node reference or literal) |
label | string | Output handle label |
Example: Route by Steel Type
{
"mode": "switch",
"switchField": "input[\"Extract Order\"][\"Order Request\"][\"steel_type\"]",
"cases": [
{ "id": 1, "value": "\"carbon\"", "label": "carbon_handler" },
{ "id": 2, "value": "\"stainless\"", "label": "stainless_handler" },
{ "id": 3, "value": "\"alloy\"", "label": "alloy_handler" }
],
"defaultCase": "other_handler"
}Graph:
──carbon_handler──→ [Carbon Pricing]
[Conditional] ──stainless_handler──→ [Stainless Pricing]
──alloy_handler──→ [Alloy Pricing]
──other_handler──→ [Default Pricing]Only the matching handle's branch executes. All other branches are skipped.
Output Handles
One handle per case label, plus the default. The handle name is the sanitized version of the case label (spaces and hyphens replaced with underscores).
Conditional Data Flow
The conditional node passes all its input data through to the active output handle. Downstream nodes receive the same data that the conditional received, so they can reference upstream nodes normally:
{{input["Extract Order"]["Order Request"]["weight_lbs"]}}This works in nodes downstream of the conditional because ancestor data is forwarded through.
Loop Node
The loop node iterates a subgraph (a set of connected nodes) over an array of items or a set of files.
Type: loopNode
Loop Structure
A loop node has two special output handles:
start-- connects to the first node(s) in the loop bodyoutput-- carries the aggregated results after all iterations complete
The loop body is a subgraph of nodes between the loop's start handle and nodes that connect back to the loop's end handle.
┌─── start ──→ [Process Item] ──→ [Validate] ───┐
[Loop Node] ───────│ │──→ end
└── output ──→ [Aggregate Results]Config
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
label | string | Yes | -- | Display name |
mode | string | No | "sequential" | "sequential" or "parallel" |
loopSource | string | No | "data" | "data" or "files" |
arrayFieldPath | string | No | auto-detect | Dot-separated path to the array field |
maxConcurrency | int | No | 10 | Max parallel iterations (parallel mode only) |
continueOnError | bool | No | false | Continue if an iteration fails |
includeParent | bool | No | false | Include parent data in each iteration |
passthroughFiles | bool | No | false | Forward files |
Data Loop
Iterates over an array of data items.
Array Detection
The engine finds the array to iterate over:
- If
arrayFieldPathis set (e.g.,"items"or"data.orders"), extract that field. - Otherwise, auto-detect: search input data for the first array value.
- If no array found, wrap the entire input as a single-item array (the loop runs once).
Iteration Data
Each iteration receives:
{
"data": { ... }, // Current array item
"parent": { ... } // Parent data (if includeParent is true)
}Template reference from within the loop body:
{{input["Loop Label"]["data"]["order_id"]}}
{{input["Loop Label"]["parent"]["customer_name"]}}Example: Process Multiple Line Items
Input to the loop:
{
"items": [
{ "product": "Carbon Steel", "qty": 5000 },
{ "product": "Stainless Steel", "qty": 2000 },
{ "product": "Alloy Steel", "qty": 1000 }
]
}Config:
{
"mode": "sequential",
"loopSource": "data",
"arrayFieldPath": "items"
}The loop body runs 3 times. In each iteration:
Iteration 0: input["Loop"]["data"] = { "product": "Carbon Steel", "qty": 5000 }
Iteration 1: input["Loop"]["data"] = { "product": "Stainless Steel", "qty": 2000 }
Iteration 2: input["Loop"]["data"] = { "product": "Alloy Steel", "qty": 1000 }File Loop
Iterates over files from upstream nodes. Each iteration processes one file.
Iteration Data
Each iteration receives file metadata:
{
"__loop_file_name__": "invoice-001.pdf",
"__loop_file_link__": "https://storage.example.com/files/invoice-001.pdf",
"__loop_file_size__": 245760,
"__loop_file_node_key__": "Email Input",
"__loop_file_index__": 0
}Template reference:
{{input["Loop Label"]["__loop_file_name__"]}}
{{input["Loop Label"]["__loop_file_link__"]}}The current file is also available as a file reference for nodes that accept files (OCR, ML Model, etc.).
Sequential vs Parallel
Sequential
- Iterations run one at a time, in order.
- Each iteration completes before the next starts.
- Prior iterations' results are available via
Loop Label_iter_N:
{{input["Loop Label_iter_0"]["result"]}}
{{input["Loop Label_iter_1"]["result"]}}Use sequential when:
- Iterations depend on previous results
- Order matters
- External API rate limits require serialization
Parallel
- Multiple iterations run concurrently.
- Concurrency limited by
maxConcurrency(default 10). - Iteration order is not guaranteed.
- Prior iteration results are NOT available (each iteration is independent).
Use parallel when:
- Iterations are independent
- Throughput matters more than order
- Processing large batches
Continue on Error
When continueOnError: true:
- A failed iteration is recorded but does not stop the loop.
- Remaining iterations continue processing.
- The loop output includes both successful and failed results.
When continueOnError: false (default):
- The first failed iteration cancels the entire loop.
- The execution may fail (depending on error handling upstream).
Loop Output
After all iterations complete, the loop output contains:
{
"items": [
{
"index": 0,
"success": true,
"data": { "result": "..." }
},
{
"index": 1,
"success": true,
"data": { "result": "..." }
},
{
"index": 2,
"success": false,
"error": "API rate limit exceeded"
}
],
"summary": {
"total": 3,
"successful": 2,
"failed": 1
}
}Nodes connected to the loop's output handle receive this aggregated result.
Nested Loops
Loops can be nested. The inner loop body has access to both the inner and outer loop's data:
[Outer Loop] ──start──→ [Inner Loop] ──start──→ [Process] ──→ end
──output──→ endEach level of nesting creates its own scoped data bucket, preventing iteration data from leaking between levels.
Wait Node
Pauses execution for a configurable duration.
Type: waitNode
Config
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
label | string | Yes | -- | Display name |
duration | int | Yes | -- | Wait time in seconds |
Behavior
- The node starts, waits for
durationseconds, then completes. - All input data is passed through unchanged.
- The execution timeout is paused during the wait, so a 5-minute wait doesn't consume 5 minutes of the execution timeout.
- Files are passed through if
passthroughFilesis set.
Use Cases
- Rate limiting: Add a wait between API calls to avoid rate limits.
- Delayed notification: Wait before sending a follow-up email.
- Polling pattern: Combined with a conditional and loop, create a poll-wait-check cycle.
Flow Control Patterns
If-Then-Else
[Input] → [Conditional] ──true──→ [Process A] ──→ [Output]
──false──→ [Process B] ──↗Multi-Way Branch (Switch)
──case_a──→ [Handler A] ──→ [Merge] → [Output]
[Input] → [Conditional] ──case_b──→ [Handler B] ──↗
──default──→ [Handler C] ──↗Loop with Conditional
[Input] → [Loop] ──start──→ [Conditional] ──true──→ [Process] → end
──false──→ [Skip] → end
──output──→ [Aggregate]Sequential Processing with Wait
[Input] → [API Call 1] → [Wait 5s] → [API Call 2] → [Wait 5s] → [API Call 3] → [Output]
Parallel Fan-Out
──→ [Process A] ──→
[Input] ──→ ──→ [Process B] ──→ [Output]
──→ [Process C] ──→All three processing nodes run in parallel (no loop needed). The output node waits for all three to complete.