How early access works
What building on Workflows during early access commits you to: one fixed 0.x stack, a stricter contract for stored documents, what the Content Lake does not yet enforce, and the runtime you supply.
Workflows is in early access. The packages are pre-1.0, the documents the engine stores in the Content Lake are governed more strictly than the package APIs, and you supply the runtime that makes anything move. Read the release notes before every upgrade.
Versions and breaking changes
Workflows packages release together at one version. Install the packages your application uses at matching versions. Each package remains on 0.x; a minor version can include breaking API changes. Read the release notes and package changelogs before upgrading.
The stored-data contract is stricter than the package API
The 0.x version number covers package APIs. Stored workflow documents have a separate compatibility contract: new engines must keep reading existing documents, and writers cannot silently make old data incompatible.
The engine owns three document types: sanity.workflow.definition, sanity.workflow.instance, and a guard document deployed beside the content it protects. Definitions and instances each carry two numbers. modelVersion records the data model the writing engine conformed to. minReaderModel records the oldest engine that can safely read that particular document.
An engine reads every model version at or below its own, so upgrading a reader never orphans a document you already hold. Reading forward fails loudly rather than quietly. An engine that meets a document whose minReaderModel is higher than the model it understands throws ModelVersionAheadError and tells you to upgrade @sanity/workflow-engine, rather than misinterpreting a shape it predates.
Reader-model compatibility
A deployment’s expectedMinReaderModel records the reader model you have verified across every runtime sharing its workflow resource. Set it as a reviewed numeric literal. A package upgrade does not automatically justify raising it.
Upgrade and verify every runtime sharing workflow data before raising the deployment’s acknowledged reader model. Existing instances can require newer readers after an engine commit, even without redeploying a definition. Follow Upgrade Workflows packages for the rollout sequence and feature-specific requirements.
What is not enforced yet
Every check the engine makes is advisory. Action verdicts, permission gates, readiness pre-flights, and editability checks exist so a UI can disable the right controls and explain why. Anyone with a write token can talk to the Content Lake directly and skip the engine, so the Content Lake is the only enforcement point. Treat an engine-side check as UX, never as a security boundary.
A guard is meant to be the mechanism for a rule that must actually hold: a lock deployed beside the content it protects. During early access the Content Lake does not enforce deployed guard documents yet. So a guard currently previews an allow or deny verdict for engine-aware surfaces, and explains a denied engine commit. Until the lake enforces guards, a rule you cannot afford to have bypassed belongs in dataset access control. See Guards and enforcement for the current boundary and deployment behavior.
The engine also does not yet separate the identity a call is made as from the identity its own writes run under. Both ride the caller’s token. So dataset access control, or a lake-enforced guard, evaluated against an editor’s token can block the engine’s own housekeeping, and leave that editor in a stage they can no longer advance, retract, or reconcile.
You run the runtime
Workflows is a library, not a service. Nothing runs in the background and nothing moves on a timer by itself: the engine acts only inside a call your code makes. A transition that should fire when a deadline passes needs something to call tick after the clock crosses it.
The engine queues an effect rather than running it, and a drainer you operate completes it: a Sanity Function, a server, or the CLI during development. A drainer supplies its effect handlers when it constructs its engine, then calls engine.drainEffects() to dispatch the queued work. Nothing drains on its own. Run Workflows with Sanity Functions shows how to drain new work with a Document Function and tick existing instances with a Scheduled Function, using a robot token for both.
Where workflow data lives
Each deployment chooses where its engine-owned state lives, in its workflowResource.
- Definitions and instances: stored as Sanity documents in the deployment’s
workflowResource. A dedicated dataset is common, but the configured resource is the authority. - Deployment partitions: every definition and instance carries the deployment tag. Different tags can share one workflow resource without sharing workflow state.
- Content stays where it is: a workflow can coordinate documents across datasets, projects, and resource types such as Canvas and Media Library. Those documents never move; workflow fields hold global document references to them. The deployment’s credentials and resource routing must reach every referenced resource.
See workflowResource decides where engine documents live for the deployment configuration.
Future storage
No managed storage destination other than workflowResource is part of the contract today. Treat workflowResource as the early-access storage contract. If that changes, the release notes will carry the destination and the migration steps.
Surfaces still in development
The Studio plugin requires Sanity Studio 6.15 or later in the 6.x line. See Add Workflows to Sanity Studio for installation and configuration.
Blueprints can deploy the Functions that operate workflows, but the Blueprints service does not yet register the sanity.workflow resource. Deploy definitions with npx @sanity/workflow-cli deploy before deploying their Functions.
Generated runtimes are experimental
The npx @sanity/workflow-cli blueprint generate command creates Sanity Functions from your workflow definitions. It is experimental and not ready for production use. When workflows.blueprint.ts exists beside the config, definition deployment checks the generated runtime against the whole config, including during --check and --dry-run. Regenerate and resolve drift before deploying.
Generated start watchers use each filter’s dependencies to select relevant updates. With no start filter, or a filter that reads nothing beyond _type, the watcher observes creates only and does not revisit existing documents. Reads of $now, other documents, or dynamic paths retain every update. The heartbeat ticks only function-hosted workflows, while function-hosted effect recovery can include workflows hosted elsewhere. Generation does not verify that handler retries fit the Function timeout, and recovery has no guaranteed completion cadence. Declaring runtime.kind: 'durableFunction' does not create a durable workflow runtime. See Run Workflows with Sanity Functions for setup and current limitations.
Resetting data during development
sanity-workflows nuke --deployment <DEPLOYMENT_NAME> deletes that deployment’s definitions, instances, and guards across every alias-bound resource. Content documents are never touched. It prints a plan and asks you to type every target to confirm. --force skips the prompt. Use it only when you intend to discard that workflow state. To delete one instance, use sanity-workflows nuke --instance <INSTANCE_ID>; abort an active instance first because deletion requires a terminal instance.
Visiting agent?
Workflows includes an MCP server for inspecting, operating, authoring, validating, and deploying workflows. Ask your human to set up the MCP server.