Nimbu Developer Docs
ReferencePages

Apply a batch of page item operations

POST
/pages/{page_id}/batch

Apply 1–10 operations to a live page. Operation fields live on PageBatchOperation.

ETag flow

  1. GET /pages/{page_id} and copy the ETag header (quoted strong md5).
  2. Send that exact value as If-Match (or the previous batch's ETag / unquoted body etag).
  3. Missing or blank If-Match → 428. Stale or unsupported (W/"..." / multi-value) → 412 with top-level current_etag so 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?}. Header ETag is quoted. Non-atomic: failed ops are reported and the rest is saved.
  • 422 validation_failed, concurrent_modification, atomic_failure (body may include results). Structural: batch_too_large, empty_batch, invalid_request.
  • 503 service_unavailable (Redis).
  • delete / move only on repeatables (invalid_delete_target, invalid_move_target).
  • insert path must end with /repeatables (invalid_insert_path, not_a_canvas).

Authorization

AuthorizationBearer <token>

Personal access token tied to a user account. Requires the X-Nimbu-Site header to scope requests.

In: header

X-Nimbu-Site<token>

Identifier of the site context when authenticating with a personal access token.

In: header

Path Parameters

page_id*string

Page identifier or fullpath

Query Parameters

atomic?boolean

All-or-nothing. On any op error nothing is saved and the response is 422 atomic_failure with results. Ignored during dry-run.

include?string

include=result adds page (full Page) to the 200 body. Ignored during dry-run.

Value in"result"
content_locale?string

Write translated content in this locale. Must be one of the site locales, else 422. Default = site default locale.

dry_run?boolean

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

If-Match*string

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"
}