Mutations
How to modify documents directly using the HTTP API
The mutation API lets you create and modify documents. All requests have to be authenticated.
Gotcha
Please note that validation rules set up on your document types or fields will only run within the studio (client-side). They will not run when using this mutation API (server-side), so you may want to take care of validation separately.
This endpoint accepts the following query parameters:
returnIdsboolean
(Default
false
) Iftrue
, the id's of modified documents are returned.returnDocumentsboolean
(Default
false
) Iftrue
, the entire content of changed documents is returned.autoGenerateArrayKeysboolean
(Default
false
) Iftrue
, adds a_key
attribute to array items, unique within the array, to ensure each can be addressed uniquely in a real-time collaboration context, even if elements are inserted or removed elsewhere in the array.transactionIdstring
By default every transaction gets a random ID. This is included in Listener
mutation
events and the History API. This parameter lets you set your own transaction ID. It must be unique in the dataset.skipCrossDatasetReferenceValidationboolean
(Default
false
) Iftrue
then any cross-dataset references will be considered weak for the purposes of this mutation.visibilitystring
(Default
sync
) Can be:sync
,async
ordeferred
Ifsync
the request will not return until the requested changes are visible to subsequent queries, ifasync
the request will return immediately when the changes have been committed, but it might still be a second or so until you can see the change reflected in a query. For maximum performance, useasync
always, except when you need your next query to see the changes you made.deferred
is the fastest way to write. It bypasses the real time indexing completely, and should be used in cases where you are bulk importing/mutating a large number of documents and don't need to see that data in a query for several tens of seconds.dryRunboolean
(Default
false
) Iftrue
, the query and response will be a dry run. That is, the response will be identical to the one returned had this property been omitted orfalse
(including error responses) but no documents will be affected.tagstring
Request tags are values assigned to API and CDN requests that can be used to filter and aggregate log data within request logs from your Sanity Content Lake. Learn more in the request tags documentation.
curl 'https://<project-id>.api.sanity.io/v2021-06-07/data/mutate/<dataset-name>' \
-H 'Authorization: Bearer <token>' \
-H 'Content-Type: application/json' \
--data-binary '{"mutations":[<transactions>]}'
The mutation API is transactional. You may submit an array of mutations and if the operation succeeds you can rest assured that every mutation you submitted was executed. A transaction may look like this:
{
"mutations": [
{"createOrReplace": {
"_id": "person-1",
"_type": "person",
"name": "John Appleseed"
}},
{"createOrReplace": {
"_id": "person-2",
"_type": "person",
"name": "Carrie Anderton"
}}
]
}
The following mutation types exist in Sanity:
create
createOrReplace
createIfNotExists
delete
patch
A create
mutation creates a new document. It takes the literal document content as its argument. The rules for the new document's identifier are as follows:
- If the
_id
attribute is missing, then a new, random, unique ID is generated. - If the
_id
attribute is present but ends with.
, then it is used as a prefix for a new, random, unique ID. For example,foo.
might generatefoo.s4tZYDUyXCCef1YpYu6Js5
. - If the
_id
attribute is present, it is used as-is. _createdAt
and_updatedAt
may be submitted and will override the default which is of course the current time. This can be used to reconstruct a data-set with its timestamp structure intact.
The _type
attribute must always be included and must be a valid Sanity type-name.
The operation will fail if a document by the provided ID already exists.
{ "create": document }
{
"mutations": [
{
"create": {
"_id": "123",
"_type": "cms.article",
"title": "An article"
}
}
]
}
A createOrReplace
mutation attempts to create a new document, and will replace its contents if it already exists. If the document already exists and the type is the same as before, the provided document will act as a set
patch, replacing its entire contents. If the document changes type, it will act as a delete
then create
. If a document has hard references pointing to it, you won't be allowed to change its type with this (or any other) mutation.
{ "createOrReplace": document }
const mutations = [{
createOrReplace: {
_id: '123',
_type: 'cms.article',
title: 'An article'
}
}]
fetch(`https://${projectId}.api.sanity.io/v2021-06-07/data/mutate/${datasetName}`, {
method: 'post',
headers: {
'Content-type': 'application/json',
Authorization: `Bearer ${tokenWithWriteAccess}`
},
body: JSON.stringify({mutations})
})
.then(response => response.json())
.then(result => console.log(result))
.catch(error => console.error(error))
A createIfNotExists
mutation creates a new document, but will silently fail if the document already exists. It is otherwise identical to create
.
{ "createIfNotExists": document }
{
"mutations": [
{
"createIfNotExists": {
"_id": "123",
"_type": "cms.article",
"title": "An article"
}
}
]
}
Deletes a document. The content should reference a single ID. The operation will be considered a success even if the document did not exist.
{ "delete": { "id": string } }
{
"mutations": [
{
"delete": {
"id": "123"
}
}
]
}
By submitting GROQ-query instead of an id, multiple documents can be deleted in a single mutation.
{
"mutations": [
{
"delete": {
"query": "*[_type == 'feature' && viewCount < $views]",
"params": {
"views": 5
},
}
}
]
}
Deletes all documents of type "feature" where the visitCount
is less than 5. See the GROQ documentation for valid queries.
Gotcha
A mutation that specifies a query can only operate on up to 10,000 documents! This means that a mutation based on a query such as *[_type == "article"]
is in fact executed as if the query were written *[_type == "article"][0..10000]
.
To perform mutations on larger sets of documents, you will need to split them into multiple transactions. We recommend paginating by _id
.
E.g., *[_type == "article" && _id > $lastId]
. This works because GROQ will, by default, sort documents by ascending _id
. Since each transaction returns the _id
s of modified documents, you can use the last returned _id
as the next lastId
parameter.
You can use the optional flag purge
to request the document history to be fully purged from the Content Lake. When using this option, all transactions related to the document will be immediately removed, consistent with our data retention policy, and no longer show on Studio's history experience, nor on the Content Lake history API endpoint.
{
"mutations": [
{
"delete": {
"id": "123",
"purge": true
}
}
]
}
A patch
mutation updates an existing document's contents. A patch will fail if the document does not exist. A patch mutation contains one or more patch operations.
If multiple patches are included, then the order of execution is as follows: set
, setIfMissing
, unset
, inc
, dec
, insert
.
For supported patches, see the patch reference.
{
"mutations": [
{
"patch": {
"id": id,
"ifRevisionID": string,
"set": set,
"setIfMissing": setIfMissing,
"unset": unset,
"inc": inc,
"dec": dec,
"insert": insert,
"diffMatchPatch": diffMatchPatch,
}
}
]
}
{
"mutations": [
{
"patch": {
"id": "123",
"set": {
"name.first": "John"
},
"inc": {
"visitCount": 1
},
"params": {
"id": "123"
}
}
}
]
}
A patch may include an optional ifRevisionID
field. If the document's current revision ID does not match the provided revision ID, the patch will fail. This can be used for optimistic locking, to make sure that mutations are applied in the exact order expected by the application.
By submitting a query instead of an id, you may patch multiple documents at once. This will reset the score and add a bonus point to any person that has more than 100 points:
{
"mutations": [
{
"patch": {
"query": "*[_type == 'person' && points >= $threshold]",
"params": {
"threshold": 100
},
"dec": {
"points": 100
},
"inc": {
"bonuses": 1
}
}
}
]
}
To test a mutation without impacting any documents, you can set the dryRun
query parameter to true
:
curl 'https://<project-id>.api.sanity.io/v2021-06-07/data/mutate/<dataset-name>?dryRun=true' \
-H 'Authorization: Bearer <token>' \
-H 'Content-Type: application/json' \
--data-binary '{"mutations": [{"patch": {"query": "*[_type == \"person\" && points >= 100]","dec": {"points": 100},"inc": {"bonuses": 1}}}]}'