API Reference

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

GET/api/auth/setup-status

No authentication required.

Check whether the CMS has been set up. Returns { needsSetup: true } when no users exist.

// Response
{ "needsSetup": false }

POST/api/auth/setup

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" } }

POST/api/auth/login

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" }

GET/api/auth/me

Requires Bearer token.

Return the authenticated user's profile.

// Response 200
{ "id": "uuid", "name": "Alice", "email": "alice@example.com", "role": "admin", "isActive": true }

POST/api/auth/logout

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 }

POST/api/auth/refresh

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" }

POST/api/pages/preview

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>" }

GET/api/pages/tags

Requires Bearer token + pages permission.

Aggregate all unique tags across every page, sorted alphabetically.

// Response 200
{ "tags": ["guide", "news", "tutorial"] }

GET/api/pages

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"
  }
]

GET/api/pages/*

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" }

POST/api/pages

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

PUT/api/pages/*

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" }

DELETE/api/pages/*

Requires Bearer token + pages permission.

Delete a page and its source file.

// Response 200
{ "success": true }

// Error 404
{ "error": "Page not found" }

GET/api/settings

Requires Bearer token + settings permission.

Return the full site settings object from config/site.json.

// Response 200
{ "siteName": "My Site", "adminTheme": "charcoal-dark", "smtp": { ... }, ... }

PUT/api/settings

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 }

POST/api/settings/test-email

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." }

GET/api/settings/custom-css

Requires Bearer token + settings permission.

Return the current custom CSS from content/custom.css.

// Response 200
{ "css": "body { font-family: sans-serif; }" }

PUT/api/settings/custom-css

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 }

GET/api/layouts

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": [...] }
}

PUT/api/layouts

Requires Bearer token + layouts permission.

Replace the entire layout presets object.

// Response 200
{ "success": true }

GET/api/layouts/options

Requires Bearer token + layouts permission.

Return layout display options stored in config/site.json under layoutOptions.

// Response 200
{ "spacerSize": 8 }

PUT/api/layouts/options

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 }

GET/api/navigation

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" } ] }
  ]
}

PUT/api/navigation

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 }

GET/api/media

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" } ]

POST/api/media

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" }

PATCH/api/media/:name

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", ... }

DELETE/api/media/:name

Requires Bearer token + media permission.

Delete a media file from the uploads directory.

// Response 200
{ "success": true }

GET/api/media/:name/info

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 }

POST/api/media/:name/transform

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.

GET/api/users

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 } ]

GET/api/users/:id

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" }

POST/api/users

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" }

PUT/api/users/:id

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

DELETE/api/users/:id

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" }

GET/api/plugins

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": {}
  }
]

PUT/api/plugins/:name

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" }

GET/api/plugins/admin-config

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).

Schema Management

GET/api/collections

Requires Bearer token + collections permission.

List all collection schemas (metadata only, no entries).

// Response 200
[ { "slug": "blog", "title": "Blog Posts", "description": "...", "fields": [...] } ]

GET/api/collections/pro-status

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"] }

GET/api/collections/connections

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" } }

PUT/api/collections/connections

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" }

POST/api/collections

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" }

GET/api/collections/:slug

Requires Bearer token + collections permission.

Return the schema for a single collection by slug.

PUT/api/collections/:slug

Requires Bearer token + collections permission.

Update a collection schema.

DELETE/api/collections/:slug

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" }

Admin Entry CRUD

GET/api/collections/:slug/entries

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 }

GET/api/collections/:slug/entries/:id

Requires Bearer token + collections permission.

Return a single entry by ID.

POST/api/collections/:slug/entries

Requires Bearer token + collections permission.

Create a new entry. Data is validated against the collection schema.

// Response 201 — returns the created entry

PUT/api/collections/:slug/entries/:id

Requires Bearer token + collections permission.

Update an entry. Data is validated against the schema.

DELETE/api/collections/:slug/entries/:id

Requires Bearer token + collections permission.

Delete a single entry.

// Response 200
{ "success": true }

DELETE/api/collections/:slug/entries

Requires Bearer token + collections permission.

Clear all entries from a collection. Irreversible.

// Response 200
{ "success": true }

Export & Import

GET/api/collections/:slug/export

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"

POST/api/collections/:slug/import

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 Access

Public endpoints respect the per-collection api config. Each operation can be disabled, public (no auth), or restricted to a minimum role level.

GET/api/collections/:slug/public

Access level: per collection api.read config.

List entries publicly. Supports the same pagination and search query params as the admin endpoint.

GET/api/collections/:slug/public/:id

Access level: per collection api.read config.

Return a single entry publicly by ID.

POST/api/collections/:slug/public

Access level: per collection api.create config.

Create an entry publicly (e.g. form submissions). Entry is tagged with source: "api".

PUT/api/collections/:slug/public/:id

Access level: per collection api.update config.

Update an entry publicly.

DELETE/api/collections/:slug/public/:id

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.

Admin Endpoints

GET/api/views

Requires: views permission

List all view configs, sorted by creation date descending.

POST/api/views

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 }
}

GET/api/views/:slug

Requires: views permission

Return a single view config by slug.

PUT/api/views/:slug

Requires: views permission

Update a view config. Accepts the same body shape as POST; all fields are optional.

DELETE/api/views/:slug

Requires: views permission

Delete a view config.

GET/api/views/:slug/execute

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 }

GET/api/views/collection/:slug

Requires: views permission

List all view configs whose pipeline.source matches the given collection slug.

Public Endpoint

GET/api/views/:slug/public

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.

Admin Endpoints

GET/api/actions

Requires: actions permission

List all action configs.

POST/api/actions

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"] }
}

GET/api/actions/:slug

Requires: actions permission

Return a single action config by slug.

PUT/api/actions/:slug

Requires: actions permission

Update an action config.

DELETE/api/actions/:slug

Requires: actions permission

Delete an action config.

POST/api/actions/:slug/execute

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" } ] }

GET/api/actions/collection/:slug

Requires: actions permission

List all action configs targeting a given collection slug. Used by the entry list view to populate per-row trigger buttons.

Public Endpoint

POST/api/actions/:slug/public

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.