Skip to main content

Split / Join 🔀🔗

Splits workflow execution into parallel paths (Split) or waits for multiple paths to converge (Join).

Node types: split, join Category: Flow Control Actor: parallelGateway, inclusiveGateway, exclusiveGateway (1 thread each)


Description​

Split and Join nodes control parallel execution patterns:

  • Split — divides one execution path into multiple simultaneous paths
  • Join — waits for the required number of paths to complete before continuing

Both nodes support three modes that determine how branching and convergence work.


Properties​

PropertyTypeRequiredDescription
modeselectYesexclusive, inclusive, or parallel

Modes​

Exclusive (XOR)​

Routes to exactly one outgoing path based on conditions. Only the first matching path is taken.

Split behavior: Evaluates conditions on outgoing connections. Takes the first trueFlow path found.

Join behavior: Continues when any one incoming path completes.

Use when: You want a conditional branch where only one path executes (similar to If/Else but with more than two options).

[Split: mode=exclusive]
↓ trueFlow (${tier == 'gold'}) → [Premium Handler]
↓ trueFlow (${tier == 'silver'}) → [Standard Handler]
↓ sequenceFlow (default) → [Basic Handler]

Inclusive (OR)​

Routes to one or more paths where conditions are true.

Split behavior: Evaluates all conditions. Takes all paths where the condition is true (potentially multiple).

Join behavior: Waits for all activated paths to complete before continuing.

Use when: Multiple non-exclusive things need to happen based on conditions (e.g., notify by email AND SMS if certain criteria are met).

[Split: mode=inclusive]
↓ trueFlow (${notifyEmail == 'true'}) → [Send Email]
↓ trueFlow (${notifySMS == 'true'}) → [Send SMS]
↓ trueFlow (${notifySlack == 'true'}) → [Send Slack]
→ [Join: mode=inclusive] ← (waits for all activated paths)
→ [Continue Workflow]

Parallel (AND)​

Split behavior: Activates all outgoing paths simultaneously, regardless of conditions.

Join behavior: Waits for all incoming paths to complete before continuing.

Use when: You need all downstream activities to complete before proceeding (e.g., enrich data from three APIs simultaneously, then proceed).

[Split: mode=parallel]
↓ sequenceFlow → [Fetch Customer Data]
↓ sequenceFlow → [Fetch Order History]
↓ sequenceFlow → [Fetch Loyalty Points]
→ [Join: mode=parallel] ← (waits for all three)
→ [Compose Response]

Connections​

Split​

TypeDirectionDescription
sequenceFlowincomingArrives from previous node
sequenceFlow / trueFlowoutgoingEach branch path

Join​

TypeDirectionDescription
sequenceFlowincomingMultiple paths arriving from parallel branches
sequenceFlowoutgoingSingle path continuing after all branches complete

Parallel Split Example​

Fetch data from three APIs simultaneously and merge results:

[Start Event]
↓
[Set Variable: customerId = {orderId.split('-')[0]}]
↓
[Split: parallel]
↓ (branch 1) ↓ (branch 2) ↓ (branch 3)
[REST: Get Customer] [SQL: Get Orders] [REST: Get Loyalty]
↓ ↓ ↓
[Join: parallel] ←─────────────────────────────────┘
↓
[AI Task: Compose personalized response]

All three service calls execute in parallel. The Join waits until all three complete, then the AI Task receives all results.


Variable Scoping in Parallel Branches​

Each parallel branch runs in the same variable scope. If two parallel branches write to the same variable, the last one to complete wins. To avoid conflicts:

  • Use unique variable names per branch (e.g., customerData, orderData, loyaltyData rather than all using serviceTaskResponse)
  • Use the outputVariable property in Service Task nodes to name outputs distinctly

Notes​

  • Do not mix modes between a Split and its corresponding Join — both should use the same mode
  • Unmatched paths in exclusive mode (no condition matches) will leave the instance stuck — always include a default path
  • For deeply nested parallel patterns, each level needs its own Split/Join pair
  • The engine tracks all active token paths per process instance — the Join waits until all tokens arrive