A project holds the content of one site or app. Its structure is a set of types, each with its fields, and a list of languages.
Types
| Kind | Holds | Read with |
|---|---|---|
| Collection | many entries: articles, products, events | entries(type), entry(type, slug) |
| Singleton | exactly one entry: site settings, a home page | singleton(type) |
Every type has a key (article, site_settings), a name and a title field, the field the admin uses as the entry's title in lists, search and references. Keys use lowercase letters, digits and underscores, and start with a letter.
Fields
| Type | Stores | Notes |
|---|---|---|
text | a short string | up to 500 characters by default |
longtext | a long string | up to 20,000 characters by default |
number | a number | optional min, max, integer |
boolean | true or false | |
date | 2026-10-12 | |
datetime | 2026-10-12T18:00:00.000Z | any offset in, returned in UTC |
time | 18:30 | 24-hour clock |
select | one value, or several with multiple | from a fixed list of choices |
slug | my-first-post | unique in the type, can follow a text field with from |
url | https://example.com | http and https only |
email | hello@example.com | stored in lowercase |
color | #1f5c4a | six-digit hex |
reference | the id of another entry | to limits the types, multiple allows several |
list | repeated items, each with its own fields | up to 500 items |
group | a set of fields kept together | |
json | any JSON value | for data that has no form yet |
media | one file from the media library, or several with multiple | kinds limits it to images, video or files; images need alt text to publish |
rich_text | formatted text: headings, lists, quotes, code, links and images | delivered as JSON and as safe HTML |
embed | a YouTube or Vimeo video | any link to the video works, stored as provider and id |
blocks | the sections of a page: an ordered list of blocks, each of a kind you define with its own fields | top level only; the list is shared by all languages, the fields inside a block can be translated |
Fields can be nested in lists and groups up to three levels deep.
Each field can also have:
- required: publishing is blocked until it has a value, in every language for fields that change per language
- help: a line under the field in the editor
- maxLength and pattern for text; patterns run on RE2, so they always finish in linear time
- unique for
textandslug
Languages
A project has a list of languages and a main one, for example en and ro with en as main. A field marked per language keeps one value for each language; the others keep a single value.
When you ask the Delivery API for locale=ro, every translated field comes back in Romanian and any field without a Romanian value falls back to the main language, field by field. The _locale on each entry tells you which language you asked for.
Drafts, versions and publishing
Every save is a version you can open and restore. An entry is in one of three states:
- draft: never published, invisible to the Delivery API
- published: the live copy matches the last save
- changed: published, with newer edits waiting
Publishing copies the current version to the live copy in one step. Unpublishing removes it from the Delivery API; the entry and its versions stay in the admin.
Changing the structure
You can add, rename and reorder fields at any time. A removed field keeps its data, so bringing it back restores the values. The Delivery API only returns fields that exist in the current structure, so a removed field never leaks to your site.
When the structure changes while someone has an entry open, saving returns 409 schema_changed and the editor asks them to reload, so no one overwrites a field they could not see.