Page operations
6 operations named page.*. Every one runs in a batch through apply_ops, dry_run_ops, the CLI or the admin.
| Operation | What it does | Permission | Confirmation |
|---|---|---|---|
| page.read | Read a page row: slug, title, status, frame and SEO fields. | site.read | none |
| page.create | Create a page with an empty tree. | site.structure | none |
| page.update | Patch page fields as { pageId, patch }; patch.status: "published" marks a draft page for the next site publish. | site.edit | none |
| page.delete | Delete a page and its tree. | site.structure | destructive |
| page.forkTemplate | Fork a theme template into an editable site page row. | site.structure | none |
| page.setPassword | Protect a page with a password, change it, or remove it with password: null. | site.edit | none |
page.read
Read a page row: slug, title, status, frame and SEO fields. Page validation and ownership checks apply.
Example input
{
"pageId": "6f1d2c3b-4a59-4e8d-9c7b-1a2b3c4d5e6f"
}Input
| Field | Type | Required | Notes |
|---|---|---|---|
| pageId | string | yes | At least 1 character. |
Output
| Field | Type | Required | Notes |
|---|---|---|---|
| page | object | yes | |
| page.slug | string | yes | At least 1 character. |
| page.title | record of string | yes | |
| page.frame | string | yes | |
| page.dynamic | boolean | yes | |
| page.position | integer | yes | |
| page.parentId | string | null | yes | |
| page.requireAuth | boolean | yes | |
| page.requiredRoles | array of string | no | At most 50 items. |
| page.meta | record of any JSON | yes | |
| page.pageType | string | no | One of "static", "blog-index", "article", "brand-canvas", "template", "region", "dynamic", "component". |
| page.templateId | string | null | no | |
| page.collectionKey | string | null | no | |
| page.status | string | no | One of "draft", "published". |
| page.authorUserId | string | null | no | |
| page.authorName | string | null | no | |
| page.featuredImageId | string | null | no | |
| page.excerpt | record of string | null | no | |
| page.metaTitle | record of string | null | no | |
| page.metaDescription | record of string | null | no | |
| page.ogImageId | string | null | no | |
| page.noIndex | boolean | no | |
| page.canonicalUrl | string | null | no | |
| page.id | string | yes | |
| page.passwordProtected | boolean | no | |
| page.publishedAt | string | null | no | |
| page.createdAt | string | yes | |
| page.updatedAt | string | yes | |
| page.tree | object | no | |
| warnings | array of string | no |
Fields nested deeper are in the output schema that discover_operations answers with outputSchemas: true.
Try it
yarn splash page.read '{"pageId":"6f1d2c3b-4a59-4e8d-9c7b-1a2b3c4d5e6f"}' --dry-runpage.create
Create a page with an empty tree. status defaults to draft; a draft page stays out of the site publish, so pass status: "published" for a page the next publication.publish should serve. Fill the tree with tree.commit in the same batch: read the new root id with tree.read through a $ref to this operation's page.id. A section's anchor is the node field anchor in that tree, not a page field, meta.anchor or props.anchor. A pageType: "dynamic" page names a collection in collectionKey and is the detail page of its items: it answers /<slug>/<item slug> in each locale, and its tree binds the item. Page validation and ownership checks apply.
Example input
{
"slug": "studio",
"title": {
"en-US": "Studio"
},
"status": "published"
}Input
| Field | Type | Required | Notes |
|---|---|---|---|
| slug | string | yes | At least 1 character. |
| title | record of string | yes | |
| frame | string | no | |
| dynamic | boolean | no | |
| position | integer | no | |
| parentId | string | null | no | |
| requireAuth | boolean | no | |
| requiredRoles | array of string | no | At most 50 items. |
| meta | record of any JSON | no | |
| pageType | string | no | One of "static", "blog-index", "article", "dynamic". |
| templateId | string | null | no | |
| collectionKey | string | null | no | |
| status | string | no | One of "draft", "published". |
| authorUserId | string | null | no | |
| authorName | string | null | no | |
| featuredImageId | string | null | no | |
| excerpt | record of string | null | no | |
| metaTitle | record of string | null | no | |
| metaDescription | record of string | null | no | |
| ogImageId | string | null | no | |
| noIndex | boolean | no | |
| canonicalUrl | string | null | no | |
| publishedAt | string (date-time) | null | no |
Output
| Field | Type | Required | Notes |
|---|---|---|---|
| page | object | yes | |
| page.slug | string | yes | At least 1 character. |
| page.title | record of string | yes | |
| page.frame | string | yes | |
| page.dynamic | boolean | yes | |
| page.position | integer | yes | |
| page.parentId | string | null | yes | |
| page.requireAuth | boolean | yes | |
| page.requiredRoles | array of string | no | At most 50 items. |
| page.meta | record of any JSON | yes | |
| page.pageType | string | no | One of "static", "blog-index", "article", "brand-canvas", "template", "region", "dynamic", "component". |
| page.templateId | string | null | no | |
| page.collectionKey | string | null | no | |
| page.status | string | no | One of "draft", "published". |
| page.authorUserId | string | null | no | |
| page.authorName | string | null | no | |
| page.featuredImageId | string | null | no | |
| page.excerpt | record of string | null | no | |
| page.metaTitle | record of string | null | no | |
| page.metaDescription | record of string | null | no | |
| page.ogImageId | string | null | no | |
| page.noIndex | boolean | no | |
| page.canonicalUrl | string | null | no | |
| page.id | string | yes | |
| page.passwordProtected | boolean | no | |
| page.publishedAt | string | null | no | |
| page.createdAt | string | yes | |
| page.updatedAt | string | yes | |
| page.tree | object | no | |
| warnings | array of string | no |
Fields nested deeper are in the output schema that discover_operations answers with outputSchemas: true.
Try it
yarn splash page.create '{"slug":"studio","title":{"en-US":"Studio"},"status":"published"}' --dry-runpage.update
Patch page fields as { pageId, patch }; patch.status: "published" marks a draft page for the next site publish. Page validation and ownership checks apply.
Example input
{
"pageId": "6f1d2c3b-4a59-4e8d-9c7b-1a2b3c4d5e6f",
"patch": {
"title": {
"en-US": "Studio and team"
},
"status": "published"
}
}Input
| Field | Type | Required | Notes |
|---|---|---|---|
| pageId | string | yes | At least 1 character. |
| patch | object | yes | |
| patch.slug | string | no | At least 1 character. |
| patch.title | record of string | no | |
| patch.frame | string | no | |
| patch.dynamic | boolean | no | |
| patch.position | integer | no | |
| patch.parentId | string | null | no | |
| patch.requireAuth | boolean | no | |
| patch.requiredRoles | array of string | no | At most 50 items. |
| patch.meta | record of any JSON | no | |
| patch.pageType | string | no | One of "static", "blog-index", "article", "brand-canvas", "template", "region", "dynamic", "component". |
| patch.templateId | string | null | no | |
| patch.collectionKey | string | null | no | |
| patch.status | string | no | One of "draft", "published". |
| patch.authorUserId | string | null | no | |
| patch.authorName | string | null | no | |
| patch.featuredImageId | string | null | no | |
| patch.excerpt | record of string | null | no | |
| patch.metaTitle | record of string | null | no | |
| patch.metaDescription | record of string | null | no | |
| patch.ogImageId | string | null | no | |
| patch.noIndex | boolean | no | |
| patch.canonicalUrl | string | null | no |
Output
| Field | Type | Required | Notes |
|---|---|---|---|
| page | object | yes | |
| page.slug | string | yes | At least 1 character. |
| page.title | record of string | yes | |
| page.frame | string | yes | |
| page.dynamic | boolean | yes | |
| page.position | integer | yes | |
| page.parentId | string | null | yes | |
| page.requireAuth | boolean | yes | |
| page.requiredRoles | array of string | no | At most 50 items. |
| page.meta | record of any JSON | yes | |
| page.pageType | string | no | One of "static", "blog-index", "article", "brand-canvas", "template", "region", "dynamic", "component". |
| page.templateId | string | null | no | |
| page.collectionKey | string | null | no | |
| page.status | string | no | One of "draft", "published". |
| page.authorUserId | string | null | no | |
| page.authorName | string | null | no | |
| page.featuredImageId | string | null | no | |
| page.excerpt | record of string | null | no | |
| page.metaTitle | record of string | null | no | |
| page.metaDescription | record of string | null | no | |
| page.ogImageId | string | null | no | |
| page.noIndex | boolean | no | |
| page.canonicalUrl | string | null | no | |
| page.id | string | yes | |
| page.passwordProtected | boolean | no | |
| page.publishedAt | string | null | no | |
| page.createdAt | string | yes | |
| page.updatedAt | string | yes | |
| page.tree | object | no | |
| warnings | array of string | no |
Fields nested deeper are in the output schema that discover_operations answers with outputSchemas: true.
Try it
yarn splash page.update '{"pageId":"6f1d2c3b-4a59-4e8d-9c7b-1a2b3c4d5e6f","patch":{"title":{"en-US":"Studio and team"},"status":"published"}}' --dry-runpage.delete
Delete a page and its tree. Page validation and ownership checks apply.
Example input
{
"pageId": "6f1d2c3b-4a59-4e8d-9c7b-1a2b3c4d5e6f"
}Input
| Field | Type | Required | Notes |
|---|---|---|---|
| pageId | string | yes | At least 1 character. |
Output
| Field | Type | Required | Notes |
|---|---|---|---|
| success | true | yes |
Needs confirmed: ["destructive"] on the batch, which the single-operation CLI form does not set: put it in a batch file and run yarn splash apply <file> --dry-run.
page.forkTemplate
Fork a theme template into an editable site page row. Page validation and ownership checks apply.
Example input
{
"templateId": "page"
}Input
| Field | Type | Required | Notes |
|---|---|---|---|
| templateId | string | yes | At least 1 character. |
Output
| Field | Type | Required | Notes |
|---|---|---|---|
| page | object | yes | |
| page.slug | string | yes | At least 1 character. |
| page.title | record of string | yes | |
| page.frame | string | yes | |
| page.dynamic | boolean | yes | |
| page.position | integer | yes | |
| page.parentId | string | null | yes | |
| page.requireAuth | boolean | yes | |
| page.requiredRoles | array of string | no | At most 50 items. |
| page.meta | record of any JSON | yes | |
| page.pageType | string | no | One of "static", "blog-index", "article", "brand-canvas", "template", "region", "dynamic", "component". |
| page.templateId | string | null | no | |
| page.collectionKey | string | null | no | |
| page.status | string | no | One of "draft", "published". |
| page.authorUserId | string | null | no | |
| page.authorName | string | null | no | |
| page.featuredImageId | string | null | no | |
| page.excerpt | record of string | null | no | |
| page.metaTitle | record of string | null | no | |
| page.metaDescription | record of string | null | no | |
| page.ogImageId | string | null | no | |
| page.noIndex | boolean | no | |
| page.canonicalUrl | string | null | no | |
| page.id | string | yes | |
| page.passwordProtected | boolean | no | |
| page.publishedAt | string | null | no | |
| page.createdAt | string | yes | |
| page.updatedAt | string | yes | |
| page.tree | object | no | |
| warnings | array of string | no |
Fields nested deeper are in the output schema that discover_operations answers with outputSchemas: true.
Try it
yarn splash page.forkTemplate '{"templateId":"page"}' --dry-runpage.setPassword
Protect a page with a password, change it, or remove it with password: null. A visitor sees the password form instead of the page until they enter it; members are asked too. The password is stored hashed and never answered: page answers say only passwordProtected. Like the other page fields it reaches the public site with the next publish. Run it alone in a batch: the input is redacted from the history and the audit log, and the change is not undone.
Example input
{
"pageId": "6f1d2c3b-4a59-4e8d-9c7b-1a2b3c4d5e6f",
"password": "press-preview-2026"
}Input
| Field | Type | Required | Notes |
|---|---|---|---|
| pageId | string | yes | At least 1 character. |
| password | string | null | yes |
Output
| Field | Type | Required | Notes |
|---|---|---|---|
| page | object | yes | |
| page.slug | string | yes | At least 1 character. |
| page.title | record of string | yes | |
| page.frame | string | yes | |
| page.dynamic | boolean | yes | |
| page.position | integer | yes | |
| page.parentId | string | null | yes | |
| page.requireAuth | boolean | yes | |
| page.requiredRoles | array of string | no | At most 50 items. |
| page.meta | record of any JSON | yes | |
| page.pageType | string | no | One of "static", "blog-index", "article", "brand-canvas", "template", "region", "dynamic", "component". |
| page.templateId | string | null | no | |
| page.collectionKey | string | null | no | |
| page.status | string | no | One of "draft", "published". |
| page.authorUserId | string | null | no | |
| page.authorName | string | null | no | |
| page.featuredImageId | string | null | no | |
| page.excerpt | record of string | null | no | |
| page.metaTitle | record of string | null | no | |
| page.metaDescription | record of string | null | no | |
| page.ogImageId | string | null | no | |
| page.noIndex | boolean | no | |
| page.canonicalUrl | string | null | no | |
| page.id | string | yes | |
| page.passwordProtected | boolean | no | |
| page.publishedAt | string | null | no | |
| page.createdAt | string | yes | |
| page.updatedAt | string | yes | |
| page.tree | object | no | |
| warnings | array of string | no |
Fields nested deeper are in the output schema that discover_operations answers with outputSchemas: true.
Try it
yarn splash page.setPassword '{"pageId":"6f1d2c3b-4a59-4e8d-9c7b-1a2b3c4d5e6f","password":"press-preview-2026"}' --dry-runGenerated from the operation registry and the MCP tool catalogue (source 3529441cd9df8ff0).
Something wrong on this page? Write to docs@librettocms.com.