Skip to content

Notifications

Orchestra reaches people at points you define while modeling a workflow. You decide where to notify, who to notify, and what to send; a delivery channel decides how it is sent and in which medium. This page covers the modeling side. For the channels (email, Easy Email, SMS, a custom subscriber) and the event they consume, see Notification delivery.

Notifications never ride the audit event: the audit log is a separate concern (OrchestraAuditableEvent), so recording history and reaching people stay apart.

From the modeler's view

Defining notifications is part of building the workflow, not an afterthought. At each point where the workflow should reach someone, you decide three things in the model:

  • Where to notify: on a task assignment, when a branch arrives at an interactive wait (notify on arrival), or at an explicit Notify node you place on a branch.
  • Who to notify: an audience, the same plugin type a task uses to say who it reaches, from named users to "the user held in this variable" to a fixed external email address.
  • What to send: a notification type (a template id). The wording lives in the channel, so the model stays channel-neutral and every recipient is rendered in their own language. A node may also set named texts of its own, its Values, for a shared template to read (see One template for many notifications).

Everything below is how the where and who are expressed while modeling; the what and its rendering belong to delivery.

How a notification flows

flowchart LR
  subgraph M["chosen while modeling"]
    P1[task assigned<br/>or reassigned]
    P2[branch parks on<br/>an interactive wait]
    P3[Notify node<br/>on a branch]
  end
  P1 --> D["the point's dispatcher<br/>resolves the audience"]
  P2 --> D
  P3 --> D
  D --> EV["OrchestraNotificationEvent<br/>type + params + recipients"]
  EV --> CH{delivery channel}
  CH --> MAIL[orchestra_mail]
  CH --> EE[Easy Email]
  CH --> SUB[custom subscriber]
  CH --> SMS[SMS / chat / push]

The where is a dispatch point, the who resolves to Recipient objects, and the what is the type a channel templates on. The model never names a channel, so enabling or swapping one (the right half of the diagram) changes delivery without touching the workflow. That half is Notification delivery.

The when is a notification profile: a Notify node and an interaction's arrival notification each name one, or follow their workflow, their tenant and the site. By default a notification goes once the page has been sent, and an interaction link, the only way its recipient can act, before it and as an incident when it cannot be sent. See When a notification is sent.

Where: the notification points

Three points can notify. The code that resolves the audience and dispatches at a point is its dispatcher, and each one lives in the module that owns the thing being notified about, so a point needs nothing installed beyond that module:

Provided by Notification point Type
orchestra_inbox a task is assigned / reassigned (WorkItemEvent), times out (TaskTimedOutEvent), or is canceled (WorkItemEvent) task_assigned, task_reminder, task_canceled
orchestra_interaction a branch parks on a bearer-interaction node with a "Notify on arrival" audience (TokenParkedEvent) interaction_link
orchestra_engine (the Notify node) a token traverses a Notify node the node's configured type (default generic)

Each resolves its audience to recipients (audience plugins, shared through the kernel AudienceResolver, so "Users by variable" resolves the original poster) and dispatches; none knows how the message is sent.

Who gets notified

Audiences are Audience plugins, the same plugin type a task uses to say who it reaches (a staffing audience additionally implements AssignmentInterface):

Audience Notifies by default Resolves to
Users yes the named users
Users by variable yes the user(s) named in a process variable, by user ID, username or email address
Users by variable, including users who have not logged in yet yes as Users by variable, plus the address of a username with no account yet that will be provisioned at first login
Roles no (opt in) the members of the roles
Roles by variable no (opt in) the members of the role(s) in a variable
Groups no (opt in) the active members of the named groups (the optional orchestra_group submodule)
Groups by variable no (opt in) the active members of the group(s) whose ID is in a process variable (the optional orchestra_group submodule)
Email address yes a fixed email address, with no Drupal account
Email by variable yes the email address(es) held in a process variable

The last two are notify-only audiences: they resolve to an account-less recipient (an external address), so they never appear in the task assignment editor and cannot staff a task; they exist for the Notify node and on-arrival notifications.

For tasks, the per-assignment Notify this audience flag decides who is mailed, so pooling a task to a broad role never mails its members by surprise; a reassignment always notifies the new assignee. When a task is canceled (a threshold or quorum join, or a join timeout, fired on another branch, the instance was canceled or failed), the people told about it are told it is gone and why: whoever took it from its pool or was reassigned it, otherwise the audience its assignment mail went to, so a pool nobody mailed is not mailed then either, nor one named user whose assignment does not notify them. A task canceled because its holder was blocked or deleted tells nobody. Whoever is covering for a resolved recipient is added to the audience, and that recipient drops out only if their own delegations unanimously ask to be left alone, the one case where an audience narrows rather than widens. For a Notify node, every recipient row is notified (you placed the node deliberately). The authenticated and anonymous pseudo-roles match nobody, so "notify all authenticated users" never mass-mails the site.

The Notify node (visible, conditional)

A Notify node is a workflow node, so it shows on the chart and the flow routes to it conditionally: send the "approved" branch through a Notify node to mail people only then. It renders nothing itself, it resolves its Recipients (the audience-row editor, the same widget tasks use) and dispatches the neutral event with its notification type, carrying the process context, then advances immediately. The message layout lives in the channel (a mail template, or an Easy Email template); the words that change from one node to the next can live on the node, in its Values.

Each recipient row also has a Delivery setting, To (default), Cc or Bcc. It keeps the full audience-plugin range (users, roles, by-variable, custom rules) and only tags which mail header the resolved people land in, so you can say "poster in To, validator in Cc" without giving up the powerful audience mechanism. It is a hint the channel interprets: the mail channel maps it to headers (see Notification delivery), and a channel with no such notion (SMS, chat) ignores it and reaches everyone.

To address the person who completed a task (a validator), set Store completer in variable on the task node; the completer's uid is written there on completion (and recorded as the audit actor), so a later Notify node names them with "Users by variable". The submission-validation example closes with a Notify node that mails both the original poster and the validator once the request is processed.

One template for many notifications

Most notifications share one layout, a title, a body and a link, and differ only in their words. Rather than copying the template once per notification, give each node that sends one its own Values, and let one template read them.

A value is a machine name mapped to text, and the text may contain tokens:

Name Text
title Your request [orchestra:var:reference] was approved
body It will be processed within [orchestra:var:delay] days.
link [orchestra:var:review_url]

The template reads them as [orchestra:node-value:title], [orchestra:node-value:body] and so on, and reads only the names it needs. A name the node does not set reads as nothing, so one template serves a node with a title and a node without. The values belong to the step, not to the instance: nothing is written to the process variables, and a value set for one notification is never there for the next. See Node values for how they are rendered.

The Values section is offered on a Notify node and on every node that parks, whose task, arrival and reminder notifications are sent while the branch stands on it. With Easy Email, one theme layout can also leave out a block whose value is empty: see Easy Email templating.

Where a notification is rendered

A process is started in one place and advanced in another: someone submits on a public site, an operator acts from a back office, cron and queue workers run with no request at all. Anything a notification is built from would otherwise resolve against whichever request happens to be running, so links point at the wrong place and the site name, the mail settings and the theme are the wrong ones.

Orchestra therefore names two places, and they are not the same:

  • The workspace, where people do the work. A task notification links to the inbox, and the inbox is the same place for everyone, whoever caused the task. A parked interaction step is work too, so its link goes there as well.
  • The origin, where the process was started. A Notify node is author-composed content, normally addressed to whoever started the run, so it is rendered where they were. Each instance records its origin when it starts, since by the time the message goes out the run may be advancing anywhere; a subprocess belongs where its parent does. A run started with nothing to record, by cron or the command line, falls back to where its tenant receives people.

Orchestra itself knows nothing about sites or domains. The origin is an opaque string and the shipped implementation of orchestra_engine.notification_context changes nothing at all, so a site with a single front end pays nothing for this. Install Orchestra Domain on a Domain Access site to give both places meaning. Each tenant says which domains it lives on, on its own Domains form beside its retention settings: its primary domain, any secondary domains it is also reached through, and a workspace domain where its operators work.

Which domain a run belongs to is the run's own origin, recorded when it started, so a tenant reached through several sites needs no rule about them. Only a run that recorded no origin needs one, and the primary domain is the answer: a run started by cron or the command line is rendered there, which is why it is the one the form requires. A tenant naming neither a primary nor a workspace inherits the default tenant's, which is how tenants sharing a front door or a back office name it once between them. That inheritance is for rendering only: which requests reach a tenant is never inherited, or two tenants would answer for one domain. Deleting a domain clears it from every tenant that named it, whichever of the three it was named as, so a tenant is never left pointing at a machine name a later domain could take over.

Outbound only

This is for output built outside the request it belongs to. Never wrap the rendering of a page being served: on a Domain site the negotiated domain also drives routing and language negotiation for that page.