Under the Hood (Developers)
The inner workings of the For Each node — collections, per-item execution in batches, loop-scoped flow tags, result aggregation, and the partial status.
Loops look simple on the canvas — a For Each node runs its branch once per item in a list. Underneath there’s more going on: where the list comes from, how the engine runs thousands of items without timing out, how each item’s data reaches the nodes inside, and what “partial” really means. This page is the under-the-hood tour. For the plain how-to, see Processing Lists.

A collection is the list a loop iterates. It comes in two shapes, and the For Each node handles both:
Top-level steps normally execute as separate background jobs. For Each is different: instead of dispatching a job for every body step and item, the engine runs the body inline, in batches, within the iterator’s job.
When the For Each node runs, it returns a small descriptor naming its two branches (the per-item body and the after-loop branch) and its source (a saved collection or a resolved array). The engine recognizes that descriptor and drives the iteration itself:
That batching is why a loop over a huge list finishes reliably instead of blowing the request’s time limit — it does as much as fits, saves its place, and continues.
sequenceDiagram
participant Q as Background queue
participant E as Engine
participant L as For Each node
participant S as Collection or array
participant B as Loop body
Q->>E: run the For Each node
E->>L: execute
L->>S: resolve the list (saved refs or upstream array)
L-->>E: descriptor (per-item branch, after branch, source)
loop items, until the batch budget is hit
E->>B: run the body for this item (item hydrated and in scope)
B-->>E: ok or failed
E->>E: update counters, save progress on the loop node
end
alt items remain
E->>Q: record a continue-loop job, resume later
else stopped on an error
E->>E: mark the loop failed, skip the after branch
else all items done
E->>E: run the after branch once
end
The canvas makes the two execution paths visible: per-item work and a separate continuation.


The For Each node has two output ports:
Wiring a node into both branches is a validation error — the two are meant to be distinct.
Inside the loop body, two families of flow tag become available, and a third behaves differently:
The current item — {{ item }} for the whole item, and {{ item.<key> }} for a field (for a Gravity Forms entry, that includes the entry’s field ids, e.g. {{ item.2 }}). The item is set fresh before each iteration and cleared right after, so one item’s data can never leak into the next.
Loop position — from the loop node: the current index (0-based), number (1-based), the total count, and how many remaining. Handy for “item 3 of 50” messaging or throttling.
Other nodes — a plain {{ "Node Name".field }} still works inside the body. Pointing it at another body node gives you that node’s result for the item being processed right now. Pointing it at a node from before the loop gives you that node’s single result, same as anywhere else.
In the builder’s flow tag picker, the item’s fields show up before you’ve run anything when the source declares them (a form’s labeled fields), and get enriched with real keys after a test run samples an actual item. Each item’s body output is stored under that iteration only, so one item can never read another item’s results.
partial statusBy default, what the body produces stays iteration-scoped and isn’t surfaced — a loop over 500 items doesn’t dump 500 outputs into your run. When you do want the results, turn on Include this step’s output in the For Each result on a body node. Those get rolled up into the loop’s results, keyed by the body node’s name, and you select them downstream like:
{{ "Loop".results["Call shipping API"] }}The loop reports a status on its output that sums up how it went:
| Loop output status | Meaning |
|---|---|
complete |
Every item succeeded. |
partial |
Some items failed, but the loop was set to keep going, so it finished the rest. |
halted |
The loop was set to stop on the first failure, and one failed. |
pending |
Still mid-flight (between batches). |
On-error policy is a node setting: continue (the default — count the failure, keep going, end up partial) or stop (first failure halts the loop and fails the run). An item “fails” if its saved reference can’t be loaded, or if a body node returns an error — both routed through the same path.
The loop persists live counters (processed / succeeded / failed, plus a rolling average item duration) onto the node, which is how the Runs mode shows loop progress without loading every body output. Body nodes in the overlay show an iteration count; top-level nodes show an attempt count.
flowcrafter_loop_max_items_per_callback, flowcrafter_loop_time_budget_seconds) for extension authors.