packages/sanity/src/core/variants/ACTIONS.md
Variant definition writes should use the actions API instead of direct document mutations. The actions target the persisted system.variant definition documents under _.variants.*.
The actions operate on variant definition documents with this persisted shape:
interface VariantDefinitionDocument {
_id: `_.variants.${string}`
_type: 'system.variant'
name: string
conditions: Record<string, string>
priority: number
metadata?: Record<string, unknown>
}
variantId is the generated short ID suffix, not the full document ID. For example, a variantId such as Ab12cd34 maps to the stored document ID _.variants.Ab12cd34.
sanity.action.variant.definition.createCreates a new variant definition document.
Required fields:
actionType: 'sanity.action.variant.definition.create'variantId: the short variant name used to create _.variants.{variantId}Optional fields:
conditions: exact-match condition map. Empty conditions are accepted, but such a definition is inert for matching.priority: numeric priority. Defaults to 0 when omitted.metadata: free-form metadata for Studio UI concerns such as title and description.Example:
await client.action({
actionType: 'sanity.action.variant.definition.create',
variantId: 'Ab12cd34',
conditions: {audience: 'loyal'},
priority: 0,
metadata: {title: 'Loyal customers'},
})
Behavior:
_.variants.{variantId} with _type: 'system.variant' and name: variantId.Condition validation:
[a-z][a-z0-9_-]{0,63}._ or $ are rejected.sanity.action.variant.definition.editEdits an existing variant definition document.
Required fields:
actionType: 'sanity.action.variant.definition.edit'variantId: the short variant name for _.variants.{variantId}patch: a patch without an _id; the action applies it to the matching variant definition documentOptional fields:
ifRevisionId: optimistic concurrency guard. The action fails if the current document revision does not match.Example:
await client.action({
actionType: 'sanity.action.variant.definition.edit',
variantId: 'Ab12cd34',
patch: {
set: {
'conditions': {audience: 'loyal', locale: 'en-US'},
'priority': 10,
'metadata.title': 'Loyal customers in the US',
},
},
})
Behavior:
ifRevisionId is provided and does not match the current document revision.conditions or conditions.*prioritymetadata or metadata.*_id, _type, name, and system timestamps.Useful patch patterns:
await client.action({
actionType: 'sanity.action.variant.definition.edit',
variantId: 'Ab12cd34',
patch: {
set: {conditions: {audience: 'loyal'}},
},
})
await client.action({
actionType: 'sanity.action.variant.definition.edit',
variantId: 'Ab12cd34',
patch: {
unset: ['metadata'],
},
})
sanity.action.variant.definition.deleteDeletes an existing variant definition document.
Required fields:
actionType: 'sanity.action.variant.definition.delete'variantId: the short variant name for _.variants.{variantId}Optional fields:
ifRevisionId: optimistic concurrency guard. The action fails if the current document revision does not match.Example:
await client.action({
actionType: 'sanity.action.variant.definition.delete',
variantId: 'Ab12cd34',
})
Behavior:
ifRevisionId is provided and does not match the current document revision._.variants.{variantId}.Variant document lifecycle writes (beyond sanity.action.document.variant.create, documented with the creation flow) use these actions. They address variant-scoped version documents by (publishedId, variantId, bundleId) instead of raw ids, because variant version ids (versions.<scopeId>.<publishedId>) carry opaque server-generated scope hashes.
sanity.action.document.variant.publishPublishes a variant-scoped version into the variant-of-published document.
Required fields:
actionType: 'sanity.action.document.variant.publish'publishedId: the group (base published) document idvariantId: the short variant name for _.variants.{variantId}bundleId: the SOURCE bundle being published — 'drafts' or a release name. 'published' is rejected (target equals source).Optional fields:
ifSourceRevisionId: optimistic lock on the source variant document's revision.ifPublishedRevisionId: optimistic lock on the variant-of-published target's revision.Behavior:
sanity.action.document.variant.unpublishUnpublishes a variant document. The behavior depends on which variant version bundleId
addresses (see CLDX-5781 / SAPP-4012).
Required fields:
actionType: 'sanity.action.document.variant.unpublish'publishedId: the group (base published) document idvariantId: the short variant namebundleId: the bundle of the variant version being unpublished — omitted/undefined for the
variant-of-published document, or a release name for a release-scoped variant. 'drafts' is
not a valid target (a drafts-scoped variant has nothing published to unpublish).Behavior:
bundleId omitted (variant-of-published): hard unpublish — deletes the published variant and
recreates its content as the variant draft, mirroring base unpublish.bundleId = release name: soft unpublish — marks the release-scoped variant with
_system.delete: true (the same "going to unpublish" marker release versions use); publishing
the release completes the unpublish.sanity.action.document.variant.deleteDeletes a variant-scoped version document.
Required fields:
actionType: 'sanity.action.document.variant.delete'publishedId: the group (base published) document idvariantId: the short variant nameOptional fields:
bundleId: the bundle whose variant document is deleted — 'drafts' or a release name. Omitted targets the variant-of-published document.purge: also removes history from the translog. Defaults to false.Behavior:
sanity.action.document.discard addresses drafts by raw id and is kept for base/release paths). Discarding the variant-of-published document is not allowed — removing it is unpublish's job.