Studio API: the managed Studio service
Preview: not yet GA. This chapter documents the Composable Studio API service, a managed REST layer for compositions and the project records they hang off. The service is rolling out. Endpoint paths and response shapes may still change, and it may not be enabled on your region/tenant yet. Behavior here is verified against the service source. Anything that depends on gateway/tenant configuration is called out inline. If a call 404s at the host, the service isn't deployed for you yet. Use the raw CMA path in the meantime.
What this is (and isn't). A thin REST API that creates compositions for you: it validates the layout against Studio's rules, compresses the ui spec, forwards your identity to Contentstack, and returns clean, field-anchored errors. It does not replace the CMS: compositions are still Contentstack entries underneath. This page is the shared envelope. The per-group route pages and the error/validation reference are linked at the end.
Looking for one route in particular? The Endpoint reference lists all 16 routes across the three groups in a single table.
Two ways to create a composition: pick the right one
There are now two documented paths to the exact same result (a composition entry in your stack). They are not competitors. They sit at different levels.
| Compositions API service (this chapter) | Raw CMA (that chapter) | |
|---|---|---|
| You call | POST /v1/projects/:projectUid/compositions | POST /v3/content_types/{ct}/entries |
| ui spec | Send the uncompressed node tree, the service compresses it | You zlib-encode it yourself before sending |
| Validation | Done for you (Studio structural rules, collect-all) | None: a bad tree fails later at render |
| composable_uid | Backfilled / uniqueness-checked for you | You manage it |
| Auth | Your forwarded session authtoken or OAuth token (acts as you) | Management token or session token |
| Best for | App/backend integrations that require enforced request validation + a stable contract | Migrations, CI, server-to-server scripts that require full low-level control |
Rule of thumb. Reach for the service when you want Studio to catch mistakes before they reach the canvas and you're acting on behalf of a signed-in user. Reach for the raw CMA when you're scripting bulk/migration work with a management token and want to own every field yourself. The ui-tree vocabulary is identical either way: the Building Blocks, Section Catalog, and Template Catalog references apply to both.
1. What you're creating
A composition is a saved layout tree (the ui spec) plus the data bindings that drive it, stored as a Contentstack entry of your project's composition content type. The service doesn't invent a new storage model. It wraps the CMS. Two flavors, exactly as elsewhere in Studio:
- Template: a full page with a URL. (Wire value: place_composition_as: "page".)
- Section: a reusable block, no URL of its own. (Wire value: place_composition_as: "section".)
Concept guides: What's a composition?, Templates, Sections, Templates vs Sections.
2. Base URL & versioning
The service is URI-versioned: every route is prefixed with /v1. Compositions are nested under a project, because the project record is what ties the request to a stack, a content type, and a locale:
/v1/projects/{projectUid}/compositionsThe other route groups sit alongside it: /v1/projects for projects, and registered components nested under a project like compositions. The Endpoint reference has the full list.
Pick the base URL for your region:
| 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 |
You never specify the stack, content type, or locale in the URL. The service reads them from the project (projectUid). If your project uses a custom composition content type, the service has that value stored. You don't pass it.
On the composition routes the service acts as the calling user: it forwards your own credential to the CMA rather than holding a service credential of its own. Send one credential plus the organization:
| Header | Value | Required |
|---|---|---|
| authtoken | Your Contentstack session token, how the Studio browser app authenticates | one of the two |
| authorization | Bearer <access_token>, how OAuth clients authenticate | one of the two |
| organization_uid | The organization UID that owns the project | Yes, everywhere |
Both credential styles work. A session authtoken and an OAuth Authorization: Bearer token are equally first-class, and the service forwards whichever one you used. 401 error_code 39 composition_missing_auth_token fires only when neither reaches the service. The Bearer prefix is required. The CMA reads a bare authorization value as a management token. For how the credential is chosen when a request carries both, and which routes forward one, see The second hop. Resolving a credential in the first place is covered by authenticate-cma.
There are three checks, each with its own failure code, useful for telling which layer rejected you:
| Check | What it verifies | On failure |
|---|---|---|
| Identity | The gateway resolved a caller from the request | request never reaches the handler |
| Project access | You have read/write access to the project's connected stack | 422: access denied |
| Delegation | A credential actually reached the service, and Contentstack accepts it | 401 if neither credential arrived. The upstream 401/403 is preserved if Contentstack rejects the one that did |
Reads vs writes. GET routes require read access to the project's stack. POST/PUT/DELETE require write access. Denial returns 422 (the Studio convention), not 403.
Management tokens. The service authenticates as the caller, forwarding the session or OAuth credential you presented. A caller-supplied management token is not accepted: a non-Bearer authorization value is deliberately never relayed, so the service won't pass on a stack-scoped credential it didn't issue. Server-to-server automation that needs a management token should use the raw CMA path.
4. The composition entry & the ui spec
The single biggest difference from the raw CMA: you send the ui tree uncompressed, and the service compresses it. You never touch zlib.
| Field | You send | The service does |
|---|---|---|
| ui | The node tree as a JSON object | Validates it against Studio rules, then compresses to zlib:<base64> before saving |
| data_sources | A plain array | JSON-stringifies it for storage |
| composable_uid | Optional | If omitted, backfills it with the created entry uid. If you provide it, it must be non-blank and unique across the content type |
Everything about how to shape the node tree itself (node anatomy, value sources, Repeater / Condition Block / Section Slot / Binding Override, the closed sets of prop and node types) is shared with the raw-CMA path and lives in one place:
Build the ui tree: Building Blocks (start with "the anatomy of a node"), worked JSON for every shape in the Section Catalog and Template Catalog.
The service adds a safety net the raw path doesn't have: structural validation on every write, returning all problems at once, field-anchored, split into blocking errors and non-blocking warnings. See the Errors & validation reference.
5. Where to go next
You now have the envelope. The rest of the chapter:
- Endpoint reference: all 16 routes in one table, plus the envelope they share: base URLs, versioning, the authentication split, and the request/response conventions. Start here if you're looking for a specific route.
- Projects: the five project routes and the record every composition call resolves through.
- Compositions: the ten composition routes (create, list, fetch, references, update, delete, plus the flavor-scoped variants), with request/response shapes, query params, and curl examples.
- Registered components: the read-only route that lists the components a project has synced, with their prop schemas.
- Errors and validation: the response envelope, the full status/error_code catalog, and the Studio validation rules the service enforces.
- OpenAPI 3.1 spec: the machine-readable contract for every route in this chapter. Generate an MCP tool server, an SDK, or a Postman collection with any OpenAPI-consuming generator. Each operationId is designed to be used verbatim as the MCP tool name. summary / description fields carry the tool docs.
- Generated service spec: the OpenAPI 3.0 document the service itself emits (npm run openapi:studio in composable-studio-api). Use it when you want worked request bodies for each create route (repeater + condition block, freeform pinned entry/query, section slot, exposed props), the full 412 / 502 error contract with service error codes, or the node and binding schemas. It omits the three flavor-scoped read routes, and its operationIds are handler names, so prefer the 3.1 spec for MCP tooling.
For the layout-tree vocabulary shared with the raw-CMA path, keep Building Blocks, Sections, and Templates open alongside.