Blocks

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

The loop step in the Inspector 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.