Skip to main content

Inclusive Gateway (OR)

The Inclusive Gateway (also known as OR Gateway) is a hybrid gateway that activates one or more outgoing paths based on conditions. It combines features of both Exclusive and Parallel Gateways.

Symbol

In BPMN diagrams, it's shown as a diamond with an "O" (circle) marker.

When to Use

Use Inclusive Gateways when you need to:

  • Send notifications through multiple channels (some optional based on availability)
  • Perform optional checks based on data presence
  • Route to multiple teams conditionally
  • Execute a subset of parallel tasks based on conditions
  • Any scenario where 1 or more paths should execute (not all, not just one)

Comparison with Other Gateways

GatewayPaths ActivatedBased On
Exclusive (XOR)Exactly 1Condition selects which one
Parallel (AND)All (always)No conditions (unconditional)
Inclusive (OR)1 or moreEach path has own condition

Visual Example

Result: Email always + SMS if customer has phone + Push if customer has app


Token-Based Execution Model

Like Parallel Gateways, Inclusive Gateways use token-based execution:

How It Works

Key Difference from Parallel Gateway

  • Parallel Gateway: ALL paths activated (no conditions)
  • Inclusive Gateway: Paths activated ONLY if their condition = true

The join gateway knows which paths were activated and waits only for those specific tokens.


Implementation Options

Each outgoing flow has its own condition. All flows where condition evaluates to true are activated.

BPMN XML Example

<bpmn:inclusiveGateway id="Gateway_Notifications" 
name="Send Notifications"
default="Flow_Email">
<bpmn:incoming>Flow_In</bpmn:incoming>
<bpmn:outgoing>Flow_Email</bpmn:outgoing>
<bpmn:outgoing>Flow_SMS</bpmn:outgoing>
<bpmn:outgoing>Flow_Push</bpmn:outgoing>
</bpmn:inclusiveGateway>

<!-- Flow 1: Email (always sent - no condition means always active) -->
<bpmn:sequenceFlow id="Flow_Email"
name="Email Notification"
sourceRef="Gateway_Notifications"
targetRef="Task_SendEmail" />

<!-- Flow 2: SMS (only if phone exists) -->
<bpmn:sequenceFlow id="Flow_SMS"
name="SMS Notification"
sourceRef="Gateway_Notifications"
targetRef="Task_SendSMS">
<bpmn:conditionExpression>
customerPhone !== null &amp;&amp; customerPhone !== ''
</bpmn:conditionExpression>
</bpmn:sequenceFlow>

<!-- Flow 3: Push (only if app installed) -->
<bpmn:sequenceFlow id="Flow_Push"
name="Push Notification"
sourceRef="Gateway_Notifications"
targetRef="Task_SendPush">
<bpmn:conditionExpression>
hasMobileApp === true
</bpmn:conditionExpression>
</bpmn:sequenceFlow>

<!-- Tasks execute if their flow was activated -->
<bpmn:serviceTask id="Task_SendEmail" name="Send Email" />
<bpmn:serviceTask id="Task_SendSMS" name="Send SMS" />
<bpmn:serviceTask id="Task_SendPush" name="Send Push Notification" />

<!-- Connect all to join gateway -->
<bpmn:sequenceFlow sourceRef="Task_SendEmail" targetRef="Gateway_NotificationJoin" />
<bpmn:sequenceFlow sourceRef="Task_SendSMS" targetRef="Gateway_NotificationJoin" />
<bpmn:sequenceFlow sourceRef="Task_SendPush" targetRef="Gateway_NotificationJoin" />

<!-- JOIN: Wait for all activated paths to complete -->
<bpmn:inclusiveGateway id="Gateway_NotificationJoin"
name="Wait for Notifications">
<bpmn:incoming>Flow_AfterEmail</bpmn:incoming>
<bpmn:incoming>Flow_AfterSMS</bpmn:incoming>
<bpmn:incoming>Flow_AfterPush</bpmn:incoming>
<bpmn:outgoing>Flow_Continue</bpmn:outgoing>
</bpmn:inclusiveGateway>

<bpmn:sequenceFlow id="Flow_Continue"
sourceRef="Gateway_NotificationJoin"
targetRef="Task_Next" />

Fields Reference

FieldLocationRequiredDescriptionExample
idGateway element✅ YesUnique gateway identifierGateway_Notifications
nameGateway element⚪ OptionalDescriptive name"Send Notifications"
defaultGateway element✅ RecommendedFlow activated if no conditions matchFlow_Email
conditionExpressionInside <sequenceFlow>⚪ OptionalBoolean expressionSee examples

Condition Rules

  • Flows WITHOUT conditions → Always activated
  • Flows WITH conditions → Activated only if condition evaluates to true
  • At least ONE flow must activate → Use default to guarantee this

Possible Outcomes

Based on the example above:

Customer DataFlows ActivatedTokens CreatedTasks Executed
Email onlyFlow_Email1Email sent
Email + PhoneFlow_Email, Flow_SMS2Email + SMS sent
Email + Phone + AppFlow_Email, Flow_SMS, Flow_Push3All three sent
No phone, has appFlow_Email, Flow_Push2Email + Push sent

Option 2: Gateway-Level Condition (Advanced)

The gateway itself contains a script that returns a comma-separated list of flow names/IDs to activate.

BPMN XML Example

<bpmn:inclusiveGateway id="Gateway_Alerts" 
name="Alert Channels">
<bpmn:incoming>Flow_In</bpmn:incoming>
<bpmn:outgoing>Flow_Email</bpmn:outgoing>
<bpmn:outgoing>Flow_SMS</bpmn:outgoing>
<bpmn:outgoing>Flow_Push</bpmn:outgoing>

<!-- Gateway-level condition -->
<bpmn:extensionElements>
<custom:properties>
<custom:property name="Condition" value="
// Build list of channels based on urgency
const channels = [];

// Email always included
channels.push('Flow_Email');

// SMS for high/medium urgency
if (urgency === 'high' || urgency === 'medium') {
channels.push('Flow_SMS');
}

// Push for high urgency only
if (urgency === 'high') {
channels.push('Flow_Push');
}

// Return comma-separated flow names or IDs
return channels.join(',');
" />
</custom:properties>
</bpmn:extensionElements>
</bpmn:inclusiveGateway>

Return Value Format

The script must return a comma-separated string of flow IDs or names:

// By flow ID
return 'Flow_Email,Flow_SMS';

// By flow name
return 'Email Notification,SMS Notification';

// Single flow
return 'Flow_Email';

// All flows
return 'Flow_Email,Flow_SMS,Flow_Push';

When to Use Gateway-Level vs Flow-Level

Use Gateway-Level Condition WhenUse Flow-Level Conditions When
Complex logic determines which pathsEach path has simple independent condition
Multiple factors affect decisionConditions don't overlap
Need centralized decision logicPrefer declarative approach
Conditions share common calculationsEach condition is self-contained

Execution Flow

Split Execution

┌─────────────────────────────────────┐
│ Token arrives at Inclusive Gateway │
└───────────────┬─────────────────────┘


┌─────────────────────────────────────┐
│ Evaluate conditions for each flow │
│ (or execute gateway condition) │
└───────────────┬─────────────────────┘


┌─────────────────────────────────────┐
│ Identify flows where condition=true │
│ (or parse gateway condition result) │
└───────────────┬─────────────────────┘


┌─────────────────────────────────────┐
│ Create token for EACH active flow │
└───────────────┬─────────────────────┘


┌─────────────────────────────────────┐
│ Record which tokens were created │
│ (join will wait for these) │
└───────────────┬─────────────────────┘


┌─────────────────────────────────────┐
│ Deactivate parent token │
└───────────────┬─────────────────────┘


┌─────────────────────────────────────┐
│ Activated tokens execute │
└─────────────────────────────────────┘

Join Execution

┌─────────────────────────────────────┐
│ Token arrives at join gateway │
└───────────────┬─────────────────────┘


┌─────────────────────────────────────┐
│ Record token arrival │
└───────────────┬─────────────────────┘


┌─────────────────────────────────────┐
│ Check: Were all activated tokens │
│ from split received? │
└───────┬───────────────┬─────────────┘
│ NO │ YES
▼ ▼
┌──────────────┐ ┌────────────────────┐
│ Deactivate │ │ All activated paths│
│ this token │ │ completed │
│ │ └─────────┬──────────┘
│ WAIT for │ │
│ remaining │ ▼
└──────────────┘ ┌────────────────────┐
│ Merge tokens │
└─────────┬──────────┘


┌────────────────────┐
│ Continue process │
└────────────────────┘

Execution Log Examples

Example 1: Two Paths Activated

Scenario: Customer has email and phone, but no app.

[INCLUSIVE GATEWAY] Evaluating inclusive gateway: Gateway_Notifications (Send Notifications)
[INCLUSIVE GATEWAY] Incoming flows: 1
[INCLUSIVE GATEWAY] Outgoing flows: 3
[INCLUSIVE GATEWAY] Evaluating individual flow conditions:

[INCLUSIVE GATEWAY] Flow: Flow_Email (Email Notification)
[INCLUSIVE GATEWAY] No condition → Always active
[INCLUSIVE GATEWAY] ✓ Activated

[INCLUSIVE GATEWAY] Flow: Flow_SMS (SMS Notification)
[INCLUSIVE GATEWAY] Condition: customerPhone !== null && customerPhone !== ''
[INCLUSIVE GATEWAY] Variables: customerPhone="+1234567890"
[INCLUSIVE GATEWAY] Evaluation result: true
[INCLUSIVE GATEWAY] ✓ Activated

[INCLUSIVE GATEWAY] Flow: Flow_Push (Push Notification)
[INCLUSIVE GATEWAY] Condition: hasMobileApp === true
[INCLUSIVE GATEWAY] Variables: hasMobileApp=false
[INCLUSIVE GATEWAY] Evaluation result: false
[INCLUSIVE GATEWAY] ✗ Not activated

[INCLUSIVE GATEWAY] ✓ Activated 2 paths out of 3
[INCLUSIVE GATEWAY] Creating execution tokens:
[INCLUSIVE GATEWAY] → Token 4f8a9b2c-3d5e-4f7a-8c6b-5d7e9f1a2b3c
[INCLUSIVE GATEWAY] Path: Email Notification → Task_SendEmail
[INCLUSIVE GATEWAY] → Token 7d3e6a1f-2c4b-4e9a-7f5d-8c9a1b2e3f4a
[INCLUSIVE GATEWAY] Path: SMS Notification → Task_SendSMS
[INCLUSIVE GATEWAY] Recording activated token count: 2
[INCLUSIVE GATEWAY] ✓ Inclusive split complete. 2 tokens now active.

[TOKEN PROCESSOR] Detected 2 active tokens. Processing conditional parallel paths...

[TOKEN 4f8a9b2c] Processing Task_SendEmail...
[TOKEN 4f8a9b2c] ✓ Email sent successfully

[TOKEN 7d3e6a1f] Processing Task_SendSMS...
[TOKEN 7d3e6a1f] ✓ SMS sent successfully

[INCLUSIVE GATEWAY] Token 4f8a9b2c arrived at join: Gateway_NotificationJoin
[INCLUSIVE GATEWAY] Expected tokens from split: 2
[INCLUSIVE GATEWAY] Arrived so far: 1
[INCLUSIVE GATEWAY] ⏳ WAITING - 1/2 activated paths completed

[INCLUSIVE GATEWAY] Token 7d3e6a1f arrived at join: Gateway_NotificationJoin
[INCLUSIVE GATEWAY] Expected tokens from split: 2
[INCLUSIVE GATEWAY] Arrived so far: 2
[INCLUSIVE GATEWAY] ✓ ALL ACTIVATED PATHS COMPLETED (2/2)
[INCLUSIVE GATEWAY] Merging tokens and continuing...
[INCLUSIVE GATEWAY] Created merged token: 9e2f5a8b-7c4d-4f6a-9b3e-2d5c7a1f4b8e
[INCLUSIVE GATEWAY] Moving to: Task_Next

Example 2: All Paths Activated

Scenario: Customer has email, phone, and app.

[INCLUSIVE GATEWAY] Evaluating inclusive gateway: Gateway_Notifications
[INCLUSIVE GATEWAY] ✓ Activated 3 paths out of 3
[INCLUSIVE GATEWAY] Creating execution tokens:
[INCLUSIVE GATEWAY] → Token abc... (Email)
[INCLUSIVE GATEWAY] → Token def... (SMS)
[INCLUSIVE GATEWAY] → Token ghi... (Push)
[INCLUSIVE GATEWAY] Recording activated token count: 3

... tasks execute ...

[INCLUSIVE GATEWAY] ✓ ALL ACTIVATED PATHS COMPLETED (3/3)
[INCLUSIVE GATEWAY] Merging 3 tokens and continuing...

Example 3: Gateway-Level Condition

[INCLUSIVE GATEWAY] Evaluating inclusive gateway: Gateway_Alerts (Alert Channels)
[INCLUSIVE GATEWAY] Gateway has Condition property
[INCLUSIVE GATEWAY] Executing gateway-level condition script
[INCLUSIVE GATEWAY] Variables: urgency='high'
[INCLUSIVE GATEWAY] ✓ Condition returned: 'Flow_Email,Flow_SMS,Flow_Push'
[INCLUSIVE GATEWAY] Parsing activated flow list...
[INCLUSIVE GATEWAY] ✓ Matched: Flow_Email → Task_Email
[INCLUSIVE GATEWAY] ✓ Matched: Flow_SMS → Task_SMS
[INCLUSIVE GATEWAY] ✓ Matched: Flow_Push → Task_Push
[INCLUSIVE GATEWAY] ✓ Activated 3 paths. Creating tokens...

Use Cases

Use Case 1: Multi-Channel Customer Notifications

Scenario: Notify customer through all available channels.

Requirements:

  • Email is mandatory (always send)
  • SMS if customer has phone number
  • Push notification if customer has mobile app
  • In-app notification if customer is logged in

Implementation:

<bpmn:inclusiveGateway id="Gateway_CustomerNotify" default="Flow_Email">
<bpmn:outgoing>Flow_Email</bpmn:outgoing>
<bpmn:outgoing>Flow_SMS</bpmn:outgoing>
<bpmn:outgoing>Flow_Push</bpmn:outgoing>
<bpmn:outgoing>Flow_InApp</bpmn:outgoing>
</bpmn:inclusiveGateway>

<!-- Email: no condition = always -->
<bpmn:sequenceFlow id="Flow_Email" sourceRef="Gateway_CustomerNotify" targetRef="Task_Email" />

<!-- SMS: conditional -->
<bpmn:sequenceFlow id="Flow_SMS" sourceRef="Gateway_CustomerNotify" targetRef="Task_SMS">
<bpmn:conditionExpression>
phoneNumber !== null &amp;&amp; phoneNumber !== ''
</bpmn:conditionExpression>
</bpmn:sequenceFlow>

<!-- Push: conditional -->
<bpmn:sequenceFlow id="Flow_Push" sourceRef="Gateway_CustomerNotify" targetRef="Task_Push">
<bpmn:conditionExpression>
hasMobileApp === true &amp;&amp; pushToken !== null
</bpmn:conditionExpression>
</bpmn:sequenceFlow>

<!-- In-App: conditional -->
<bpmn:sequenceFlow id="Flow_InApp" sourceRef="Gateway_CustomerNotify" targetRef="Task_InApp">
<bpmn:conditionExpression>
isLoggedIn === true &amp;&amp; sessionId !== null
</bpmn:conditionExpression>
</bpmn:sequenceFlow>

Visual Flow:

     Notification
◇O
╱ | | ╲
Email SMS Push InApp
✓ ? ? ?
╲ | | ╱
◇O

Continue

Use Case 2: Conditional Verification Checks

Scenario: Perform different verifications based on data availability.

Requirements:

  • Credit check always required
  • Employment verification if applicant is employed
  • Business verification if self-employed
  • Reference check if references provided

Implementation:

<bpmn:inclusiveGateway id="Gateway_Verifications" default="Flow_Credit">
<bpmn:outgoing>Flow_Credit</bpmn:outgoing>
<bpmn:outgoing>Flow_Employment</bpmn:outgoing>
<bpmn:outgoing>Flow_Business</bpmn:outgoing>
<bpmn:outgoing>Flow_References</bpmn:outgoing>
</bpmn:inclusiveGateway>

<!-- Credit: always (no condition) -->
<bpmn:sequenceFlow id="Flow_Credit" sourceRef="Gateway_Verifications"
targetRef="Task_CreditCheck" />

<!-- Employment: if employed -->
<bpmn:sequenceFlow id="Flow_Employment" sourceRef="Gateway_Verifications"
targetRef="Task_EmploymentVerify">
<bpmn:conditionExpression>
employmentStatus === 'EMPLOYED' &amp;&amp; employerName !== null
</bpmn:conditionExpression>
</bpmn:sequenceFlow>

<!-- Business: if self-employed -->
<bpmn:sequenceFlow id="Flow_Business" sourceRef="Gateway_Verifications"
targetRef="Task_BusinessVerify">
<bpmn:conditionExpression>
employmentStatus === 'SELF_EMPLOYED' &amp;&amp; businessName !== null
</bpmn:conditionExpression>
</bpmn:sequenceFlow>

<!-- References: if provided -->
<bpmn:sequenceFlow id="Flow_References" sourceRef="Gateway_Verifications"
targetRef="Task_CheckReferences">
<bpmn:conditionExpression>
references !== null &amp;&amp; references.length > 0
</bpmn:conditionExpression>
</bpmn:sequenceFlow>

<!-- Join after all activated checks complete -->
<bpmn:inclusiveGateway id="Gateway_VerificationsJoin">
<bpmn:incoming>Flow_FromCredit</bpmn:incoming>
<bpmn:incoming>Flow_FromEmployment</bpmn:incoming>
<bpmn:incoming>Flow_FromBusiness</bpmn:incoming>
<bpmn:incoming>Flow_FromReferences</bpmn:incoming>
<bpmn:outgoing>Flow_ToDecision</bpmn:outgoing>
</bpmn:inclusiveGateway>

Possible Outcomes:

Applicant TypeChecks ActivatedPaths
Employed, no refsCredit + Employment2
Self-employed, has refsCredit + Business + References3
Unemployed, no refsCredit only1
Employed, has refsCredit + Employment + References3

Use Case 3: Alert Escalation Based on Severity

Scenario: Send alerts to different teams based on issue severity.

<bpmn:inclusiveGateway id="Gateway_Alerts">
<bpmn:extensionElements>
<custom:properties>
<custom:property name="Condition" value="
const teams = [];

// Support always notified
teams.push('Flow_Support');

// Manager for medium/high severity
if (severity === 'MEDIUM' || severity === 'HIGH') {
teams.push('Flow_Manager');
}

// Executive for high severity
if (severity === 'HIGH') {
teams.push('Flow_Executive');
}

// Security team for security issues
if (category === 'SECURITY') {
teams.push('Flow_Security');
}

return teams.join(',');
" />
</custom:properties>
</bpmn:extensionElements>
<bpmn:outgoing>Flow_Support</bpmn:outgoing>
<bpmn:outgoing>Flow_Manager</bpmn:outgoing>
<bpmn:outgoing>Flow_Executive</bpmn:outgoing>
<bpmn:outgoing>Flow_Security</bpmn:outgoing>
</bpmn:inclusiveGateway>

Examples:

IssueTeams Notified
Low severity, non-securitySupport only
Medium severity, non-securitySupport + Manager
High severity, non-securitySupport + Manager + Executive
Low severity, securitySupport + Security
High severity, securitySupport + Manager + Executive + Security

Best Practices

✅ DO

  1. Set a default flow to ensure at least one path activates
  2. Use flow-level conditions for simple cases - easier to understand
  3. Use gateway-level condition for complex logic - centralized decision
  4. Test all combinations - ensure each subset of paths works
  5. Match split and join - join must have all possible incoming flows

❌ DON'T

  1. Don't make all conditions mutually exclusive - use Exclusive Gateway instead
  2. Don't forget the default flow - at least one path must activate
  3. Don't modify shared variables in parallel paths - can cause race conditions
  4. Don't forget the join - activated paths must synchronize
  5. Don't confuse with Parallel Gateway - Inclusive is conditional, Parallel is not

Common Patterns

Pattern 1: Mandatory + Optional Paths

    ◇O
╱ | ╲
M O O M=Mandatory, O=Optional
╲ | ╱
◇O

One path always activates, others conditional

Pattern 2: Multiple Optional Paths

    ◇O
╱ | ╲
O O O All optional
╲ | ╱
◇O

At least one must activate (use default)

Pattern 3: Nested Inclusive

       ◇O Main
╱ ╲
A ◇O Nested
╱ ╲
B C

Nested gateway operates on its own token


Troubleshooting

Issue: No paths activate

Symptoms: Process error - "No flows activated at inclusive gateway"

Cause: All conditions false and no default flow

Solution:

<!-- ❌ Wrong - no default, all conditions can be false -->
<bpmn:inclusiveGateway id="Gateway_1">
<bpmn:outgoing>Flow_A</bpmn:outgoing>
<bpmn:outgoing>Flow_B</bpmn:outgoing>
</bpmn:inclusiveGateway>

<bpmn:sequenceFlow id="Flow_A">
<bpmn:conditionExpression>hasPhoneNum === true</bpmn:conditionExpression>
</bpmn:sequenceFlow>

<bpmn:sequenceFlow id="Flow_B">
<bpmn:conditionExpression>hasEmail === true</bpmn:conditionExpression>
</bpmn:sequenceFlow>

<!-- ✅ Correct - add default -->
<bpmn:inclusiveGateway id="Gateway_1" default="Flow_A">

Issue: Join waits indefinitely

Symptoms: Log shows ⏳ WAITING - 2/3 activated paths completed forever

Cause: Mismatch between activated paths and join expectations

Check:

[INCLUSIVE GATEWAY] Activated 2 paths  ← Split created 2 tokens
[INCLUSIVE GATEWAY] Expected: 3 ← Join expects 3

Solution: This indicates a bug. Join should only wait for activated tokens. Check logs to see which tokens were created at split.


Issue: Activates wrong paths

Symptoms: Wrong tasks execute

Cause: Condition logic error

Debug Steps:

  1. Check execution logs for condition evaluation:
[INCLUSIVE GATEWAY]   Flow: Flow_SMS
[INCLUSIVE GATEWAY] Condition: customerPhone !== null
[INCLUSIVE GATEWAY] Variables: customerPhone=null ← Check this
[INCLUSIVE GATEWAY] Evaluation result: false
  1. Verify variable values are set before gateway
  2. Check condition syntax (XML entities for &&, <, >)

Issue: Gateway-level condition returns invalid flow

Symptoms: Warning in logs, falls back to default

[INCLUSIVE GATEWAY] ✗ WARNING: Condition returned 'Flow_XYZ' but no such flow exists
[INCLUSIVE GATEWAY] Available flows: Flow_Email, Flow_SMS, Flow_Push
[INCLUSIVE GATEWAY] ⚠ Falling back to default flow

Solution: Ensure script returns exact flow IDs or names:

// ❌ Wrong - typo in flow name
return 'Flow_Email,Flow_Sms'; // Should be 'Flow_SMS'

// ✅ Correct
return 'Flow_Email,Flow_SMS';

Summary

Inclusive Gateway (OR):

  • ✅ Activates one or more paths based on conditions
  • ✅ Creates token for each path where condition = true
  • ✅ Join waits for all activated tokens (not all possible tokens)
  • ✅ Use for conditional parallelism
  • ✅ Two options: flow-level or gateway-level conditions
  • ✅ Always set a default flow to ensure at least one activates

When to Use:

  • Multiple optional parallel tasks
  • Notification to available channels
  • Conditional data collection
  • Alert escalation based on rules

Next Steps: