Skip to content

API Reference

All routes below are injected by @caretcms/core when you add caret() to your Astro config. The base path defaults to /api/cms and is configurable via apiBasePath.

StatusMeaning
200Success
400Validation error (bad payload, prototype-pollution attempt, malformed field)
401No session cookie or session expired
403Missing CSRF header or insufficient permissions
404Entry / collection not found
409Optimistic-locking conflict (expectedRevision mismatch)
500Server error
RouteMethodPurpose
/api/cms/auth/loginPOSTLog in with CARET_EDIT_PASSWORD
/api/cms/auth/sessionGETCheck whether the current editor session is authenticated
/api/cms/auth/logoutPOSTClear the editor session
POST /api/cms/auth/login
Content-Type: application/json
{ "password": "your-password" }

Sets caret_session cookie on success (HttpOnly; SameSite=Lax; Secure on HTTPS).

RouteMethodPurpose
/api/cms/entriesGETList entries by collection
/api/cms/schemaGETCollection schema (registered, dynamic, or inferred)
/api/cms/collections-metadataGETList metadata for all dynamic collections
/api/cms/mutatePOSTExecute a mutation command
/api/cms/historyGET / POSTRead or restore from revision history
/api/cms/uploadPOSTUpload a file (multipart/form-data)
GET /api/cms/entries?collection=pages
→ {
"collection": "pages",
"entries": [
{ "id": "home", "data": { "headline": "..." }, "revision": 7 },
{ "id": "about", "data": { "title": "..." }, "revision": 2 }
]
}
GET /api/cms/schema?collection=pages
→ {
"collection": "pages",
"source": "registered", // or "dynamic" or "inferred"
"schema": { /* JSON Schema */ },
"template": { /* default value */ },
"metadata": { // present when source is "dynamic"
"label": "Pages",
"icon": "📄",
"creatable": true,
"orderable": false
}
}

See Schemas for the resolution order and supported features.

GET /api/cms/collections-metadata
→ {
"collections": [
{
"id": "products",
"label": "Products",
"icon": "📦",
"creatable": true,
"orderable": true,
"schema": { /* JSON Schema */ },
"created_at": 1714225200000,
"updated_at": 1714225200000
}
]
}

All writes go through here.

POST /api/cms/mutate
Content-Type: application/json
x-caret-request: 1
Cookie: caret_session=...
{ "type": "save_field", "collection": "pages", "id": "home", "field": "headline", "value": "New" }
→ { "ok": true, "revision": 8 }
typePurpose
save_fieldUpdate one field on one entry
put_entryCreate or replace an entry’s full data
delete_entryDelete an entry
reorder_entriesReorder entries within a collection
update_page_layoutUpdate section composer state
create_collectionCreate a dynamic collection (schema + metadata)
delete_collectionDelete a dynamic collection (and all its entries + history)
FieldRequired byNotes
typeallOne of the values above
collectionmostLowercase, alphanumeric, - / _
idmostEntry ID
fieldsave_fieldDot-path (hero.title); rejects __proto__ etc.
valuesave_field, put_entryNew value
expectedRevisionoptional on writesPass for optimistic locking; returns 409 on mismatch
{ "error": "Revision conflict", "currentRevision": 9 }

with HTTP 409. Re-read the entry, re-apply the user’s intent, and retry with the new currentRevision.

GET /api/cms/history?collection=pages&id=home
→ {
"history": [
{ "ts": 1714225200000, "data": {...}, "action": "save_field" }
]
}

Capped at the last 50 revisions per entry.

Restore an entry to a previous revision:

POST /api/cms/history
x-caret-request: 1
Cookie: caret_session=...
{ "collection": "pages", "id": "home", "ts": 1714225200000 }

The corresponding history entry is re-applied as a put_entry mutation.

Multipart form with a single file field:

curl
curl -X POST http://localhost:4321/api/cms/upload \
-H 'x-caret-request: 1' \
-H 'Cookie: caret_session=...' \
-F file=@./hero.jpg
→ { "ok": true, "url": "/uploads/abc123.jpg", "size": 184320, "type": "image/jpeg" }
RoutePurpose
/adminLogin / redirect
/admin/cmsContent Studio dashboard
/admin/cms/[collection]Collection list view
/admin/cms/[collection]/[id]Entry editor

These mount at ${mountPath} (default /admin). Disable with enableAdmin: false.

RoutePurpose
/__caret/editor.jsInline editor runtime
/__caret/editor.cssEditor styles

Loaded automatically when an editor session cookie is present. Disable with enableInlineEditor: false.


Hosted cloud API Coming soon

Section titled “Hosted cloud API ”

The hosted version of CaretCMS exposes a separate API surface for cloud mode clients. These routes are served by the hosted control plane, not by your Astro site.

RouteMethodPurpose
/contentGETFetch entries for a project
/content/mutatePOSTWrite mutations
/content/schemaGETProject schema
/content/historyGETEntry history
/content/uploadPOSTFile upload
/content/auth/loginPOSTEditor login (Better Auth)
/content/auth/logoutPOSTEditor logout

Cloud routes use bearer token authentication (Authorization: Bearer ...) instead of cookies. Project membership is verified on every request. These docs will be expanded once the hosted service launches.