Compose pages through the API
Insert patterns, place components, edit a component's fields and variants, and choose a page's Layout from a script, with a dry run before each change.
Everything an editor does to compose a page can be done through the API: insert a pattern, place a component, change a placement's values or variant, choose a page's Layout, and edit a component's exposed fields and variants. A script or an agent can ask what a change would do before making it.
These routes are experimental: their requests and answers may still change before they are marked stable.
The routes are plugin routes under /admin/api/plugins/@nextlyhq/plugin-page-builder.
They run as the caller, signed in or with an API key, with the same checks as the
admin, and answer in the usual { message, item } shape.
How every action works
dryRun: trueanswers what the action would write, and whether the site would accept it, and writes nothing. The write is made as the caller and rolled back, so the site's own checks answer: field rules over the whole page, locks, component loops and the caller's rights. Why the site would refuse it is initem.refusals, empty when it would accept it. The answer shows the item as the save would store it, after the site's hooks.- Hooks run for a dry run's write too, with
context["pageBuilder.transferJudgement"]set totrue. A hook with an effect a rollback cannot undo, such as sending a message, should skip it then and act on the write that commits. item.fingerprintnames the stored item the answer was planned against. Send it back asexpectand the action applies only to that item: one that changed since answers409; plan again. An action is also refused with409when someone else saves the item between the action's read and its write.item.bysays who the action ran as:{ kind: "user", userId }, or{ kind: "api-key", userId, apiKeyId }. A change made by a signed-in person is recorded against them on the item's content events.publish: truepublishes the item with the change and needs the publish right. Without it the change is saved to the item's working draft, as the editor saves it.
An action the site refuses answers 422, with each reason in the error's
data.errors, and writes nothing.
Change a page
POST /admin/api/plugins/@nextlyhq/plugin-page-builder/pages/actions
Content-Type: application/json
{
"page": { "collection": "pages", "id": "<page id>" },
"action": { "kind": "insert-pattern", "patternId": "<pattern id>" },
"dryRun": true
}page may also name the blocks field (content by default) and, for a translated
field, the locale; a field that holds no blocks is refused. The caller needs update
on the page's collection. A collection with a Draft/Published lifecycle but no working
drafts saves an edit straight onto the published entry, so there the caller needs
publish too. action is one of:
kind | Fields | What it does |
|---|---|---|
insert-pattern | patternId, at? | Copies a published pattern's blocks into the page, as the editor's insert panel does. |
place-component | componentId, variant?, values?, at? | Places a component by reference, with a variant's id and values keyed by exposed field id. |
set-placement-values | instanceId, values?, reset? | Sets a placement's values. { "$unset": true } empties a field; reset lists fields to restore. |
choose-variant | instanceId, variant | Chooses a variant by its id, or null for the component's defaults. |
set-layout | layout, noHeaderFooter? | Chooses the page's Layout by id, or null for the site default. noHeaderFooter: true switches it off. Pages collection only. |
at is { "index": 2 } for the page's top level, or { "parentId", "slot", "index" }
inside a block; without it the block goes at the end of the page. The answer holds the
ops the change makes, the page's blocks as it leaves them (document) or its Layout
fields (layout), and insertedId, the new block's id for the next action to address.
New blocks get fresh ids on every write, as in the editor, so the ids in a dry run's
answer are not the ones an apply stores: address what the apply's own answer names.
Change a component's definition
POST /admin/api/plugins/@nextlyhq/plugin-page-builder/components/:id/definition
Content-Type: application/json
{ "action": { "kind": "add-variant", "label": "Dark" }, "dryRun": true }The caller needs update on components. Each edit is held to the rules of the component's own view: a variant's label is unique, ids are stable and never handed out twice, a removed field's id is never reused, and a value must fit its field.
kind | Fields |
|---|---|
expose | label, nodeId, and propPath or visibility |
rename-exposure | exposureId, label |
retarget-exposure | exposureId, nodeId, and propPath or visibility |
remove-exposure | exposureId |
remove-exposed-slot | slotId |
add-variant | label |
rename-variant | variantId, label |
delete-variant | variantId |
set-variant-value | variantId, exposureId, value |
reset-variant-value | variantId, exposureId |
The answer holds the definition as the edit leaves it, targetId (the field, slot or
variant made or changed), warnings, and, when a removed field emptied variants,
prunedVariants. A refused edit answers 422 with the planner's reason as the error
code, such as label-taken.
Other routes with a dry run
POST /save-as-patterntakesdryRun: trueand answers the pattern it would save, withrefusalssaying why the site would refuse it.- Making a Layout the default and removing one both take
dryRun, and their answers carryby. - Importing a file takes
dryRunandexpectin the same way.
Move patterns, components and Layouts between sites
Export an item with everything it depends on as one file, and import it into another site after a preview.
Blocks from plugins
Contribute blocks to the page builder from a plugin, give a block its own inspector, and write block code that works inside the editor's canvas.