Loop
The loop primitive has two modes: foreach for deterministic batch processing and react for AI agent reasoning. Both share the same type: "loop" ā the mode property determines behavior.
Foreach Mode
Process an array of items with configurable concurrency and failure handling.
{
"type": "loop",
"properties": {
"mode": "foreach",
"items_path": "@input.customers",
"item_variable_name": "customer",
"actions_to_execute": [
{
"type": "gmail_send",
"properties": {
"__oauth_account__": "[email protected]",
"to": "@customer.email",
"subject": "Monthly Statement for {{customer.name}}",
"body": "Dear {{customer.name}},\n\nPlease find your statement attached."
}
}
],
"max_concurrency": 10,
"failure_strategy": "continue_on_error",
"collect_results": true,
"result_key": "emailResults"
}
}
Foreach Properties
| Property | Type | Required | Description |
|---|---|---|---|
mode |
"foreach" |
Yes | Selects foreach mode |
items_path |
@path | Yes | Array of items to iterate over |
item_variable_name |
string | Yes | Variable name for the current item (accessible as @{name}) |
actions_to_execute |
array | Yes | Steps to run for each item ā same structure as workflow actions |
max_concurrency |
number | No | Maximum parallel executions (default: 1) |
failure_strategy |
string | No | "stop_on_error" (default) or "continue_on_error" |
collect_results |
boolean | No | Whether to gather results from all iterations (default: false) |
result_key |
string | No | Context key for collected results |
Item Access
Inside actions_to_execute, reference the current item using the variable name:
{
"item_variable_name": "order",
"actions_to_execute": [
{
"type": "process_payment",
"properties": {
"order_id": "@order.id",
"amount": "@order.total",
"currency": "@order.currency"
}
}
]
}
Failure Strategies
stop_on_error (default): If any item fails, the loop stops and the run fails. Use when all items must succeed.
continue_on_error: Failed items are logged but processing continues. Use for best-effort batch operations like sending notifications.
React Mode
Run an AI agent that reasons step-by-step toward an objective. See Agents for full details.
{
"type": "loop",
"properties": {
"mode": "react",
"objective": "Investigate this expense report. Check policy compliance, verify receipts, recommend approval or rejection.",
"tools": [
{ "type": "action", "name": "lookup_employee" },
{ "type": "action", "name": "check_expense_policy" },
{ "type": "action", "name": "verify_receipt" }
],
"model": "gpt-4",
"max_iterations": 15,
"timeout_ms": 300000,
"temperature": 0.7,
"on_stuck": {
"action": "escalate",
"iterations": 3
},
"include_reasoning_trace": true,
"result_key": "expenseDecision"
}
}
React Properties
| Property | Type | Required | Description |
|---|---|---|---|
mode |
"react" |
Yes | Selects react mode |
objective |
string | Yes | What the agent should accomplish. Supports {{ }} templates |
tools |
array | Yes | Available tools ā typed declarations or strings (action names). See Tool Declarations |
model |
string | No | LLM model to use (default: configured in environment) |
max_iterations |
number | No | Maximum think-act-observe cycles (default: 10) |
timeout_ms |
number | No | Maximum execution time in milliseconds (default: 300000) |
temperature |
number | No | LLM temperature 0ā1 (default: 0.7) |
on_stuck |
object | No | Recovery when agent loops without progress |
on_stuck.iterations |
number | No | Repeated iterations before triggering (default: 3) |
on_stuck.action |
string | No | "fail", "escalate", or "retry_with_hint" |
on_stuck.hint |
string | No | Guidance text for retry_with_hint |
include_reasoning_trace |
boolean | No | Store full reasoning trace (default: true) |
result_key |
string | No | Context key for the agent's final answer |
When to Use Which Mode
| Use Foreach When | Use React When |
|---|---|
| You know exactly what to do with each item | The task requires judgment or reasoning |
| Processing is deterministic | The approach depends on intermediate results |
| Items are independent of each other | The agent needs to decide what to do next |
| You need parallel processing | You need natural language understanding |
Nested Loops: Foreach Containing React
A common pattern: iterate over a list of items, and for each item, run an AI agent:
{
"type": "loop",
"properties": {
"mode": "foreach",
"items_path": "@unmatched_invoices",
"item_variable_name": "exception",
"actions_to_execute": [
{
"type": "loop",
"properties": {
"mode": "react",
"objective": "Investigate why invoice {{exception.invoice_id}} for ${{exception.amount}} has no matching payment. Check vendor history, look for partial payments, and recommend next steps.",
"tools": [
{ "type": "action", "name": "search_payments" },
{ "type": "action", "name": "lookup_vendor" }
],
"max_iterations": 8,
"result_key": "investigation"
}
}
],
"max_concurrency": 3,
"failure_strategy": "continue_on_error",
"collect_results": true,
"result_key": "all_investigations"
}
}
This processes each exception in parallel (up to 3 at a time), with an AI agent investigating each one independently. Results are collected into @all_investigations.
Concurrency for nested react loops. When running agents in parallel via foreach, keep max_concurrency moderate (3ā5). Each agent makes LLM API calls, so high concurrency can hit rate limits. Use continue_on_error to ensure one failed investigation doesn't block the others.