Orchestra runs business processes modelled as a graph of nodes connected by flows. A running process is advanced by tokens: a token sits on a node, the engine executes that node, and the token is then either consumed, producing new tokens on the outgoing flows, or parked to wait for an external signal. Parallel work is many tokens advancing at once; synchronization is many tokens meeting at a join; a human step simply parks its token until someone completes it.
Design goals
- Lightweight kernel. The
orchestramodule is the engine and nothing else: no UI, no editor, no dependencies beyond Drupal core. A process advances inline by default, in the request that started or resumed it, so nothing waits on cron; a queued mode hands the same advances to a cron-run queue instead, for sites that would rather pay for them in the background. - Everything is a plugin. Node behavior (
TaskType), routing (FlowCondition,Split,Join), who a node reaches (Audience), what a node carries (NodeFeature), the values a run computes (VariableProvider), when a wait runs out (DeadlineProvider) and what happens once it has (TimeoutAction) are all attribute-based plugins. You extend Orchestra by adding plugins, never by patching it. - Optional weight in submodules. Human tasks, a browser UI, BPMN and form editors, ECA integration and a cross-site HTTP API are each a submodule you enable only when you need it.
- Multi-tenant from the start. Every install has one default tenant and behaves exactly like a single-tenant site; isolation into several realms is opt-in.
Features
- Composable routing. Branching is not a special node type: it falls out of per-flow conditions, a node's split (which live flows to take) and its join (when to fire). The classic gateways (exclusive, parallel, inclusive) are presets over those knobs, and advanced joins ship too: early-firing threshold (N of M), value-aware quorum, and a timeout join that gives up on a slow branch.
- Human tasks. A parked token becomes a task in an inbox, to be claimed and completed. Tasks are pooled by default or targeted at users, roles or any custom audience through pluggable audiences, support reassignment and delegation, and can be handed off for completion to a built-in review form or to an external site over a signed URL.
- Content and automation. Bind a process to a content entity and edit it as the work of a step, drive a content entity's moderation state from a process, or run any Drupal Action plugin as automated work.
- Versioning. An instance runs against an immutable snapshot of the workflow it started on, so editing a workflow never changes a run already under way and removing a node cannot strand a token. Automatic mode pins each new instance to the shape it finds; manual mode is deploy-style, where running and new instances stay on the last published version until you publish again. Identical definitions share a snapshot, and running instances can be migrated onto a newer version when you want them to move.
- Timeouts and resilience. A parked task can time out (resume, notify, escalate, release a claim) or follow a staged ladder of timers. When the wait runs out is itself a plugin: count a duration from the park, the run or the first arrival at a step, wait until a date, or let a module derive the moment from a live subject the run does not own, so no step exists merely to keep a deadline current. Beyond timeouts, a deterministically failing step is retried a bounded number of times, then raised as an incident for an operator to resolve, rather than failing the whole process.
- Payment as a step. Take a payment as workflow work, through a resolver plugin that says what to charge and a settlement subscriber that resumes the step when the provider answers. The step prices nothing it has not first held still: it announces a checkout opening so a domain module can freeze what it owns, and a domain rule can refuse the checkout with reasons the payer reads.
- Distributed execution. A transport-agnostic client contract drives a workflow the same way in-process or on a remote site, over an OAuth-gated HTTP API, so the same caller code runs same-site and cross-site.
- Visual and form authoring. Edit workflows in BPMN.io through the Modeler API, round-tripping id-stable so running instances are never orphaned, or in accessible Drupal forms without a diagram canvas.
- Operations and audit. A browser UI plus Views dashboards and bulk operations, a durable tamper-evident audit log, and channel-neutral notifications: a visible Notify node the flow can route to conditionally, delivered by email out of the box or by any other channel (Easy Email, SMS, chat).
- Retention. Finished instances, and the tokens, variables, incidents and tasks they own, are deleted once they pass a configured age, set per terminal state, so the runtime tables stay bounded. It is off by default, and the audit log outlives what it prunes.
- ECA integration. Start processes from ECA events, and emit ECA events from process tasks.
Module layout
| Module | Responsibility |
|---|---|
orchestra |
Engine kernel: entities, plugin types, the engine service and queue worker. |
orchestra_inbox |
Human tasks: assign, claim and complete parked work from an inbox. |
orchestra_interaction |
External-party interactive waits: a public, capability-token-gated dispatcher that lets a non-logged-in party act on a parked step (the customer-facing counterpart of orchestra_inbox). |
orchestra_delegation |
Absence cover: for a period, one user acts on another's tasks, seeing them in their own inbox and completing them on that person's behalf. |
orchestra_interaction_webform |
Collect a Webform submission during an interactive wait and resume the workflow on submit, via a single binding element; or start a workflow from a submission. |
orchestra_content |
Bind a process to a content entity and edit it as the work of a step. |
orchestra_content_moderation |
Drive a content entity's moderation state from a process (a state-transition task). |
orchestra_content_eca |
ECA glue: start a process for an entity, and expose a process's attached entity to ECA. |
orchestra_action |
Run a Drupal Action plugin as automated work in a process. |
orchestra_ui |
Browser UI to start, observe and manage instances. |
orchestra_modeler |
Author workflows visually through the Modeler API (BPMN.io). |
orchestra_cm |
Author workflows in accessible Drupal forms, without a diagram canvas. |
orchestra_bpmn_io |
Adapt BPMN.io to Orchestra: preserve the diagram layout across modeler switches and restrict the editor to shapes Orchestra can model. |
orchestra_eca |
ECA integration: start processes from events, emit events from tasks. |
orchestra_api |
The client contract and its in-process implementation. |
orchestra_server_api |
OAuth-gated HTTP API exposing the contract to remote consumers. |
orchestra_client |
Drives a remote Orchestra over HTTP. |
orchestra_views |
Expose processes, tokens, variables and tasks to Views, with readable labels, a tenant filter and ready-made dashboards. |
orchestra_presentation |
The markup and styles the Orchestra surfaces share: the status tag, the lifecycle marker, the labeled column and the card row every personal list renders, as theme hooks. |
orchestra_vbo |
Bulk actions on processes and tokens (cancel, delete, signal) from a dashboard, via Views Bulk Operations. |
orchestra_vbo_inbox |
Bulk actions on the task inbox (claim, complete, reassign) via Views Bulk Operations. |
orchestra_inbox_views |
Expose work items to Views: the personal task lists, the tenant task overview, and the action and operations columns that open and complete a task from a row. |
orchestra_audit_trail |
Record process transitions into the Audit Trail chain: a durable, tamper-evident log. |
orchestra_notification |
Channel-neutral notifications: resolves an audience and dispatches one notification event that any delivery channel can send. Provides the visible Notify workflow node. |
orchestra_inbox_notification |
Dispatches a notification when a human task is assigned, reassigned or times out. |
orchestra_interaction_notification |
Dispatches a notification carrying the capability link when a bearer-interaction step parks. |
orchestra_interaction_task |
An interaction task: a human task whose completion screen is an interaction plugin, completed by the assigned operator under their own identity. |
orchestra_mail |
The default delivery channel: turns each channel-neutral notification into one email per recipient. Disable it to deliver the same events another way. |
orchestra_interaction_operation |
The pull-based sibling of the interaction task: an assignment-gated operation a user acts on from their pending-actions list rather than the inbox. |
orchestra_easy_email |
Delivers notifications through Easy Email templates, as an alternative to orchestra_mail. |
orchestra_domain |
Resolve the active tenant from the domain being served, binding each domain to a tenant, and keep every link people follow on the domain it belongs to: a notification links to the domain its process started on, an inbox link to the domain people work on. |
orchestra_payment |
Take a payment as a workflow step: a payment interaction node and a settlement subscriber, backed by the Kessai payment engine (experimental). |
orchestra_examples |
Ready-to-run example workflows. |
orchestra_payment_example |
A worked payment example workflow, backed by the Kessai simulator (experimental). |
orchestra_interaction_webform_examples |
A worked Webform example: a submission-validation workflow (poster, validator, processor) with a modify loop, in both the task and the link doorway. |
Core concepts
| Concept | Stored as | Role |
|---|---|---|
| Tenant | Config entity | An isolated realm; instances, tokens and variables partition per tenant. |
| Workflow | Config entity | The template: nodes, flows and the start node; shared or tenant-scoped. |
| Process instance | Content entity | One running execution of a workflow. |
| Token | Content entity | A marker on a node; the unit of execution. |
| Variable | Content entity | A named value carried by an instance. |
Multi-tenancy
Instances, tokens and variables each carry a tenant field, stamped at create time from the active tenant, so a query, a listing or a purge in one tenant never reaches another's rows. The active tenant is decided by tenant resolvers: services tagged orchestra.tenant_resolver that return a tenant machine name or NULL to defer. The first non-null answer wins; with none (the single-tenant case) the default tenant is used. A workflow is either shared across every tenant (the default) or scoped to a single tenant. The orchestra_domain submodule adds a resolver that maps the current domain to a tenant.
Documentation and demo
- Handbook: https://project.pages.drupalcode.org/orchestra/
- Live demo: https://1-x-3upwpvb3jsrwsswrmfk411oolwdxo1uy.tugboatqa.com/
Requirements
- Drupal 11.3 or newer, including Drupal 12
- PHP 8.3 or newer
Status
The engine is feature-complete against its roadmap and the test suite is green. The public API is not yet frozen and may still change before the 1.0.0 stable release. Bug reports, API feedback and documentation fixes are all welcome in the issue queue.
Project information
- Ecosystem: BPMN.iO, ECA: Event - Condition - Action, Webform, Kessai Payment Engine
2 sites report using this module
- Created by mably on , updated
Stable releases for this project are covered by the security advisory policy.
There are currently no supported stable releases.
Releases
Development version: 1.x-dev updated 5 Sep 2026 at 22:04 UTC






