Catalog Product — Update
Updates a catalog product (any of the 7 types). This is a partial patch — send only the fields you want to change. Omitted fields keep their current value.
Endpoint
| Endpoint | Method |
|---|---|
/api/admin/catalog/products/{id} | PUT |
Path parameters
| Field | Type | Required | Notes |
|---|---|---|---|
id | integer | yes | Product ID. |
Query parameters
| Param | Default | Notes |
|---|---|---|
locale | store default | The locale that translatable fields (name, description, short_description, meta fields, …) are written to. One locale per request — repeat the call with a different ?locale= to edit another translation. |
channel | store default | The channel used for channel-scoped attribute values. |
Editing attributes by code
Every attribute on the product's family is editable by its code, sent at the top level of the JSON body (snake_case). This includes the common fields and any family-specific ones:
| Field code | Notes |
|---|---|
name, url_key, short_description, description | Translatable — written to the requested ?locale=. |
meta_title, meta_keywords, meta_description | SEO fields (translatable). |
price, weight | Decimal as string. |
status, new, featured, visible_individually, guest_checkout | Boolean flags as 0 / 1. |
tax_category_id | Existing tax category id. |
color, size, brand, product_number, … | Any family-specific attribute, by its code. |
Translatable fields write to the requested locale only — pass ?locale=fr&channel=default to target the French translation; the other locales are untouched.
Replace-on-send relations
These fields replace the product's current set when present, and are preserved when omitted:
| Field | Notes |
|---|---|
categories | int[] — the product's category assignment. |
channels | int[] — the product's channel assignment. |
up_sells, cross_sells, related_products | int[] — product relation lists. |
Type-structure fields
The type-specific structure keys replace that structure when sent — send the full set. See the examples: dropdown for the verified payload of each:
| Type | Field | Notes |
|---|---|---|
| downloadable | downloadable_links, downloadable_samples | Keyed map of link/sample rows. |
| grouped | links | Keyed map of { associated_product_id, qty, sort_order }. |
| bundle | bundle_options | Keyed map of option groups (type ∈ radio / checkbox / select / multiselect) each with a products map. |
| configurable | variants | Keyed by variant product id (from the create response or detail variants[].id). Replace-semantics — send every variant to keep. |
| booking | booking | Object with type (default / appointment / event / rental / table) plus sub-type fields (slots / tickets / pricing). |
Sub-resources are not updated here
images, videos, inventories, and customer_group_prices are not handled by this endpoint — they have dedicated endpoints. If sent, they are ignored and noted in the _warnings array on the response:
- Images →
POST /api/admin/catalog/products/{id}/images - Inventories →
PUT /api/admin/catalog/products/{productId}/inventories - Customer-group prices →
POST …/customer-group-prices
Response
200 OK returning the full product detail payload — same shape as GET /api/admin/catalog/products/{id}.
_warnings is an array of human-readable strings; it is empty when nothing was dropped, and non-empty (naming each dropped sub-resource field and the endpoint it should be sent to instead) when sub-resource fields were stripped.
Errors
| HTTP | Cause |
|---|---|
401 Unauthorized | Missing or invalid admin Bearer token. |
403 Forbidden | Admin role lacks catalog.products.edit. |
404 Not Found | Product not found. |
422 Unprocessable Entity | Validation failure (duplicate SKU / url_key, invalid boolean, special_price ≥ price, invalid date range). |

