All admin endpoints are prefixed with /api and require a Bearer token in the
Authorization header unless noted otherwise. Content type for request bodies is
application/json. Tokens are obtained via POST /api/auth/login.
Base URL: http://your-domain/api
No authentication required.
Check whether the CMS has been set up. Returns { needsSetup: true } when no users
exist.
// Response
{ "needsSetup": false }
No authentication required. Only succeeds when zero users exist.
Create the initial admin account. Blocked once any user exists (returns 403).
| Field | Type | Description |
|---|---|---|
name |
string | Display name |
email |
string | Email address |
password |
string | Minimum 8 characters |
// Response 201
{ "token": "eyJ...", "refreshToken": "eyJ...", "user": { "id": "...", "name": "...", "email": "...", "role": "admin" } }
No authentication required.
Authenticate with email and password. Returns access and refresh tokens.
| Field | Type | Description |
|---|---|---|
email |
string | User email |
password |
string | User password |
// Response 200
{ "token": "eyJ...", "refreshToken": "eyJ...", "user": { "id": "uuid", "name": "Alice", "email": "alice@example.com", "role": "admin" } }
// Error 401
{ "error": "Invalid credentials" }
Requires Bearer token.
Return the authenticated user's profile.
// Response 200
{ "id": "uuid", "name": "Alice", "email": "alice@example.com", "role": "admin", "isActive": true }
No authentication required. Safe to call without a token.
Blacklists the provided refresh token. The in-memory blacklist is cleared on server restart.
| Field | Type | Description |
|---|---|---|
refreshToken |
string | The refresh token to revoke (optional) |
// Response 200
{ "ok": true }
No authentication required. Provide a valid refresh token.
Exchange a refresh token for a new access token.
| Field | Type | Description |
|---|---|---|
refreshToken |
string | A valid, non-revoked refresh token |
// Response 200
{ "token": "eyJ..." }
// Error 401
{ "error": "Invalid or expired refresh token" }
Requires Bearer token + pages permission.
Render Markdown to HTML (shortcodes processed, no frontmatter). Useful for live editor previews.
| Field | Type | Description |
|---|---|---|
markdown |
string | Markdown string to render |
// Response 200
{ "html": "<p>Hello <strong>world</strong></p>" }
Requires Bearer token + pages permission.
Aggregate all unique tags across every page, sorted alphabetically.
// Response 200
{ "tags": ["guide", "news", "tutorial"] }
Requires Bearer token + pages permission.
List all pages with their metadata. Body content is excluded.
// Response 200
[
{
"urlPath": "/about",
"title": "About Us",
"slug": "about",
"status": "published",
"layout": "default",
"tags": [],
"updatedAt": "2024-01-15T10:30:00.000Z",
"createdAt": "2024-01-01T09:00:00.000Z"
}
]
Requires Bearer token + pages permission. The * is the
URL path,
e.g. /api/pages/about.
Retrieve a single page including its frontmatter and full body content.
// Response 200
{ "urlPath": "/about", "title": "About Us", "body": "## Our Story\n\nWe started...", ... }
// Error 404
{ "error": "Page not found" }
Requires Bearer token + pages permission.
Create a new page. Fails with 409 if a page already exists at the given path.
| Field | Type | Description |
|---|---|---|
urlPath |
string | Required. Public URL path e.g. /about |
frontmatter |
object | YAML frontmatter fields (title, status, etc.) |
body |
string | Markdown content |
// Response 201 — returns the created page object
Requires Bearer token + pages permission.
Update an existing page. Optionally rename it to a new URL path.
| Field | Type | Description |
|---|---|---|
frontmatter |
object | Updated frontmatter fields |
body |
string | Updated Markdown content |
newUrlPath |
string | Optional. Rename the page to a different URL path |
// Response 200 — returns the updated page object
// Error 404
{ "error": "Page not found" }
// Error 409
{ "error": "A page already exists at that path" }
Requires Bearer token + pages permission.
Delete a page and its source file.
// Response 200
{ "success": true }
// Error 404
{ "error": "Page not found" }
Requires Bearer token + settings permission.
Return the full site settings object from config/site.json.
// Response 200
{ "siteName": "My Site", "adminTheme": "charcoal-dark", "smtp": { ... }, ... }
Requires Bearer token + settings permission.
Replace the site settings object. Send the full merged object — partial updates overwrite the entire config.
// Request body — full site settings object
{ "siteName": "My Site", "adminTheme": "ocean-dark", ... }
// Response 200
{ "success": true }
Requires Bearer token + settings permission.
Send a test email using the stored SMTP configuration.
| Field | Type | Description |
|---|---|---|
to |
string | Optional. Recipient address. Defaults to the configured From Address. |
// Response 200
{ "success": true, "message": "Test email sent to alice@example.com" }
// Error 400
{ "error": "SMTP is not configured. Save your SMTP settings first." }
Requires Bearer token + settings permission.
Return the current custom CSS from content/custom.css.
// Response 200
{ "css": "body { font-family: sans-serif; }" }
Requires Bearer token + settings permission.
Write CSS to content/custom.css. Maximum size is 100 KB.
| Field | Type | Description |
|---|---|---|
css |
string | CSS string (max 100 KB) |
// Response 200
{ "success": true }
Requires Bearer token + layouts permission.
Return all layout presets from config/presets.json.
// Response 200
{
"default": { "label": "Default", "sections": [...] },
"full-width": { "label": "Full Width", "sections": [...] }
}
Requires Bearer token + layouts permission.
Replace the entire layout presets object.
// Response 200
{ "success": true }
Requires Bearer token + layouts permission.
Return layout display options stored in config/site.json under
layoutOptions.
// Response 200
{ "spacerSize": 8 }
Requires Bearer token + layouts permission.
Merge layout option updates into the existing options. Existing keys not included are preserved.
| Field | Type | Description |
|---|---|---|
spacerSize |
number | Default spacer block size in pixels |
// Response 200
{ "success": true }
Requires Bearer token + navigation permission.
Return the navigation configuration from config/navigation.json.
// Response 200
{
"items": [
{ "label": "Home", "url": "/" },
{ "label": "About", "url": "/about" },
{ "label": "Resources", "items": [ { "label": "Blog", "url": "/blog" } ] }
]
}
Requires Bearer token + navigation permission.
Replace the navigation config. Sub-items must use the items key (the server normalises
children
to items automatically).
// Request body
{ "items": [ { "label": "Home", "url": "/" }, { "label": "About", "url": "/about" } ] }
// Response 200
{ "success": true }
Requires Bearer token + media permission.
List all media files in the uploads directory.
// Response 200
[ { "name": "hero.jpg", "url": "/media/hero.jpg", "size": 204800, "mime": "image/jpeg" } ]
Requires Bearer token + media permission. Content-Type: multipart/form-data.
Upload one or more files. Returns a single object for one file, or an array for multiple.
// Response 201 (single)
{ "name": "photo.jpg", "url": "/media/photo.jpg", "size": 98304, "mime": "image/jpeg" }
Requires Bearer token + media permission.
Rename a media file. Fails with 409 if the new name already exists.
| Field | Type | Description |
|---|---|---|
newName |
string | New filename (will be sanitised) |
// Response 200
{ "name": "new-photo.jpg", "url": "/media/new-photo.jpg", ... }
Requires Bearer token + media permission.
Delete a media file from the uploads directory.
// Response 200
{ "success": true }
Requires Bearer token + media permission.
Return image metadata (dimensions, format, file size) for editable image formats (JPEG, PNG, WebP, GIF, TIFF).
// Response 200
{ "width": 1920, "height": 1080, "format": "jpeg", "size": 204800 }
Requires Bearer token + media permission.
Apply image transformations (resize, crop, rotate, watermark, etc.) and optionally save to a new filename.
| Field | Type | Description |
|---|---|---|
operations |
object | Transformation operations to apply |
saveAs |
string | Optional. Output filename. Defaults to overwriting the source. |
// Request body
{ "operations": { "resize": { "width": 800, "height": 600 }, "format": "webp" }, "saveAs": "hero-800.webp" }
// Response 200
{ "name": "hero-800.webp", "url": "/media/hero-800.webp", ... }
Role hierarchy governs which users can manage other users. A manager cannot create, edit, or delete an admin. Self-deletion is always blocked.
Requires Bearer token + users permission (admin or manager).
Return all users. Passwords are stripped from the response.
// Response 200
[ { "id": "uuid", "name": "Alice", "email": "alice@example.com", "role": "admin", "isActive": true } ]
Requires Bearer token. Accessible to the user themselves, or a user with users
permission.
Return a single user by ID.
// Response 200
{ "id": "uuid", "name": "Alice", "email": "alice@example.com", "role": "admin", "isActive": true }
// Error 404
{ "error": "User not found" }
Requires Bearer token + users permission.
Create a new user. The actor cannot assign a role higher than their own level.
| Field | Type | Description |
|---|---|---|
name |
string | Display name |
email |
string | Unique email address |
password |
string | Minimum 8 characters |
role |
string | Optional. Defaults to editor. |
// Response 201
{ "id": "uuid", "name": "Bob", "email": "bob@example.com", "role": "editor", "isActive": true }
// Error 409
{ "error": "Email already in use" }
Requires Bearer token + users permission.
Update a user's details. Managers cannot edit admins. Role escalation beyond the actor's own level is blocked.
| Field | Type | Description |
|---|---|---|
name |
string | New display name |
email |
string | New email address |
password |
string | New password (min 8 chars) |
role |
string | New role |
isActive |
boolean | Enable or disable the account |
// Response 200 — returns the updated user object
Requires Bearer token + users permission.
Delete a user. Cannot delete your own account or a user with a higher role level.
// Response 200
{ "success": true }
// Error 403
{ "error": "You cannot delete your own account" }
Requires Bearer token + admin role.
List all discovered plugins with their current enabled state and settings.
// Response 200
[
{
"name": "form-builder",
"displayName": "Form Builder",
"version": "1.0.0",
"description": "Build and manage contact forms.",
"author": "Domma Team",
"date": "2024-01-01",
"icon": "clipboard",
"enabled": true,
"settings": {}
}
]
Requires Bearer token + admin role.
Enable or disable a plugin, or update its settings.
| Field | Type | Description |
|---|---|---|
enabled |
boolean | Whether the plugin is active |
settings |
object | Plugin-specific settings object |
// Response 200
{ "success": true }
// Error 404
{ "error": "Plugin not found" }
Requires Bearer token (any role).
Return the merged admin configuration for all enabled plugins — sidebar items, SPA routes, and view entry points.
// Response 200
{
"sidebar": [ { "id": "form-builder", "text": "Form Settings", "icon": "clipboard", "url": "#/form-settings" } ],
"routes": [ { "path": "/form-settings", "view": "formSettings", "title": "Form Settings - Domma CMS" } ],
"views": { "formSettings": { "entry": "form-builder/admin/views/settings.js", "exportName": "formSettingsView" } }
}
Collections have two access planes: admin endpoints (authenticated, role-gated) and public endpoints (access level configured per collection).
Requires Bearer token + collections permission.
List all collection schemas (metadata only, no entries).
// Response 200
[ { "slug": "blog", "title": "Blog Posts", "description": "...", "fields": [...] } ]
Requires Bearer token + collections permission.
Check whether the Pro (MongoDB) storage adapter is available.
// Response 200 (free)
{ "pro": false, "connections": [] }
// Response 200 (pro)
{ "pro": true, "connections": ["default", "analytics"] }
Requires Bearer token + admin role.
Return configured MongoDB connections from config/connections.json.
// Response 200
{ "default": { "type": "mongodb", "uri": "mongodb://localhost:27017", "database": "my_cms" } }
Requires Bearer token + admin role.
Save MongoDB connection definitions. Each connection requires type, uri,
and database.
// Response 200
{ "success": true }
// Error 400
{ "error": "Connection \"default\" requires type, uri, and database" }
Requires Bearer token + collections permission.
Create a new collection. A slug is auto-generated from the title if not provided.
| Field | Type | Description |
|---|---|---|
title |
string | Required. Human-readable collection name |
slug |
string | Optional. URL-safe identifier. Auto-generated if omitted. |
description |
string | Optional description |
fields |
array | Field definitions |
api |
object | Public API access config per operation |
storage |
object | Optional Pro: { "adapter": "mongodb", "connection": "default" } |
// Response 201 — returns the created schema object
// Error 409
{ "error": "A collection with that slug already exists" }
Requires Bearer token + collections permission.
Return the schema for a single collection by slug.
Requires Bearer token + collections permission.
Update a collection schema.
Requires Bearer token + collections permission.
Delete a collection and all its entries. Preset collections cannot be deleted.
// Response 200
{ "success": true }
// Error 403
{ "error": "Cannot delete a preset collection" }
Requires Bearer token + collections permission.
List entries with pagination, sorting, and full-text search.
| 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 |
— | Full-text search query |
// Response 200
{ "entries": [ { "id": "uuid", "data": { ... }, "createdAt": "...", "updatedAt": "..." } ], "total": 42, "page": 1, "limit": 50 }
Requires Bearer token + collections permission.
Return a single entry by ID.
Requires Bearer token + collections permission.
Create a new entry. Data is validated against the collection schema.
// Response 201 — returns the created entry
Requires Bearer token + collections permission.
Update an entry. Data is validated against the schema.
Requires Bearer token + collections permission.
Delete a single entry.
// Response 200
{ "success": true }
Requires Bearer token + collections permission.
Clear all entries from a collection. Irreversible.
// Response 200
{ "success": true }
Requires Bearer token + collections permission.
Download all entries as a file attachment.
| Query param | Values | Description |
|---|---|---|
format |
json (default), csv |
Export format |
// Response 200 — file download
// Content-Disposition: attachment; filename="blog-entries.json"
Requires Bearer token + collections permission.
Bulk-import entries from a JSON array. Existing entries are not removed.
| Field | Type | Description |
|---|---|---|
entries |
array | Array of entry objects with a data field each |
// Request body
{ "entries": [ { "data": { "title": "Post 1" } }, { "data": { "title": "Post 2" } } ] }
// Response 201
{ "imported": 2, "skipped": 0 }
Public endpoints respect the per-collection api config. Each operation can be disabled,
public (no auth), or restricted to a minimum role level.
Access level: per collection api.read config.
List entries publicly. Supports the same pagination and search query params as the admin endpoint.
Access level: per collection api.read config.
Return a single entry publicly by ID.
Access level: per collection api.create config.
Create an entry publicly (e.g. form submissions). Entry is tagged with source: "api".
Access level: per collection api.update config.
Update an entry publicly.
Access level: per collection api.delete config.
Delete an entry publicly.
Views require a MongoDB connection. All admin endpoints require authentication and the
views permission. View configs are stored in the cms__views MongoDB
collection.
Requires: views permission
List all view configs, sorted by creation date descending.
Requires: views permission
Create a new view config. Returns 201 on success.
{
"title": "Active Premium Users",
"slug": "active-premium-users",
"connection": "default",
"pipeline": {
"source": "users",
"stages": [
{ "type": "$match", "config": { "data.status": "active" } },
{ "type": "$sort", "config": { "meta.createdAt": -1 } },
{ "type": "$project", "config": { "data.name": 1, "data.email": 1 } }
]
},
"display": { "mode": "table", "columns": [ { "key": "data.name", "label": "Name" } ], "pageSize": 25 },
"access": { "roles": ["admin", "manager"], "public": false }
}
Requires: views permission
Return a single view config by slug.
Requires: views permission
Update a view config. Accepts the same body shape as POST; all fields are optional.
Requires: views permission
Delete a view config.
Requires: views permission
Execute the view's aggregation pipeline and return paginated results. Query params:
page (default 1), limit (default 25).
// Response
{ "results": [ ... ], "total": 142, "page": 1, "limit": 25 }
Requires: views permission
List all view configs whose pipeline.source matches the given collection slug.
Access level: per view access config
Execute the view publicly. If access.public is false, a valid JWT and a
role listed in access.roles is required.
Actions require a MongoDB connection. All admin endpoints require authentication and the
actions permission. Action configs are stored in cms__actions.
Requires: actions permission
List all action configs.
Requires: actions permission
Create a new action config. Returns 201 on success.
{
"title": "Approve Application",
"slug": "approve-application",
"collection": "applications",
"trigger": { "type": "manual", "label": "Approve", "icon": "check-circle", "confirmMessage": "Approve this application?" },
"steps": [
{ "type": "updateField", "config": { "field": "status", "value": "approved" } },
{ "type": "updateField", "config": { "field": "approvedAt", "value": "{{now}}" } },
{ "type": "email", "config": { "to": "{{entry.data.email}}", "subject": "Application approved",
"template": "Hi {{entry.data.name}}, your application is approved." } }
],
"access": { "roles": ["admin", "manager"] }
}
Requires: actions permission
Return a single action config by slug.
Requires: actions permission
Update an action config.
Requires: actions permission
Delete an action config.
Requires: actions permission
Execute an action against a specific entry.
// Request body
{ "entryId": "uuid-of-the-entry" }
// Response
{ "success": true, "stepsCompleted": 4, "results": [ { "type": "updateField", "success": true, ... } ] }
// Partial failure
{ "success": false, "stepsCompleted": 2, "results": [ ..., { "type": "webhook", "success": false, "error": "Webhook returned HTTP 500" } ] }
Requires: actions permission
List all action configs targeting a given collection slug. Used by the entry list view to populate per-row trigger buttons.
Requires: JWT + role in access.roles
Execute an action publicly. Always requires a valid JWT — the role is checked against access.roles.
Request body and response shape are identical to the admin /execute endpoint.