Contents
- Domma CMS User Manual
- 1. Using the CMS
- 2. Tutorials
- 3. Components
- 4. API Reference
- 5. Tools
- Tools
- Analytics Pro
- Backup Pro
- Blog
- Blog Pro
- Booking
- Booking Pro
- Calendar
- Campaigns Pro
- Cards
- Card credits
- Contacts Pro
- Dashboard Pro
- Data Transfer
- Docs
- Docs Pro
- Email Pro
- Feedback
- Garage
- Invoices
- Job Board
- Mail Reader
- Messages
- MongoDB Adapter
- Onboard Pro
- Security
- Security Pro
- SEO Pro
- Sheets
- Shopping Cart
- Shopping Cart Pro
- Social Media Pro
- Support
- Surveys
- Theme Roller
- Waypoint Pro
Content API
Updated by Darryl Waterhouse on 13 October 2026 · 4 min read
The Content API is read-only, headless access to what the site publishes - pages, menus, the site settings public pages already show, and search - as JSON at /api/content/v1. Where the External API exposes collection data, this exposes content.
It is off until you turn it on at System > Content API (saved in config/content-api.json); until then every address answers 404.
Endpoints
| Method | Path | Returns |
|---|---|---|
| GET | /api/content/v1/pages | { pages, total, page, limit } - summaries, no bodies |
| GET | /api/content/v1/pages/<path> | One page. /pages/ is the home page; a directory path serves its index |
| GET | /api/content/v1/menus/:location | { location, slug, name, items } |
| GET | /api/content/v1/site | Title, brand, footer, social links, SEO defaults, theme |
| GET | /api/content/v1/search?q= | { results: [{ urlPath, title, description, tags, snippet, score }] } |
Which pages
A page the API serves is exactly a page the site would serve the same caller - the two make one decision.
- Only published pages. Drafts, scheduled and status-less pages answer
404. - Visibility is the site's: a role-gated page is
403to a caller who could not open it. - A page in a switched-off project is
404. - Front matter
api: falsekeeps a page off the API - not served, listed or searched. It still shows on the site.
Page list parameters
| Parameter | Default | Description |
|---|---|---|
page | 1 | Page number |
limit | 50 | Page size, capped at the site's page size limit (default 200). 0 means the default |
prefix | - | Only pages whose path starts with this, e.g. /services |
tag / category | - | Only pages with this tag, or in this category |
project | - | Only pages in this project |
sort / order | site order | title, updatedAt or createdAt; order=desc reverses |
The list can be switched off on its own (single pages stay), so a caller then has to know a page's path.
A page
GET /api/content/v1/pages/about?format=html (html | markdown | both)
{
"urlPath": "/about", "title": "About", "description": "…", "tags": ["company"],
"category": "general", "layout": "default", "createdAt": "…", "updatedAt": "…",
"seo": { "title": "…", "description": "…", "image": "…" },
"html": "<section>…</section>",
"requires": { "domma": true, "components": ["tabs", "effects"] },
"perViewer": false
}
htmlis the page body (no navbar or footer), shortcodes already rendered.markdownis the source, shortcodes included. A format the site does not offer answers400.requiresnames the Domma behaviour the HTML relies on - tabs, accordions, carousels, countdowns, tooltips, reveals, effects, forms and custom components. Without Domma's CSS and JS those parts do nothing.- Only the front matter keys shown are returned - never your own custom keys.
perViewer: true- the body was rendered for this caller and must not be shared between users.- Drafts: in role mode only,
?preview=1from someone withpages.readreturns unpublished pages, never cached.
Menus, site settings and search
- Menus -
?page=/aboutresolves page-bound menus as on that page. Items carry only what a navbar needs (type,text,url,icon,target,size,badge,items) and are filtered for the caller. The admin sidebar and page overlays are never served. - Site - title, brand, footer (hidden links stay hidden), social links, SEO defaults and theme. Never mail settings, the site URL or anything from the admin.
- Search - the site's own search. A query shorter than the site's minimum is
400; with search off in System > Search it is404.
Access
| Who may call it | Credential | Pages rendered as |
|---|---|---|
| Public | None | An anonymous visitor |
| API token (default) | Authorization: Bearer dcms_… - a JWT is refused | An anonymous visitor |
| A role | Authorization: Bearer <access JWT> of that role or more senior. The site's login cookie is never accepted | That user |
- A token reads only the pages of its own project - single pages, the list and search. A page in another project is
404. Menus and site settings are site-wide, so any valid token may read them. - Scopes narrow a token: in the token editor, a line of
@pages,@menus,@siteor@search(read-only, no verbs). A token with no scopes covers everything in its project; one whose scopes name only collections gets no content. - A role the site does not have is refused when you save the settings.
Responses
| Case | Status | Cache-Control |
|---|---|---|
| Public | 200 | public, max-age=60 + ETag |
| API token | 200 | private, no-cache |
| Role, or a draft | 200 | no-store |
Off, missing, unpublished, disabled project, api: false | 404 | no-store |
| No or invalid credential | 401 | no-store |
| Role too junior, scope or visibility refuses | 403 | no-store |
| Format not offered, query too short | 400 | no-store |
Calling it
# Public mode
curl 'https://example.com/api/content/v1/pages?prefix=/blog&limit=10'
# Token mode
curl -H 'Authorization: Bearer dcms_0123...cdef' \
'https://example.com/api/content/v1/pages/about?format=both'
A browser on another domain can call it only when that origin is allowed by cors.origin in config/server.json. With a token or a role the browser sends a preflight first; allowed origins are answered automatically. Calls from a server are unaffected.
Try any page as an anonymous caller would get it - before switching the API on - on the Content API screen's Try it tab.