Apply a batch of page item operations
Apply 1–10 operations to a live page. Operation fields live on PageBatchOperation.
ETag flow
GET /pages/{page_id}and copy theETagheader (quoted strong md5).- Send that exact value as
If-Match(or the previous batch'sETag/ unquoted bodyetag). - Missing or blank
If-Match→ 428. Stale or unsupported (W/"..."/ multi-value) → 412 with top-levelcurrent_etagso you can retry without another GET.
?dry_run=1 makes If-Match optional; when sent it is still validated.
Path grammar
Repeatables are addressed by 24-hex ObjectId from GET /pages/{id}/schema →
current_structure.<Canvas>.repeatables[n].id (0-based, position order), never by index.
Insert slugs come from available_blocks.
Worked example
Change Title of the third Blokken repeatable to X in locale en:
POST /pages/{page_id}/batch?content_locale=en
If-Match: "<etag from GET>"{"operations":[{"op":"set","path":"/items/Blokken/repeatables/66f1a2b3c4d5e6f7a8b9c0a3/items/Title","value":"X"}]}If that block has no Title editable, the op fails with path_not_found or editable_not_in_theme.
Read available_blocks.<Canvas>[].fields for the editable slugs a block defines.
Responses
- 200
{results, etag, updated_at, page?}. HeaderETagis quoted. Non-atomic: failed ops are reported and the rest is saved. - 422
validation_failed,concurrent_modification,atomic_failure(body may includeresults). Structural:batch_too_large,empty_batch,invalid_request. - 503
service_unavailable(Redis). delete/moveonly on repeatables (invalid_delete_target,invalid_move_target).insertpath must end with/repeatables(invalid_insert_path,not_a_canvas).
Personal access token tied to a user account. Requires the X-Nimbu-Site header to scope requests.
In: header
Identifier of the site context when authenticating with a personal access token.
In: header
Path Parameters
Page identifier or fullpath
Query Parameters
All-or-nothing. On any op error nothing is saved and the response is 422 atomic_failure with results. Ignored during dry-run.
include=result adds page (full Page) to the 200 body. Ignored during dry-run.
"result"Write translated content in this locale. Must be one of the site locales, else 422. Default = site default locale.
Validate without persisting. If-Match optional (still checked if present). 200 {dry_run: true, valid, results, etag (unchanged), updated_at}. valid: false when any op errored or the page would be invalid. atomic and include ignored. FileRef sources are validated (URI, access, existence, size/extension) but not copied. batch_too_large / empty_batch / invalid_request / concurrent_modification still 422; Redis 503.
Header Parameters
Quoted strong ETag from GET /pages/{page_id} (ETag: "") or the previous batch response. Required unless ?dry_run=1; when sent on dry-run it is still validated (412 if stale). Only a single strong ETag is supported; W/"..." and multi-value If-Match are rejected as stale. Missing/blank (including "") → 428 precondition_required.
Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Response Body
application/json
application/json
application/json
application/json
curl -X POST "https://example.com/pages/string/batch" \ -H "If-Match: string" \ -H "Content-Type: application/json" \ -d '{ "operations": [ { "op": "set", "path": "/items/Blokken/repeatables/66f1a2b3c4d5e6f7a8b9c0a3/items/Title", "value": "X" } ] }'{
"results": [
{
"index": 0,
"status": "ok",
"path": "/items/Title"
},
{
"index": 1,
"status": "ok",
"path": "/items/Blokken/repeatables/66f1a2b3c4d5e6f7a8b9c0a3/items/Button"
},
{
"index": 2,
"status": "ok",
"path": "/items/Blokken/repeatables",
"id": "66f1a2b3c4d5e6f7a8b9c0a4"
},
{
"index": 3,
"status": "ok",
"path": "/items/Blokken/repeatables/66f1a2b3c4d5e6f7a8b9c0a3"
}
],
"etag": "a1b2c3d4e5f60718293a4b5c6d7e8f90",
"updated_at": "2026-09-10T19:00:00Z"
}{
"message": "Page was modified",
"code": "precondition_failed",
"current_etag": "e3c78daa7f857e0c5fa813f2a03e5c96"
}{
"message": "Maximum 10 operations allowed",
"code": "batch_too_large"
}{
"message": "If-Match header is required",
"code": "precondition_required"
}