Skip to main content

Gateways

Gateways control how tokens split and merge in a process. Each gateway type has its own rules for which outgoing flows fire (split) and what it takes to advance (join).

Common attributes

AttributeNotes
idRequired
nameOptional
defaultFlow ID — fallback flow taken when no condition matches (exclusive and inclusive only)
gatewayDirectionInformational; not enforced

A default flow must be one of the gateway's outgoing flows and must not carry a conditionExpression.


Exclusive gateway (XOR)

Picks exactly one outgoing flow at a split. The first flow whose condition evaluates to true is taken; all others receive a "dead" token (the path is skipped). When used as a join, it's a pass-through merge — no synchronisation.

Behavior

ScenarioBehavior
Split with conditionsFlows are evaluated in document order; first true wins
Split with no match and a defaultDefault flow is taken
Split with no match and no defaultRuntime error
JoinPass-through — every incoming token advances

Example

<bpmn:exclusiveGateway id="gw-route" name="Route?" default="flow-default">
<bpmn:incoming>f1</bpmn:incoming>
<bpmn:outgoing>flow-vip</bpmn:outgoing>
<bpmn:outgoing>flow-default</bpmn:outgoing>
</bpmn:exclusiveGateway>

<bpmn:sequenceFlow id="flow-vip" name="VIP" sourceRef="gw-route" targetRef="task-vip">
<bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">=tier = "vip"</bpmn:conditionExpression>
</bpmn:sequenceFlow>
<bpmn:sequenceFlow id="flow-default" name="Standard" sourceRef="gw-route" targetRef="task-standard" />

Validation

CheckSeverity
default doesn't match an outgoing flowError
Default flow carries a conditionExpressionError
Non-default outgoing flow has no condition (when default is set)Warning
Single outgoing flow has no condition and no defaultWarning

Parallel gateway (AND)

Splits into all outgoing flows simultaneously and joins by waiting for a token on every incoming flow.

Behavior

ScenarioBehavior
SplitAll outgoing flows are activated. No condition evaluation
JoinWaits until a live token has arrived on every incoming flow, then activates a single outgoing token

Conditions on outgoing flows are ignored (with a warning at deployment).

Example

<bpmn:parallelGateway id="fork" name="Split">
<bpmn:incoming>f1</bpmn:incoming>
<bpmn:outgoing>f2</bpmn:outgoing>
<bpmn:outgoing>f3</bpmn:outgoing>
</bpmn:parallelGateway>

<!-- ... tasks in parallel branches ... -->

<bpmn:parallelGateway id="join" name="Join">
<bpmn:incoming>f2-done</bpmn:incoming>
<bpmn:incoming>f3-done</bpmn:incoming>
<bpmn:outgoing>f4</bpmn:outgoing>
</bpmn:parallelGateway>

Validation

CheckSeverity
Outgoing flow carries a conditionExpressionWarning (condition is ignored)

Inclusive gateway (OR)

Activates every outgoing flow whose condition evaluates to true. The matching join waits for all of those branches to arrive.

Behavior

ScenarioBehavior
SplitAll matching conditions activate their flows in parallel; if none match and a default is set, only the default fires; if none match and no default, runtime error
JoinWaits for every branch the matching split activated; merges branch variables into the parent scope before advancing

Example

<bpmn:inclusiveGateway id="gw-or" name="Select Channels" default="flow-email">
<bpmn:incoming>f1</bpmn:incoming>
<bpmn:outgoing>flow-sms</bpmn:outgoing>
<bpmn:outgoing>flow-push</bpmn:outgoing>
<bpmn:outgoing>flow-email</bpmn:outgoing>
</bpmn:inclusiveGateway>

<bpmn:sequenceFlow id="flow-sms" sourceRef="gw-or" targetRef="send-sms">
<bpmn:conditionExpression>=smsEnabled</bpmn:conditionExpression>
</bpmn:sequenceFlow>
<bpmn:sequenceFlow id="flow-push" sourceRef="gw-or" targetRef="send-push">
<bpmn:conditionExpression>=pushEnabled</bpmn:conditionExpression>
</bpmn:sequenceFlow>
<bpmn:sequenceFlow id="flow-email" sourceRef="gw-or" targetRef="send-email" />

Validation

CheckSeverity
default doesn't match an outgoing flowError
Default flow carries a conditionExpressionError
Non-default outgoing flow has no condition (when default is set)Warning

Event-based gateway

Waits for the first of several events to fire and then takes the corresponding branch. Each outgoing flow must lead directly to an intermediate catch event (message, timer, or signal).

Attributes

AttributeDefaultNotes
instantiatefalseWhen true, this gateway can start a new process instance on the first event
eventGatewayTypeExclusiveExclusive = first event wins (the rest are cancelled); Parallel = every event that fires within the race window advances its branch

Behavior

ScenarioBehavior
Exclusive (default)Races all outgoing catch events; the first to fire wins. The losing branches are cancelled
ParallelAll events that fire within the race window advance their branches
No event firesToken waits indefinitely

Example

<bpmn:eventBasedGateway id="gw-event" name="Wait for First">
<bpmn:incoming>f1</bpmn:incoming>
<bpmn:outgoing>flow-timer</bpmn:outgoing>
<bpmn:outgoing>flow-msg</bpmn:outgoing>
</bpmn:eventBasedGateway>

<bpmn:sequenceFlow id="flow-timer" sourceRef="gw-event" targetRef="catch-timer" />
<bpmn:intermediateCatchEvent id="catch-timer" name="30s Timeout">
<bpmn:incoming>flow-timer</bpmn:incoming>
<bpmn:outgoing>f-timeout</bpmn:outgoing>
<bpmn:timerEventDefinition>
<bpmn:timeDuration xsi:type="bpmn:tFormalExpression">PT30S</bpmn:timeDuration>
</bpmn:timerEventDefinition>
</bpmn:intermediateCatchEvent>

<bpmn:sequenceFlow id="flow-msg" sourceRef="gw-event" targetRef="catch-msg" />
<bpmn:intermediateCatchEvent id="catch-msg" name="Confirmation Received">
<bpmn:incoming>flow-msg</bpmn:incoming>
<bpmn:outgoing>f-confirmed</bpmn:outgoing>
<bpmn:messageEventDefinition messageRef="Msg_Confirm" />
</bpmn:intermediateCatchEvent>

Validation

CheckSeverity
Fewer than 2 outgoing flowsError
Outgoing flow leads to a node that isn't an intermediate catch eventError
Outgoing flow carries a conditionExpressionError