---
gitea: none
include_toc: true
---

# Schemas

Schemas define both the shape of your data and how the admin UI behaves.

There are 2 types of schemas:

- **collection** — Regular data records
- **files** — Images and file uploads


## Collection Reference

| Field | Required | Description |
|---|---|---|
| `name` | yes | Unique ID. Use camelCase plural e.g. `blogPosts` |
| `label` | yes | Friendly display name |
| `type` | yes | Must be `"collection"` |
| `fields` | yes | Array of field definitions. See [Fields](Fields.md) |
| `visible` | no | Field IDs to show in the content browser list |
| `groups` | no | Group IDs to split fields into tabs |
| `isEntry` | no | Show in sidebar. Default: `false` |
| `sortBy` | no | Default sort in browser. Prefix with `-` for descending e.g. `-_sys.updatedAt` |
| `cardTitle` | no | Mustache template for the record preview title e.g. `{{name}} - {{slug}}` |
| `cardImage` | no | Field name to use as the preview image |
| `revisions` | no | Number of revisions to keep per record. Default: `0` (disabled) |
| `read` | no | Roles with read access. Empty means all roles can read |
| `write` | no | Roles with write access. Empty means all roles can write |


## Files Reference

| Field | Required | Description |
|---|---|---|
| `name` | yes | Unique ID. Use camelCase plural e.g. `heroImages` |
| `label` | yes | Friendly display name |
| `type` | yes | Must be `"files"` |
| `fields` | yes | Array of field definitions. See [Fields](Fields.md) |
| `groups` | no | Group IDs to split fields into tabs |
| `isEntry` | no | Show in sidebar. Default: `false` |
| `sortBy` | no | Default sort in browser |
| `cardTitle` | no | Mustache template for the record preview title |
| `cardImage` | no | Field name to use as the preview image |
| `revisions` | no | Number of revisions to keep per record |
| `read` | no | Roles with read access |
| `write` | no | Roles with write access |

### File Metadata

Every uploaded file is automatically stored with the following metadata (stored in `lucent_files`):

| Field | Description |
|---|---|
| `id` | Unique file ID |
| `recordId` | ID of the record this file belongs to |
| `originalName` | Original filename as uploaded |
| `mime` | MIME type e.g. `image/webp` |
| `path` | Storage path e.g. `files/{recordId}/{filename}` |
| `size` | File size in bytes |
| `width` | Image width in pixels (0 for non-images) |
| `height` | Image height in pixels (0 for non-images) |
| `checksum` | SHA-1 hash of the file contents |

Image files (jpeg, png, webp, gif, tiff) also get a 300×300 thumbnail generated automatically at `thumbs/{path}`, plus any image filter presets configured in `lucent.imageFilters`.


## System Fields

Every record automatically has these read-only system fields available in queries:

| Field | Description |
|---|---|
| `_sys.createdAt` | ISO 8601 creation timestamp |
| `_sys.updatedAt` | ISO 8601 last update timestamp |
| `_sys.createdBy` | User ID who created the record |
| `_sys.updatedBy` | User ID who last updated the record |
| `_sys.version` | Revision version number |
| `status` | `draft` or `published` |


## Example

```json
{
  "schemas": [
    {
      "label": "Blog Posts",
      "name": "blogPosts",
      "isEntry": true,
      "type": "collection",
      "visible": [
        "title",
        "slug",
        "_sys.updatedAt",
        "status"
      ],
      "groups": [
        "Content",
        "SEO"
      ],
      "sortBy": "-_sys.createdAt",
      "cardTitle": "{{title}}",
      "cardImage": "cover",
      "revisions": 15,
      "read": [
        "admin",
        "editors",
        "reviewers"
      ],
      "write": [
        "admin",
        "editors"
      ],
      "fields": []
    }
  ],
  "roles": ["admin", "editors", "reviewers"]
}
```
