Content model
Models, namespaces, field types and relations, and how each one comes back from the API.
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.
| Property | What it does |
|---|---|
| Name | What editors see in the admin. |
| Namespace | What the API uses: GET /api/entries/article. Lowercase letters, digits, _ and -, unique within the project. |
| Single instance | The model holds one entry, such as site settings or a home page. Set it when you create the model. |
| Searchable | Its 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.
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 admin | Type | The API returns |
|---|---|---|
| Text | string | a string |
| Rich Text | markdown | an HTML string from the admin's editor, or Markdown written through the API |
| HTML | html | an HTML string |
| Number | number | a JSON number |
| True/False | true_false | true or false |
| Date | date | an ISO 8601 string, with or without a time |
| Options | enum | the chosen option, as a string |
| Color | color | a hex string, such as "#3f6c1a" |
| Image, Video, File | image, video, file | { id, url, alt, type, width, height } |
| Model | relation | a reference to another entry (below) |
| Array | array | a 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 field | What happens |
|---|---|
| Reorder it, move it in the layout, or change its width | Saved at once |
| Change its name or label, or turn Required on or off | Saved at once |
| Add an option to an Options field | Saved at once |
| Change its type, including Text to Rich Text or one value to a list | Capa shows what happens to every stored value, and asks before it changes anything |
| Change its namespace or its related model, or remove an option | The same review |
| Remove it | The 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.
Related
- Build a model in the admin, step by step.
- Entries reference for
select, filters and sorting. - TypeScript to generate types from your models.