Model content, edit it in the CMS and publish: your application reads JSON and controls the design. REST and GraphQL access the same content.
Your path to a first integration
1 / Try without signing in
Edit text in the local demo. See when a change appears on the website and in both API responses.
Try the demo2 / Query your own live content
Sign in, choose your space and an environment. In the right panel under Tools, open API Explorer · REST / GraphQL. Select a content type, published example and language, then run both queries.
Open CMS · sign-in required3 / Connect your application
Copy the matching example from the explorer. Create a Delivery key for your application and run the download below with your IDs.
Set up and run the codeThe API Explorer reads published content. Running queries requires permission to manage API keys. Its temporary access lasts one hour and is excluded from copied code. The public reference is readable; its CMS space is not a shared test account.
Start with an empty project
- Open your space in the CMS and select your project's environment. This walkthrough requires permission to manage content types, publish content and create API keys.
- Under Content Types, create a type named article. Add a text field named title and save the content type.
- Under Content, create a record of that type with the title Hello world. Publish it in an active content language of your space. Your website language is independent of this setting.
Next, create a Delivery key and read the same content through REST and GraphQL. No framework or AI generation is required.
01 / Create a key
Settings → API Keys → “New API Key”. Choose the Delivery type and the environment(s) the key may read.
02 / Find your IDs
The space ID is part of your space's URL (…/spaces/<space id>/…). The environment ID is listed for each environment on the space's environments page (…/spaces/<space id>/environments).
03 / Publish content
A delivery key only ever reads published content. Publish the record before fetching it.
From content to an API response
Change language and page to update the code and example response. This is a local demonstration with fictional data; no API request is sent.
Without locale, REST returns the language values of a field. With locale, it returns a flat projection. GraphQL uses the hydrated JSON field for this; its contents are not individually selectable GraphQL fields.
curl --get \
'https://www.smartcms.ai/api/content/v1/environments/'"$SMARTCMS_ENVIRONMENT_ID"'/content' \
--data-urlencode 'limit=10' \
--data-urlencode 'skip=0' \
--header "Authorization: Bearer $SMARTCMS_DELIVERY_TOKEN"interface DeliveryContentResponse {
items: Array<{
id: string;
contentTypeId: string;
status: string;
data: Record<string, unknown>;
}>;
total: number;
limit: number;
skip: number;
}
const res = await fetch(
`https://www.smartcms.ai/api/content/v1/environments/${process.env.SMARTCMS_ENVIRONMENT_ID}/content?limit=10&skip=0`,
{
headers: {
Authorization: `Bearer ${process.env.SMARTCMS_DELIVERY_TOKEN}`,
},
}
);
if (!res.ok) {
throw new Error(`smartcms delivery request failed: ${res.status}`);
}
const { items, total }: DeliveryContentResponse = await res.json();curl "https://www.smartcms.ai/api/content/v1/spaces/$SMARTCMS_SPACE_ID/environments/$SMARTCMS_ENVIRONMENT_ID/graphql" \
--header "Authorization: Bearer $SMARTCMS_DELIVERY_TOKEN" \
--header "Content-Type: application/json" \
--data '{"query":"query { contentRecords(limit: 10, skip: 0) { id contentTypeId status hydrated } }"}'Example response
Shortened to one entry. The selected page changes skip; total counts all matches.
{
"items": [
{
"id": "example-1",
"contentTypeId": "example-content-type-id",
"status": "published",
"data": {
"title": {
"de-DE": "Hallo Welt",
"it-IT": "Ciao mondo",
"en-US": "Hello world"
}
}
}
],
"total": 42,
"limit": 10,
"skip": 0
}Real responses from Berg & Tal
These excerpts come from successful Delivery requests for the public, fictional Alpenlicht project. REST and GraphQL return the same IDs and content values.
Captured on . Saved snapshot, not a live request. Shortened to one record and its IDs, title, summary and slug. total, limit and skip come from the original REST list. Pagination in the simulated example above does not change this excerpt.
Compare saved responses
REST · items[].data
{
"items": [
{
"id": "cmu4fkt0700269ja8peyvpeeh",
"contentTypeId": "cmu4fkrs3000l9ja81bqr53iw",
"data": {
"title": "Alpenlicht — hospitality reimagined",
"summary": "A fictional boutique hotel receives a calm visual identity and a multilingual website.",
"slug": "alpenlicht"
}
}
],
"total": 9,
"limit": 100,
"skip": 0
}GraphQL · data.contentRecords[].hydrated
{
"data": {
"contentRecords": [
{
"id": "cmu4fkt0700269ja8peyvpeeh",
"contentTypeId": "cmu4fkrs3000l9ja81bqr53iw",
"hydrated": {
"title": "Alpenlicht — hospitality reimagined",
"summary": "A fictional boutique hotel receives a calm visual identity and a multilingual website.",
"slug": "alpenlicht"
}
}
]
}
}For current responses from your own content, open the API Explorer in the CMS. These IDs belong to the reference; they do not grant access to your space.
Query your content with GraphQL
GraphQL is an alternative to the REST API. Your query selects the record fields you need, such as id, contentTypeId and hydrated. Both APIs read the same published content using your delivery key.
Endpoint and authentication
POST https://www.smartcms.ai/api/content/v1/spaces/{spaceId}/environments/{environmentId}/graphql
Authorization: Bearer <delivery-token>
Content-Type: application/jsonReplace the placeholders with your space ID, environment ID and key. Send the query in a JSON body with a query field. The interactive GraphQL example handles this format for you.
Your first query
query {
contentRecords(limit: 5, skip: 0, locale: "en-US") {
id
contentTypeId
hydrated(locale: "en-US")
}
}The response contains the list under data.contentRecords. hydrated returns your content fields as JSON in the requested language. Custom fields such as title are inside that JSON; they cannot be selected as subfields of hydrated. Use limit and skip to paginate. Check the response's errors field even when the HTTP status is 200.
Use the API Explorer in the right CMS panel to compare REST and GraphQL. Use GraphQL Playground to inspect the schema and run custom queries. Creating the temporary playground key requires permission to manage API keys.
Continue with your own content
Set SMARTCMS_ENVIRONMENT_ID and SMARTCMS_DELIVERY_TOKEN in your shell (cURL) or server environment (TypeScript). GraphQL also needs SMARTCMS_SPACE_ID. The TypeScript example runs on the server, for example in Node.js or a Next.js Server Component.
For real queries, open your space, select an environment and open API Explorer · REST / GraphQL in the right panel. It shows the request, real response and copyable code for the same selection. REST filters by content type name with content_type; GraphQL uses contentTypeId with the internal ID.
Set up once, test both APIs
Replace these three placeholders and run the lines in your terminal (macOS/Linux, bash/zsh). Use a Delivery key restricted to the intended environment. These values are set locally only.
export SMARTCMS_SPACE_ID='<your-space-id>'
export SMARTCMS_ENVIRONMENT_ID='<your-environment-id>'
export SMARTCMS_DELIVERY_TOKEN='<your-delivery-token>'Download the Node.js example and run it from your download directory. It requires Node.js 18 or newer and no packages. Replace de-DE with the language in which you published your record. It reads real content without changing anything.
Download the Node.js examplenode smartcms-delivery.mjs rest de-DE
node smartcms-delivery.mjs graphql de-DECompare the IDs: in your new project, both responses contain the published record. Both examples read at most ten entries; larger datasets require pagination. REST returns fields in items[].data; GraphQL uses data.contentRecords[].hydrated. Do not rely on record order for comparison. Empty results are explained; HTTP failures and GraphQL errors produce a failing terminal exit status.
Understand languages, drafts and fallback
The interface and your content have separate languages. Publish the record in the intended content language and pass its code as locale. Without locale, REST returns raw language values; GraphQL hydrated uses the record’s default language. A missing field value may be filled through the configured fallback. This is not automatic translation.
In Berg & Tal, Talmarkt intentionally has no Italian translation: the reference shows the German fallback. Saving a new draft changes the published version only when you publish again.
If your first query fails
401 / 403
Check the key, key type and allowed environment. Drafts require preview access; delivery returns published content.
Empty response
Check the environment and publication state. Start without filters and with skip=0. IDs and names are not interchangeable.
429 / Rate limit
Wait before retrying and respect the Retry-After header when present. Avoid rapid retry loops.
GraphQL errors
HTTP 200 may still contain errors. Check the query, arguments and permissions against the playground schema.
The variables stand for your environment. Never expose preview or management keys in public frontend code.
Delivery / Preview
Delivery reads published content. Preview enables authorized access to drafts. Key scope determines space and environment.
Write content
Use a management key with write permissions for imports and automation. REST and GraphQL are access methods; delivery, preview and management determine which content and actions are allowed.
Berg & Tal · DE / IT / EN
Try the editorial workflow
Change some text, publish it in the local simulation and compare the website excerpt, REST and GraphQL. The demo does not change real content.
Open the interactive demo