Docs
Concepts

Content model

Models, namespaces, field types and relations, and how each one comes back from the API.

View as Markdown

Your content model is the set of content types in a project and the fields each one has. You design it in the admin. The API serves exactly what you design, under the names you give it.

Models

A model is one content type: Article, Author, Product, Home page.

PropertyWhat it does
NameWhat editors see in the admin.
NamespaceWhat the API uses: GET /api/entries/article. Lowercase letters, digits, _ and -, unique within the project.
Single instanceThe model holds one entry, such as site settings or a home page. Set it when you create the model.
SearchableIts entries are indexed for search. On by default.

Capa fills in the namespace from the name: Blog Post becomes blog_post. You can change it when you create the model, and later in the model's settings.

Changing a model's namespace changes its API address at once. Reads of the old namespace answer 404 model_not_found. Update your code first.

Every model starts with a required title field.

Fields

A field is one value on an entry. Each has a name editors see and a namespace the API uses, unique within the model.

In the adminTypeThe API returns
Textstringa string
Rich Textmarkdownan HTML string from the admin's editor, or Markdown written through the API
HTMLhtmlan HTML string
Numbernumbera JSON number
True/Falsetrue_falsetrue or false
Datedatean ISO 8601 string, with or without a time
Optionsenumthe chosen option, as a string
Colorcolora hex string, such as "#3f6c1a"
Image, Video, Fileimage, video, file{ id, url, alt, type, width, height }
Modelrelationa reference to another entry (below)
Arrayarraya list of strings

Two more types exist for content written through the API rather than in the admin: code, a string, and mixed, any JSON value.

A field you never filled comes back as null. A value that does not fit its type, such as text saved in a number field, also reads as null.

One value or a list

Any field can hold a list. In the field's settings, set Entries to A list of values. The API then returns an array: a list of numbers, a list of images, a list of related entries.

Required

Required is the one rule a field can enforce. The admin will not save an entry with a required field left empty.

Capa has no unique, minimum, maximum or pattern rules on fields. Check those in your own code if you need them.

Relations

A Model field points at entries of another model: an article's author, a product's related products. A model can point at itself, for example a page with a parent page.

A relation comes back as a reference until you ask for more:

"author": { "id": "…07", "model": "author" }

Expand it with select, and you get the related entry with its own fields:

curl -G https://cdn.capacms.com/api/entries/article \
  -H "x-api-key: $CAPA_KEY" -H 'Capa-Version: 2026-10-01' \
  --data-urlencode 'select=title,author(name,photo)'
"author": { "id": "…07", "model": "author", "status": "published",
            "fields": { "name": "Ana Ruiz", "photo": { "url": "https://cdn.capacms.com/files/…" } } }

A list relation comes back as { "items": [...], "pageInfo": {...} } so a long list can be paged.

An expanded relation that points at a draft a production key cannot see shows its id, marked "missing": true. A relation you do not expand is a plain reference, and Capa does not check it. When an entry is deleted, Capa removes it from every relation that pointed at it.

How deep

One request can expand relations 5 levels deep, counting the entry you asked for: author(employer(city(country(name)))). It can expand at most 12 relations, and return at most 5,000 entries in all. See the size caps.

Names in GraphQL

GraphQL turns each namespace into a type name: article becomes Article, blog_post becomes BlogPost. Field names stay as you wrote them, with any character GraphQL cannot use turned into _. See GraphQL names.

Changing a model that has entries

Adding a field is always safe. Existing entries read null for it until someone fills it in.

Once a model has entries, Capa checks each change to an existing field against the values already stored:

Change to an existing fieldWhat happens
Reorder it, move it in the layout, or change its widthSaved at once
Change its name or label, or turn Required on or offSaved at once
Add an option to an Options fieldSaved at once
Change its type, including Text to Rich Text or one value to a listCapa shows what happens to every stored value, and asks before it changes anything
Change its namespace or its related model, or remove an optionThe same review
Remove itThe same review. Confirming deletes the field and its data

The title field cannot be removed, change type or change namespace. See Change a field's type for the review and what converts to what.