🎤 Builder Talk: The Story Behind Lady Gaga’s Digital Experience – Register now

Content Release API Cheat Sheet

Query and modify Content Releases using the API

Interfacing with Content Releases is similar to interfacing with other documents and relationships in the Content Lake.

Prerequisites:

  • The examples below use a variety of APIs to cover common patterns. As releases aren't public, make sure your requests are authenticated and match the correct URL format for each API. For example, using Sanity clients or other integrations, you'll need to configure them with an appropriate token and permissions to view unpublished content.
  • Release APIs and features are available in API version 2025-02-19 and later unless otherwise noted.

Get all releases for a project and dataset

Access releases by querying for documents with a _type of system.release. This is the preferred method for retrieving a list of releases.

JS Client

Use the client's fetch method to query for releases.

Input

import { createClient } from "@sanity/client";

const client = createClient({
    projectId: '<project-id>',
    dataset: '<dataset>',
    useCdn: true,
    apiVersion: 'vX',
    token: '<token>',
    perspective: 'raw' //default
})

const query = "*[_type == 'system.release']"
const params = {}
client.fetch(query, params).then((data)=>{
  console.log(data)
})

Output

[
    {
      "_createdAt": "2024-11-26T21:30:57Z",
      "finalDocumentStates": null,
      "_updatedAt": "2024-12-17T16:33:26Z",
      "_type": "system.release",
      "name": "rHw6FBu82",
      "_id": "_.releases.rHw6FBu82",
      "state": "active",
      "metadata": {
        "releaseType": "scheduled",
        "title": "End of year release",
        "intendedPublishAt": "Mon Dec 30 2024"
      },
      "publishAt": "2024-12-30T08:00:00Z",
      "_rev": "JmI5JuFTDPq3paS6p09Jmu",
      "userId": "paATypsg4"
    },
    {
      "publishAt": null,
      "_rev": "1kbjGQwz5Z0FmijO2l7Lwl",
      "finalDocumentStates": [
        {
          "id": "versions.rglJO3Sfg.movie_70981",
          "_key": "1kbjGQwz5Z0FmijO2l7M0E"
        }
      ],
      "_id": "_.releases.rglJO3Sfg",
      "state": "published",
      "metadata": {
        "title": "Quick fixes",
        "releaseType": "asap"
      },
      "_createdAt": "2024-11-26T22:01:56Z",
      "_type": "system.release",
      "name": "rglJO3Sfg",
      "_updatedAt": "2024-12-02T17:32:59Z",
      "userId": ""
    },
    {
      "_createdAt": "2024-11-26T18:34:14Z",
      "name": "rqZSzJ1uS",
      "finalDocumentStates": [
        {
          "id": "versions.rqZSzJ1uS.movie_10681"
        }
      ],
      "userId": "paATypsg4",
      "_id": "_.releases.rqZSzJ1uS",
      "state": "published",
      "_updatedAt": "2024-12-05T17:22:00Z",
      "metadata": {
        "releaseType": "scheduled",
        "description": "Experimental updates for testing",
        "title": "Experimental updates",
        "intendedPublishAt": "2024-12-05T17:22:00.000Z"
      },
      "publishAt": "2024-12-05T17:22:00Z",
      "_rev": "5dKCVUpSDccCmU1E23aUAc",
      "_type": "system.release"
    },
  // ...
  ],

Query API

You access releases by querying for documents with a _type of system.release using the Query API. This is the preferred method for retrieving a list of releases.

Use the following GROQ query in the API request:

// Target the document type
*[_type == 'system.release']

// This is also equivalent
releases::all()

Example GET request

curl "https://<project-id>.api.sanity.io/vX/data/query/<dataset-name>?query=<GROQ-QUERY>" \
    --H "Authorization: Bearer <token>" \

Example response

{
  "query": "*[_type == 'system.release']",
  "result": [
    {
      "_createdAt": "2024-11-26T21:30:57Z",
      "finalDocumentStates": null,
      "_updatedAt": "2024-12-17T16:33:26Z",
      "_type": "system.release",
      "name": "rHw6FBu82",
      "_id": "_.releases.rHw6FBu82",
      "state": "active",
      "metadata": {
        "releaseType": "scheduled",
        "title": "End of year release",
        "intendedPublishAt": "Mon Dec 30 2024"
      },
      "publishAt": "2024-12-30T08:00:00Z",
      "_rev": "JmI5JuFTDPq3paS6p09Jmu",
      "userId": "paATypsg4"
    },
    {
      "publishAt": null,
      "_rev": "1kbjGQwz5Z0FmijO2l7Lwl",
      "finalDocumentStates": [
        {
          "id": "versions.rglJO3Sfg.movie_70981",
          "_key": "1kbjGQwz5Z0FmijO2l7M0E"
        }
      ],
      "_id": "_.releases.rglJO3Sfg",
      "state": "published",
      "metadata": {
        "title": "Quick fixes",
        "releaseType": "asap"
      },
      "_createdAt": "2024-11-26T22:01:56Z",
      "_type": "system.release",
      "name": "rglJO3Sfg",
      "_updatedAt": "2024-12-02T17:32:59Z",
      "userId": ""
    },
    {
      "_createdAt": "2024-11-26T18:34:14Z",
      "name": "rqZSzJ1uS",
      "finalDocumentStates": [
        {
          "id": "versions.rqZSzJ1uS.movie_10681"
        }
      ],
      "userId": "paATypsg4",
      "_id": "_.releases.rqZSzJ1uS",
      "state": "published",
      "_updatedAt": "2024-12-05T17:22:00Z",
      "metadata": {
        "releaseType": "scheduled",
        "description": "Experimental updates for testing",
        "title": "Experimental updates",
        "intendedPublishAt": "2024-12-05T17:22:00.000Z"
      },
      "publishAt": "2024-12-05T17:22:00Z",
      "_rev": "5dKCVUpSDccCmU1E23aUAc",
      "_type": "system.release"
    },
  ],
  "syncTags": [
    "s1:r6H+EQ"
  ],
  "ms": 3
}

To view only active releases, and exclude archived releases, adjust your GROQ query to compare the state property.

*[_type == 'system.release' && state == 'active']

// or, with the function
releases::all()[state == 'active']

Modify release information

As releases are Sanity documents, you can interact with them the same way you would any other document. The preferred method is with the Actions API.

Create the action:

// Example release edit action
{
  "actionType": "sanity.action.release.edit",
  // Exclude the _.releases. prefix from the releaseId
  "releaseId":"rEGM2JqQ3",
  "patch": {
    "set": {
      "metadata": {        
        "title": "New release title"
      }
    }
  }
}

Include the action in the <action> portion below:

Example request

curl -X POST 'https://<project-id>.api.sanity.io/vX/data/actions/<dataset-name>' \
    -H 'Authorization: Bearer <token>' \
    -H 'Content-Type: application/json' \
    -D '{"actions":[<action>]}'

Example response

{
  "transactionId": "71GXGRT7Io3CmtCwpl6n8V"
}

You can also edit releases using the Mutate API.

Get all documents from a release

Query all documents associated with a release.

sanity::partOfRelease GROQ function

The sanity::partOfRelease GROQ function accepts a release ID and returns all documents associated with the release.

Use the function in a GROQ query and pass the release ID string.

import { createClient } from "@sanity/client";

const client = createClient({
    projectId: '<project-id>',
    dataset: '<dataset>',
    useCdn: false,
    apiVersion: '2025-02-19',
    token: '<token>',
    perspective: 'raw'
})

const query = "*[sanity::partOfRelease(<releaseID>)] { _id }"
const params = {}
client.fetch(query, params).then((data)=>{
  console.log(data)
})

Get all versions of a document

Query all versions (published, drafts, and release versions) of a document.

sanity::versionOf GROQ function

The sanity::versionOf GROQ function accepts a document ID and returns all versions of a document.

Use the function in a GROQ query and pass the document ID string.

import { createClient } from "@sanity/client";

const client = createClient({
    projectId: '<project-id>',
    dataset: '<dataset>',
    useCdn: false,
    apiVersion: '2025-02-19',
    token: '<token>',
    perspective: 'raw'
})

const query = "*[sanity::versionOf(<documentID>)] { _id }"
const params = {}
client.fetch(query, params).then((data)=>{
  console.log(data)
})

Gotcha

The function expects a non-prefixed document ID. For example: abc123 is acceptable, but drafts.abc123 and versions.r1324.abc123 are not.

The function also works with the Query API and anywhere that supports GROQ functions.

Doc API

Use the document ID, along with the Doc API endpoint to retrieve all versions of a specific document. The includeAllVersions boolean query parameter returns all versions for the document.

Example request for ID movie_70981

GET /vX/data/doc/production/movie_70981?includeAllVersions=true

Example response

{
  "documents": [
    {
      "_createdAt": "2018-06-13T08:57:45Z",
      "_id": "movie_70981",
      "_rev": "1kbjGQwz5Z0FmijO2l7Lwl",
      "_type": "movie",
      "_updatedAt": "2024-12-02T17:32:59Z",
      // ...
    },
    {
      "_createdAt": "2018-06-13T08:57:45Z",
      "_id": "drafts.movie_70981",
      "_rev": "3276f9d1-0343-4b78-a79e-8c6561942f1b",
      "_type": "movie",
      "_updatedAt": "2024-12-02T17:14:27Z",
      // ...
    },
    {
      "_createdAt": "2018-06-13T08:57:45Z",
      "_id": "versions.rHw6FBu82.movie_70981",
      "_rev": "a000fa99-072c-434c-8669-825103a111b7",
      "_type": "movie",
      "_updatedAt": "2024-11-27T18:36:19Z",
      // ...
    }
  ],
  "omitted": []
}

Use releases in perspective queries

Content Releases use a layering system that layers document versions atop one another, allowing you to create a custom perspective stack. You can learn more about layering in the Content Releases User Guide.

Perspective in addition to accepting the raw, published, and drafts states, also accepts a comma-separated list of release names. Releases take priority from left to right.

For example, in the perspective a,b,c you would see changes in a take priority over b and c, and changes in b take priority over c.

The published perspective is automatically added to the end, so even if a release only contains changes to one document, the response will include all matching published documents in addition to the release changes.

Query API

To query against a list of releases, use the perspective query parameter and order the release names by priority from left to right. For example: ?perspective=a,b,c.

Using a GROQ query such as *[_type == 'movie'] { _id } will return all document IDs that match the releases layer.

Query all movie documents from the listed release perspectives

GET https://<projectId>.api.sanity.io/vX/data/query/<dataset>?query‌‌‌‌‌‌=<GROQ-QUERY>&perspective=<release-name1>,<release-name2>

Example response

{
  "query": "*[_type == 'movie']{ _id }",
  "result": [
    { "_id": "a306e7cf-ea18-4a43-8ce2-0586073c41c8" },
    { "_id": "movie_10681" },
    { "_id": "movie_118340" },
    { "_id": "movie_126889" },
    { "_id": "movie_157336" },
    { "_id": "movie_17654" },

  ],
  "syncTags": ["s1:+jIWIw"],
  "ms": 5
}

JavaScript Client

In addition to the raw, published, and drafts values, the client also accepts an array of release name strings. You can add drafts to the end of the array to include drafts that aren't part of any release.

Edit the perspective value to insert your release names.

import { createClient } from "@sanity/client";

const client = createClient({
    projectId: '<project-id>',
    dataset: '<dataset>',
    useCdn: false, // Don't use the CDN for draft/release previewing
    apiVersion: '2025-02-19',
    token: '<token>',
    perspective: ['<release-name-1>', '<release-name-2>']
})

const query = "*[_type == 'movie']"
const params = {}
client.fetch(query, params).then((data)=>{
  console.log(data)
})

Another common pattern is to extend your client configuration for releases and draft preview by creating a new client from the existing one.

import { createClient } from "@sanity/client";

const client = createClient({
    projectId: '<project-id>',
    dataset: '<dataset>',
    useCdn: true,
    apiVersion: '2024-08-01',
    token: '<token>',
    perspective: 'published' //default
})

const previewClient = client.withConfig({
  useCdn: false,
  apiVersion: '2025-02-19',
  perspective: ['<release name>', 'drafts']
})

Trigger webhook by release state

The Release documents can be queried and trigger assigned webhooks.

The most useful way of triggering webhooks might be off the release state.

A release may have the following states:

  • active: The general state of a release that is not within one of the other states.
  • scheduled: A state resulting from calling the sanity.action.release.schedule action on the release.
  • published: A state resulting from either calling the sanity.action.release.publish action, or when a scheduled release is published due to reaching its publishAt time.
  • archived: A state resulting from calling the sanity.action.release.archive action.
  • deleted: A state resulting from calling the sanity.action.release.delete action on an archived release.

Additional transient states exist to indicate the asynchronous points when releases move between states:

  • scheduling/unscheduling: Intermediate states which will exist when moving to/from the scheduled state.
  • archiving/unarchiving: Intermediate states which will exist when moving to/from the archived state.
  • publishing: Intermediate state which will exist before reaching the published state. Note that a scheduled release will also transition through publishing.

To create a webhook that listens to all new releases, define a webhook rule as follows:

"rule":{
  "on":["create"],
  "filter":"_type == 'system.release'"
}

Additionally filters can enable triggering only on releases of a particular state. In this example only releases that have transitioned into a published state will trigger the webhook:

"rule": {
  "on": ["update"],
  "filter": "_type == 'system.release' && delta::changedAny(state) && state == 'published'"
}

Was this article helpful?