Loop
The loop step: run a body subgraph once per item of a list, then fire done once with the collected results.
Run a body of steps once per item of an upstream list, then continue once with everything collected. Two output ports do the work:
- iterate — the body subgraph wires here; it runs N times, once per item.
- done — fires once after every iteration finishes, with
the aggregate:
{ "items": [each iteration's result], "total": N, "failed": K }.
Config
One field: a path to the list to iterate.
Iterate over is a path to a list the upstream step already
emits — payload["items"], payload["data"]["posts"] — not a
transform. If the data isn't already a list (stringified JSON,
needs filtering), fix it upstream with
reshape or run code and
point the path at that.
Inside the body, each pass sees payload.item (the element) and
payload.index alongside the upstream payload —
{{ payload.item.title }} in templates.
Shape the loop for cost
Only work that genuinely must run per item belongs off iterate —
typically the per-item fetch. Anything that can consume the whole
collection belongs off done: one AI call summarizing 30 items is
~30× cheaper than 30 calls, and one digest reads better than 30
messages. The default shape:
loop ─ iterate → fetch detail
└ done → AI summarize all → send digest
Leave done unwired only when the outcome is genuinely per-item
(reply to each email individually).
Gotchas
- Testing a loop from the Inspector runs the body once with a sample item — a guard against firing 50 sends while building. Real runs iterate the full list.
- Iterations run sequentially, and loops don't nest (the compiler rejects a loop inside a loop's body).
- Each iteration records its own step execution in the run history, grouped per loop.