My workflow instances

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_engine module is the engine and nothing else: no UI, no editor, no dependencies beyond Drupal core and the orchestra contract. 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

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 suggest in composer.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.

Supporting organizations: 
Development

Project information

Releases