Skip to content
OmniRetailConnect Docs

Commerce · REST API

Catalogue structure

Reference lists (brands, labels, tax classes, properties) and writes for brands, labels, categories and featured products.

View as Markdown

Overview

These endpoints read and change the structure around the products: brands, product labels, the category tree and which products are featured on top of a category. Each write is the MCP tool of the same name over HTTP: the data equals the structured result, the change is logged with the token (name, id, channel api) and the calls count on the write limit. The path names the subject and must not be repeated in the body. Bodies are JSON objects, at most 65536 bytes. The tools are described in Catalogue write tools.

  • Scopes. Reading needs commerce.read. Brands, labels and putting a product in or out of a category need commerce.write.products (edit permission Products). Creating, renaming and moving categories and featured products need commerce.write.collections (Categories). The two scopes are separate: a products token cannot edit the tree.
  • Result. {"data": {...}} with the subject plus changed (false when nothing differed) and changes ({field: {old, new}}).
  • PIM. Brand names are owned by the PIM when a PIM-linked product uses the brand. Labels and category placements by the shop itself are always editable; detaching a placement the PIM import made is 422 field_owned_by_pim.

Reference lists

Four lists give the ids the writes take. They need commerce.read only.

Reference lists
Request Tool Result
GET /api/v1/brands MCP list_brands Pages by id (limit, cursor, meta.next_cursor). Items: id, name, slug, url, logo_url, product_count (products of any status; a brand in use cannot be deleted) and pim_managed (a PIM-linked product uses it, so it cannot be renamed).
GET /api/v1/labels MCP list_labels All labels in display order, at most 200 (meta.truncated). Items: id, name, slug, tone (neutral, primary, accent, dark, success), position (lowest first), product_count.
GET /api/v1/tax-classes MCP list_tax_classes All tax classes (VAT rates), at most 100 (meta.truncated). Items: id, name, default.
GET /api/v1/properties MCP list_properties Pages by id. Items: id, name, handle, type (list: one or more labels per product; number: one number), unit, values ([{id, label}], at most 100, values_truncated).
GET /api/v1/brands

Needs commerce.read. Equivalent MCP tool: list_brands.

GET /api/v1/labels

Needs commerce.read. Equivalent MCP tool: list_labels.

GET /api/v1/tax-classes

Needs commerce.read. Equivalent MCP tool: list_tax_classes.

GET /api/v1/properties

Needs commerce.read. Equivalent MCP tool: list_properties.

List brands
curl -s -H "Authorization: Bearer $COMMERCE_API_TOKEN" \
  "https://<your-shop-domain>/api/v1/brands?limit=50"
Example response · 200
{
  "data": [
    {"id": 7, "name": "Bosch", "slug": "bosch", "url": "https://<your-shop-domain>/merken/bosch",
     "logo_url": null, "product_count": 12, "pim_managed": false}
  ],
  "meta": {"next_cursor": null, "has_more": false}
}

Brands

Needs commerce.write.products. Result of the first four and the two logo endpoints: brand (id, name, slug, url, logo_url, product_count, pim_managed), changed, changes; delete answers brand_id, deleted, changed, changes. Assign a brand to a product by name with PATCH /products/{id}.

Brand writes
Request Tool Notes
POST /api/v1/brands create_brand Body: name (at most 255 characters, plain text). A name must be new ignoring case; a repeat is 422, so a retry never makes a duplicate.
PATCH /api/v1/brands/{id} rename_brand Body: name. The address stays. A brand that a PIM-linked product uses is 422 field_owned_by_pim.
PUT /api/v1/brands/{id}/slug change_brand_slug Body: slug (made URL-safe; empty after that, the name is used). A slug another brand uses is 422. The old address becomes a permanent redirect.
DELETE /api/v1/brands/{id} delete_brand No body. Only a brand no product uses (of any status); otherwise 422. Its old address redirects to the brands overview. A repeat is 404.
PUT /api/v1/brands/{id}/logo set_brand_logo Body: image_url (public https URL) and alt. Downloaded with the product image rules (no redirects, at most 10 MB, no SVG); JPEG and PNG are stored as WebP. Every call stores the image again.
DELETE /api/v1/brands/{id}/logo remove_brand_logo No body. Without a logo nothing changes.
POST /api/v1/brands

Needs commerce.write.products. Equivalent MCP tool: create_brand.

PATCH /api/v1/brands/{id}

Needs commerce.write.products. Equivalent MCP tool: rename_brand.

DELETE /api/v1/brands/{id}

Needs commerce.write.products. Equivalent MCP tool: delete_brand.

PUT /api/v1/brands/{id}/slug

Needs commerce.write.products. Equivalent MCP tool: change_brand_slug.

PUT /api/v1/brands/{id}/logo

Needs commerce.write.products. Equivalent MCP tool: set_brand_logo.

DELETE /api/v1/brands/{id}/logo

Needs commerce.write.products. Equivalent MCP tool: remove_brand_logo.

Set a brand logo
curl -sS -X PUT "https://<your-shop-domain>/api/v1/brands/7/logo" \
  -H "Authorization: Bearer $COMMERCE_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"image_url": "https://images.example.com/bosch.png", "alt": "Bosch logo"}'

Labels

Needs commerce.write.products. Putting labels on a product is PUT /products/{id}/labels (see Product writes). Result: label (id, name, slug, tone, position, product_count), changed, changes (a move names position:{label id} for both labels); delete answers label_id, deleted, changed, changes.

Label writes
Request Tool Notes
POST /api/v1/labels create_label Body: name (at most 40 characters, new ignoring case) and optional tone. The label goes to the end of the label order. A repeat is 422.
PATCH /api/v1/labels/{id} update_label Body: at least one of name, tone. The slug is made once and stays on a rename, because themes style by it.
POST /api/v1/labels/{id}/move move_label Body: direction (up or down). Swaps with its neighbour; nothing at either end. Not idempotent: every call moves one more place.
DELETE /api/v1/labels/{id} delete_label No body. Takes the label off its products. A repeat is 404.
POST /api/v1/labels

Needs commerce.write.products. Equivalent MCP tool: create_label.

PATCH /api/v1/labels/{id}

Needs commerce.write.products. Equivalent MCP tool: update_label.

POST /api/v1/labels/{id}/move

Needs commerce.write.products. Equivalent MCP tool: move_label.

DELETE /api/v1/labels/{id}

Needs commerce.write.products. Equivalent MCP tool: delete_label.

Categories

Read the tree with GET /categories. Result of the tree writes: collection (id, slug, name, parent_id, position, description), changed, changes. The placement endpoints answer product_id, collection, in_collection, changed, changes. A category of the hidden PIM import tree, or a parent there, is 422; an unknown category or product is 404.

Category writes
Request Tool Notes
POST /api/v1/categories create_collection commerce.write.collections. Body: name and optional parent_id (omitted or null: a new top-level category). Not idempotent: every call adds a category.
PATCH /api/v1/categories/{id} update_collection commerce.write.collections. Body: at least one of name, description (plain text; a blank line starts a paragraph; empty or null removes it).
POST /api/v1/categories/{id}/move move_collection commerce.write.collections. Body: operation (up, down, make_root or into) and target_id (required for into, not allowed otherwise). up and down are not idempotent.
PUT /api/v1/categories/{id}/products/{productId} attach_product_to_collection commerce.write.products. No body (an empty JSON object is accepted). Puts the product in the category.
DELETE /api/v1/categories/{id}/products/{productId} detach_product_from_collection commerce.write.products. No body. The product itself is not deleted.
POST /api/v1/categories

Needs commerce.write.collections. Equivalent MCP tool: create_collection.

PATCH /api/v1/categories/{id}

Needs commerce.write.collections. Equivalent MCP tool: update_collection.

POST /api/v1/categories/{id}/move

Needs commerce.write.collections. Equivalent MCP tool: move_collection.

PUT /api/v1/categories/{id}/products/{productId}

Needs commerce.write.products. Equivalent MCP tool: attach_product_to_collection.

DELETE /api/v1/categories/{id}/products/{productId}

Needs commerce.write.products. Equivalent MCP tool: detach_product_from_collection.

Create a category and put a product in it
curl -sS -X POST "https://<your-shop-domain>/api/v1/categories" \
  -H "Authorization: Bearer $COMMERCE_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "Hanglampen", "parent_id": 30}'

curl -sS -X PUT "https://<your-shop-domain>/api/v1/categories/31/products/101" \
  -H "Authorization: Bearer $COMMERCE_API_TOKEN"

Featured products sort above all other products of a category listing. All three need commerce.write.collections and the product must already be in the category (PUT .../products/{productId}), otherwise 422. Result: product_id, collection, featured, featured_product_ids (top first, at most 100), changed, changes (position:{product id}).

Featured product writes
Request Tool Notes
PUT /api/v1/categories/{id}/featured/{productId} feature_product_in_collection No body. Puts the product on top of the category listing. Already on top: no change. Featuring another later puts that one on top.
DELETE /api/v1/categories/{id}/featured/{productId} unfeature_product_in_collection No body. The product stays in the category. Not featured: no change.
POST /api/v1/categories/{id}/featured/{productId}/move move_featured_product_in_collection Body: direction (up or down). Swaps with its neighbour among the featured products. A product that is not featured is 422. Not idempotent.
PUT /api/v1/categories/{id}/featured/{productId}

Needs commerce.write.collections. Equivalent MCP tool: feature_product_in_collection.

DELETE /api/v1/categories/{id}/featured/{productId}

Needs commerce.write.collections. Equivalent MCP tool: unfeature_product_in_collection.

POST /api/v1/categories/{id}/featured/{productId}/move

Needs commerce.write.collections. Equivalent MCP tool: move_featured_product_in_collection.

Errors

Errors of the catalogue writes
Status error.code When
403 forbidden_scope The token lacks the scope of the endpoint: commerce.write.products for brands, labels and category placements, commerce.write.collections for the category tree and featured products.
404 not_found Unknown brand, label, category, product or image; a repeated delete.
409 conflict Another write holds the category tree, the category product list or the brand address for more than 5 seconds. Retry later.
422 invalid_argument A name that is taken, a brand that is still in use, a category of the hidden PIM import tree, a product that is not in the category, a refused logo URL, an unknown field or a missing body field.
422 field_owned_by_pim Renaming a brand that a PIM-linked product uses (the import finds brands by name).
429 too_many_requests The write limit of 30 calls per token per minute. The write did not happen.