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

Blocks API

Read and edit the block tree inside an entry.

Block methods live at cms.api.<collection>.<method> and read or mutate the block tree of a single entry on a branch. The same methods are available on the client with identical types. Every commit-producing mutation returns commit: { id, message, createdAt, createdBy }, the new branch head.

Read a Block Tree

Load an entry's block tree for editing, at a branch head or a specific commit. You get back the nested tree with each block's typed properties, ready to render in your editor.

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

The entry.

branchIdstringrequired

Branch to read (resolves the head commit when `commitId` is omitted).

commitIdstring

A specific commit to read; defaults to the branch head.

rawboolean

Keep stored values for editing: skip variable and link resolution.

includeReferencePreviewsboolean

Also return a `references` sidecar (published previews of embedded references).

Returns
treeBlockTreeNode

The nested block tree, root node first. A discriminated union over `type`; the top node is `type: 'root'` and each node carries its typed `properties` and `children`.

reconstructedboolean

True when the commit was rebuilt from a partial snapshot.

referencesRecord<string, BlockTreeNode>optional

Published preview tree of every embedded reference, keyed by its stored reference value. Only when `includeReferencePreviews` is set.

The tree is a discriminated union over type, and the top node is type: 'root'. reconstructed is true when the commit was rebuilt from a partial snapshot. With includeReferencePreviews: true, references is a Record<storedReferenceValue, tree> holding the published render tree of every reference embedded in the entry, resolved through the active scope.

Resolve an Unsaved Tree

Run the same resolution getBlockTree applies to a stored tree — variables substituted, links resolved to their current href, references as a published-preview sidecar or inlined — on a tree you post. Nothing is written: this is how an editor previews its unsaved working copy (live canvas, HTML/PDF preview, e-mail send-preview). The tree is not validated against the schema; unknown block types and undeclared properties pass through untouched.

block:read
POST/{collection}/resolveTree
const { data, error } = await client.pages.resolveTree({
  body: {
    rootId: 'root_home', // required
    branchId: 'br_main', // required
    tree, // required: the (edited) tree, root node first
    includeReferencePreviews: true,
  },
});
Parameters
rootIdstringrequired

The entry the tree belongs to (scope check only).

branchIdstringrequired

A branch of that entry (must exist; nothing is read from it).

treeblock tree noderequired

The tree to resolve, root node first: `{ blockId, type, properties, children }` — the shape `getBlockTree` returns, with your unsaved edits applied.

includeReferencePreviewsboolean

Also return the `references` sidecar (published previews of embedded references).

inlineReferencesboolean

Replace reference values inside `tree` by their resolved published trees, like `getPublishedContent` does.

Returns
treeBlockTreeNode

The posted tree with variables substituted and links resolved. With `inlineReferences` every reference value is replaced by its resolved published tree (the `getPublishedContent` shape).

referencesRecord<string, BlockTreeNode>optional

Published preview tree of every embedded reference, keyed by its stored reference value. Only when `includeReferencePreviews` is set.

resolveTree never persists anything and creates no commit; save with updateBlocks when you are done. Roots outside your scope (or of another collection) are rejected with ROOT_NOT_FOUND, exactly like getBlockTree.

Add a Block

Add a block as a child of a parent block. The body is a discriminated union over type: each block type accepts its own typed properties. You get back the new commit and the id of the block you just created.

block:create
POST/{collection}/createBlock
const { data, error } = await client.pages.createBlock({
  body: {
    rootId: 'root_home', // required
    branchId: 'br_main', // required
    parentBlockId: 'root_home', // required (pass the rootId to attach at the top level)
    type: 'Hero', // required
    properties: { heading: 'Welcome' }, // required
  },
});
Parameters
rootIdstringrequired

The entry.

branchIdstringrequired

The branch.

parentBlockIdstringrequired

Block to attach the new block under (pass the `rootId` to attach at the top level).

typeblock type namerequired

One of the collection's block types.

propertiestyped block propertiesrequired

The block's properties, typed from its block definition.

positionnumber

Index in the parent's children (defaults to append).

messagestring

Commit message (defaults to `Add {type} block`).

expectedHeadCommitIdstring

Optimistic-concurrency guard: reject with `HEAD_MISMATCH` if the branch head moved.

Returns
commitCommit

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

blockIdstring

The id of the newly created block.

Update a Block

Edit one block with patch semantics: the fields you send overwrite, omitted fields stay, and a null value deletes a key.

block:update
POST/{collection}/updateBlock
const { data, error } = await client.pages.updateBlock({
  body: {
    rootId: 'root_home', // required
    branchId: 'br_main', // required
    blockId: 'blk_hero', // required
    type: 'Hero', // required (must match the stored type)
    properties: { heading: 'Updated heading' }, // required
  },
});
Parameters
rootIdstringrequired

The entry.

branchIdstringrequired

The branch.

blockIdstringrequired

Block to update.

typeblock type namerequired

The block's current type (must match the stored type, else `TYPE_MISMATCH`).

propertiespartial block propertiesrequired

Properties to merge. A `null` value deletes that key; omitted keys stay unchanged.

messagestring

Commit message (defaults to `Update {type} block {blockId}`).

expectedHeadCommitIdstring

Optimistic-concurrency guard: reject with `HEAD_MISMATCH` if the branch head moved.

Returns
commitCommit

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

Save a Block Tree

Save a whole tree in one commit: createCMS diffs the tree you send against the branch head and creates, updates, and deletes blocks to match. This is the batch save path your editor calls after local edits, instead of one call per change.

block:update
POST/{collection}/updateBlocks
const { data, error } = await client.pages.updateBlocks({
  body: {
    rootId: 'root_home', // required
    branchId: 'br_main', // required
    tree, // required: the edited tree from getBlockTree, root node included
    expectedHeadCommitId: headCommitId,
  },
});
Parameters
rootIdstringrequired

The entry.

branchIdstringrequired

The branch to write.

treeblock tree noderequired

The desired final tree, root node first: `{ blockId, type, properties, children }` (the shape `getBlockTree` returns). The root node `blockId` must equal `rootId`.

messagestring= 'Batch update'

Commit message.

expectedHeadCommitIdstring

Optimistic-concurrency guard: reject with `HEAD_MISMATCH` if the branch head moved.

Returns
commitCommit

The new branch head commit, or the unchanged head commit when nothing differed.

changedboolean

True when a real commit was written; false when the tree already matched the head.

changed is false when the tree already matches the head (no commit is written and the current head commit is returned), and true when a real commit was created. Every created or updated node is validated before the commit (known block type, valid properties, legal placement), so any violation rejects the whole batch with no partial write.

Move a Block

Move a block (and its subtree) to a new parent and/or a new position within a parent's children.

block:update
POST/{collection}/moveBlock
const { data, error } = await client.pages.moveBlock({
  body: {
    rootId: 'root_home', // required
    branchId: 'br_main', // required
    blockId: 'blk_hero', // required
    newParentBlockId: 'blk_section', // required
    newIndex: 0, // required
  },
});
Parameters
rootIdstringrequired

The entry.

branchIdstringrequired

The branch.

blockIdstringrequired

Block to move.

newParentBlockIdstringrequired

Block to move it under.

newIndexnumberrequired

Index in the new parent's children (integer >= 0, clamped to range).

messagestring

Commit message (defaults to `Move block {blockId}`).

expectedHeadCommitIdstring

Optimistic-concurrency guard: reject with `HEAD_MISMATCH` if the branch head moved.

Returns
commitCommit

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

Moving into itself, into one of its own descendants, or moving the root block is rejected (CANNOT_MOVE_INTO_SELF, CANNOT_MOVE_INTO_DESCENDANT, CANNOT_MOVE_ROOT).

Duplicate a Block

Clone a block subtree under an existing parent. targetParentBlockId is required — this endpoint only ever duplicates into a parent. To spin a subtree off into a brand-new top-level entry instead, use duplicateRoot.

block:create
POST/{collection}/duplicateBlock
const { data, error } = await client.pages.duplicateBlock({
  body: {
    rootId: 'root_home', // required
    branchId: 'br_main', // required
    blockId: 'blk_hero', // required
    targetParentBlockId: 'blk_section', // required: duplicate under this parent
  },
});
Parameters
rootIdstringrequired

Source entry.

branchIdstringrequired

Source branch.

blockIdstringrequired

Block (and its whole subtree) to duplicate.

targetParentBlockIdstringrequired

Parent for the copy.

targetIndexnumber

Index in the target parent's children (integer >= 0).

messagestring

Commit message.

Returns
mode'child'

Always `'child'`: this endpoint only ever duplicates under a parent.

commitCommit

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

blockIdstring

The new copy's id.

mode is always 'child'. The response also carries blockId, the new copy's id.

Delete a Block

Soft-delete a block and all its descendants (tombstones), and remove it from its parent's children. You get back the ids of everything that was tombstoned.

block:delete
POST/{collection}/deleteBlock
const { data, error } = await client.pages.deleteBlock({
  body: {
    rootId: 'root_home', // required
    branchId: 'br_main', // required
    blockId: 'blk_hero', // required
  },
});
Parameters
rootIdstringrequired

The entry.

branchIdstringrequired

The branch.

blockIdstringrequired

Block to delete (with all its descendants).

messagestring

Commit message (defaults to `Delete block {blockId}`).

expectedHeadCommitIdstring

Optimistic-concurrency guard: reject with `HEAD_MISMATCH` if the branch head moved.

Returns
commitCommit

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

deletedBlockIdsstring[]

Every block tombstoned in this commit: the target plus all its descendants.

deletedBlockIds lists every block tombstoned in the commit (the target plus its descendants).

List Reference Usages

List the pages that embed this reusable block, so you can see where it's used before you change or remove it. Under i18n the inspected entry's translation group expands to all sibling entries, so usage is reported across languages.

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

Root id of the reusable block to inspect.

Returns
pageCountnumber

How many distinct live pages embed this reusable block.

pages{ rootId, collection, slug, occurrences }[]

One entry per host page. Each `occurrences` item is `{ branchId, blockId, propertyKey }`, locating where the block is embedded.

This endpoint checks root:read, not block:read, because it reports usage of an entry across the collection.

On this page