Studio provisioning API reference
Provisioning a Studio project touches five hosts, and each one is region-specific. One wrong host provisions a content type, tokens and a Studio project in the wrong data center. Nothing errors, because the calls succeed against whichever region that host belongs to. This page is the host map across regions, the non-prod host pattern, and the /v1/projects request shape that registers the project.
Overview
Two API surfaces do the work.
- Content Management API (CMA) creates the stack, the compositions content type, the environment, the delivery token and the preview token, and enables Live Preview on the stack.
- Studio API creates the Studio project through /v1/projects. It binds the project to the stack (connectedStackApiKey) and to the compositions content type (contentTypeUid), then configures the environment, the locale and Freeform.
Three more hosts appear at runtime rather than at provisioning time: the Content Delivery API (CDA), the Live Preview preview channel, and the images content delivery network (CDN). They belong in the same map because the SDK derives one host from another, so a mismatch between them fails after provisioning succeeds.
Every host below belongs to a single region. A project created in one region is not reachable from another region's host.
Prerequisites
Before you send any call on this page, have all four of the following:
- The region your stack lives in. Establish this first, because every host below depends on it. See Check Studio access.
- A resolved Contentstack Management API credential, either an Open Authorization (OAuth) access token or a session token. See Studio API authentication.
- The organization uid that owns the Studio project. The Studio interface shows it under organization settings.
- The stack api_key for the target stack. Read it from Settings > Stack.
Production host map
The table below lists every service Studio provisioning and rendering touch, for the seven production regions.
| Region | CMA | Studio API | CDA (host) | Live Preview (live_preview.host) |
|---|---|---|---|---|
| AWS NA (us) | api.contentstack.io | composable-studio-api.contentstack.com | (omit, default) | rest-preview.contentstack.com |
| AWS EU (eu) | eu-api.contentstack.com | eu-composable-studio-api.contentstack.com | eu-cdn.contentstack.com | eu-rest-preview.contentstack.com |
| AWS AU (au) | au-api.contentstack.com | au-composable-studio-api.contentstack.com | au-cdn.contentstack.com | au-rest-preview.contentstack.com |
| Azure NA (azure-na) | azure-na-api.contentstack.com | azure-na-composable-studio-api.contentstack.com | azure-na-cdn.contentstack.com | azure-na-rest-preview.contentstack.com |
| Azure EU (azure-eu) | azure-eu-api.contentstack.com | azure-eu-composable-studio-api.contentstack.com | azure-eu-cdn.contentstack.com | azure-eu-rest-preview.contentstack.com |
| GCP NA (gcp-na) | gcp-na-api.contentstack.com | gcp-na-composable-studio-api.contentstack.com | gcp-na-cdn.contentstack.com | gcp-na-rest-preview.contentstack.com |
| GCP EU (gcp-eu) | gcp-eu-api.contentstack.com | gcp-eu-composable-studio-api.contentstack.com | gcp-eu-cdn.contentstack.com | gcp-eu-rest-preview.contentstack.com |
AWS NA is the only region whose CMA host ends in .contentstack.io. Every other region uses <region>-api.contentstack.com. For the canonical, always-current list of Contentstack regional endpoints, see API endpoints by region.
Match the pair. Mixing an NA Studio API host with an EU CMA host returns 401 and 404 responses with no clear error. Pick a region and use both region-matched hosts.
Non-production hosts
Internal QA, staging and development stacks live on csnonprod.com. The Delivery SDK has no region shortcut for them, so you set every host explicitly. The pattern is <env>-<service>.csnonprod.com, where <env> is the non-production environment name, for example dev10, dev11, dev15, stag or eu-dev. Treat the environment name as user-supplied: there are many, and the list changes.
| Service | Non-production host pattern | Used by |
|---|---|---|
| CDA (Delivery) | <env>-cdn.csnonprod.com | Contentstack.stack({ host }) |
| Live Preview | <env>-rest-preview.csnonprod.com | live_preview.host |
| CMA | <env>-api.csnonprod.com | Management scripts, provisioning |
| Studio API | <env>-composable-studio-api.csnonprod.com | Studio project creation, project listing |
| Editor (browser) | <env>-app.csnonprod.com | The Studio interface. You open this URL |
| Images CDN | <env>-images.csnonprod.com | Asset URLs in entry responses |
Without an explicit host:, non-production work lands in production. The Studio React SDK derives its Studio API host from the Delivery stack you pass it. Omit host: on Contentstack.stack({...}) and it defaults to the AWS NA production CDA, so the Studio SDK points at the production Studio API too. Set the CDA host explicitly:
const stack = Contentstack.stack({
apiKey,
deliveryToken,
environment,
host: `${csEnv}-cdn.csnonprod.com`,
live_preview: {
enable: true,
preview_token,
host: `${csEnv}-rest-preview.csnonprod.com`,
},
});Authentication headers
Both surfaces expect a credential and a scope identifier together, but they scope differently.
| Surface | Credential header | Scope header |
|---|---|---|
| CMA | authorization: Bearer <access_token>, or authtoken: <session_token> | api_key: <stack api_key> |
| Studio API | authorization: Bearer <access_token>, or authtoken: <session_token> | organization_uid: <org uid> |
Studio projects are organization-scoped, so /v1/projects takes organization_uid and not a stack api_key. Sending a stack api_key instead returns 422 error_code 21, "Stack not found".
Try OAuth first. authorization: Bearer <oauth-access-token> paired with organization_uid returns 200 and the project list on AWS NA production. Escalate to a session authtoken only after OAuth returns 401 error_code 105 ("authtoken is not valid") or 422 error_code 21 ("Stack not found"). A session authtoken is a full user-session credential that covers every organization, stack and permission the user holds, so scope it to the single call and discard it. Do not write it to .env.
Send the OAuth token in the right header. OAuth goes in authorization: Bearer, and authtoken: carries session tokens only. Sending an OAuth token as authtoken: returns 401 error_code 105. For the full credential ladder, see Studio API authentication.
Register a project: POST /v1/projects
POST creates the project. A separate PUT configures it, because the environment, the locale and Freeform attach after the project exists.
curl -s -X POST "https://<studio-api-host>/v1/projects" \
-H "authorization: Bearer <ACCESS_TOKEN>" \
-H "organization_uid: <ORG_UID>" \
-H "Content-Type: application/json" \
-d '{
"name": "<PROJECT_NAME>",
"description": "<DESCRIPTION>",
"connectedStackApiKey": "<STACK_API_KEY>",
"contentTypeUid": "<COMPOSITIONS_CT_UID>",
"canvasUrl": "/canvas"
}'| Field | Type | Required | Notes |
|---|---|---|---|
| name | string | Yes | Display name in the Studio project list |
| description | string | No | Free text |
| connectedStackApiKey | string | Yes | The stack api_key, not a token |
| contentTypeUid | string | Yes | The compositions content type created through the CMA |
| canvasUrl | string | Yes | The route on your app that mounts <StudioCanvas />, for example /canvas |
A successful create returns 201. An empty or missing connectedStackApiKey produces a project the canvas cannot resolve compositions against, so verify the response carries the value you sent.
Configure a project: PUT /v1/projects/{projectUid}
curl -s -X PUT "https://<studio-api-host>/v1/projects/<PROJECT_UID>" \
-H "authorization: Bearer <ACCESS_TOKEN>" \
-H "organization_uid: <ORG_UID>" \
-H "Content-Type: application/json" \
-d '{
"name": "<PROJECT_NAME>",
"canvasUrl": "/canvas",
"connectedStackApiKey": "<STACK_API_KEY>",
"contentTypeUid": "<COMPOSITIONS_CT_UID>",
"settings": {
"configuration": { "environment": "<ENVIRONMENT_UID>", "locale": "en-us" },
"isFreeformEnabled": true
}
}'settings.configuration.environment takes the environment uid, not the name. The Studio API accepts a name without erroring, and the environment binding then misses, so compositions never resolve. Look the uid up with GET /v3/environments against the CMA before you send the PUT.
Send the full body on every PUT. To re-bind an existing project to a new stack, send the current connectedStackApiKey. A project holding a stale value serves the previous stack's compositions, and the current stack then looks empty in the canvas.
List projects: GET /v1/projects
Run this before creating anything. Creating blind against a stack that already has a project produces a duplicate, nothing errors, and half your tooling then points at each project.
curl -s "https://<studio-api-host>/v1/projects" \ -H "authorization: Bearer <ACCESS_TOKEN>" \ -H "organization_uid: <ORG_UID>" | jq -r \ '.projects[] | "\(.uid) \(.name) stack=\(.connectedStackApiKey) ct=\(.contentTypeUid)"'
A 401 or 422 response is an authentication failure, not evidence that no project exists. Resolve the credential first, then re-check.
The two-call CT create
The compositions content type references itself through linked_sections, and the CMA validates reference targets when it creates a content type. A content type cannot reference itself before it exists, so a single POST carrying linked_sections fails with error_code 115: symbols.reference_to: content type does not exist.
Send the two calls below in order:
- POST /v3/content_types with the schema minus linked_sections.
- PUT /v3/content_types/<uid> with the full schema, including linked_sections.
linked_sections is a reference field with multiple: true and reference_to set to the content type's own uid. For the complete field list and the cardinality each field requires, see the provision-studio-project skill.
Troubleshooting
| Symptom | Cause | Resolution |
|---|---|---|
| Content type creation returns 404 | The CMA host belongs to a different region than the stack | Use the region-matched CMA host from the production host map above |
| 401 and 404 responses with no field diagnostic | The CMA host and the Studio API host belong to different regions | Pick one region and use both of its hosts |
| 422 error_code 21, "Stack not found", on /v1/projects | The request carries a stack api_key where organization_uid belongs | Send organization_uid. Studio projects are organization-scoped |
| 401 error_code 105, "authtoken is not valid" | The request carries an OAuth token in the authtoken header | Send it as authorization: Bearer <access_token> |
| 403 error_code 316, "You don't have the permission to do this operation" | The credential is valid but aims at another organization, or the user lacks stack-create rights | Switch the active organization, or request stack-create rights. Do not re-authenticate |
| error_code 115, symbols.reference_to: content type does not exist | The initial POST carries linked_sections | Create the content type in two calls, as the section above describes |
| error_code 248, "could not find environment" | PUT /v3/environments/<uid> puts the uid in the path | This CMA endpoint keys on the environment name: PUT /v3/environments/preview |
| The canvas loads, but compositions never resolve | settings.configuration.environment holds the environment name | Look the uid up with GET /v3/environments and send the uid |
| Non-production work appears in production | Contentstack.stack({...}) omits host: | Set host to <env>-cdn.csnonprod.com explicitly |
| The canvas iframe hangs on "Loading composition" | The stack has Live Preview turned off, so the preview channel fails its cross-origin preflight | PUT /v3/stacks with stack.settings.live_preview.enable set to true |
Next steps
- Studio API endpoint reference: every documented route, the versioning scheme, and the response envelope they share.
- Studio API errors and validation: the full error_code catalog.
- Check Studio access: confirm the organization has Studio enabled before provisioning.
- Troubleshoot common Studio issues: symptoms that appear after provisioning succeeds.