Studio API: the managed Studio service

View as Markdown
Last updated September 14, 2026

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 callPOST /v1/projects/:projectUid/compositionsPOST /v3/content_types/{ct}/entries
ui specSend the uncompressed node tree, the service compresses itYou zlib-encode it yourself before sending
ValidationDone for you (Studio structural rules, collect-all)None: a bad tree fails later at render
composable_uidBackfilled / uniqueness-checked for youYou manage it
AuthYour forwarded session authtoken or OAuth token (acts as you)Management token or session token
Best forApp/backend integrations that require enforced request validation + a stable contractMigrations, 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}/compositions

The 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:

RegionBase URL
AWS NAhttps://composable-studio-api.contentstack.com/v1
AWS EUhttps://eu-composable-studio-api.contentstack.com/v1
AWS AUhttps://au-composable-studio-api.contentstack.com/v1
Azure NAhttps://azure-na-composable-studio-api.contentstack.com/v1
Azure EUhttps://azure-eu-composable-studio-api.contentstack.com/v1
GCP NAhttps://gcp-na-composable-studio-api.contentstack.com/v1
GCP EUhttps://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.

3. Authentication and authorization

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:

HeaderValueRequired
authtokenYour Contentstack session token, how the Studio browser app authenticatesone of the two
authorizationBearer <access_token>, how OAuth clients authenticateone of the two
organization_uidThe organization UID that owns the projectYes, 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:

CheckWhat it verifiesOn failure
IdentityThe gateway resolved a caller from the requestrequest never reaches the handler
Project accessYou have read/write access to the project's connected stack422: access denied
DelegationA credential actually reached the service, and Contentstack accepts it401 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.

FieldYou sendThe service does
uiThe node tree as a JSON objectValidates it against Studio rules, then compresses to zlib:<base64> before saving
data_sourcesA plain arrayJSON-stringifies it for storage
composable_uidOptionalIf 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:

  1. 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.
  2. Projects: the five project routes and the record every composition call resolves through.
  3. 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.
  4. Registered components: the read-only route that lists the components a project has synced, with their prop schemas.
  5. Errors and validation: the response envelope, the full status/error_code catalog, and the Studio validation rules the service enforces.
  6. 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.
  7. 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.