An administrator makes one in the site's admin: Settings → CMS → API keys → New key. Choose what it may do:
| Template | Reach |
|---|---|
| Read only | The schema, the generated contract, every type's published content, and taxonomy terms. |
| Content poster | Read and write the types you pick, and upload media — for an automation that publishes one kind of thing. |
| Read & write | Everything: all content, terms and their page copy, and media. |
The key is shown once. Keep it in a secret store, never in a page, a repository or a log. Edit, rotate, revoke or delete it from the same screen. Copy instructions for AI on that screen copies a ready briefing for an AI agent, with this site's address in it.
Base URL https://hq.<your domain>/api/v1
Header Authorization: Bearer <API key>
GET /api/v1/ping answers without a key, for uptime checks.
| Call | Why |
|---|---|
GET /api/v1 | Proves the key: the site, what this key may do, and the endpoints. |
GET /api/v1/help | Every endpoint on this site — one row per content type — marked with whether your key may call it. The source of truth; this page is the guide. |
GET /api/v1/schema | The content model: each type, and each field's id, type and rules. Values are written by field id. |
GET /api/v1/contract | The generated TypeScript contract for this site's model, as text. |
GET /api/v1/content/{type}?limit=50&offset=0 published entries, whole records
GET /api/v1/content/{type}/{slug} one entry
Each entry comes whole, with its public path and its parent. limit is 1–100; a full page carries next, the offset of the following one. ?status=draft lists drafts instead (oldest first) and needs the content:write:{type} capability for that type — a Content poster or Read & write key, not a Read only one.
POST /api/v1/content/{type}
{
"slug": "spring-open-day", // optional
"status": "draft", // draft (default) · published · scheduled
"publishedAt": "2026-11-01", // a past date backdates; a future one with "scheduled" publishes then
"parent": null, // nest under another entry of a hierarchical type (slug or id)
"values": { "fld_title": "Spring open day", "fld_body": "<p>…</p>" }
}
PUT /api/v1/content/{type}/{slug} // merges the values you send; records a revision first
"status": "published".termsCreated.key from the media upload below.parent) adds 301s from its old address, and from everything beneath it.POST /api/v1/media?filename=front-door.jpg&alt=The%20front%20door&description=Opening%20day%2C%20March%202026&path=gallery
Content-Type: image/jpeg
<the file's bytes>
→ { "id": "…", "key": "ab/cd/abcd…", "mime": "image/jpeg", "bytes": 182345, "width": 2400, "height": 1600 }
Use the returned key as an image field's value. alt is what the image shows, for someone who cannot see it; description is the library's own note on the file (who or what, where and when — up to 2,000 characters); path files it into a folder. All three are optional.
GET /api/v1/terms/{taxonomy}
GET /api/v1/terms/{taxonomy}/{slug}
PUT /api/v1/terms/{taxonomy}/{slug} { "label", "description", "body", "seoTitle", "seoDescription", "image" }
A term's page copy: fields merge over what is stored, null or "" clears one. seoTitle ≤ 120 and seoDescription ≤ 320 characters; body is HTML.
Every refusal is JSON with an error sentence.
| Status | Means |
|---|---|
| 400 | The request could not be read (malformed JSON, a missing part). |
| 401 | The key is missing, wrong or revoked — one sentence for all three, deliberately. |
| 403 | The key may not do that; the reply's need names the capability. Also: writing the platform's own fields. |
| 404 | No such type, entry or term on this site. |
| 405 | That method is not taken at that address. |
| 422 | A value broke a field's rules, or a field id is not on that type; the reply names them. |
KEY="…"; SITE="https://hq.example.co.uk/api/v1"
curl -s "$SITE" -H "Authorization: Bearer $KEY" # what may this key do?
curl -s "$SITE/schema" -H "Authorization: Bearer $KEY" # which field ids does "post" have?
KEY_IMG=$(curl -s -X POST "$SITE/media?filename=cover.jpg&alt=Our%20new%20studio" \
-H "Authorization: Bearer $KEY" -H "Content-Type: image/jpeg" --data-binary @cover.jpg | jq -r .key)
curl -s -X POST "$SITE/content/post" -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d "{\"values\": {\"fld_title\": \"We moved\", \"fld_cover\": \"$KEY_IMG\"}}" # saved as a draft