Skip to main content

Update a product

PATCH 

/products/:id

Writes the name and the variant prices (there is no separate name or price route). The selling status is changed with PATCH /products/{id}/status.

Request​

  • Send name and variants, each variant with its price.
  • The variants you send are the full set to keep: an existing variant you leave out is deleted.
  • Identify an existing variant by variant_id; leave it out and send sku to add a variant.
  • A variant with a new sku and no variant_id is a new variant, even when you meant to change an existing one: that existing variant is then removed, because it is not listed.
  • An id key on a variant is ignored. An unknown field is ignored.
  • Images are copied from the public HTTPS URLs you send.

Behaviour​

  • A field you do not send is kept: no collection_id, outlet_ids or attachments key keeps the stored value, and null clears it (no collection, sold at every outlet, no images; [] clears outlet_ids and attachments too).
  • On an existing variant the same holds for sku, barcode, cost, attachment and variant_values: a key you leave out keeps the stored value, and null clears barcode, cost (to 0) and attachment.
  • The option values of an existing variant can be replaced with a list.
  • To remove option values, send variant_values: null or [] on every variant that has them, and variant_groups: [] (or no variant_groups key). The options of all variants are cleared together, so the product ends with no variant groups, as POST /products creates it without options. The SKU, barcode, price and cost of each variant stay.
  • A clear is refused with 400 VALIDATION_FAILED on variants[i].variant_values when any variant would still have options (a variant that leaves variant_values out keeps its options; a list gives options) or variant_groups is not empty. Nothing is changed.
  • A new variant has nothing stored: what it leaves out is empty or 0.
  • A service product needs its Duration option on every SKU, so clearing the options of a service product fails with 400 SERVICE_DURATION_REQUIRED: one data.fields entry with code DURATION_REQUIRED on variants[i].variant_values for each SKU left without a Duration. Nothing is changed.
  • The category and the product details this body cannot express (description, translations) are never changed here.
  • Concurrent updates: the last write wins within the legacy scalar contract.
  • With Price Management enabled, real inherited-base changes and initial prices for new SKUs on an existing product also require catalog.price.edit. A scalar echo creates no price intent.
  • Every target outlet must be in the current actor's grants. Explicit, missing, suspended and permanent-owned cells are protected. New and existing intents share one command and the caller's metadata/outbox transaction.
  • The current local user owns the audit. MAG client metadata is retained. Compatibility responses use final permanent maxima.

Errors​

A SKU that is already used answers 409.

StatusCodeWhen
400VALIDATION_FAILEDA variant has no price, or variant_values is null or [] on an existing variant that has option values while another variant keeps or gets options, or variant_groups is not empty (on variants[i].variant_values).
400SERVICE_DURATION_REQUIREDA service product's SKU would have no Duration option (for example its options are cleared). data.fields has code DURATION_REQUIRED on variants[i].variant_values.
404NOT_FOUNDThe product does not exist, belongs to another merchant, or is not sold at the outlet in X-Outlet-ID.
With the Price Management master switch off, company-wide legacy product configuration writes return 409 PRICE_ROLLOUT_PAUSED when any company outlet is prepared or was activated. No product, price, history, feed or product-change event is written. Unprepared legacy companies retain the existing compatibility semantics.

Request​

Responses​

The product