⚠️ Work in progress — createCMS is pre-1.0 and not production-ready (not tested in production). Expect breaking changes.
createCMS
Reference

Roots API

Create, read, move, and version entries (roots).

Roots are the top-level entries of a collection (a page, a post, a product). These methods live at cms.api.<collection>.<method> and mirror on the client as client.<collection>.<method> with identical types. Every commit-producing mutation (createRoot, updateRoot, duplicateRoot) returns a commit: { id, message, createdAt, createdBy } envelope.

Create an Entry

Create a new entry (root) in a collection. You get back a fresh entry with its own draft branch and initial commit, ready for you to start adding blocks to.

root:create
POST/{collection}/createRoot
const { data, error } = await client.pages.createRoot({
  body: {
    slug: 'welcome', // required on slug-enabled collections
    properties: { title: 'Welcome' }, // required
  },
});
Parameters
propertiestyped root propertiesrequired

The entry's root-level properties, typed from the collection's root definition.

slugstring

Slug for the entry (slug-enabled collections).

parentRootIdstring

Parent entry id (nesting-enabled collections).

messagestring= 'Initial commit'

Commit message.

Returns
commitCommit

The new head commit: `{ id, message, createdAt, createdBy }`.

rootIdstring

The id of the new entry.

branchIdstring

The id of the entry's initial draft branch.

slugstringoptional

The server-normalized slug. Slug-enabled collections only.

pathstringoptional

The resolved URL path. Slug-enabled collections only.

List Entries

Page through the entries of a collection with search, filter, and sort. Every field is optional, so a bare call hands you the most recent entries. Each row comes back enriched with its publication, branch, and open-merge-request counts.

root:read
GET/{collection}/listRoots
const { data, error } = await client.pages.listRoots({
  query: { limit: 20, sortBy: 'createdAt', sortDirection: 'desc' },
});
Parameters
limitnumber= 20

Page size, 1 to 100.

offsetnumber= 0

Rows to skip.

searchstring

Substring matched (ILIKE `%term%`) against `searchField`.

searchField'rootId' | 'slug' | 'createdAt' | 'createdBy'

Column, or any root property key, to search.

sortBy'rootId' | 'slug' | 'createdAt' | 'createdBy'= 'createdAt'

Column, or any root property key, to sort by.

sortDirection'asc' | 'desc'= 'desc'

Sort order.

filterField'rootId' | 'slug' | 'createdAt' | 'createdBy'

Column, or any root property key, to filter on.

filterValuestring

ILIKE pattern for `filterField`, passed raw (add `%`/`_` yourself).

hasPublicationsboolean

Keep only entries with (`true`) or without (`false`) any publication.

createdAfterDate

Keep entries created after this date.

createdBeforeDate

Keep entries created before this date.

parentRootIdstring

Filter by parent entry; pass `'null'` for top-level entries.

Returns
rootsRootListItem[]

The entries on this page. Each carries `id`, `slug`, `properties`, `createdAt`, `createdBy`, publication/branch/merge-request counts, and (on slug-enabled collections) `path`.

totalnumber

Total entries matching the filters across all pages, ignoring limit/offset.

hasMoreboolean

Whether more entries exist after this page.

Get an Entry

Fetch a single entry by id, with its current properties, metadata, and publication counts. Use this when you already hold the entry's id; to look one up by slug, reach for getRootBySlug instead.

root:read
GET/{collection}/getRoot
const { data, error } = await client.pages.getRoot({
  query: { rootId: 'root_home' }, // required
});
Parameters
rootIdstringrequired

Entry (root) id.

Returns
rootRootListItem

The entry, with its current `properties`, metadata, `slug`, and publication/branch/merge-request counts.

Get an Entry by Slug

Look up an entry by its draft slug (and optional parent). It matches the per-branch __slug, not the published slug, so you can find an unpublished entry by the slug it is about to publish.

root:read
GET/{collection}/getRootBySlug
const { data, error } = await client.pages.getRootBySlug({
  query: { slug: 'welcome' }, // required
});
Parameters
slugstringrequired

Draft slug to look up.

parentRootIdstring

Parent entry id for nested lookups; omit for top-level.

Returns
rootRootListItem

The matched entry (same shape as `getRoot`). Throws `AMBIGUOUS_SLUG` if more than one draft matches.

Get an Entry's History

Walk the full commit history of an entry across all its branches, newest first, so you can render a timeline or an activity feed. Opt into per-commit change counts with withChanges.

root:read
GET/{collection}/getRootHistory
const { data, error } = await client.pages.getRootHistory({
  query: {
    rootId: 'root_home', // required
    limit: 20,
  },
});
Parameters
rootIdstringrequired

Entry (root) id.

limitnumber= 50

Max commits to return, 1 to 200.

offsetnumber= 0

Commits to skip.

withChangesboolean

Add a per-commit `changes: { added, modified, deleted }` block count.

Returns
commits{ id, message, createdBy, createdAt, branch, parents, type, isPublished }[]

Commit records, newest first (fields detailed below). With `withChanges: true`, each also gains a `changes` count.

totalnumber

Total commits for this entry across all branches.

hasMoreboolean

Whether more commits exist after this page.

Each commit carries id, message, createdBy, createdAt, branch (the branch it was created on), parents (one commit id, two for a merge), type ('initial' | 'commit' | 'merge'), and isPublished. With withChanges: true, the counts are version-level (from a cheap version-id comparison against the parent snapshot, no properties loaded), coarser than getDiff: a pure move counts as modified on the parent whose children array changed, an initial commit counts every block as added, and a merge commit is diffed against its first parent only. The field is omitted (not zeroed) on commits whose snapshot was pruned by retention.

Update an Entry

Edit an entry's properties and/or slug on a branch. You get back the new commit and, on slug-enabled collections, the server-normalized draft slug; redirects are auto-created when a published slug changes.

root:update
POST/{collection}/updateRoot
const { data, error } = await client.pages.updateRoot({
  body: {
    rootId: 'root_home', // required
    branchId: 'br_main', // required
    properties: { title: 'Welcome home' }, // required
  },
});
Parameters
rootIdstringrequired

Entry (root) id.

branchIdstringrequired

Branch to commit the change on.

propertiespartial root propertiesrequired

Root properties to merge; a `null` value deletes that key.

slugstring

New draft slug (slug-enabled collections).

messagestring= 'Update root block {rootId}'

Commit message.

expectedHeadCommitIdstring

Optimistic-concurrency guard: reject if the branch head has moved.

Returns
commitCommit

The new head commit on the branch.

slugstringoptional

The server-normalized draft slug, echoed back on slug-enabled collections.

Move an Entry

Reparent an entry (or promote it to top-level) and reorder it among its siblings. Nesting must be enabled and circular references are rejected; redirects are auto-created when the parent actually changes.

root:update
POST/{collection}/moveRoot
const { data, error } = await client.pages.moveRoot({
  body: {
    rootId: 'root_home', // required
    newParentRootId: 'root_docs', // required (or null for top-level)
  },
});
Parameters
rootIdstringrequired

Entry (root) id to move.

newParentRootIdstring | nullrequired

New parent entry id, or `null` for top-level.

positionnumber

Sort order among the parent's children.

Returns
rootIdstring

The id of the moved entry.

newParentRootIdstring | null

The new parent id, or `null` if the entry is now top-level.

pathstringoptional

The entry's resolved URL path. Slug-enabled collections only.

sortOrdernumber

The new sort position among the parent's children.

redirectsCreatednumber

How many redirects were auto-created (nonzero only when the parent actually changed).

Duplicate an Entry

Deep-copy an entire entry's block tree into a brand-new top-level entry. To copy a subtree under an existing parent instead, reach for duplicateBlock.

root:create
POST/{collection}/duplicateRoot
const { data, error } = await client.pages.duplicateRoot({
  body: {
    rootId: 'root_home', // required
    branchId: 'br_main', // required
    blockId: 'root_home', // required (the entry's root block id)
    targetProperties: { title: 'Welcome (copy)' }, // required
    targetSlug: 'welcome-copy',
  },
});
Parameters
rootIdstringrequired

Source entry (root) id.

branchIdstringrequired

Source branch id.

blockIdstringrequired

Root block id to duplicate.

targetPropertiesRecord<string, unknown>required

Properties for the new root block.

targetSlugstring

Slug for the new entry (slug-enabled collections).

messagestring= 'Duplicated root'

Commit message.

Returns
mode'root'

Always `'root'`: this endpoint only ever produces a new top-level entry.

commitCommit

The initial commit of the new entry.

rootIdstring

The id of the new entry.

branchIdstring

The id of the new entry's draft branch.

slugstringoptional

The new entry's server-normalized slug. Slug-enabled collections only.

pathstringoptional

The new entry's resolved URL path. Slug-enabled collections only.

Archive an Entry

Soft-archive an entry (its history is preserved) and auto-create a redirect from its old path to its parent. You cannot archive an entry that still has live child pages or is embedded as a reusable block on live content.

root:delete
POST/{collection}/archiveRoot
const { data, error } = await client.pages.archiveRoot({
  body: {
    rootId: 'root_home', // required
  },
});
Parameters
rootIdstringrequired

Entry (root) id.

Returns
rootIdstring

The id of the archived entry.

pathstringoptional

The now-archived URL, for a "removed /x" confirmation. Slug-enabled collections only.

redirectsCreatednumber

How many redirects were auto-created (1 when an archive redirect to the parent was written).

On this page