Content Delivery API (CDA)
Viglet Shio 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
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/**) | |
|---|---|---|
| Purpose | Authoring (admin console) | Public delivery to sites/apps |
| Methods | Read and write | Read-only (GET) |
| Content | Drafts + published | Published only |
| Response | Rich internal models | Frozen, minimal DTOs |
| Auth | Session + CSRF | API 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).
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.
| Method | Path | Purpose |
|---|---|---|
| GET | /cda/site | List published sites |
| GET | /cda/site/{siteId} | One site (with root folder id) |
| GET | /cda/site/{siteId}/redirects | The site's redirects, ordered by from |
| GET | /cda/object/{folderId}/list?page&size | A folder's children (subfolders + posts) |
| GET | /cda/object/{id}/path | Breadcrumb path for a folder or post |
| GET | /cda/post/{id} | One published post (attributes expanded) |
| GET | /cda/post/by-url?siteId&url | Resolve a published post by friendly URL |
| GET | /cda/query?siteId&folderId&postType&page&size | Query 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>"
Redirects on a site with its own front end
A redirect you create in Viglet Shio (a Redirect post, or the offer to keep an old address
working when you rename a page) is served by Shio's own /sites/** delivery. If your site is
rendered by its own front end, such as Next.js, visitors never pass through that delivery, so
the front end has to do the redirecting itself. This endpoint gives it the table:
curl "https://cms.example.com/api/v2/cda/site/SITE_ID/redirects" -H "Key: <your-cda-token>"
[
{ "from": "/about", "to": "https://example.org/about", "permanent": false },
{ "from": "/pricing", "to": "/plans", "permanent": true }
]
fromis the path that moved andtois where it went: a path on the same site, or a full URL.permanent: truemeans answer with a301;falsemeans a302, which is the default.- A production token sees published redirects only. A preview token also sees drafts.
- The table is small and changes rarely, so fetch it once and cache it rather than asking on every
request. Consult it before answering
404.
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
- JavaScript Client: the framework-agnostic
@viglet/shio-client. - React SDK: hooks and render components.
- Next.js Starter:
npx create-shio-app.