Commerce · REST API
Catalogue structure
Reference lists (brands, labels, tax classes, properties) and writes for brands, labels, categories and featured products.
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 needcommerce.write.products(edit permission Products). Creating, renaming and moving categories and featured products needcommerce.write.collections(Categories). The two scopes are separate: a products token cannot edit the tree. - Result.
{"data": {...}}with the subject pluschanged(falsewhen nothing differed) andchanges({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
422field_owned_by_pim.
Reference lists
Four lists give the ids the writes take. They need commerce.read only.
| 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). |
/api/v1/brands
Needs commerce.read. Equivalent MCP tool: list_brands.
/api/v1/labels
Needs commerce.read. Equivalent MCP tool: list_labels.
/api/v1/tax-classes
Needs commerce.read. Equivalent MCP tool: list_tax_classes.
/api/v1/properties
Needs commerce.read. Equivalent MCP tool: list_properties.
curl -s -H "Authorization: Bearer $COMMERCE_API_TOKEN" \
"https://<your-shop-domain>/api/v1/brands?limit=50"
{
"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}.
| 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. |
/api/v1/brands
Needs commerce.write.products. Equivalent MCP tool: create_brand.
/api/v1/brands/{id}
Needs commerce.write.products. Equivalent MCP tool: rename_brand.
/api/v1/brands/{id}
Needs commerce.write.products. Equivalent MCP tool: delete_brand.
/api/v1/brands/{id}/slug
Needs commerce.write.products. Equivalent MCP tool: change_brand_slug.
/api/v1/brands/{id}/logo
Needs commerce.write.products. Equivalent MCP tool: set_brand_logo.
/api/v1/brands/{id}/logo
Needs commerce.write.products. Equivalent MCP tool: remove_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.
| 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. |
/api/v1/labels
Needs commerce.write.products. Equivalent MCP tool: create_label.
/api/v1/labels/{id}
Needs commerce.write.products. Equivalent MCP tool: update_label.
/api/v1/labels/{id}/move
Needs commerce.write.products. Equivalent MCP tool: move_label.
/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.
| 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. |
/api/v1/categories
Needs commerce.write.collections. Equivalent MCP tool: create_collection.
/api/v1/categories/{id}
Needs commerce.write.collections. Equivalent MCP tool: update_collection.
/api/v1/categories/{id}/move
Needs commerce.write.collections. Equivalent MCP tool: move_collection.
/api/v1/categories/{id}/products/{productId}
Needs commerce.write.products. Equivalent MCP tool: attach_product_to_collection.
/api/v1/categories/{id}/products/{productId}
Needs commerce.write.products. Equivalent MCP tool: detach_product_from_collection.
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 in a category
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}).
| 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. |
/api/v1/categories/{id}/featured/{productId}
Needs commerce.write.collections. Equivalent MCP tool: feature_product_in_collection.
/api/v1/categories/{id}/featured/{productId}
Needs commerce.write.collections. Equivalent MCP tool: unfeature_product_in_collection.
/api/v1/categories/{id}/featured/{productId}/move
Needs commerce.write.collections. Equivalent MCP tool: move_featured_product_in_collection.
Errors
| 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. |