Contents
- Domma CMS User Manual
- 1. Using the CMS
- 2. Tutorials
- 3. Components
- 4. API Reference
- 5. Tools
Collections API
Updated by Darryl Waterhouse on 29 September 2026 · 6 min read
Collections have two access planes: admin endpoints (/api/collections/..., signed-in, gated by the collections permission for the action named - read, create, update, delete) and public endpoints, gated per collection and per verb by the collection's API settings (the collection editor's API & Export tab). The same public endpoints are also served at /api/v1/:slug for external programs - see External API & tokens.
An entry looks like this on every storage adapter:
{ "id": "uuid", "data": { "title": "..." },
"meta": { "createdAt": "...", "updatedAt": "...", "createdBy": "user-id", "source": "admin" } }
Schema Management
GET /api/collections
Requires Bearer token + collections read permission.
Every collection schema you may see (no entries), each with its entry count.
// Response 200
[ { "slug": "jobs", "title": "Jobs", "description": "...", "fields": [...], "entryCount": 12 } ]
POST /api/collections
Requires Bearer token + collections create permission.
Create a collection. The slug is made from the title if left out.
| Field | Type | Description |
|---|---|---|
title | string | Required. The collection's name |
slug | string | Optional. URL-safe identifier |
description | string | Optional |
fields | array | Field definitions |
api | object | Public access per verb - see Public Access below |
export | object | Who may export from the public site's right-click menu |
storage | object | Optional, needs MongoDB (Pro): { "adapter": "mongodb", "connection": "default" }. Files otherwise. |
// Response 201 - the created schema
// Error 409
{ "error": "A collection with that slug already exists" }
GET /api/collections/:slug
Requires Bearer token + collections read permission.
One collection's schema.
PUT /api/collections/:slug
Requires Bearer token + collections update permission.
Update a schema. To move a collection between file and MongoDB storage, use migrate-storage below rather than editing storage, so the entries move with it.
DELETE /api/collections/:slug
Requires Bearer token + collections delete permission.
Delete a collection and all its entries. Built-in (preset) collections cannot be deleted.
// Response 200
{ "success": true }
// Error 403
{ "error": "Cannot delete a preset collection" }
POST /api/collections/:slug/migrate-storage
Requires Bearer token + collections update permission.
Move a collection's entries to file or MongoDB storage (the collection editor's Storage tab). Each entry is copied as it is - the same id, data and meta (created and updated dates, who created it) - so references, links and row ownership keep working. Nothing is moved, and nothing changes, if the target already holds an entry with one of the ids or the source holds an id twice (409, with the ids in collisions). The old copy is set aside, never deleted: data.json becomes data.json.bak, and a MongoDB collection is renamed cms_<slug>__moved_<time>. System collections (roles, user profiles, projects, notifications, API tokens and endpoints) always use files and cannot be moved (400).
// Request body
{ "storage": { "adapter": "mongodb", "connection": "default" } } // or { "adapter": "file" }
// Response 200
{ "migrated": 42, "total": 42, "archived": "..." }
// Error 409
{ "error": "The target storage already holds 3 of these entries ...", "collisions": ["id-1", "id-2", "id-3"] }
// Error 503 - the MongoDB connection is not available
GET /api/collections/pro-status
Requires Bearer token + collections read permission.
Whether any database connection is set up (MongoDB storage is a Pro feature).
// Response 200
{ "pro": true, "connections": ["default"] }
GET /api/collections/connections
Requires Bearer token + an admin role (level 0 or 1).
The MongoDB connections in config/connections.json. PUT saves them; each needs type, uri and database. Passwords are never sent: the one in a URI, and any secret-looking option, comes back as ••••••••. Send the mask back unchanged to keep the stored value; it is restored only while scheme, user, hosts, query and options are unchanged.
// Response 200
{ "default": { "type": "mongodb", "uri": "mongodb://site:••••••••@localhost:27018/my_cms", "database": "my_cms" } }
// PUT - Error 400
{ "error": "Connection \"default\" requires type, uri, and database" }
{ "error": "Connection \"default\": type the password again - it was hidden, and the connection now points somewhere else or it was never saved" }
Admin Entry CRUD
GET /api/collections/:slug/entries
Requires Bearer token + collections read permission.
List entries with paging, sorting, search and filters.
| Query param | Default | Description |
|---|---|---|
page | 1 | Page number |
limit | 50 | Entries per page |
sort | createdAt | Field to sort by |
order | desc | asc or desc |
search | - | Text found in any field |
filter[<field>], filter[<field>_<op>] | - | Structured filters, ANDed. Operators: eq, ne, gt, gte, lt, lte, in, nin, contains, starts, ends, exists. createdBy and id work as field names. |
// GET /api/collections/jobs/entries?filter[location]=London&filter[salary_gte]=50000
// Response 200
{ "entries": [ { "id": "uuid", "data": { ... }, "meta": { ... } } ], "total": 42, "page": 1, "limit": 50 }
GET /api/collections/:slug/entries/:id
Requires Bearer token + collections read permission.
One entry.
POST /api/collections/:slug/entries
Requires Bearer token + collections create permission.
Create an entry. Its data is checked against the schema (required fields, and that every reference points at an entry that exists).
// Request body
{ "data": { "title": "Senior Developer", "location": "London" } }
// Response 201 - the created entry
PUT /api/collections/:slug/entries/:id
Requires Bearer token + collections update permission.
Update an entry. data replaces the stored data as a whole, so send every field. POST /api/collections/:slug/entries/:id/spam flags one entry as spam without touching the rest.
DELETE /api/collections/:slug/entries/:id
Requires Bearer token + collections delete permission.
Delete one entry. Entries that referenced it are not changed; they show the reference as missing.
// Response 200
{ "success": true }
DELETE /api/collections/:slug/entries
Requires Bearer token + collections delete permission.
Delete every entry in a collection. Cannot be undone.
// Response 200
{ "success": true }
Export & Import
GET /api/collections/:slug/export
Requires Bearer token + collections read permission.
Download every entry as ?format=json (default) or ?format=csv.
// Response 200 - file download
// Content-Disposition: attachment; filename="jobs-entries.json"
POST /api/collections/:slug/import
Requires Bearer token + collections create permission.
Add entries from a JSON array (JSON only). Existing entries are kept. Each entry is checked like any other save - required fields, and references to entries that must exist - and one that fails is skipped and reported. Fields the collection does not define are stored as given.
// Request body
{ "entries": [ { "data": { "title": "Post 1" } }, { "data": { "title": "Post 2" } } ] }
// Response 201
{ "imported": 2, "skipped": 0, "errors": [] }
Public Access
Each verb (read, create, update, delete) has its own setting in the schema's api block: switched off (403), public (no sign-in), token (a project API token only - see External API & tokens), or a role name, which admits a signed-in user whose role is that senior or more. A role name the site does not have admits only the level-0 role. api.read.fields, when set, limits which fields are returned. A collection in a disabled project answers 404 on every public route.
"api": {
"read": { "enabled": true, "access": "public", "fields": ["title", "location"] },
"create": { "enabled": true, "access": "user" },
"update": { "enabled": false, "access": "admin" },
"delete": { "enabled": false, "access": "admin" }
}
GET /api/collections/:slug/public
Access level: the collection's api.read setting.
List entries. The same page, limit, sort, order, search and filter[...] params as the admin endpoint, plus resolveRefs=true to fill in referenced entries. scope=mine returns only the entries the signed-in user created (401 without a sign-in); it does not need api.read switched on.
GET /api/collections/:slug/public/:id
Access level: the collection's api.read setting.
One entry.
POST /api/collections/:slug/public
Access level: the collection's api.create setting.
Create an entry ({"data": {...}}). It is marked source: "api", and createdBy is the signed-in user or token:<id>.
PUT /api/collections/:slug/public/:id
Access level: the collection's api.update setting.
Update an entry ({"data": {...}}).
DELETE /api/collections/:slug/public/:id
Access level: the collection's api.delete setting.
Delete an entry.
POST /api/collections/render-scope
Requires a signed-in user (JWT).
Used by the public site to draw a [collection scope="mine"] block for the signed-in visitor: only their own entries. An interactive block (searchable, sortable, filterable or paginate) comes back as the Collection Browser, with transition buttons when the block asks for transitions. POST /api/collections/render-fragment does the same for other block-display pages of the Browser, gated by api.read. Both take the block's attributes as base64 JSON in attrs and answer {"html": "..."}.
See also: External API & tokens for /api/v1/:slug and project-scoped API tokens, and API Builder for your own endpoints at /api/x/....
On this page
- Schema Management
- GET /api/collections
- POST /api/collections
- GET /api/collections/:slug
- PUT /api/collections/:slug
- DELETE /api/collections/:slug
- POST /api/collections/:slug/migrate-storage
- GET /api/collections/pro-status
- GET /api/collections/connections
- Admin Entry CRUD
- GET /api/collections/:slug/entries
- GET /api/collections/:slug/entries/:id
- POST /api/collections/:slug/entries
- PUT /api/collections/:slug/entries/:id
- DELETE /api/collections/:slug/entries/:id
- DELETE /api/collections/:slug/entries
- Export & Import
- GET /api/collections/:slug/export
- POST /api/collections/:slug/import
- Public Access
- GET /api/collections/:slug/public
- GET /api/collections/:slug/public/:id
- POST /api/collections/:slug/public
- PUT /api/collections/:slug/public/:id
- DELETE /api/collections/:slug/public/:id
- POST /api/collections/render-scope