Loops
Most flows are a fixed chain — do A, then B, then C. A Loop node breaks that: it repeats a subgraph once per element of a list, once per number in a range, or a fixed number of times. This is what turns "check out this one item" into "check out every item in the cart."
The three sources
Section titled “The three sources”Set the loop's source in its inspector:
| Source | What it does |
|---|---|
| Expression | A {{...}} reference (or a literal JSON array) that must resolve to an array — e.g. {{cart.body.items}}, or a literal [1, 2, 3]. One iteration per element. |
| Count | Runs a fixed number of iterations, 0 to count - 1. {{loopName.item}} is just the index as a string — use this when you don't need a real data element, just "do this N times." |
| Range | An inclusive numeric sequence from a start to an end value, stepping by a configurable amount. Direction (counting up or down) is automatic — set start 10, end 1, and it counts down. |
If an expression source doesn't resolve, or resolves to something that isn't a JSON array, the loop node fails and its done branch (and everything past it) is skipped — same failure shape as any other node.
What runs each iteration — the loop body
Section titled “What runs each iteration — the loop body”Connect the loop's body out-port to the node (or chain of nodes) you want repeated. Everything reachable from body is owned by the loop and runs once per iteration; everything connected to the loop's other out-port, done, runs exactly once, after every iteration finishes.
Each iteration gets a fresh scope — a request node inside the body sees its own iteration's values and does not leak into the next iteration.
Loop variables
Section titled “Loop variables”Inside the body, reference the current iteration with:
{{loopName.item}}— the whole element (a JSON object becomes its string form; for Count/Range sources this is just the index).{{loopName.item.path}}— a field of an object element, using the same dotted-path syntax as an extraction (e.g.{{loopName.item.id}}).{{loopName.index}}— the 0-based iteration index.{{loopName.count}}— the total number of iterations.
Aggregates after the loop
Section titled “Aggregates after the loop”Downstream of done, reference the loop's overall results:
{{loopName.results}}— a JSON array with one entry per iteration.{{loopName.passed}}/{{loopName.failed}}— counts.
Failure handling
Section titled “Failure handling”continueOnErroroff (default) — the first failing iteration stops the loop; the loop node itself is marked failed, anddoneis skipped.continueOnErroron — every iteration runs regardless of failures; the loop node passes, and{{loopName.failed}}tells you how many didn't.
Runaway guard: maxIterations
Section titled “Runaway guard: maxIterations”Every loop has a maxIterations cap (default 1000) that bounds how many elements it will ever process, even if the source array is longer or a Count/Range source would generate more. The excess isn't silently dropped — the run report shows the count was capped.
Concurrency and per-iteration delay
Section titled “Concurrency and per-iteration delay”By default iterations run one at a time, in order. Raise Concurrency (up to 16) to run that many iterations in parallel — useful for exercising an endpoint under load or just finishing a long list faster. Regardless of concurrency, iterations always start in index order and their results are always reported in index order, so {{loopName.results}} and the run report stay predictable.
Iteration delay (ms) adds a pause between iteration starts — a wait between sequential iterations, or a stagger between parallel launches. It's never applied before the very first iteration.
Example: check out every item in a cart
Section titled “Example: check out every item in a cart”- A Login request node extracts
token. - A Get Cart request node extracts
items(body pathdata.items, an array of{id, qty}objects). - A Loop node, source = Expression,
{{getCart.outputs.items}}. - The loop's
bodyport connects to a Checkout Item request node, sendingPOST /checkoutwith body{ "itemId": "{{cartLoop.item.id}}", "qty": "{{cartLoop.item.qty}}" }and anAuthorization: Bearer {{login.outputs.token}}header. It assertsstatus equals 200. - The loop's
doneport connects to a final Assert All Checked Out step, which could be a Condition node checking{{cartLoop.failed}} equals 0.
Run it, and the report expands the loop node into one row per cart item, each showing its own status and response.