> ## Documentation Index
> Fetch the complete documentation index at: https://docs.omnicommerce.sg/llms.txt
> Use this file to discover all available pages before exploring further.

# Price and inventory updates

> Update OmniCommerce catalog values and optionally synchronize exact marketplace accounts.

Use the dedicated catalog endpoints when Spresso or another integration needs to
change commercial values:

```text theme={null}
PATCH /api/v1/products/{productId}/price
PATCH /api/v1/products/{productId}/inventory
```

`productId` may be a simple-product UUID or a variant UUID. For a product with
variants, update each variant rather than the parent product.

For the complete Spresso bootstrap, outcome, write, and recovery flow, see
[Spresso integration](/guides/spresso-integration).

## Authentication and organization scope

Both endpoints require a bearer credential with `catalog:write`.
OmniCommerce derives `organizationId` from the credential. Do not include an
organization ID in either request body.

## Update Omni only

Marketplace synchronization is opt-in. Omitting `syncToMarketplaces`, or setting
it to `false`, changes the OmniCommerce catalog without writing to a marketplace.

<CodeGroup>
  ```bash Price theme={null}
  curl --request PATCH \
    --url "https://omnicommerce.sg/api/v1/products/PRODUCT_UUID/price" \
    --header "Authorization: Bearer $OMNI_API_KEY" \
    --header "Content-Type: application/json" \
    --data '{
      "price": 29.90,
      "salePrice": 24.90,
      "currency": "SGD"
    }'
  ```

  ```bash Inventory theme={null}
  curl --request PATCH \
    --url "https://omnicommerce.sg/api/v1/products/PRODUCT_UUID/inventory" \
    --header "Authorization: Bearer $OMNI_API_KEY" \
    --header "Content-Type: application/json" \
    --data '{
      "quantity": 125
    }'
  ```
</CodeGroup>

## Synchronize marketplaces

Set `syncToMarketplaces` 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:

```json theme={null}
{
  "quantity": 125,
  "syncToMarketplaces": true,
  "targets": [
    { "platform": "shopee", "accountId": "shop-456" },
    { "platform": "lazada", "accountId": "seller-123" }
  ]
}
```

OmniCommerce validates every explicit `{ 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.

## Async response and job status

A marketplace sync request returns `202 Accepted`. `jobIds` contains the durable
jobs, and `targets` shows the immediate queue status for each account.

```json theme={null}
{
  "apiVersion": "developer_v1",
  "ok": true,
  "status": "accepted",
  "productId": "11111111-1111-4111-8111-111111111111",
  "jobId": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
  "data": {
    "changed": true,
    "quantity": 125,
    "syncToMarketplaces": true,
    "jobIds": ["aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa"],
    "targets": [
      {
        "platform": "lazada",
        "accountId": "seller-123",
        "status": "queued",
        "jobId": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa"
      }
    ],
    "emittedEvents": ["product.inventory.changed"]
  }
}
```

Poll each returned job with:

```text theme={null}
GET /api/v1/jobs/{jobId}
```

Inventory returns one durable outbox job per marketplace account. Price updates
return a durable batch job whose counters cover all target accounts.

## Webhook events

The local catalog transaction and outbound marketplace work are separate. A
successful local value change emits:

* `product.price.changed` for price, sale-price, or currency changes
* `product.inventory.changed` for on-hand quantity changes

Subscribe through the [Webhooks API](/api-reference) and see the normalized
[product webhook payloads](/webhooks/products). Marketplace job completion is
tracked through the job endpoint; it does not create a second catalog-change
webhook.

## Source ownership safeguards

The endpoints keep the same catalog rules as the OmniCommerce workspace:

* ERP-managed price or inventory must be changed in the ERP.
* 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.

Use `GET /api/v1/catalog/items` with `catalog:read` to discover product and
variant UUIDs, pricing guardrail inputs, promotion context, and exact published
marketplace identities before writing.
