Docs

Agent API

Let an agent create models and entries with an API key on /v2/agent, with no person signed in.

View as Markdown

An agent can stand up a content model, fill it with entries and hand the result to a frontend, using an API key and nothing else. It does that on /v2/agent/*, the same routes the Capa admin uses, mounted a second time behind key authentication.

To read content from an agent, or to let an assistant like Claude Code or Cursor query it, use the MCP server instead. This page is for writes.

What a key can reach

SurfacePrefixAuthenticationHostFor
Content reads/v2/api, /v3/api, /api/API keyhttps://cdn.capacms.comyour site
Agent writes/v2/agent/*API keyhttps://api.capacms.comthis page
Admin/v2/models, /v2/model-instances and the resta signed-in personhttps://api.capacms.comthe Capa admin

The admin routes refuse a key with 401. Send key requests to /v2/agent/..., not to the admin path of the same name.

The key

Every request carries one header:

x-api-key: pk_...

A key belongs to one project, and the project comes from the key. No path or body below names a project, so nothing you send can reach outside it.

/v2/agent/* takes a legacy key: pk_ for production, sk_ for other environments, or an older key with no prefix. A cap_ key is refused with 401 {"error":"Invalid API key"}. See Keys and scopes.

A legacy key holds one of four permissions:

PermissionCan
readread models, entries, media, types, categories, workspaces and search
writeeverything read can, plus create, update and publish entries, upload media, and arrange workspaces
deleteeverything write can, plus delete entries and media
agenteverything write can, plus create and update models

You want agent. It is the only permission that can create a model. It deletes nothing, on purpose: an agent building a project never needs to remove a model or an entry, and a key that cannot delete cannot destroy content by misreading an instruction. No legacy key can publish a model version or delete a model.

A person on the project mints the key once, with their own session (see Management API):

curl -X POST https://api.capacms.com/v2/tenants/api-keys \
  -H "Authorization: Bearer $SESSION_TOKEN" \
  -H 'content-type: application/json' \
  -d '{"environment":"production","permission":"agent"}'

A permission that is not one of the four is refused with 400, rather than becoming a read key.

Create a model

curl -X POST https://api.capacms.com/v2/agent/models/ \
  -H "x-api-key: $CAPA_AGENT_KEY" \
  -H 'content-type: application/json' \
  -d '{
    "modelName": "Author",
    "namespace": "guide_author",
    "fields": [{ "name": "Name", "namespace": "name", "type": "string", "sortOrder": 0 }]
  }'

The answer is 201 with the model, including its id.

Every model gets a required title field, whether you ask for one or not. The model above comes back with two fields, name and title. So every entry you create must carry a title, or it is refused with 400 {"error":"Field title is required"}.

Field types: string, markdown, html, code, color, number, true_false, date, enum, image, video, file, relation, array and mixed. Anything else is refused with 400 and the list of valid types.

Relations

A relation field names the model it points at by namespace:

{ "name": "Author", "namespace": "author", "type": "relation", "relationRef": "guide_author", "sortOrder": 1 }

Use the namespace. A model id is refused with 400 and Model with namespace <id> does not exist. For a list of related entries, use an array of relations:

{ "name": "Co-authors", "namespace": "coauthors", "type": "array", "arrayType": "relation", "relationRef": "guide_author", "sortOrder": 2 }

Create and publish entries

curl -X POST https://api.capacms.com/v2/agent/model-instances/ \
  -H "x-api-key: $CAPA_AGENT_KEY" \
  -H 'content-type: application/json' \
  -d '{
    "modelId": "<model id>",
    "publish": true,
    "data": { "title": "Notes on the Analytical Engine", "author": "<author entry id>" }
  }'

Three things to know:

  • data values are flat. Send "title": "...", not "title": { "value": "..." }. The read API returns the wrapped form, and the write routes do not accept it.
  • A relation value is the id of the entry it points at. Anything else is refused with 400 {"error":"Field author must be a valid UUID"}.
  • publish is false by default, which saves a draft. A production key on the read API sees published entries only.

Read what you made

Your site reads with a separate read key, never the agent key. Reads go to the CDN:

curl -H "x-api-key: $CAPA_KEY" \
  'https://cdn.capacms.com/v2/api/guide_post?limit=5&depth=1'
{
  "data": [{
    "title":  { "type": "string",   "value": "Notes on the Analytical Engine" },
    "author": { "type": "relation", "value": "f3e288a2-…" }
  }],
  "relations": { "f3e288a2-…": { "…": "the author entry" } },
  "meta": { "total": 1, "limit": 5, "environment": "production" }
}

On /v2/api, values come back wrapped as type and value, and a relation's value is the target's id. The target itself is in the top-level relations map. depth sets how far relations expand. The legacy API reference has the whole format.

For new code, read the same entries from /api/entries. The same legacy key works there.

Keep the agent key out of your site. A NEXT_PUBLIC_ variable is compiled into the browser bundle, and a leaked agent key can rewrite your models.

End to end

KEY=pk_...       # permission: agent
READ=pk_...      # permission: read, for the site
API=https://api.capacms.com
H="content-type: application/json"

# 1. The models
AUTHOR=$(curl -s -X POST $API/v2/agent/models/ -H "x-api-key: $KEY" -H "$H" \
  -d '{"modelName":"Author","namespace":"guide_author",
       "fields":[{"name":"Name","namespace":"name","type":"string","sortOrder":0}]}' \
  | jq -r .id)

POST=$(curl -s -X POST $API/v2/agent/models/ -H "x-api-key: $KEY" -H "$H" \
  -d '{"modelName":"Post","namespace":"guide_post",
       "fields":[{"name":"Title","namespace":"title","type":"string","sortOrder":0},
                 {"name":"Author","namespace":"author","type":"relation",
                  "relationRef":"guide_author","sortOrder":1}]}' \
  | jq -r .id)

# 2. The entries. Both models have a required title.
ADA=$(curl -s -X POST $API/v2/agent/model-instances/ -H "x-api-key: $KEY" -H "$H" \
  -d "{\"modelId\":\"$AUTHOR\",\"publish\":true,
       \"data\":{\"title\":\"Ada Lovelace\",\"name\":\"Ada Lovelace\"}}" | jq -r .id)

curl -s -X POST $API/v2/agent/model-instances/ -H "x-api-key: $KEY" -H "$H" \
  -d "{\"modelId\":\"$POST\",\"publish\":true,
       \"data\":{\"title\":\"Notes on the Analytical Engine\",\"author\":\"$ADA\"}}"

# 3. What the site reads
curl -s -H "x-api-key: $READ" "https://cdn.capacms.com/v2/api/guide_post?limit=5&depth=1"

More an agent key can do

The same key reaches the other routes mounted under /v2/agent:

PrefixWhatPermission it needs
/v2/agent/scheduled-actionsschedule entries to publish or unpublish laterwrite, delete or agent
/v2/agent/workspacesarrange the admin's left railwrite, delete or agent
/v2/agent/models/:id/layoutarrange the entry editoragent
/v2/agent/models/:id/field-changeschange a field's type on a model with entriesagent

Each works as its session twin does. See Management API.

Errors

StatusBodyCause
400Field title is requiredevery model has a required title
400Field <name> must be a valid UUIDa relation value must be the id of an entry
400Field <name> must be a stringdata values are flat, not { "value": … }
401API key requiredno x-api-key header
401Invalid API keyan unknown or inactive key, or a cap_ key
403Insufficient permissionsthe key's permission does not allow this

A 403 does not say which permission was missing. If the call is right, check that the key's permission is agent.

What this surface does not do

  • Delete models. No legacy key can, and an agent key deletes nothing at all.
  • Name an author. A write made with a key has no person behind it, so its createdBy is null.
  • Show who edited. Where a record names the person who saved it, a key gets { "id": "…" } and no name, email or avatar.
  • Create projects. A key acts inside one project. Creating a project is done in the Capa admin.