Skip to main content

Content Delivery API (CDA)

Shio CMS is headless-first: your content engine runs on Java/Spring Boot, but the sites and apps that consume it are pure JavaScript/TypeScript. The Content Delivery API (CDA) is the stable, read-only contract that JS clients depend on.

Shio (Java/Spring) ──/api/v2/cda/**──▶ @viglet/shio-client ──▶ Next.js / React / Vue / Astro / Node
JS dev, zero Java

A front-end developer builds an entire site against the CDA without ever touching the Java backend. Use the typed @viglet/shio-client, the React SDK, or scaffold a ready-made Next.js app with create-shio-app.


What makes the CDA different from the console REST API

Console API (/api/v2/...)Content Delivery API (/api/v2/cda/**)
PurposeAuthoring (admin console)Public delivery to sites/apps
MethodsRead and writeRead-only (GET)
ContentDrafts + publishedPublished only
ResponseRich internal modelsFrozen, minimal DTOs
AuthSession + CSRFAPI token (Key header)

The CDA never exposes drafts or authoring-only fields (owner, publisher, publishStatus), and its response shape is frozen — safe to build a public site on.


Authentication

The CDA is authenticated with an API token sent in the Key header. CDA tokens are read-only — any non-GET request is rejected with 403.

curl https://cms.example.com/api/v2/cda/site \
-H "Key: <your-cda-token>"

Create and manage tokens in the admin console (API Tokens). A token can sit safely behind a CDN or in a server-side environment (Next.js server components, Node).

note

Keep tokens server-side where possible. The CDA token grants read access to published content of the instance.


Endpoints

All endpoints are under /api/v2/cda, return application/json, and are GET only.

MethodPathPurpose
GET/cda/siteList published sites
GET/cda/site/{siteId}One site (with root folder id)
GET/cda/object/{folderId}/list?page&sizeA folder's children (subfolders + posts)
GET/cda/object/{id}/pathBreadcrumb path for a folder or post
GET/cda/post/{id}One published post (attributes expanded)
GET/cda/post/by-url?siteId&urlResolve a published post by friendly URL
GET/cda/query?siteId&folderId&postType&page&sizeQuery published posts

Unknown ids, unpublished content, or an unresolvable URL return 404. A draft is never returned.

Resolve a page by URL

The by-url endpoint powers catch-all routing (e.g. Next.js [...slug]):

curl "https://cms.example.com/api/v2/cda/post/by-url?siteId=SITE_ID&url=/blog/hello-world" \
-H "Key: <your-cda-token>"

Response shapes

Site

{
"id": "string",
"name": "string",
"description": "string | null",
"url": "string | null",
"rootFolderId": "string | null"
}

Post

{
"id": "string",
"title": "string | null",
"summary": "string | null",
"furl": "string | null",
"postType": "string | null",
"siteId": "string | null",
"folderId": "string | null",
"published": true,
"date": "ISO-8601 | null",
"modifiedDate": "ISO-8601 | null",
"publicationDate": "ISO-8601 | null",
"attrs": { "title": "Hello", "content": "<p>...</p>" }
}

attrs is the post's field map, keyed by your post-type attribute names. A malformed payload yields {} rather than an error.

Listing

{
"folderId": "string | null",
"folders": [{ "id": "string", "name": "string", "root": false }],
"posts": [
{
"id": "string",
"title": "string | null",
"summary": "string | null",
"furl": "string | null",
"postType": "string | null",
"date": "ISO-8601 | null",
"modifiedDate": "ISO-8601 | null"
}
],
"page": 0,
"size": 50,
"totalPosts": 0
}

Path (breadcrumb)

{
"site": { "...": "ShioSite" },
"breadcrumb": [{ "id": "string", "name": "string", "root": true }],
"currentFolder": { "id": "string", "name": "string", "root": false }
}

The breadcrumb array is ordered root → … → current.


Next steps