API endpoints
Look up any Studio API endpoint, the headers it needs and the parameters it takes. Use it when you call the API from a script and need the exact route or field. To create a composition through the API from start to finish, see What the API is for. For what a failed call returns, see API errors and validation.
The example on this page is a project whose UID is 0123456789abcdef01234567, and its Blog post template, whose entry UID is blt4444444444444444. The template's URL pattern is /blog/{{entry.slug}}.
Base URL for each region
Every route on this page starts with /v1.
| Region | Base URL |
|---|---|
| AWS NA | https://composable-studio-api.contentstack.com/v1 |
| AWS EU | https://eu-composable-studio-api.contentstack.com/v1 |
| AWS AU | https://au-composable-studio-api.contentstack.com/v1 |
| Azure NA | https://azure-na-composable-studio-api.contentstack.com/v1 |
| Azure EU | https://azure-eu-composable-studio-api.contentstack.com/v1 |
| GCP NA | https://gcp-na-composable-studio-api.contentstack.com/v1 |
| GCP EU | https://gcp-eu-composable-studio-api.contentstack.com/v1 |
Use the region your stack is in. Each region keeps its own projects, so you can't reach a project in one region from another region's URL.
Headers every call needs
| Header | Value | Required |
|---|---|---|
authtoken | Your Contentstack session token. | One of authtoken or authorization |
authorization | Bearer <access_token>, an OAuth access token. The Bearer prefix is required. | One of authtoken or authorization |
organization_uid | The UID of the organization that owns the project. | Yes, on every call |
Content-Type | application/json | On POST and PUT only |
The API acts as you. On composition and registered component routes, it passes your token on to Contentstack, so you can do what your token can do.
The API passes on only an authtoken session token or an authorization: Bearer OAuth access token. It ignores an authorization value without the Bearer prefix, such as a bare management token.
If you send both authtoken and authorization, the API uses the one that matches the identity Contentstack found for the request.
Access your token needs
| Method | What your token needs |
|---|---|
GET | Read access to the project's connected stack |
POST, PUT, DELETE | Write access to the project's connected stack |
On composition and registered component routes, a token without that access gets 422 with error code 21, not 403. On project routes, a project you can't read answers 404 with error code 1. The project list never fails for lack of access. It leaves out the projects whose stack you can't read. See API errors and validation.
How the API handles a request
| You send | What happens |
|---|---|
| A top-level body field the route doesn't know | The API drops it without an error, so it ignores a misspelled field name. Keys inside settings stay, so a misspelled key there is saved. |
| A trailing slash | The API ignores it. /v1/projects and /v1/projects/ are the same route. |
A DELETE with Content-Type: application/json and no body | The API answers 400 before the route runs. Send a DELETE without Content-Type. |
| A call that runs longer than 60 seconds | The API stops waiting and answers 408. The write can still finish, so list before you retry. |
| A call that succeeds | POST answers 201. Every other method answers 200. |
Project routes
| Method | Path | What it does |
|---|---|---|
GET | /v1/projects | Lists the projects in the organization whose stack you can read, plus the projects you created. |
GET | /v1/projects/{uid} | Returns one project. If you can't read it, the API answers 404 instead of telling you it exists. |
POST | /v1/projects | Creates a project. You need write access to the stack you connect. |
PUT | /v1/projects/{uid} | Changes the top-level fields you send and keeps the rest. settings is replaced whole. The API checks your access against the stack the project is connected to now, even when you send a new one. |
DELETE | /v1/projects/{uid} | Removes the project from Studio. Its compositions stay in the stack as entries. There's no way to restore it through the API. |
The fields you can send when you create or change a project:
| Field | Required on create | What it holds |
|---|---|---|
name | Yes | The project name. Up to 256 characters. |
connectedStackApiKey | Yes | The API key of the stack the project uses. |
contentTypeUid | Yes | The content type in that stack that holds compositions. |
description | No | Up to 1024 characters. Defaults to empty. |
canvasUrl | No | The path of your section preview route, such as /studio. Up to 100 characters. Studio opens a Section's canvas at the environment's Base URL plus this path. With no Base URL for the environment and language, Studio uses the Playground canvas. With a Base URL and an empty path, the canvas shows "Canvas URL is not configured". |
settings | No | { "configuration": { "environment": "…", "locale": "…" } }, plus isFreeformEnabled when Freeform is on. environment is an environment UID and locale is a language code. The API keeps any key you send and replaces settings whole. Composition writes always use the locale, and reads use it when you don't pass one. |
The API sets every other field, such as uid, organizationUid or createdBy. If you send one, nothing changes.
Composition routes
Every composition path starts with /v1/projects/{projectUid}/compositions. The project decides which stack, content type and default locale the call uses, so none of them go in the path.
| Method | Path after /compositions | What it does |
|---|---|---|
POST | none | Creates a template or a section. place_composition_as in the body decides which. |
POST | /templates | Creates a template. Leave out place_composition_as and linked_schemas. |
POST | /sections | Creates a section. Leave out place_composition_as, url and connected_content_type. |
GET | none | Lists templates and sections. |
GET | /templates | Lists templates only. |
GET | /sections | Lists sections only. |
GET | /{uid} | Returns one composition. |
GET | /{uid}/references | Lists the entries that use this composition, in every language. |
PUT | /{uid} | Changes the fields you send and keeps the rest. |
DELETE | /{uid} | Deletes the composition. Answers 409 if another composition uses it, unless you pass force=true. |
For the Blog post template, GET /v1/projects/0123456789abcdef01234567/compositions/blt4444444444444444/references lists what uses it.
Fields you can send for a composition
These fields apply when you create a composition and when you PUT one. On a PUT, every field is optional.
| Field | Required on create | What it holds |
|---|---|---|
title | Yes | Up to 256 characters. Can't be only spaces. |
place_composition_as | Yes, on the plain create route | page for a template, section for a section. |
ui | Yes | The layout tree as a JSON object, not compressed. The API checks it and compresses it. Its root node has the type page, for sections too. |
url | No | A template's URL pattern, such as /blog/{{entry.slug}}. See URL variables. |
connected_content_type | No | The content type a connected template uses. Empty for a freeform template and for a section. |
composable_uid | No | The composition's stable ID. If you leave it out, the API sets it to the entry UID. If you send it, it can't be blank, and no other composition in the content type can have it, in any language. You can't change it later. |
schema_version | No | A string. Defaults to 1.0.0. |
data_sources | No | An array. Defaults to []. |
static_value | No | An object of fixed values used by the layout. |
url_metadata | No | An object. |
linked_schemas | No | An array of the schemas a section can match. The /templates route drops it, but the plain create route stores what you send, even on a template. See Linked schema matching rules. |
ui_preview | No | A preview image asset. |
The API works out which sections a composition places from ui, so you never send that list.
A create or PUT answers with the saved composition. In it, ui is a compressed string that starts with zlib:, and data_sources is a JSON string. A PUT that sends no ui returns the stored value, which can be plain JSON text if something else wrote the entry. If the layout has problems that don't block the save, the answer also has a warnings array. See Layout rules the API checks on every write.
Parameters for listing compositions
The three list routes take these query parameters. The /templates and /sections routes don't take place_composition_as.
| Parameter | Default | What it does |
|---|---|---|
place_composition_as | none | page or section. |
composable_uid | none | Returns the composition with exactly this ID. |
title | none | Returns compositions whose title contains this text, ignoring case. |
limit | 50 | How many to return, from 1 to 100. A higher number answers 400. |
skip | 0 | How many to skip. |
include_count | false | true adds the total number of matches as meta.count. |
include_ui | false | true adds ui and data_sources to each item. Without it, each item leaves both out. |
decompression | false | With include_ui=true, returns each ui as a JSON object. An item whose ui can't be read stays compressed. |
locale | The project's locale | The language to read. |
The answer has compositions and meta. meta holds limit, skip, returned, include_ui, and count when you asked for it.
Parameters for getting or deleting one composition
| Route | Parameter | Default | What it does |
|---|---|---|---|
GET /{uid} | decompression | false | true returns ui as a JSON object and data_sources as an array. meta.compressed says which form you got. |
GET /{uid} | locale | The project's locale | The language to read. |
DELETE /{uid} | force | false | true deletes the composition even when another composition uses it. |
Registered components route
| Method | Path | What it does |
|---|---|---|
GET | /v1/projects/{projectUid}/registered-components | Lists the components synced to the project with csdx studio:component:sync, with their props. The route is read-only. |
This list isn't the palette. If you registered a component in your app's code but never synced it, it doesn't appear here. See CLI commands and Register a component.
The route takes no query parameters. It returns every synced component at once, as registered_components, with meta.count. A project with nothing synced answers 200 with an empty list.
| Component field | What it holds |
|---|---|
uid | The component's UID. |
organization_uid | The organization that owns it. |
projectId | The project UID. |
componentName | The component's name in code, such as HeroBanner. A node's type is the string your app passes to registerComponent, which this list doesn't return. |
displayName | The label in the palette. It's often empty, so fall back to componentName. |
description, aiDescription | Descriptions for people and for AI tools. Either can be empty. |
version | The version recorded at sync. |
importPath | Where the component's source file is, such as src/components/HeroBanner.tsx. |
createdAt, updatedAt | When you first synced it, and when it last changed. |
props | The component's props. Each has type and name, and can have displayName, description, placeholder and defaultValue. |
Where to go next
- Read what a failed call returns: API errors and validation.
- Create a project in Studio instead: Create a project.