Skip to content
OmniRetailConnect Docs

Commerce · REST API

Pages

Read, create, change and delete CMS pages with blocks, images, search-engine fields and a version check.

View as Markdown

Overview

Read the CMS pages of the shop (the home page, the legal pages and every other content page) with commerce.read; create, change and delete them with commerce.write.content (edit permission Pages, Pagina's, in Settings > API tokens; a token with it lives at most 30 days). Each endpoint is the MCP tool of the same name, described in Content tools, with the same data, the same change log (channel api) and the same write limit. Every write goes through the page editor's own rules (Website > Pages), is recorded in the shop's content activity log with the token as the causer, and shows in the page's history as "API-token" followed by the token name.

  • Drafts are visible to read tokens. GET /pages and GET /pages/{id} include drafts and scheduled or expired pages for every token with commerce.read. Do not give a read token to anyone who may not see unpublished content.
  • Request size. POST /pages and PATCH /pages/{id} accept a body of up to 262144 bytes (256 KiB). Every other write keeps 65536 bytes.
  • No customer data. Pages hold shop content only.

List pages

GET /api/v1/pages

Needs commerce.read. Equivalent MCP tool: list_pages.

Query parameters: status (draft, published or all, the default), limit and cursor. Pages by id. The rows have no blocks; use the next endpoint for those.

List the draft pages
curl -s -H "Authorization: Bearer $COMMERCE_API_TOKEN" \
  "https://<your-shop-domain>/api/v1/pages?status=draft"

Get a page

GET /api/v1/pages/{id}

Needs commerce.read. Equivalent MCP tool: get_page.

Fields of a page
Field Type Meaning
id, title, slug Page id, title (at most 255 characters) and the address part. The home page has the slug home.
is_home, url Whether it is the home page, and its public address.
status draft or published.
visibility Whether visitors can open it now: draft, scheduled, published or expired.
publish_at, unpublish_at string|null ISO 8601, UTC.
noindex boolean Keeps the page out of search results.
updated_at The version. PATCH needs it back as expected_updated_at.
meta_title, meta_description, canonical_url, og_image, og_image_url, blocks GET /pages/{id} only. og_image is the stored share image path, og_image_url its public URL. blocks is the whole list as stored: each {type, data, image_urls}, where image_urls holds the public URL per image field (null without images).

The writable fields have the names and forms the write endpoints take, so the answer can be sent back as it is (the image_urls of a block are ignored). With the current version that answers changed: false.

Create a page

POST /api/v1/pages

Needs commerce.write.content. Equivalent MCP tool: create_page.

Body fields of POST /pages and PATCH /pages/{id}
Name Type Required Description
title string Required POST only (optional on PATCH). At most 255 characters.
slug string Required POST only (optional on PATCH). Lower-case letters, digits and hyphens, unique, never home. The home page keeps home.
expected_updated_at string Required PATCH only. The updated_at you read. When the page was saved after that moment the call is 409 and nothing changes.
status string Optional published publishes, draft makes it a draft. Left out keeps it; a new page is a draft.
publish_at, unpublish_at string|null Optional ISO 8601 with an offset (a date that does not exist is 422). null clears. The end must be after the start.
meta_title, meta_description string|null Optional At most 255 and 320 characters. null or empty: none.
canonical_url string|null Optional An https URL or a path of this shop (/pagina/...), only for a copy of another page. null: the page's own address.
noindex boolean Optional true adds noindex to the robots meta tag and the X-Robots-Tag header and leaves the page out of the sitemap.
og_image string|null Optional The share image: an https URL (downloaded), its current value, or null to remove it.
blocks array Optional The whole list in page order, at most 60, each {type, data}. It replaces every block; [] empties the page.

Not idempotent: a repeat with the same slug is 422 because the slug is taken; a slug that another page takes at the same moment is 409. The answer is the page as GET /pages/{id} returns it, plus changed and changes ({field: {old, new}}; blocks appear as a count and a sha256 fingerprint so the change log does not keep whole texts).

Change a page

PATCH /api/v1/pages/{id}

Needs commerce.write.content. Equivalent MCP tool: update_page.

Send expected_updated_at and the fields you want to change; the others keep their value. The input is merged onto the page as it is read under a row lock. Every committed change moves the version, also a second change within the same second, so two writers with the same expected_updated_at never both succeed. When the page was saved after that moment the answer is 409 conflict and nothing changes: read the page again and retry.

Change the title and blocks of a page
curl -sS -X PATCH "https://<your-shop-domain>/api/v1/pages/7" \
  -H "Authorization: Bearer $COMMERCE_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"expected_updated_at": "2026-11-01T11:58:00+00:00", "title": "Over ons", "noindex": false, "blocks": [{"type": "heading", "data": {"text": "Over ons", "level": 2}}, {"type": "image", "data": {"path": "https://images.example.com/winkel.jpg", "alt": "Onze winkel"}}]}'

Delete a page

DELETE /api/v1/pages/{id}

Needs commerce.write.content. Equivalent MCP tool: delete_page.

No body. Deletes the page with its saved versions and the menu links that point at it. The answer is page_id, deleted, menu_links_removed, changed, changes. The home page cannot be deleted (422); a repeat is 404.

Blocks

blocks is the whole list in page order and replaces every block, so read the page first, change what you need and send the list back. There are 20 block types. Ten work on any page; ten work only on the home page, or on a page that has that type already. The exact fields of each type are in the OpenAPI document (PageBlock, one schema per type). A block that breaks a rule of its type is 422 and names the field.

Block types
type Where Main fields
heading any page text (at most 255), level (2, 3 or 4).
richtext any page html (sanitised), optional aside (reviews).
faq any page items: at most 20 questions and answers.
cta any page label (at most 60), url (at most 500), style (primary or secondary).
usp any page items: at most 6 selling points.
callout any page heading or text is required; optional note, button_label, button_url.
image any page path (image), alt (at most 200), optional caption. Width and height are set by the server.
media-text any page An image beside a heading and rich text: position (left or right), heading or html required, optional path and alt, button_label, button_url.
feature-grid any page A titled list of at most 8 features with an icon each, and an optional image.
promo-banners any page Two to four image tiles that link somewhere: ratio (landscape, wide, square), tiles.
product-row home page A row of products from a collection: collection, sort (featured, newest, sale), limit.
category-tiles home page Tiles of categories: layout (tiles or list), source (collections or manual), limit, at most 8 entries.
brand-carousel home page Brand logos; no brands means the most popular ones. At most 50 brands.
article-teaser home page The latest blog articles: limit.
locations-teaser home page The shop locations: limit.
related-products home page Hand-picked products (source manual, at most 12); the other sources only work on a product page.
product-tabs home page At most 4 tabs of product rows.
hero-slider home page One to five slides with an image each; autoplay, interval.
newsletter home page The newsletter sign-up: heading, text, privacy_note.
goal-tiles home page The health-goal tiles: title, intro.
  • Rich text (richtext.html, media-text.html) is sanitised as in the editor: scripts, event handlers, inline styles and unsafe links (javascript:) are removed. Other block text is stored as plain text.
  • References. Collection, brand and product slugs in blocks must exist (422 otherwise), unless the page has them already.

Images

An image field and og_image take either a path the page already uses, which is kept as it is, or an https URL that the shop downloads before the write.

  • Public hosts only (no private, loopback or link-local addresses), no redirects, JPEG, PNG or WebP of at most 5 MB each, stored as WebP.
  • At most 20 new image URLs per call, 40 MB and 30 seconds of downloading in all. Past the budget the call is 422 and names the field.
  • A GIF, an AVIF or an image above 5 MB is 422 and names the field (blocks.2.data.path, og_image). Any other value (an http URL, a path of another page) is 422.
  • og_image takes only an https URL, null (removes the share image) or its current value, never a path a block uses.
  • A file stored by a call that then fails or rolls back is deleted again.

The home page

  • It keeps the slug home; another slug is 422, and no other page may take home.
  • It cannot be deleted.
  • The ten home-page block types (product-row, category-tiles, brand-carousel, article-teaser, locations-teaser, related-products, product-tabs, hero-slider, newsletter, goal-tiles) are accepted there, and on another page only when that page has the type already.

Search engines and addresses

  • noindex: true keeps the page out of search results, the sitemap and llms.txt. The shop-wide "hide from search engines" setting still wins when it is on.
  • A page whose canonical_url points to another address stays in the sitemap for now; set noindex as well if it must not be listed.
  • A slug change of a page that is public keeps the old address working with a redirect, as in the editor.

Errors

Errors of the page endpoints
Status error.code When
403 forbidden_scope A write without commerce.write.content.
404 not_found Unknown page, or a repeated delete.
409 conflict PATCH with an expected_updated_at that is no longer current; a slug that another page takes at the same moment.
422 invalid_argument A rule that fails, naming the field (blocks.2.data.alt: ...): a slug that is taken or home, a block of a home-page-only type, an unknown collection, brand or product slug, an image that is refused, more than 60 blocks, deleting the home page, or a date that does not exist.
429 too_many_requests The write limit. The write did not happen.