Studio Docs

Studio API: Endpoint Reference

Every documented route of the managed Studio API service, in one table, plus the envelope they all share: base URLs, versioning, the authentication split, and the request/response conventions. Each group links to the page that documents its routes in full.

Preview: not yet GA. Paths and response shapes may still change, and the service may not be enabled on your region or tenant yet. If a call 404s at the host, the service isn’t deployed for you. Use the raw CMA path in the meantime.

The API surface is 16 routes across three groups. Compositions are the reason the service exists. The rest support them: projects hold the stack/content-type/locale binding every composition route reads, and registered components report what a project can place.

Do It With a Skill

authenticate-cma is the credential ladder these endpoints assume, and use-compositions-api the skill that calls them.

curl -fsSL https://studio-documentation.contentstackapps.com/install.sh | sh

Every Route at a Glance

GroupOperationMethod and path
ProjectsList projectsGET /v1/projects
Fetch a projectGET /v1/projects/{uid}
Create a projectPOST /v1/projects
Update a projectPUT /v1/projects/{uid}
Delete a projectDELETE /v1/projects/{uid}
CompositionsCreate a compositionPOST /v1/projects/{projectUid}/compositions
Create a templatePOST /v1/projects/{projectUid}/compositions/templates
Create a sectionPOST /v1/projects/{projectUid}/compositions/sections
List / queryGET /v1/projects/{projectUid}/compositions
Fetch oneGET /v1/projects/{projectUid}/compositions/{uid}
Fetch as templateGET /v1/projects/{projectUid}/compositions/{uid}/template
Fetch as sectionGET /v1/projects/{projectUid}/compositions/{uid}/section
List referencesGET /v1/projects/{projectUid}/compositions/{uid}/references
UpdatePUT /v1/projects/{projectUid}/compositions/{uid}
DeleteDELETE /v1/projects/{projectUid}/compositions/{uid}
Registered componentsList synced componentsGET /v1/projects/{projectUid}/registered-components

Every route requires the organization_uid header, and every route is scoped to a project, either through the path or through the project record it resolves.

Success codes follow the verbs: POST returns 201, everything else 200. Two exceptions are worth knowing up front: the flavor-scoped fetches return 404 when the uid is the other flavor, and DELETE …/compositions/{uid} returns 409 when something still references it. Failure statuses and the error_code catalog live in Errors and validation.

1. Base URL and Versioning

The service is URI-versioned: defaultVersion: '1' with prefix v, so every path begins /v1. There is no unversioned alias.

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

Paths are identical across regions: only the host changes. Each region is a separate deployment with its own data, so a project created in one region is not reachable from another region’s host.

2. Authentication: Where It Actually Happens

The service does not authenticate you itself. The Contentstack API gateway does, and passes the resolved caller downstream as headers (x-user-type, x-user-uid, x-organization-uid, plus principal-specific fields). A global request hook turns those into the request’s identity before any handler runs.

Three consequences worth knowing:

  • No route is anonymous. The hook runs for every request without exception.
  • Your principal type is preserved. A user session, an OAuth app, a management token, and a preview token each reach the access check as themselves. This matters: an earlier version forced every caller to SystemUser, which discarded OAuth identity and denied OAuth writes with a misleading 422 stack_not_found.
  • If identity can’t be resolved (for instance the authorization sidecar is unreachable), the request fails with 500 rather than continuing unauthenticated.

Send one credential plus the organization:

HeaderValueRequired
authtokenContentstack session token, how the Studio browser app authenticatesone of the two
authorizationBearer <access_token>, how OAuth clients (the CLI, the Figma plugin) authenticateone of the two
organization_uidThe organization UID that owns the projectYes

The second hop: which credential gets forwarded

Gateway identity alone isn’t enough for the routes that call another Contentstack service on your behalf. Those handlers re-read your raw credential header and forward it, because the service holds no credential of its own. It acts as you. Both styles are first-class:

You sendForwarded upstream as
authtoken: <session token>authtoken: <session token>
authorization: Bearer <access token>authorization: Bearer <access token>

Exactly one credential header is ever sent upstream. When a request carries both, the gateway-asserted principal decides (x-user-type: OAuthBot selects the OAuth token) so the credential forwarded always matches the identity the permission check ran on. A non-Bearer authorization value, such as a raw management token, is deliberately not relayed.

The Bearer prefix is required. Upstream reads a bare authorization value as a management token, so dropping the prefix fails as the wrong credential kind rather than as a malformed one.

Which routes do this:

RoutesForwards your credentialTo
CompositionsRequired (401 without one)Contentstack’s CMA, to read and write entries
Registered componentsRequired (401 without one)Studio’s component service
ProjectsNot readNo downstream call to delegate to

Missing credentials surface as 401 error_code 39 composition_missing_auth_token on composition routes and 401 error_code 48 registered_components_missing_auth_token on registered components. Both fire only when neither credential reached the service.

A gateway dependency worth knowing. This all assumes the gateway forwards your raw authtoken / authorization header downstream, not just the identity headers derived from it. Where it doesn’t, you get the clear 401 above rather than an opaque upstream failure.

Access checks

CheckWhat it verifiesOn failure
IdentityThe gateway resolved a callerrequest never reaches the handler
Project accessYou have read/write access to the project’s connected stack422 stack_not_found (21)
Delegation (compositions, registered components)A credential reached the service and the upstream accepts it401 (39 / 48) if neither credential arrived. The upstream 401/403 is preserved if it rejects the one that did

GET routes need read access to the project’s stack. POST/PUT/DELETE need write access. Denial is 422, not 403, the Studio convention across this service.

3. Request and Response Conventions

These hold for every route, so the per-group pages don’t repeat them.

ConventionBehavior
Unknown body fieldsSilently stripped, not rejected. The service validates against a whitelist. A typo’d field name is discarded without a warning.
DTO-shape failuresReturn the framework’s { statusCode, message, error } shape, not the service error envelope. Validation stops at the first error on a field.
Service-level errorsUse the { error_message, error_code, errors } envelope. Branch on error_code, not message text. See the error catalog.
Request timeout60 seconds, applied globally.
CORSGET, POST, PUT, PATCH, DELETE, OPTIONS.
Trailing slashesIgnored: /v1/projects and /v1/projects/ are the same route.

Client gotcha: body-less DELETE. Do not send Content-Type: application/json on a DELETE with no body. The server rejects that combination with a 400 before the handler runs. Send the DELETE with no Content-Type header. This applies to both delete routes.

4. Machine-Readable Contract

Every route on this page is also described in the OpenAPI 3.1 spec. Generate an MCP tool server, an SDK, or a Postman collection with any OpenAPI-consuming generator. Each operationId doubles as an MCP tool name.

5. Cross-Cutting Pitfalls

PitfallWhy it bitesFix
Missing organization_uid returns 404, not 400The header feeds the org scope of the lookup, so an absent value reads as “no such record” rather than “bad request”. Affects every group.Send organization_uid on every call. Treat an unexplained 404 as a missing-header suspect first.
A project document with no deletedAt field is invisibleProject lookups filter on deletedAt: false, which does not match a document where the field is absent, for example one written before the field existed.Confirm the record carries deletedAt: false. A project that lists but won’t fetch is the signature.
Write denial surfaces as 422, not 403stack_not_found (21) is returned when the stack ACL rejects a write, which reads like a missing stack rather than a permissions problem.Check the caller’s access to the project’s connected stack before hunting for a bad projectUid.
Registered components has no paginationThe upstream component service returns the full set in one response.Expect the whole list. There are no limit/skip params to reach for.

Where to Go Next

  • Projects: the five project routes, the project record’s fields, and what each one binds.
  • Compositions: the ten composition routes, with request/response shapes, query params, and curl examples.
  • Registered components: what the synced-component list does and doesn’t contain.
  • Errors and validation: the response envelope, the full status and error_code catalog, and the Studio rules enforced on every write.