> For AI agents: the complete Sanity documentation index is available at [https://www.sanity.io/docs/llms.txt](https://www.sanity.io/docs/llms.txt).

# Workflow configuration reference

Every key of a sanity.workflow.ts config: defineWorkflowConfig, deployments, partitions, resource aliases, reader-model acknowledgement, and runtime blocks.

This page lists every key a `sanity.workflow.ts` config accepts at Workflows 0.37: the object passed to `defineWorkflowConfig`, each deployment in its `deployments` array, and the runtime blocks a deployment, workflow, or effect can carry. It also lists the validation errors the config raises. For what a deployment, partition, and resource alias mean, read [Deployments, partitions, and resources](https://www.sanity.io/docs/workflows/deployments-and-resources). To write and deploy a config step by step, follow [Configure and deploy workflow definitions](https://www.sanity.io/docs/workflows/deploy-definitions).

## Example

A config with two deployments of the quick start's `article-review`. `dev` keeps engine documents in the content dataset. `production` keeps them in a separate dataset and binds the `content` alias to the dataset its articles live in.

**sanity.workflow.ts**

```typescript
import {defineWorkflowConfig} from '@sanity/workflow-engine/define'
import type {WorkflowDeploymentInput} from '@sanity/workflow-engine'

import {articleReview} from './workflows/article-review'

export const dev = {
  name: 'dev',
  partition: 'dev',
  expectedMinReaderModel: 10,
  workflowResource: {type: 'dataset', id: 'PROJECT_ID.DATASET_NAME'},
  definitions: [articleReview],
} satisfies WorkflowDeploymentInput

export const production = {
  name: 'production',
  partition: 'production',
  expectedMinReaderModel: 10,
  workflowResource: {type: 'dataset', id: 'PROJECT_ID.workflows'},
  resourceAliases: [{name: 'content', resource: {type: 'dataset', id: 'PROJECT_ID.production'}}],
  runtime: {kind: 'function'},
  definitions: [articleReview],
} satisfies WorkflowDeploymentInput

export default defineWorkflowConfig({deployments: [dev, production]})

```

The CLI imports the module's default export and validates it again with `defineWorkflowConfig`, so a plain object default export fails at the same point a malformed `defineWorkflowConfig` call does. Exporting each deployment by name lets scripts, Blueprints, and the [Functions generator](https://www.sanity.io/docs/workflows/generate-functions) import the same object.

## Config file

#### Properties

**File name** ('sanity.workflow.ts' | 'sanity.workflow.js' | 'sanity.workflow.mjs')

The CLI searches the current directory, then each parent, for the first of these names. --path points at a file or a directory to start the search from. A .ts file loads without a build step.

**Default export** (WorkflowConfig)

The return value of defineWorkflowConfig. A missing file fails with No sanity.workflow.ts found in <dir> or any parent directory. A module that throws while loading fails with Failed to load <file>: and the error. A config that fails validation fails with Invalid config in <file>: and the issues listed under Validation errors.

### `WorkflowConfigInput`

`WorkflowConfigInput` is the object you pass to `defineWorkflowConfig`, imported from `@sanity/workflow-engine/define`. Every entry in `deployments` is a [WorkflowDeploymentInput](https://www.sanity.io/docs/workflows/workflow-config-reference).

#### Properties

**deployments** (WorkflowDeploymentInput[], required)

The deployments this config defines. At least one. Every name must be unique, and every pair of workflowResource and resolved partition must be unique across the array.

**telemetry** (WorkflowTelemetryLogger, optional)

A logger, an object with a log function, that receives every Workflows CLI telemetry event. Setting it replaces the CLI's built-in Sanity intake entirely: events flow to this logger unconditionally, CI and DO_NOT_TRACK included, and consent, environment suppression, and transport become your logger's responsibility. Omit it to keep the built-in behavior.

### `WorkflowDeploymentInput`

`WorkflowDeploymentInput` is the authored shape of one deployment, exported as a type from `@sanity/workflow-engine`. Annotate a named export with `satisfies WorkflowDeploymentInput` to check it where you write it and reuse it from configuration, scripts, and Blueprint entry points.

#### Properties

**name** (string, required)

The deployment's identity, unique across the config. --deployment matches it, and generated Functions derive their names from it. Lowercase ASCII letters, digits, and dashes, starting with a letter or digit, with no dots.

Renaming a deployment that generates Functions replaces those Functions; run npx sanity@latest blueprints plan and review every replacement before deploying.

**partition** (string, optional)

The storage partition every engine document of this deployment is scoped to. Defaults to default (the DEFAULT_PARTITION export). Same grammar as name.

Omit it only when this is the only deployment on its workflowResource. When several deployments share a resource, each must set a distinct partition; set partition: 'default' on the first one when you add a second, so its stored address does not change. Separate resources may each use default. The check reads this config only; it does not discover deployments in the Content Lake or in other configs.

Stored definitions and instances carry the partition in a field named tag, so raw GROQ filters on tag == $tag.

**tag** (string, deprecated)

The pre-0.37 name for partition. The config loads with a deprecation warning, which the CLI prints to stderr, and the value is used as the partition. Rename the key to partition and keep the same value: deleting it instead moves the deployment to default. A tag that is not a valid partition, is not a string, or differs from a partition set on the same deployment fails validation. See the release notes.

**workflowResource** (WorkflowResource, required)

The Sanity resource that stores this deployment's engine documents: deployed definitions and instances. The CLI derives its client's project and dataset from it. One of {type: 'dataset', id: '<projectId>.<dataset>'}, {type: 'canvas', id}, {type: 'media-library', id}, or {type: 'dashboard', id}. A dataset id without the dot fails validation.

Guard documents are not stored here; a guard is deployed beside the content it protects.

**resourceAliases** ({name: string; resource: WorkflowResource}[], optional)

Binds each @<name>: reference a definition makes to a physical resource. Needed only for content outside workflowResource; bare ids resolve against workflowResource. Names use lowercase letters, digits, and dashes with no leading dash, and must be unique within the deployment.

At deploy, every @<name>: reference expands to the bound resource before the definition is fingerprinted, so a rebind deploys as a new version. A definition that references an unbound alias fails to deploy. The CLI also serves every bound resource to the engine through deploymentResourceClients, so bound resources accept reference writes even when no definition uses the alias.

**definitions** (DefinedWorkflow[], required)

The definitions this deployment ships, each the return value of defineWorkflow. At least one. Order does not matter: deploy orders the batch so children deploy before the parents that spawn them. A spawn reference must resolve inside the batch or to an already-deployed definition.

**expectedMinReaderModel** (number, required to submit definitions)

The reader model you have verified across every runtime that shares this workflowResource, written as a reviewed integer literal. The config loads without it, so read-only commands work, but deploy and definitions diff reject the deployment until it is present.

Those commands require an integer from the submitted definitions' floor up to the installed writer's maximum (11 at 0.37). Below the floor, above the maximum, missing, or non-integer values throw ReaderModelAcknowledgementError (see Reader-model acknowledgement). Upgrading a package does not by itself require changing it. Follow Upgrade Workflows packages before raising it.

**runtime** (RuntimeBlock, optional)

Default hosting for this deployment's workflows: {kind: 'function'}, {kind: 'durableFunction'}, or {kind: 'selfHosted'}. Omitted, workflows host on function. A deployment-level block accepts no function budget. See Runtime blocks.

Unknown keys on a deployment are ignored and dropped from the parsed result.

### Runtime blocks

A deployment and a workflow (`defineWorkflow`) accept `RuntimeBlock`. An effect accepts `EffectRuntimeBlock`, which adds the function budget. A workflow inherits its deployment's kind and each effect inherits its workflow's kind; a block at a lower level overrides the inherited value. With no block at any level, the kind is `function`. The block is authoring metadata: deploy strips it before storing the definition, so changing it creates no definition version. Regenerate and redeploy the runtime after changing it.

#### Properties

**kind** ('function' | 'durableFunction' | 'selfHosted', required)

Required when a block is present; omit the whole block to inherit. function: generated Sanity Functions run effects and reevaluate time-based conditions. durableFunction: the generator reports the workflow or effect but emits no durable execution function. selfHosted: your own process runs it. Any other value fails with Invalid option: expected one of "function"|"durableFunction"|"selfHosted".

Hosting is not an execution restriction: any engine with a registered handler can drain an effect. See Runtime hosting configuration.

**timeout** (integer, 1 to 900, optional)

Effect blocks with kind: 'function' only. Seconds available to the effect's Function. Setting it gives the effect its own drain Function. Omitted, the platform default applies. A value outside the range fails at defineWorkflow; on a deployment or workflow block, the key fails as an unknown key.

**memory** (integer, 1 to 10, optional)

Effect blocks with kind: 'function' only. Gigabytes allocated to the effect's Function. Setting it gives the effect its own drain Function. Omitted, the platform default applies. A value outside the range fails at defineWorkflow.

Effects with the same name in one deployment must declare the same kind and budget: one effect name has one handler. The generator rejects a mismatch and names both definitions.

## Parsed config types

`defineWorkflowConfig` validates its input and returns the parsed shapes below. Read them when you write tooling that loads a config; author with `WorkflowConfigInput` and `WorkflowDeploymentInput`.

### `WorkflowConfig`

The return value of `defineWorkflowConfig`. Every deployment carries a resolved `partition`, `default` when the author omitted it, and `expectedMinReaderModel` may still be missing until a command that submits definitions selects that deployment.

#### Properties

**deployments** (WorkflowDeployment[])

The parsed deployments, in input order.

**warnings** (readonly string[])

One deprecation message per deployment that named its partition with tag. Nothing prints them; the CLI writes them to stderr, and other hosts show them where their user reads them.

**telemetry** (WorkflowTelemetryLogger | undefined)

The logger passed in, unchanged.

### `WorkflowDeployment`

One parsed deployment: `name`, `partition` (always set), `workflowResource`, `definitions`, and the optional `resourceAliases`, `runtime`, and `expectedMinReaderModel`. A deprecated `tag` is already migrated to `partition`. Do not pass it back to an API that requires `WorkflowDeploymentInput`.

### `AcknowledgedWorkflowDeployment`

A `WorkflowDeployment` whose `expectedMinReaderModel` has been checked against the definitions a command will submit, so the field is a `number`. The CLI's `deploy` and `definitions diff` produce it, and Workflows Blueprint resources require it.

## Reader-model acknowledgement

Deploy compares a deployment's `expectedMinReaderModel` with the highest floor its submitted definitions require. These `@sanity/workflow-engine` exports compute and check that floor. The rollout that justifies raising the value is in [Upgrade Workflows packages](https://www.sanity.io/docs/workflows/upgrade).

**requiredDefinitionReaderModel(definitions)(): number**

The reader model a deployment must acknowledge for a batch of definitions: the highest requirement in the batch. The baseline is 4. Role-constrained plural assignment fields need 8. Singular assignment fields, and guards spanning edit and lifecycle id groups, need 9. Required subject, doc.ref, and doc.refs fields, and effect retry policies, need 10. A workflow-level abort block needs 11.

**assertReaderModelAcknowledgement(value, {requiredMinReaderModel, context?})(): asserts value is number**

Throws ReaderModelAcknowledgementError unless value is an integer from requiredMinReaderModel through DATA_MODEL_MAX_READER. context labels the caller in the message and defaults to Deployment.

**DATA_MODEL_MAX_READER(): 11**

The highest acknowledgement the installed engine accepts. Keep deployment acknowledgements as reviewed literals; do not import this constant in place of checking your runtimes.

**requiredReaderModel(documentType, document)(): number**

The oldest engine model that can read one stored definition or instance document. Returns 0 when no governed feature raises the floor.

**requiredModelFeatures(documentType, document)(): readonly DataModelChange[]**

The DATA_MODEL_CHANGES entries whose detectors apply to one stored definition or instance.

**DATA_MODEL_CHANGES(): readonly DataModelChange[]**

The append-only manifest of persisted-model changes. Each entry has id, introducedInModel, minReaderModel, documentTypes, compatibility (additive or reader-floor), applicability, and summary.

`ReaderModelAcknowledgementError` has `kind` `reader-model-acknowledgement` and `code` `WORKFLOW_READER_MODEL_ACKNOWLEDGEMENT_MISMATCH`, and carries `expectedMinReaderModel`, `requiredMinReaderModel`, `engineMaxReaderModel`, and `documentationUrl`. Its message, for a deployment named `dev` that acknowledges 3:

```text
Deployment dev acknowledges reader model 3; the submitted definitions require at least 10, and this writer supports acknowledgements through 11.
Use a reviewed numeric literal in that range. Before raising it, ensure every runtime sharing the workflow resource can read the required model.
Rollout guide: https://www.sanity.io/docs/workflows/prerelease
```

## Deployment tooling exports

Exports from `@sanity/workflow-engine` for scripts that load a config and act on its deployments. The CLI uses the same functions.

**deploymentResourceClients({client, deployment})(): ResourceClientResolver | undefined**

Builds a resourceClients resolver that serves every resource deployment.resourceAliases binds, so references to them pass the write-surface check and reads route to them. Each client is derived from client with withConfig, keeping its token, API host, and request-tag prefix; those credentials must reach every bound resource. Returns undefined when the deployment binds nothing beyond workflowResource. Throws ContractViolationError when a foreign resource is bound and client has no withConfig. A runtime-supplied reference to an unbound resource is still rejected with RefResourceUndeclaredError. See Read routing and the write surface.

**resourceAliasesToMap(resourceAliases)(): ResourceAliases**

Collapses a deployment's resourceAliases array into the alias-to-resource map that deploy expands @<name>: references against.

**expandResourceAliases(definition, resourceAliases)(): WorkflowDefinition**

Returns the definition with every @<alias>: reference expanded to the bound physical GDR. Fails, naming the definition and alias, when an alias is unbound or a standalone reference is malformed (for example @content:a:b or an empty id). A no-op when the definition references no aliases.

**refsOf(definition)(): LogicalRef[]**

The definition's outgoing spawn references, each {name: string, version?: number | 'latest'}. Deploy ordering and the delete referrer check use the same walk.

**validateDefinitionInputs(definitions)(): ValidateDefinitionInputsResult**

Checks definitions in authoring or stored shape for structure, cross-field rules, and GROQ syntax without a client. Returns {valid, results} with one result per definition. Deploy also checks duplicate names, spawn dependencies, resource bindings, project roles, and the reader-model acknowledgement, so a valid result does not guarantee a deploy succeeds. sanity-workflows definitions validate and the MCP workflows_validate_definition tool return the same report.

**deployedPartitionsGroq()(): string**

A GROQ query listing the distinct partitions that hold a deployed definition in the resource, sorted ascending. Run it with a client scoped to the workflow resource. It reports what is deployed, so a partition with nothing deployed is absent. It reads the stored tag field.

**definitionPartitionsGroq()(): string**

The same query narrowed to one definition; pass {definition} as the query parameters. A definition deployed under several partitions returns several results, and the caller must choose one.

**DEFAULT_PARTITION(): 'default'**

The partition a deployment resolves to when it omits partition.

## Validation errors

`defineWorkflowConfig` throws `defineWorkflowConfig failed validation (N issues):` followed by one line per issue, each prefixed with the path of the failing key. The CLI prints the same text after `Invalid config in sanity.workflow.ts:`. An invalid `partition` value throws `ContractViolationError` with its message directly. Each line below is the issue text for one problem, for a deployment named `dev`, exactly as 0.37 prints it.

```text
# No deployments
deployments: a config needs at least one deployment

# Invalid name
deployments[0].name: invalid name — ASCII lowercase + digits + dashes, no leading dash, no dots

# Invalid partition (thrown as ContractViolationError)
partition: invalid partition "Prod" — must match ^[a-z0-9][a-z0-9-]*$ (ASCII lowercase + digits + dashes, no leading dash, no dots)

# Duplicate name
deployments: duplicate deployment name "dev" — each deployment must use a unique name

# Shared resource, omitted partition
deployments: deployment "dev" shares its workflow resource with another deployment; set an explicit partition (use "default" to keep an omitted partition's address)

# Same resource and partition
deployments: deployments "dev" and "prod" share workflow resource + partition "x" — both would write into the same partition; change one deployment’s partition or resource

# Dataset id without a dot
deployments[0].workflowResource.id: invalid dataset resource id — expected "<projectId>.<dataset>"

# Unknown resource type
deployments[0].workflowResource.type: Invalid type: Expected ("dataset" | "canvas" | "media-library" | "dashboard") but received "bucket"

# Empty definitions
deployments[0].definitions: a deployment needs at least one definition

# Not a definition
deployments[0].definitions[0]: expected a workflow definition (an object with a string `name`)

# Invalid alias name
deployments[0].resourceAliases[0].name: invalid resource handle name — lowercase letters, digits and dashes only, no leading dash

# Duplicate alias name
deployments[0].resourceAliases: duplicate resource handle name "c" — each binding name must be unique within a deployment

# tag and partition differ
deployments[0]: Deployment "dev": "tag" and "partition" disagree. Remove the deprecated "tag" and keep the partition that addresses your stored workflows.

# tag not a valid partition
deployments[0]: Deployment "dev": "tag" is not a valid partition (ASCII lowercase + digits + dashes, no leading dash, no dots). Rename it to "partition" with the value that addresses your stored workflows.

# tag not a string
deployments[0]: Deployment "dev": "tag" must be a string. Rename it to "partition" and keep the existing value to retain access to stored workflows.

# Unknown runtime kind
deployments[0].runtime.kind: Invalid option: expected one of "function"|"durableFunction"|"selfHosted"

# Budget on a deployment runtime block
deployments[0].runtime.timeout: Invalid key: Expected never but received "timeout"

# Invalid telemetry logger
telemetry: expected a telemetry logger (an object with a `log` function)
```

