The Parallel Window block is a container that processes a collection, a fixed number of repetitions, or named branches with a bounded number of items active at a time. A new item starts when one finishes, and the results keep the input order.
Use a Parallel Window when a collection is too large for the Parallel block (more than 20 items). Also use it when the body calls an API that cannot take every item at once. It accepts up to 1,000 items and runs between 1 and 20 of them concurrently.
Configuration Options
Run For
Choose the source of work:
| Mode | When to use it |
|---|---|
| Collection | Run the same body for each input, including calling the same child workflow with different inputs. |
| Count | Run the same body a fixed number of times without building an input array. Enter an integer from 0 to 1,000. |
| Branches | Run different workflows or chains once each. Add named branches, assign every body block to exactly one branch, and select a result for each branch. |
In Count mode, <parallel.currentItem> and <parallel.index> are the zero-based repetition index. In Branches mode, <parallel.currentItem> contains the branch's id and name. Results follow the configured order, and branch results include a stable branchId.
Branches can each contain a Workflow block plus their own follow-up blocks. Keep connections and references within a branch. Assign a Loop container to a branch to include all of its descendants. The canvas outlines each branch; use Move up in the editor to change its order. Empty collections, a count of zero, and an empty branch list with no body blocks produce empty results.
In Count mode, Number of runs saves when you leave the field or press Enter. You can clear the field while replacing the number. An empty, fractional, negative, or out-of-range value shows an error and leaves the saved count unchanged. Press Escape to restore the saved count.
Items
In Collection mode, the collection to process. Enter a JSON array directly or a single reference that resolves to an array, such as <api.data> or <start.input.leads>. Anything else fails the block before any item runs with parallel_window items must be a JSON array or an array reference or parallel_window items must resolve to an array.
The collection may hold up to 1,000 items. With a valid concurrency limit, an empty collection skips the body. The block then outputs an empty results array and total: 0, and the workflow continues.
Maximum Active Items
How many items run at the same time, from 1 to 20 (default 5). When an item finishes, the next queued item starts in its place, so the number of in-flight items never exceeds this value.
Enter a fixed integer or one upstream output reference, such as <start.concurrency> or <settings.result.concurrency>. Connect the source block upstream of the window so its output is available before any item starts. The reference must identify one unambiguous block. References to the window itself, body blocks, downstream or unconnected blocks, workflow variables, and expressions such as <start.concurrency> + 1 are rejected.
In the editor, a valid value saves when you leave the field or press Enter. Escape restores the saved value. An unfinished or invalid entry shows an error and does not replace the saved limit.
The resolved output must be a number and an integer from 1 to 20. An output that is still a string such as "4", a missing value, null, a boolean, a fraction, or an out-of-range number fails before any item starts, including for an empty collection. An invalid reference or value never falls back to 5. Unhandled item errors applies only after admission; choosing Record failure and continue does not make an invalid limit acceptable.
Each window captures its limit once and keeps that number through refills and checkpoint recovery. Changing the source output does not resize an active window. The limit applies to this window in one workflow run; simultaneous runs each have their own limit. It does not impose a requests-per-second limit on an API.
Choosing the Limit Through a Workflow Input
- In the Start block's Input Format, add an
itemsfield of type Array and aconcurrencyfield of type Number. Set a default such as5onconcurrencyif callers may omit it. - Connect Start to the Parallel Window. Set Items to
<start.items>and Maximum active items to<start.concurrency>. - Supply the inputs when running the workflow. For example, this input admits at most two items at a time:
{
"items": [{ "id": "a" }, { "id": "b" }, { "id": "c" }],
"concurrency": 2
}Use the actual name of your Start block in the references. Start converts Number inputs before publishing its output, so a caller's numeric string can become a valid number there. The window itself does not coerce strings. For a calculated limit, compute and validate the number in an upstream block and reference its output, for example <settings.result.concurrency>.
Item Result
Exactly one <block.field> reference to a block inside the window, for example <finish.result>. Its value for each item becomes that item's value in the window's results.
The reference must point at a body block. If that block did not run for an item (for example a Condition routed around it), the item fails with Item result block did not execute. If the referenced field is missing, the item fails with Item result did not resolve. A field that is explicitly null is kept as null.
Unhandled Item Errors
What happens when a block inside an item fails and no error path handles it:
- Stop the window (default): the first unhandled error fails the Parallel Window block and the workflow run.
- Record failure and continue: the error becomes a
rejectedcell for that item. The rest of that item's body is skipped, and the remaining items still run.
How to Use Parallel Windows
Creating a Parallel Window
- Drag a Parallel Window block from the toolbar onto your canvas
- Choose Run for: set Items for Collection, Number of runs for Count, or add named branches for Branches
- Drag the blocks that should run for each item inside the container
- Connect the blocks as needed
- Set Item result to the output of the last block in the body, for example
<finish.result>. In Branches mode, assign every body block to one branch and set each Branch result instead - Choose Maximum active items
- Choose how to treat Unhandled item errors
Referencing Parallel Window Data
Inside the window, use <parallel.> references to access the current item:
<parallel.index>: Position of this item in the collection (0-based)<parallel.currentItem>: The item being processed<parallel.items>: The full collection
// Inside a Function block within the window
const idx = <parallel.index>; // 0, 1, 2, ...
const item = <parallel.currentItem>; // This itemBody blocks can also reference blocks that ran before the window and body blocks earlier in the same item. They cannot read the window's own results, total, completed, active, or queued. Those values are not final until every item has settled.
Example Use Cases
Rate-Limited Enrichment - Enrich hundreds of records without exceeding an API's concurrency limit
API (Fetch 500 leads) → Parallel Window (5 active) → API (Enrich) → Function (Normalize) → Table (Insert results)Bulk Notifications - Send one message per row while keeping a handful of requests in flight
Table (Query rows) → Parallel Window (10 active) → Slack (Post) → Function (Summarize failures)Advanced Features
Body Restrictions
SteelEngine validates a Parallel Window before the run starts. If the body breaks a rule, it fails the run with parallel_window <id>: <reason>. Inside the window you can use sequential Loop containers (including loops within loops), Function, API, Condition, Router, JSON Mapper, Workflow, and integration blocks (third-party service blocks such as Gmail or Slack). Other block types are rejected, including Agent, Evaluator, Knowledge, Table, Variables, Wait, Human in the Loop, and Response. Document generation is also rejected.
The following are also rejected:
- Unsupported nesting: a Parallel or Parallel Window inside the body, or a Parallel Window placed inside a Loop or Parallel. Sequential Loop containers inside a window are supported; the same block restrictions apply to every descendant
- Connections that cross the boundary: every connection from a body block must stay inside the window. Body blocks can only be entered from the window
- Disabled body blocks: enable or delete them
- References out of the body: a block outside the window that references a body block's output
- Concurrent variable writers: a Variables block anywhere in the workflow that does not run strictly before or strictly after the window. Workflow variables are shared across the whole run, so they cannot be written while items run concurrently
Child Workflows Inside a Window
Put a Workflow block inside the body and map its inputs from the current item, index, or upstream outputs. Each child finishes and records its completion independently. Follow-up blocks in the same item can then run immediately. The item keeps its slot until its entire chain finishes, and the parent waits for all items before exposing the final aggregate.
For example, if 19 children finish in 3 seconds and one takes 19 seconds, the fast children can log their completion and run their reporting blocks at 3 seconds. Queued items can start as slots become free. The parent still finishes after the last item settles. Place reporting inside the body for per-child reporting, or after the window for a final summary.
For 20 different child calls, select Branches, add one branch per call, and assign each call and its reporting blocks to that branch. For one child called with 20 inputs, use Collection. Use Count when only the repetition index is needed.
Child workflows retain their own variables, Response, loops, and ordinary Parallel containers. Response finishes only that child. Within one item, child blocks execute one at a time to preserve the window's concurrency limit. With TEMPORAL_PARALLEL_WINDOW_CHAINS enabled, a child's consecutive blocks run back to back in one step instead of waiting for a window round between blocks; results, logs and order are the same. With TEMPORAL_PARALLEL_WINDOW_ITEM_STATE enabled, each item's steps are applied to that item's own state instead of to every item in flight, so a window with more items in flight or larger outputs advances faster; results, logs and order are again the same. Children containing Wait, Human in the Loop, document generation, or another Parallel Window are rejected before that child's business blocks run. The same restrictions apply to nested child calls.
These remain inline children of the parent, so cancelling the parent cancels its active work. They do not become detached runs. Existing workspace, draft/deployed version, and nesting rules still apply. An unhandled child failure follows the window's error setting; a connected error path can recover within the item.
Loops Inside a Window
Each active item owns its complete loop state. <parallel.currentItem> identifies the window item and <loop.currentItem> identifies the current element of the innermost loop. Named loop references can access enclosing loop context. Another item can advance while this item processes its next iteration.
A loop's collection is evaluated before that loop starts. Use the window item, an upstream output, or an enclosing loop's context for the collection. A loop directly inside the window cannot use <loop.items> as its own collection. While and Do While conditions can read their current loop's context, the window item, and outputs from earlier blocks in the same item. For example, <loop.index> < <parallel.currentItem.limit> uses each item's own limit. Blocks inside a loop cannot read that enclosing loop's result or results until it has completed.
The item retains its concurrency slot through all loop iterations and subsequent blocks. Place a final Function block after the loops and select that block's output as Item result. A block inside a loop cannot be selected as the item result because it can execute multiple times. Read a completed loop through its aggregate output instead of referencing an individual nested block from outside that loop.
With TEMPORAL_PARALLEL_WINDOW_CHAINS and TEMPORAL_PARALLEL_WINDOW_CHAIN_LOOP_CONTROL enabled, an item runs through its loops' starts, iterations and ends in one step instead of waiting for a window round at each of them, so items with loops finish sooner. Each loop decision, including a While or Do While condition, is evaluated once and reused; results, logs and order are the same.
An empty loop still proceeds to the item's remaining blocks. An unhandled error inside a loop follows the window's error policy; connect explicit error paths when recovery should continue within the item. Record failure and continue skips the remainder of the failed item, including any unexecuted reporting block.
All Workers must support nested window loops before deploying such a workflow. Existing ordinary Parallel blocks and flat Parallel Windows retain their scheduling behaviour.
Failure Handling
With Stop the window, the first unhandled error in any item fails the block and the workflow. Queued items never start. With Record failure and continue, the failed item is recorded as { index, status: 'rejected', error }. The remaining blocks in that item are skipped, and the other items keep running. The window still completes, and downstream blocks decide what to do with the rejected cells. An Item result that cannot be resolved counts as an unhandled error and follows the same setting.
An error path connected to a body block handles the error inside the item. Only unhandled errors reach this setting.
Limitations
The collection is limited to 1,000 items and the window to 20 active items. Larger inputs fail the block before any item runs. Nested windows are not supported.
Parallel Window vs Parallel
| Feature | Parallel Window | Parallel |
|---|---|---|
| Work sources | Collection, Count, or named Branches | Count or Collection |
| Collection size | Up to 1,000 items | Up to 20 instances |
| Concurrency | 1-20 items active, refilled as items finish | All instances at once |
| Body | Function, API, Condition, Router, JSON Mapper, integration blocks, Workflow calls and sequential Loops | Any blocks, including nested Loops and Parallels |
| Results | Flat array, one settled cell per item | One array of settled cells per instance |
| Failures | Stop the window or record and continue | Always recorded as rejected cells |
Inputs and Outputs
Run for: Collection, Count, or Branches. In Collection mode, Items is a JSON array or a reference that resolves to an array (up to 1,000 items). In Count mode, Number of runs is an integer from 0 to 1,000. In Branches mode, assign body blocks to named branches that each run once
Maximum active items: Integer from 1 to 20 (default 5), or one upstream output reference resolving to that integer
Item result: One
<block.field>reference to a body block. In Branches mode, set a Branch result for each branch insteadUnhandled item errors: 'Stop the window' or 'Record failure and continue'
Best Practices
- Size the window to the slowest dependency: Match Maximum active items to the concurrency the body's API tolerates rather than the maximum of 20
- End the body with one block: Route every path in the item to a final Function block. Point Item result at it, so the result resolves for every item
- Record and continue for bulk work: Choose Record failure and continue when one bad item should not discard the rest. Inspect the rejected cells downstream
- Keep parent variable writes outside: Read workflow variables inside the body if you need them, but place parent Variables blocks before or after the window. Child workflows may write their own local variables