Conditions
How workflow rules decide: GROQ conditions evaluated against the instance’s rendered scope, and named conditions that keep repeated rules in sync.
Conditions are GROQ expressions that answer a yes-or-no question about workflow state. The condition site determines what data the expression can read and what its result controls.
const articleReview = defineWorkflow({
name: 'article-review',
title: 'Article review',
initialStage: 'review',
fields: [
defineField({type: 'subject', name: 'subject', initialValue: {type: 'input'}, required: true}),
defineField({type: 'boolean', name: 'approved'}),
],
stages: [
defineStage({
name: 'review',
transitions: [
defineTransition({name: 'publish', to: 'published', when: '$fields.approved == true'}),
],
}),
defineStage({name: 'published'}),
],
})What conditions read
The engine evaluates each condition as GROQ against a context built for that decision. This is not an unrestricted Content Lake query. The condition variable reference lists the GROQ variables available at each condition site.
For a running instance, the engine builds a bounded in-memory snapshot containing:
- The current instance, its ancestor instances, and live spawned child instances.
- Accessible content documents declared by
subject,doc.ref, anddoc.refsfields at workflow or current-stage scope. - The release document declared by a
release.reffield.
Missing or unreadable documents are absent, and the engine does not follow references transitively. Singular reference fields resolve to hydrated documents in $fields, while doc.refs remains an array of reference identities. See Fields and Global document references for those contracts.
Reuse named predicates
When several decisions use the same instance rule, declare it once in predicates and reference it as $name. A named predicate can read instance variables, but not caller-bound variables or another named predicate.
defineWorkflow({
// ...
predicates: {
readyToPublish: '$fields.approved == true && $allActivitiesDone',
},
stages: [
defineStage({
name: 'review',
transitions: [
defineTransition({
name: 'publish',
to: 'published',
when: '$readyToPublish',
}),
],
}),
],
})Start filters and requirements
Starting has two decisions. start.filter controls discovery. start.requirements is an ordered list of gates enforced before a fresh standalone start. See the start block reference for the complete nested interfaces.
A subject field identifies the document the workflow is about. See Field types for its declaration contract.
defineWorkflow({
name: 'article-review',
title: 'Article review',
initialStage: 'review',
fields: [
defineField({
type: 'subject',
name: 'subject',
types: ['article'],
initialValue: {type: 'input'},
required: true,
}),
defineField({
type: 'boolean',
name: 'approved',
initialValue: {type: 'input'},
}),
],
start: {
filter: "status == 'draft'",
requirements: [
{
type: 'singleSubject',
name: 'one-open-review',
title: 'Review already in progress',
},
{
type: 'groq',
name: 'approved',
title: 'Approval required',
query: '$fields.approved == true',
},
],
},
stages: [defineStage({name: 'review'})],
})The subject field makes this workflow available for articles, and the filter narrows discovery to drafts. A fresh startInstance call succeeds only when no unfinished run of this definition holds the same subject and the supplied approved input is true. Resuming an unfinished start and parent-owned spawning do not evaluate start requirements.
Allow one active workflow per subject
Editorial Workflows identifies each subject with a global document reference, which combines the document ID with its dataset, project, or other Sanity resource. The full identity prevents unrelated documents with the same ID from blocking each other.
Use a singleSubject requirement when this definition may have only one unfinished run for the same subject. The rule applies across deployed versions of that definition. A different definition does not block it.
start: {
requirements: [
{
type: 'singleSubject',
name: 'one-open-review',
description: 'Finish the existing review before starting another.',
},
],
}The workflow must declare one first-class subject field with initialValue: {type: 'input'}. Completing the existing run permits another start.
Query the raw snapshot
The * operator searches this snapshot; it does not open an unrestricted Content Lake query. Use the variables in the reference below when they expose the state you need. Use the raw instance only for state those variables do not expose, such as stage-visit history:
defineTransition({
name: 'finish-after-rework',
to: 'done',
// $stage == "review" is true on every visit.
// Read the raw stage entries to detect a return visit.
when:
'count(*[_id == $self][0].stages[name == "review"]) > 1',
})The engine provides $stage, the current stage name. It does not provide a $stages history variable. The current-stage check is true on both the first and later review visits, while the $self query counts the persisted stage entries and distinguishes a return visit.
Building a custom interface or diagnostic? See Evaluation insights to explain the outcome of these conditions without changing how you compose them.
Reference
Not every condition site receives the same variables. The reference below lists the context available at each site.
Instance variables
These variables describe the running instance and are available at instance condition sites.
$fields
Rendered field values
Activity fields overlay stage fields, which overlay workflow fields. References hydrate only when their documents are in the evaluation snapshot.
$stage
Current stage name
Identifies the instance’s current stage.
$activities
Current activity rows
Includes each activity’s current status.
$allActivitiesDone
boolean
True when every current activity is done or skipped.
$anyActivityFailed
boolean
True when any current activity has failed.
$effects
Completed effect outputs
Contains declared outputs from completed effects.
$effectStatus
Effect statuses
Reports done or failed for effects queued during the current stage visit.
$subworkflows
Child workflow registry
Faceted by activity, definition, and status.
$self
Current instance GDR URI
Identifies the workflow instance document.
$parent
Parent instance
Identifies the parent instance, or null for a root instance.
$ancestors
Ancestor instances
Lists ancestor instances in root-first order.
$now
ISO timestamp
The clock value for the current evaluation.
$context
Start context
The context supplied when the instance started.
Context-specific variables
These variables appear only at the sites that supply their context.
$actor
Actor identity
Identifies the caller at caller-bound sites.
$assigned
boolean
True when the caller matches the activity assignment at caller-bound sites.
$can
Permission values
Contains advisory permission values resolved for the caller.
$params
Action parameters
Available to expressions carried by a caller-fired action.
$row
Current row
Available to row-oriented expressions such as operation where and spawn with.
Instance decision sites
These conditions make decisions about a running instance.
Transition when
Instance variables
Selects the first eligible outgoing transition in declaration order.
Activity filter
Instance variables
Determines whether the activity belongs to the current stage visit.
Action when
Instance variables
Triggers the action automatically when the condition is true.
Action filter, requirements, and editable predicates
Instance variables plus caller context
Controls caller-facing availability. These checks are advisory, not a security boundary.
Action expression sites
These expressions shape work after an action fires.
Effect input bindings
Action context
Resolve the input passed to an effect. Caller-fired actions may bind $params.
Operation where
Action context plus $row
Tests each stored array row before applying the operation.
Spawn forEach
Workflow field context
Selects the rows used to create child instances.
Spawn context
Action context
Constructs the start context for each child instance.
Spawn with
Action context plus $row
Constructs child inputs from the current row. Spawn expressions do not bind $can or $params.
Start decision sites
Start visibility and start permission use different inputs.
start.filter
Candidate document context
Controls discovery. It can read the candidate document, $tag, $definition, and $now. It cannot read $fields and does not gate startInstance.
start.requirements[]
Start input context
Ordered creation gates. GROQ requirements can read $tag, $definition, $now, and raw input $fields; singleSubject enforces definition-scoped subject exclusivity.
Guard predicates
Guard predicates use Content Lake delta GROQ with their own variables and enforcement model.
Visiting agent?
Editorial Workflows includes an MCP server for inspecting, operating, authoring, validating, and deploying workflows. Ask your human to set up the MCP server.