This pattern loads a different module for each step in a dynamic list. A Loop Initializer module contains a Data Workflow component that reads a list of module IDs, resolves the current one, and checks whether more remain. An Exclusive Gateway reads the result and routes the workflow back or ends it.
Apply this pattern when the number of modules is not known at design time. For example, use it when each participant in a list receives a different form.
You can run the entire workflow without end-user interaction by placing all Task nodes in the Automated swimlane instead of the Authenticated swimlane. Add a Timer Start node to the workflow on a set schedule.
How the Loop Works
Each iteration follows four steps: initialize, present, check, and repeat.
The Loop Initializer task runs
dwfLoopon first arrival (New Submission trigger). It also runs on each return visit (Edit trigger).dwfLoopreadsmoduleIdsat the currentcounterposition, writes the result tocurrentModuleId, incrementscounter, and computeshasNext.The Exclusive Gateway checks
hasNext. If it equalsyes, the workflow routes back to the Loop Initializer for the next iteration. If it equalsno, the workflow ends.
What You Need
Before you begin, ensure you have the following:
A workflow with an Authenticated swimlane
A list of published module IDs to loop through
A module to serve as the Loop Initializer, built following the instructions in the next section
Configure the Loop Initializer Module
Create the Supporting Fields
Create a new module to serve as the Loop Initializer. Add four Hidden components to it. These fields carry state across iterations.
Property ID | Default Value | Purpose |
|---|---|---|
| — | Stores the list of module IDs to loop through (accepts a JSON array or a JSON array string) |
|
| Tracks the current position in the list |
| — | Holds the resolved module ID for the current iteration |
|
| Signals whether more modules remain. Set the default to |
The
moduleIdsfield accepts either a native array or a JSON-encoded string like["id1","id2","id3"]. The Data Workflow handles both formats automatically.
Configure dwfLoop
Add a Data Workflow component with Property ID dwfLoop. Build the following operators in order. Each group builds on the previous one.
Detect and normalize the module list
The following operators check whether moduleIds arrived as a JSON string. If it did, they parse it. Either way, both paths merge into a clean array for the next group.
Add an operator. This operator reads the
moduleIdsfield at the start of each pass. Its output connects to thefirstCharGet operator and theisStringBranchBranch Start operator.Configure the operator as follows:
Setting
Value
Component
moduleIdsRequired
Yes
Source
Default
Add a second Input operator. This operator reads the current counter value. Its output connects to the
normalizeCounterFormula operator.Configure the operator as follows:
Setting
Value
Component
counterRequired
No
Source
Default
Add a Get operator. This retrieves the first character of
moduleIdsso the next step can detect whether it arrived as a JSON string.Configure the operator as follows:
Setting
Value
Label
firstChar
Path
[0]Connect the output port (right) of the
moduleIdsInput operator to the input port (left) of thefirstCharGet operator.Add a Formula operator. If the first character is
[,moduleIdsis a JSON string. The formula returnsyesornofor the Branch Start.Configure the operator as follows:
Setting
Value
Label
isString
Formula/Expression
=IF(A="[","yes","no")Preserve Argument Type
Default (Unchecked)
Connect the output port (right) of the
firstCharGet operator to the input port (left) of theisStringFormula operator.Add a Branch Start operator. When the condition is true (the value is a JSON string), data flows through the upper output port (top-right) to the JSON Parse operator. When false, data flows through the lower output port (bottom-right) directly to Branch End.
Configure the operator as follows:
Setting
Value
Label
isStringBranch
Condition
_arg="yes"Do Not Sanitize
Checked
Preserve Argument Type
Checked
Connect the output port (right) of the
moduleIdsInput operator to the input port (left) of theisStringBranchBranch Start operator. Connect the output port (right) of theisStringFormula operator to the argument port (top) of theisStringBranchBranch Start operator.Add a JSON Parse operator. This converts the JSON string into a native array.
Configure the operator as follows:
Setting
Value
Label
parseList
Connect the upper output port (top-right) of the
isStringBranchBranch Start operator to the input port (left) of theparseListJSON Parse operator.Add a Branch End operator. This merges both paths back into a single array output regardless of the original format.
Configure the operator as follows:
Setting
Value
Label
mergeList
Connect the output port (right) of the
parseListJSON Parse operator to the input port (left) of themergeListBranch End operator. Connect the lower output port (bottom-right) of theisStringBranchBranch Start operator to the second input port (left) of themergeListBranch End operator.
Resolve the current module and advance the counter
The following operators count the total modules, resolve the module ID at the current counter position, increment the counter, and compute whether more modules remain.
Add a Size operator. This counts the total number of module IDs in the list. Its output connects to the
hasNextFormula operator.Configure the operator as follows:
Setting
Value
Label
size
Connect the output port (right) of the
mergeListBranch End operator to the input port (left) of thesizeSize operator.Add a Formula operator. On the first pass, the counter field is empty. This formula converts an empty value to
0so the first module is read at index0. Its output connects to thecurrentModuleIdGet operator and theincrementFormula operator.Configure the operator as follows:
Setting
Value
Label
normalizeCounter
Formula/Expression
=IF(A="",0,A)Preserve Argument Type
Default (Unchecked)
Connect the output port (right) of the
counterInput operator to the input port (left) of thenormalizeCounterFormula operator.Add a Get operator. The Get operator uses the normalized counter value as the index and retrieves the matching module ID from the array.
Configure the operator as follows:
Setting
Value
Label
currentModuleId
Path
_argConnect the output port (right) of the
mergeListBranch End operator to the input port (left) of thecurrentModuleIdGet operator. Connect the output port (right) of thenormalizeCounterFormula operator to the argument port (top) of thecurrentModuleIdGet operator.Add a Formula operator. This adds 1 to the current counter so the next iteration reads the following module in the list.
Configure the operator as follows:
Setting
Value
Label
increment
Formula/Expression
=SUM(A,1)Preserve Argument Type
Default (Unchecked)
Connect the output port (right) of the
normalizeCounterFormula operator to the input port (left) of theincrementFormula operator.Add a Formula operator. If the incremented counter is less than the total module count, more modules remain and the formula returns
yes. Otherwise it returnsno.Configure the operator as follows:
Setting
Value
Label
hasNext
Formula/Expression
=IF(A<_arg,"yes","no")Preserve Argument Type
Default (Unchecked)
Connect the output port (right) of the
incrementFormula operator to the input port (left) of thehasNextFormula operator. Connect the output port (right) of thesizeSize operator to the argument port (top) of thehasNextFormula operator.
Configure the Outputs
Add three Output operators to write the results back to the module's fields.
Add an Output operator. This writes the resolved module ID to the
currentModuleIdfield.Configure the operator as follows:
Setting
Value
Component
currentModuleIdAction
valueConnect the output port (right) of the
currentModuleIdGet operator to the input port (left) of this operator.Add an Output operator. This writes the incremented counter back to the
counterfield so the next pass advances to the correct position.Configure the operator as follows:
Setting
Value
Component
counterAction
valueConnect the output port (right) of the
incrementFormula operator to the input port (left) of this Output operator.Add an Output operator. This writes
yesornoto thehasNextfield. The Exclusive Gateway reads this field to determine whether to loop or end.Configure the operator as follows:
Setting
Value
Component
hasNextAction
valueConnect the output port (right) of the
hasNextFormula operator to the input port (left) of this Output operator.
The following image displays an example of a complete dwfLoop Data Workflow:

Configure the Initializers
The Loop Initializer module needs two Initializer components to trigger dwfLoop at the right moments.
New Submission Initializer
Add an Initializer component with Property ID init. Configure the following settings:
Setting | Value |
|---|---|
Trigger Type | New Submission |
Set the output to trigger dwfLoop. This fires the first time an end-user reaches the Loop Initializer task, resolving the first module ID.
Edit Initializer
Add a second Initializer component with Property ID initEdit. Configure the following settings:
Setting | Value |
|---|---|
Trigger Type | Edit |
Set the output to trigger dwfLoop. This fires each time the workflow returns to the Loop Initializer task after a loop iteration. Because the submission already exists at that point, the task renders in edit mode and triggers this Initializer instead of the New Submission one.
The Edit Initializer is what advances the loop on each return visit. Without it,
dwfLooponly runs once and the counter never advances.
Configure the Workflow
Add the Data Setup Task
Add a Task node in the Authenticated swimlane to collect or populate moduleIds before the loop begins. Configure the following settings:
Setting | Value |
|---|---|
Screen Type | Static |
Module | Select the module that captures or populates |
This task runs once before the loop. It gives end-users or an upstream process a chance to define the list of module IDs that dwfLoop will iterate through.
Add the Loop Initializer Task
Add a Task node in the Authenticated swimlane for the Loop Initializer module. Configure the following settings:
Setting | Value |
|---|---|
Screen Type | Static |
Module | Select the Loop Initializer module you built in the previous section |
Save Data | ON |
Validate Data | ON |
This task runs dwfLoop on first arrival (New Submission) and on each return visit (Edit). The module runs the Data Workflow silently and writes currentModuleId, counter, and hasNext to the submission. No visible screen is shown to the end-user.
Configure the Exclusive Gateway
Add an Exclusive Gateway after the Loop Initializer task. Configure the following output links:
Loop (continue): Set Condition to hasNext='yes' and connect this link back to the Loop Initializer task.
End path (stop): Set Condition to hasNext='no' and connect this link to your completion task or End Event.
The gateway evaluates the hasNext field that dwfLoop wrote on the most recent pass. A value of yes routes the workflow back through the loop. A value of no proceeds to the end.
Connect the Workflow
Wire the nodes in the following order:
Start Event → Data Setup Task
Data Setup Task → Loop Initializer Task
Loop Initializer Task → Exclusive Gateway
Exclusive Gateway (hasNext='yes') → Loop Initializer Task
Exclusive Gateway (hasNext='no') → End Event
The following image displays an example of a complete module looping workflow:

Test the Workflow
Open the workflow in Express View to verify the loop. On the first visit, the Loop Initializer task runs dwfLoop and resolves the first module ID. After each module completes, the Exclusive Gateway reads the hasNext field. If the value is yes, the gateway routes back to the Loop Initializer for the next iteration. When the final module is processed, dwfLoop sets hasNext to no and the gateway routes the workflow to the End Event or completion task.
Changelog
Date | Change |
|---|---|
2026-09-18 | Added Test the Workflow section. Removed Processing Task step — gateway routes directly to Loop Initializer. Added autonomous workflow note. Updated Mermaid diagram and workflow screenshot. |
2026-09-18 | Rewrote Steps 1 through 8 based on verified working implementation. Corrected DWF operator sequence (Branch Start/End replaces Decision; JSON Parse handles string moduleIds; counter normalized before use; hasNext computed by Formula). Removed dwfOperation and initRepeatLoop (replaced by Edit Initializer). Corrected Exclusive Gateway condition syntax to hasNext='yes'/'no'. |
2026-09-17 | Initial publication. |