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