Skip to main content

Flow Control & Conditions

Basic Conditional Flow

Use conditions to control which steps execute next:

{
"id": "check-status",
"action": "template::api_call",
"parameters": {
"url": "https://api.example.com/status"
},
"condition": {
"if": "{{check-status.output.status}} == 'success'",
"then": "process-data",
"else": "handle-error"
}
}

Complex Conditions

Support for complex logical expressions:

{
"condition": {
"if": "{{previous-step.output.user.role}} == 'admin' && {{another-step.output.boolean_variable}} == true",
"then": "priority-processing",
"else": "standard-processing"
}
}

Multiple Next Steps

Execute multiple steps in parallel:

{
"id": "fan-out-step",
"action": "builtin::log",
"parameters": {
"message": "Starting parallel processing"
},
"next": ["process-images", "process-text", "update-database"]
}

Work also with conditions.

Convergence Points

Synchronize multiple parallel branches at a convergence point using the waitForAll field:

{
"id": "step-convergence",
"name": "Wait for All Branches",
"action": "builtin::log",
"waitForAll": true,
"parameters": {
"message": "All branches completed: {{branch-a.output.result}} and {{branch-b.output.result}}"
}
}

How Convergence Works:

  • Steps with "waitForAll": true wait for all their dependencies to complete before executing
  • Dependencies are automatically detected from template variables (e.g., {{branch-a.output}})
  • The step only executes once, even if multiple branches redirect to it
  • Prevents race conditions and ensures all required data is available

Example Fan-Out/Fan-In Workflow:

{
"steps": [
{
"id": "start",
"action": "builtin::log",
"parameters": {"message": "Starting parallel workflow"},
"next": ["branch-a", "branch-b", "branch-c"]
},
{
"id": "step-branch-a",
"action": "builtin::transform",
"parameters": {"rules": {"result": "Data from A"}},
"next": ["convergence-point"]
},
{
"id": "step-branch-b",
"action": "builtin::transform",
"parameters": {"rules": {"result": "Data from B"}},
"next": ["convergence-point"]
},
{
"id": "step-branch-c",
"action": "builtin::delay",
"parameters": {"seconds": 3},
"next": ["convergence-point"]
},
{
"id": "step-convergence-point",
"waitForAll": true,
"action": "builtin::log",
"parameters": {
"message": "All done: A={{branch-a.output.transformed.result}}, B={{branch-b.output.transformed.result}}"
}
}
]
}

Without Convergence:

  • convergence-point would execute 3 times (once per branch)
  • Template variables might be null if referenced branches haven't completed yet

With Convergence:

  • convergence-point waits until all branches (branch-a, branch-b, branch-c) complete
  • Executes only once with all data available
  • Handles timing differences automatically (e.g., branch-c delay)

Workflow Control Actions

Special keywords can control workflow execution flow in both condition statements and onError handlers:

Control Keywords

  • "stop-workflow" - Immediately stops the entire workflow
  • "continue-workflow" - Continues execution (mainly used in error handling)
  • "retry-step" - Retries the current step (error handling only)
  • "retry-workflow" - Restarts the entire workflow from the beginning

For more information, see the Error Handling documentation.

Loop Iteration System

The logic::loop action executes subsequent workflow steps multiple times, once per item (or batch of items):

{
"id": "prepare-items",
"action": "builtin::transform",
"parameters": {
"rules": {
"files": ["file1.txt", "file2.txt", "file3.txt"]
}
},
"next": ["loop-files"]
},
{
"id": "loop-files",
"action": "logic::loop",
"parameters": {
"items": "{{prepare-items.output.transformed.files}}",
"batchSize": 1
},
"next": ["process-file", "validate-file"]
}

How it works:

  • Takes an array of items (required)
  • Optional batchSize parameter (default: 1) to process items in batches
  • Queues all steps in the next field for all iterations upfront
  • Each iteration sets {{loop_item}} and {{loop_index}} context variables before executing
  • Multiple branches execute in parallel - if next contains multiple steps, they run as async branches
  • Within each branch, iterations execute sequentially - iteration N+1 waits for iteration N to complete
  • This ensures reliable execution and API rate limit management within each processing branch
  • Returns {"count": N, "batchSize": X, "iterations": Y} with loop metadata

Parameters:

  • items - Array of items to iterate over (required, must be a list)
  • batchSize - Number of items per iteration (optional, default: 1)

Available context variables during each iteration:

  • {{loop_item}} - Current item being processed (single item if batchSize=1, array if batchSize>1)
  • {{loop_index}} - Current iteration index (0-based)

Important: Loop variables ({{loop_item}} and {{loop_index}}) are cleared once all iterations are processed and synchronized with waitForAll.

Example with batch processing:

{
"id": "loop-batch",
"action": "logic::loop",
"parameters": {
"items": ["item1", "item2", "item3", "item4", "item5"],
"batchSize": 2
},
"next": ["process-batch"]
},
{
"id": "process-batch",
"action": "builtin::log",
"parameters": {
"message": "Processing batch [{{loop_index}}]: {{loop_item}}"
}
}

This will execute process-batch 3 times:

  • Iteration 0: loop_item = ["item1", "item2"], loop_index = 0
  • Iteration 1: loop_item = ["item3", "item4"], loop_index = 1
  • Iteration 2: loop_item = ["item5"], loop_index = 2

Loop Convergence with waitForAll:

Steps with waitForAll: true after a loop will wait for ALL iterations across ALL branches to complete:

{
"id": "loop-process",
"action": "logic::loop",
"parameters": {
"items": ["a", "b", "c"]
},
"next": ["process-item", "validate-item"]
},
{
"id": "process-item",
"action": "builtin::log",
"parameters": {
"message": "Processing: {{loop_item}}"
},
"next": ["convergence"]
},
{
"id": "validate-item",
"action": "builtin::log",
"parameters": {
"message": "Validating: {{loop_item}}"
},
"next": ["convergence"]
},
{
"id": "convergence",
"waitForAll": true,
"action": "builtin::log",
"parameters": {
"message": "All 6 executions complete (3 items x 2 branches)"
}
}

How async branches work with loops:

  • The two branches (process-item and validate-item) execute in parallel
  • Within process-item branch: iteration 0 → iteration 1 → iteration 2 (sequential)
  • Within validate-item branch: iteration 0 → iteration 1 → iteration 2 (sequential)
  • Both branches run independently and may complete at different times
  • The convergence step waits until all 6 executions finish (3 iterations × 2 branches)

Failed Iterations and Error Handling:

When an iteration fails after exhausting its retry limit (see onError.maxRetries):

  • The failed iteration does not queue next steps for that iteration
  • Other iterations in the same branch continue executing normally
  • waitForAll steps will proceed once all iterations complete (whether successful or failed)
  • This ensures the workflow continues even if some items fail processing

Example with 5 files where file 2 fails after 3 retries:

{
"id": "loop-files",
"action": "logic::loop",
"parameters": {
"items": ["file1", "file2", "file3", "file4", "file5"]
},
"next": ["download-file"]
},
{
"id": "download-file",
"action": "addon::download",
"onError": {
"action": "retry-step",
"maxRetries": 3
},
"next": ["process-file"]
}

Result:

  • File 2 iteration stops after exhausting retries (does not queue process-file)
  • Files 1, 3, 4, 5 continue and queue process-file
  • process-file executes 4 times (only for successful downloads)
  • If there's a waitForAll convergence step after process-file, it waits for those 4 executions

Loop Item Validation:

  • If items is null or None, the loop defaults to an empty list and logs a warning
  • If items is not a list, the loop returns an error and stops execution
  • Next steps will not be queued if items is empty