# Content model

Source: https://capacms.com/docs/concepts/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.

<Callout type="warn">
  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.
</Callout>

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:

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

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

```bash
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)'
```

```json
"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](https://capacms.com/docs/api/entries#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](https://capacms.com/docs/api/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](https://capacms.com/docs/modeling/change-field-type) for the review and what converts to what.

## Related

* [Build a model](https://capacms.com/docs/modeling/build-a-model) in the admin, step by step.
* [Entries reference](https://capacms.com/docs/api/entries) for `select`, filters and sorting.
* [TypeScript](https://capacms.com/docs/guides/typescript) to generate types from your models.
