Orchestra runs business processes modeled 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
orchestra_enginemodule is the engine and nothing else: no UI, no editor, no dependencies beyond Drupal core and theorchestracontract. 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 queue instead, for sites that would rather pay for them in the background. The queue, the timeouts and the scheduled starts run on cron, or every minute from a keyed drain endpoint an outside scheduler calls, without the rest of cron. Consecutive steps that only read and write the site's database are chained into one advance, with one lock and one transaction. - 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, expressions, working calendars, webhooks, jobs for outside programs, ECA and Tool API 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: an early-firing threshold (N of M) and a value-aware quorum, and any join can give up on a slow branch after a timeout.
- Expressions and decision tables. A step computes values and a flow tests conditions with short expressions, such as a total with VAT or whether it is over budget, without writing a plugin. A decision table holds the rules of a decision in a grid, one rule per row, and the flows after the step route on what it decided.
- Human tasks. A parked token becomes a task in an inbox, to be claimed and completed. Every human step names its audience: users, roles, the members of a Group, a runtime variable, any custom audience, or everyone. A task is completed in-site (outcome buttons, or a comment task), or through an attached interaction such as a Webform or a review, run by a logged-in assignee or by an external party over a capability link. A pull-based operation lists the same work on "My actions" instead of the inbox, and absence cover lets one user complete another's tasks for a period.
- Events and webhooks. A step can wait for something that happens elsewhere: a payment confirmed, a document signed, a record approved in another system. The event reaches the instances it is about, picked by id, correlation key, variable value or workflow. Incoming webhooks give outside services a signed address to call, and deliver each call as an event. A step can send an event too: to the instance waiting for it, to every instance waiting for it, or to start the workflows that start on it.
- Workflows that start themselves. A start node can start its workflow when an event arrives, at a date with an optional repeat interval, or on a cron expression such as every Monday at 09:00. The schedule lives on the workflow, so it is exported, versioned and shown with it.
- Handlers. Some things can happen at any point of an instance: the requester withdraws, the whole request must be answered within 30 days, someone asks how far it has got. A handler is a step that waits for the whole time its instance runs, whatever step the work has reached, and goes down its own flows when it fires. A handler can also wait for a condition, and is offered only while it holds. A workflow with no fixed order then runs as a case: its items are handlers, and it closes once its required steps are done.
- Subprocesses. A step runs another workflow as a child, passing variables in and mapping the child's results back, so a flow written once is called from every process that needs it. The child can also run on another Orchestra site.
- 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, on a named entity or on the content the process is attached to. An HTTP request step calls an external endpoint and routes on its answer, with no code of your own.
- AI agents and Tool API. A Tool step runs any Tool API tool inside a workflow. In the other direction, Orchestra tools let an AI agent, an MCP client, ECA or Drush start and follow workflows, and an agent with an account of its own can work through an inbox under the same access checks as a person.
- Jobs for outside programs. A step can offer its work as a job of a topic. A program outside Drupal, behind a firewall or on a server Orchestra cannot see, takes the jobs of its topic over the HTTP API, does the work, then completes or fails each one. Each job is leased so no other program gets it, and a lease that runs out hands the job to the next program.
- 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. Running instances can be migrated onto a newer version when you want them to move.
- Timeouts, working time 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: a duration from the park, the run or the first arrival at a step, a fixed date, or a moment a module derives from a live subject the run does not own. Working calendars count durations in working hours and skip closed days, for deadlines such as "answer within 10 working days". 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. A flow condition that cannot be evaluated halts its token on an incident the same way.
- 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 announces a checkout opening first, so a domain module can freeze what it owns and 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. A site with no engine of its own can start and resume a remote workflow from a Webform.
- 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 through Drupal core mail or Easy Email templates, or by any other channel.
- 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 |
The client contract every site holds: what drives a workflow, local or remote, and what both ends of the wire share. Hidden: it arrives as a dependency of the engine or of the client. |
orchestra_engine |
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_delegation |
Absence cover: for a period, one user acts on another's tasks and operations, completing them on that person's behalf. |
orchestra_group |
Staff human tasks from Group membership: a step offered to a group is offered to the people who belong to it. |
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_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_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_webform |
What a Webform needs to take part in a workflow, on the engine site or on a site calling a remote Orchestra. Hidden: the webform modules require it. |
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: expose a process's attached entity to ECA, so a reacting model can act on it. |
orchestra_action |
Run a Drupal Action plugin as automated work in a process, on the entity a variable names. |
orchestra_content_action |
Run a Drupal Action plugin on the content a process is attached to. |
orchestra_expression |
Compute values and test conditions with expressions, and decide with decision tables, without writing a plugin. |
orchestra_calendar |
Count deadlines and timers in working time, by calendars of working hours and closed days. |
orchestra_cron |
Start a workflow on a cron expression, such as every Monday at 09:00. |
orchestra_webhook |
Receive signed calls from outside services and deliver each one as an event to the steps waiting for it. |
orchestra_http |
Call an external HTTP endpoint from a workflow step and route on its answer. |
orchestra_job |
Let a program outside Drupal take the work of a step over the HTTP API, then complete or fail it. |
orchestra_tool |
Run a Tool API tool as a step, and let an AI agent, an MCP client or Drush start and follow workflows through Tool API. Drupal 11 only for now. |
orchestra_ui |
Browser UI to start, observe and manage instances. |
orchestra_presentation |
The markup and styles the Orchestra surfaces share: an instance's status and state, a task's state and outcome, a token's state, the card row every personal list renders, and the form a person acts on a step with, as theme hooks. |
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_server |
Lets other sites drive this site's engine, over an OAuth-gated HTTP API. |
orchestra_client |
Drives a remote Orchestra over HTTP. |
orchestra_client_webform |
Start and resume workflows of a remote Orchestra from a Webform, on a site with no engine of its own. |
orchestra_remote_process |
Run a child process on another Orchestra site and resume the parent with what it returns. |
orchestra_views |
Expose processes, tokens, variables and tasks to Views, with readable labels, a tenant filter and ready-made dashboards. |
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_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_audit_trail |
Record process transitions into the Audit Trail chain: a durable, tamper-evident log. |
orchestra_mail_core |
Email every notification through Drupal core mail, one plain-text template per notification type. Brings the hidden orchestra_mail, which decides which emails go out and to whom. |
orchestra_easy_email |
Email every notification through Easy Email templates instead. Takes over from orchestra_mail_core when enabled. |
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_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. |
orchestra_webhook_examples |
Example webhook formats for Stripe and GitHub, showing how a provider format reads its signature, event id and correlation key. |
orchestra_payment_example |
A worked payment example workflow, backed by the Kessai simulator (experimental). |
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_engine.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/
- Compared to Maestro: https://project.pages.drupalcode.org/orchestra/maestro/
- Compared to other BPM engines (Camunda, Flowable): https://project.pages.drupalcode.org/orchestra/bpm-engines/
- Compared to commercial platforms (Power Automate, Appian): https://project.pages.drupalcode.org/orchestra/commercial-platforms/
- Live demo: https://1-x-3upwpvb3jsrwsswrmfk411oolwdxo1uy.tugboatqa.com/
Requirements
- Drupal 11.3 or newer, including Drupal 12
- PHP 8.3 or newer
- The engine needs nothing else. Each optional submodule needs the module or library it integrates (Webform, ECA, Group, Tool API, Key...), listed under
suggestincomposer.json.
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 first 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, Tool API
4 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 12 Nov 2026 at 11:34 UTC






