FormWork documentation

For Each Loops

Run workflow steps once for every item in a list.

The For Each Loop step evaluates a list reference and runs its loop body once for every item. After the last item, execution continues through After Loop.

Build a Loop

  1. Add For Each Loop.
  2. Use the reference picker to select the list in Items.
  3. Connect Loop Body to the first step that should run for each item.
  4. Connect the loop-body steps in their execution order.
  5. Connect After Loop to the step that should run once when iteration finishes.
For Each
├─ Loop Body  → Step A → Step B
└─ After Loop → Next step

An empty list skips the loop body and continues through After Loop.

List Sources

Examples include:

entry:current.answers.selected_categories
entry:current.answers.line_items
data_table:<table-id>.rows[cells.active=true]
workflow:current.steps.build_items.outputs.items

A repeater’s top-level answer is a list of instance keys. Its child values remain in flat answer paths, so use the current iteration key to address a child:

entry:current.answers.line_items[{iteration:for_each_line.key}].quantity

Read the Current Item

References inside the body are rooted at the For Each step ID:

iteration:<for-each-step-id>.index
iteration:<for-each-step-id>.key
iteration:<for-each-step-id>.item
iteration:<for-each-step-id>.item.<field>
ReferenceMeaning
indexZero-based position in the list
keyCurrent item ID or repeater instance key
itemCurrent scalar, object, or row

For a data table row object, the reference picker can navigate from the current item to its available row and cell values. For a simple array, item is the scalar value itself.

Read a Body Step’s Output

Loop-body step outputs are scoped to the current iteration:

iteration:<for-each-step-id>.steps.<body-step-id>.outputs.<output-id>

For example, if calculate_line defines a line_total calculation:

iteration:for_each_line.steps.calculate_line.outputs.line_total

Use that reference only in later steps within the same loop iteration.

After the Loop

The For Each step records iteration count in the workflow run log, but it does not publish an aggregate iter_outputs value for downstream steps. Loop-body outputs are not a collected array after the loop.

If a result is needed after the loop, write each durable result to an entry or row during the body, then read the updated data after After Loop. When possible, calculate an aggregate directly from the original collection instead of collecting per-iteration workflow outputs.

Nested Loops

The workflow editor does not allow a For Each step inside another For Each loop. Flatten the source list first or split multi-level processing into separate workflows.

Failure Behavior

If a step in the loop body fails, the workflow run fails; FormWork does not silently continue with the next item. An Extension Action can use its error path when the HTTP request completes with a non-2xx status, but network, configuration, and reference errors still fail the run.

Example: Update Repeater Line Totals

For Each: entry:current.answers.line_items
└─ Loop Body
   ├─ Calculation step ID: calculate_line
   │  line_total = quantity × unit price
   └─ Update Entry/Row
      target: entry:current.answers.line_items[{iteration:for_each_line.key}].line_total
      value: iteration:for_each_line.steps.calculate_line.outputs.line_total

Use the reference and mapping pickers to build the actual formula, target, and value. The displayed labels can differ, but the reference strings use the stable step and field IDs.

Good Practices

  • Test with an empty list, one item, and several items.
  • Keep the body small because its steps run once per item.
  • Avoid one external API request per item when the service offers a batch action.
  • Write results that must survive the current iteration back to an entry or row.
  • Inspect the run log to see the recorded iterations and the failing body step.