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/{collection}/createRootconst data = await cms.api.pages.createRoot({
body: {
slug: 'welcome', // required on slug-enabled collections
properties: { title: 'Welcome' }, // required
},
});const { data, error } = await client.pages.createRoot({
body: {
slug: 'welcome', // required on slug-enabled collections
properties: { title: 'Welcome' }, // required
},
});propertiestyped root propertiesrequiredThe entry's root-level properties, typed from the collection's root definition.
slugstringSlug for the entry (slug-enabled collections).
parentRootIdstringParent entry id (nesting-enabled collections).
messagestring= 'Initial commit'Commit message.
commitCommitThe new head commit: `{ id, message, createdAt, createdBy }`.
rootIdstringThe id of the new entry.
branchIdstringThe id of the entry's initial draft branch.
slugstringoptionalThe server-normalized slug. Slug-enabled collections only.
pathstringoptionalThe 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/{collection}/listRootsconst data = await cms.api.pages.listRoots({
query: { limit: 20, sortBy: 'createdAt', sortDirection: 'desc' },
});const { data, error } = await client.pages.listRoots({
query: { limit: 20, sortBy: 'createdAt', sortDirection: 'desc' },
});limitnumber= 20Page size, 1 to 100.
offsetnumber= 0Rows to skip.
searchstringSubstring 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.
filterValuestringILIKE pattern for `filterField`, passed raw (add `%`/`_` yourself).
hasPublicationsbooleanKeep only entries with (`true`) or without (`false`) any publication.
createdAfterDateKeep entries created after this date.
createdBeforeDateKeep entries created before this date.
parentRootIdstringFilter by parent entry; pass `'null'` for top-level entries.
rootsRootListItem[]The entries on this page. Each carries `id`, `slug`, `properties`, `createdAt`, `createdBy`, publication/branch/merge-request counts, and (on slug-enabled collections) `path`.
totalnumberTotal entries matching the filters across all pages, ignoring limit/offset.
hasMorebooleanWhether 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/{collection}/getRootconst data = await cms.api.pages.getRoot({
query: { rootId: 'root_home' }, // required
});const { data, error } = await client.pages.getRoot({
query: { rootId: 'root_home' }, // required
});rootIdstringrequiredEntry (root) id.
rootRootListItemThe 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/{collection}/getRootBySlugconst data = await cms.api.pages.getRootBySlug({
query: { slug: 'welcome' }, // required
});const { data, error } = await client.pages.getRootBySlug({
query: { slug: 'welcome' }, // required
});slugstringrequiredDraft slug to look up.
parentRootIdstringParent entry id for nested lookups; omit for top-level.
rootRootListItemThe 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/{collection}/getRootHistoryconst data = await cms.api.pages.getRootHistory({
query: {
rootId: 'root_home', // required
limit: 20,
},
});const { data, error } = await client.pages.getRootHistory({
query: {
rootId: 'root_home', // required
limit: 20,
},
});rootIdstringrequiredEntry (root) id.
limitnumber= 50Max commits to return, 1 to 200.
offsetnumber= 0Commits to skip.
withChangesbooleanAdd a per-commit `changes: { added, modified, deleted }` block count.
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.
totalnumberTotal commits for this entry across all branches.
hasMorebooleanWhether 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/{collection}/updateRootconst data = await cms.api.pages.updateRoot({
body: {
rootId: 'root_home', // required
branchId: 'br_main', // required
properties: { title: 'Welcome home' }, // required
},
});const { data, error } = await client.pages.updateRoot({
body: {
rootId: 'root_home', // required
branchId: 'br_main', // required
properties: { title: 'Welcome home' }, // required
},
});rootIdstringrequiredEntry (root) id.
branchIdstringrequiredBranch to commit the change on.
propertiespartial root propertiesrequiredRoot properties to merge; a `null` value deletes that key.
slugstringNew draft slug (slug-enabled collections).
messagestring= 'Update root block {rootId}'Commit message.
expectedHeadCommitIdstringOptimistic-concurrency guard: reject if the branch head has moved.
commitCommitThe new head commit on the branch.
slugstringoptionalThe 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/{collection}/moveRootconst data = await cms.api.pages.moveRoot({
body: {
rootId: 'root_home', // required
newParentRootId: 'root_docs', // required (or null for top-level)
},
});const { data, error } = await client.pages.moveRoot({
body: {
rootId: 'root_home', // required
newParentRootId: 'root_docs', // required (or null for top-level)
},
});rootIdstringrequiredEntry (root) id to move.
newParentRootIdstring | nullrequiredNew parent entry id, or `null` for top-level.
positionnumberSort order among the parent's children.
rootIdstringThe id of the moved entry.
newParentRootIdstring | nullThe new parent id, or `null` if the entry is now top-level.
pathstringoptionalThe entry's resolved URL path. Slug-enabled collections only.
sortOrdernumberThe new sort position among the parent's children.
redirectsCreatednumberHow 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/{collection}/duplicateRootconst data = await cms.api.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',
},
});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',
},
});rootIdstringrequiredSource entry (root) id.
branchIdstringrequiredSource branch id.
blockIdstringrequiredRoot block id to duplicate.
targetPropertiesRecord<string, unknown>requiredProperties for the new root block.
targetSlugstringSlug for the new entry (slug-enabled collections).
messagestring= 'Duplicated root'Commit message.
mode'root'Always `'root'`: this endpoint only ever produces a new top-level entry.
commitCommitThe initial commit of the new entry.
rootIdstringThe id of the new entry.
branchIdstringThe id of the new entry's draft branch.
slugstringoptionalThe new entry's server-normalized slug. Slug-enabled collections only.
pathstringoptionalThe 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/{collection}/archiveRootconst data = await cms.api.pages.archiveRoot({
body: {
rootId: 'root_home', // required
},
});const { data, error } = await client.pages.archiveRoot({
body: {
rootId: 'root_home', // required
},
});rootIdstringrequiredEntry (root) id.
rootIdstringThe id of the archived entry.
pathstringoptionalThe now-archived URL, for a "removed /x" confirmation. Slug-enabled collections only.
redirectsCreatednumberHow many redirects were auto-created (1 when an archive redirect to the parent was written).