Skip to content

Gateways & Branching

Exclusive Gateway (XOR)

Only one outgoing path is taken — the first branch whose condition evaluates to true. One branch should always be the default to handle the fallthrough case:

import { Bpmn } from "@bpmn-sdk/core";
const xml = Bpmn.export(
Bpmn.createProcess("approval-flow")
.startEvent("start", { name: "Request Submitted" })
.userTask("review", { name: "Review Request" })
.exclusiveGateway("approved?", { name: "Approved?" })
.branch("yes", (b) =>
b.condition("= approved")
.serviceTask("notify", { taskType: "send-email", name: "Send Approval" })
.endEvent("done")
)
.branch("no", (b) =>
b.defaultFlow()
.endEvent("rejected", { name: "Request Rejected" })
)
.withAutoLayout()
.build()
);

Conditions

Branch conditions are FEEL expressions:

.branch("high-value", (b) =>
b.condition("= amount > 10000")
.serviceTask("manual-review", { taskType: "escalate" })
.endEvent("escalated")
)

Parallel Gateway (AND)

All outgoing paths run concurrently. A matching parallel join gateway waits for all paths to complete before continuing:

const xml = Bpmn.export(
Bpmn.createProcess("order-fulfillment")
.startEvent("start")
.parallelGateway("split")
.branch("warehouse", (b) =>
b.serviceTask("pick", { taskType: "warehouse-pick", name: "Pick Items" })
)
.branch("payment", (b) =>
b.serviceTask("charge", { taskType: "payment-charge", name: "Charge Card" })
)
.parallelGateway("join") // waits for all branches
.serviceTask("ship", { taskType: "shipping", name: "Ship Order" })
.endEvent("end")
.withAutoLayout()
.build()
);

Inclusive Gateway (OR)

One or more outgoing paths are taken based on conditions. All active paths converge at the matching inclusive join:

.inclusiveGateway("options")
.branch("express", (b) =>
b.condition("= expressShipping").serviceTask("fedex", { taskType: "fedex-ship" })
)
.branch("gift-wrap", (b) =>
b.condition("= giftWrap").serviceTask("wrap", { taskType: "wrap-items" })
)
.branch("standard", (b) =>
b.defaultFlow().serviceTask("standard-ship", { taskType: "usps-ship" })
)
.inclusiveGateway("join")
.endEvent("end")

Event-Based Gateway

Waits for the first of several events to occur, then takes that path:

.eventBasedGateway("wait-for-event")
.branch("payment", (b) =>
b.intermediateCatchEvent("payment-received", {
message: { name: "payment-confirmed", correlationKey: "= orderId" },
})
.endEvent("paid")
)
.branch("timeout", (b) =>
b.intermediateCatchEvent("payment-timeout", {
timer: { timeDuration: "PT24H" },
})
.endEvent("cancelled")
)

Nested Branching

Branches can contain further gateways:

.exclusiveGateway("route")
.branch("enterprise", (b) =>
b.condition("= tier == \"enterprise\"")
.parallelGateway("enterprise-split")
.branch("account-mgr", (b2) =>
b2.userTask("assign-am", { name: "Assign Account Manager" })
)
.branch("onboarding", (b2) =>
b2.serviceTask("kick-off", { taskType: "onboarding-kit" })
)
.parallelGateway("enterprise-join")
.endEvent("enterprise-done")
)
.branch("self-serve", (b) =>
b.defaultFlow()
.serviceTask("auto-setup", { taskType: "self-serve-setup" })
.endEvent("self-serve-done")
)