Workflow / BPM engine for Sanity content. Define workflows as data, run them as instances against a Sanity client, gate transitions on GROQ filters, and queue effects for runtimes to drain.
Status: 0.x, internal. Restricted-access package; the API may change between minor versions.
npm install @sanity/workflow-engine
A workflow field is the engine's state primitive: a typed value a workflow
instance carries — an assignee, a decision, a due date, a reference to the
document under review. Fields are declared in fields: [...] at workflow,
stage, or activity scope and live on the workflow instance document. They
are not Sanity schema fields: they never touch your content documents' shape.
The authoring DSL deliberately borrows Sanity's schema idiom, so
@sanity/workflow-engine/define exports its own defineField. A Studio file
that also imports defineField from sanity should alias one of them:
import {defineField as defineWorkflowField} from '@sanity/workflow-engine/define'
The fields key itself appears at four nesting levels, meaning something
different at each — read it by where it sits:
object- or array-kind field entry:
lightweight {type, name} shapes for the value's structure, mirroring
Sanity's object.fields / array.of.{type: 'object', fields: {...}} value
expression in an op payload: one expression per key of the computed
object value.fields on a workflow instance document: the
declared entries with their current values, written by ops and edits.An Actor stores durable provenance, not mutable profile data. CLI, MCP, and
server consumers resolve through the client already bound to their engine:
const result = await engine.resolveActor({actor: effect.actor, projectId})
if (result.status === 'resolved') {
await notify(result.user)
}
The package also exports resolveClientActor(client, {actor, projectId}) for
code that has a plain WorkflowClient but no constructed engine. UI adapters
implement ProjectUserDirectory<User> with their native user type and pass it
to resolveActor(directory, actor).
The result distinguishes resolved, missing, inaccessible, and
not-person. Agent and system actor IDs are never sent to a project-user API.
An integration may also provide centralized findByEmail; consumers should
not page and scan a project directory independently.