productId may be a simple-product UUID or a variant UUID. For a product with
variants, update each variant rather than the parent product. GET /api/v1/products/{productId} on the parent returns the variant UUIDs in
variants.
Authentication and organization scope
These endpoints require a bearer credential withcatalog:write.
OmniCommerce derives organizationId from the credential. Do not include an
organization ID in the request body.
Price and inventory: Omni only
Marketplace synchronization is opt-in. OmittingsyncToMarketplaces, or setting
it to false, changes the OmniCommerce catalog without writing to a marketplace.
Price and inventory: synchronize marketplaces
SetsyncToMarketplaces to true to queue durable marketplace work. Without a
targets array, OmniCommerce queues all enabled and published accounts attached
to the product.
To limit the write, pass exact account targets:
{ platform, accountId } pair against the
product’s enabled, published marketplace accounts before changing the catalog.
An unknown, disabled, or unpublished target rejects the entire request.
Cost: Omni only
Catalog cost is never pushed to marketplaces. Do not sendsyncToMarketplaces on the cost endpoints.
Store the source amount and currency. Omni does not convert the catalog
amount into the selling currency. When costPriceCurrency differs from the
product selling currency and costPriceFxRate is omitted, Omni quotes FX and
stores the quote on the product.
Prefer the batch endpoint for jobs. Each item targets
productId or sku
(Omni products.sku). You may send both when they refer to the same product.
Maximum 200 updates per request.
product_data.costPrice,
product_data.costPriceCurrency, and when currencies differ
costPriceFxRate, costPriceFxFrom, costPriceFxTo, costPriceFxAsOf, and
costPriceFxSource (frankfurter or manual). Approximate fallback rates are
rejected; pass costPriceFxRate or retry.
The batch response is 200 even when some rows fail. Top-level success is
true only when every update succeeded. Duplicate product IDs or SKUs fail after
the first occurrence. Unknown SKUs return
SKU does not match an active, draft, or archived product.
List products, then write cost
To backfill cost for every listing on one marketplace, page the product list first, keep{ id, sku }, then POST cost in chunks of 200.
sku is an exact products.sku match after trim. It is not the fuzzy q search. Repeat sku or comma-separate up to 50 values. Combined with marketplace and other filters using AND.
Follow pagination.nextOffset until hasMore is false. limit max is 50.
Use productId from the list on the cost write so you do not resolve SKU twice.
API-key and OAuth requests take organizationId from the bearer credential.
Do not send organizationId on these URLs.
Async response and job status
A marketplace price or inventory sync request returns202 Accepted.
jobIds contains the durable jobs, and targets shows the immediate queue
status for each account.
200 and do not create marketplace jobs.
Webhook events
The local catalog transaction and outbound marketplace work are separate. A successful local value change emits:product.price.changedfor price, sale-price, currency, or catalog cost changes (changedFieldsincludescostPricefor cost writes)product.inventory.changedfor on-hand quantity changesproduct.updatedfor the same catalog transaction
Source ownership safeguards
The endpoints keep the same catalog rules as the OmniCommerce workspace:- ERP-managed selling price or inventory must be changed in the ERP. Cost-only writes are allowed on ERP-managed products.
- Multi-source inventory must be changed through its source/location workflow.
- A sale price must remain below the list price.
- Active promotion assignments may prevent direct sale-price changes.
GET /api/v1/products or GET /api/v1/catalog/items with catalog:read to
discover product and variant UUIDs, SKUs, pricing guardrail inputs, promotion
context, and exact published marketplace identities before writing.