> ## 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.

# Product webhooks

> Catalog lifecycle, price, and inventory event payloads.

Product webhooks describe changes to the OmniCommerce catalog resource. They
are emitted by product and developer workflows; they are not pass-through
copies of marketplace listing notifications.

Subscribe with `POST /api/v1/webhooks`. See [Webhooks](/webhooks/overview) for
signature verification, delivery behavior, and retries.

To create these changes through the developer API, see
[Price and inventory updates](/guides/catalog-updates).

<Warning>
  Product events currently use the original event envelope shown below, not the
  versioned order envelope. Use `eventId` and `eventType`; do not expect
  `schemaVersion`, `subject`, or `resourceVersion` on product events.
</Warning>

## Events

| Event                       | Semantics                                                          |
| --------------------------- | ------------------------------------------------------------------ |
| `product.created`           | First catalog snapshot after creation                              |
| `product.updated`           | Full snapshot plus all changed public field paths                  |
| `product.deleted`           | Final available snapshot; `changedFields` normally includes status |
| `product.price.changed`     | Full snapshot plus changed pricing field paths                     |
| `product.inventory.changed` | Full snapshot with `changedFields=["inventory.quantity"]`          |

When a price or inventory mutation also changes the product, OmniCommerce can
emit both `product.updated` and the specific event. Subscribe only to the
specific events if that is all your integration needs.

## Envelope

| Field            | Type              | Meaning                                       |
| ---------------- | ----------------- | --------------------------------------------- |
| `eventId`        | string            | Stable recipient idempotency key              |
| `eventType`      | product event key | Canonical subscription event                  |
| `organizationId` | string            | Tenant boundary                               |
| `productId`      | string            | Stable OmniCommerce product ID                |
| `sku`            | string or `null`  | Product SKU when present                      |
| `changedFields`  | string array      | Public product paths changed by this mutation |
| `occurredAt`     | ISO timestamp     | Time the product event occurred               |
| `source`         | string, optional  | Internal workflow provenance                  |
| `data.product`   | object            | Complete product webhook snapshot             |

Common changed paths include `name`, `description`, `sku`, `brand`, `status`,
`price`, `salePrice`, `compareAtPrice`, `costPrice`, `mapPrice`, `msrpPrice`,
`currency`, `inventory.quantity`, `tags`, `promotions`, `variants`, and
`variationSchema`.

## Product snapshot

| Field               | Type                | Description                                     |
| ------------------- | ------------------- | ----------------------------------------------- |
| `id`                | string              | Stable OmniCommerce product ID                  |
| `parentId`          | string or `null`    | Parent product ID for a variant                 |
| `type`              | string              | `product` or `variant`                          |
| `productFamilyId`   | string              | Stable product-family grouping ID               |
| `productFamilyName` | string or `null`    | Human-facing product-family name                |
| `name`              | string or `null`    | Catalog product name                            |
| `description`       | string or `null`    | Catalog description                             |
| `sku`               | string or `null`    | Primary SKU                                     |
| `brand`             | string or `null`    | Brand                                           |
| `status`            | string or `null`    | Current catalog status                          |
| `price`             | number or `null`    | Catalog price                                   |
| `salePrice`         | number or `null`    | Optional sale price                             |
| `compareAtPrice`    | number or `null`    | Compare-at/reference price                      |
| `costPrice`         | number or `null`    | Catalog unit-cost input                         |
| `costPriceCurrency` | string or `null`    | Currency of catalog cost                        |
| `mapPrice`          | number or `null`    | Minimum advertised price                        |
| `msrpPrice`         | number or `null`    | Manufacturer suggested retail price             |
| `currency`          | string or `null`    | Currency for price values                       |
| `upc`               | string or `null`    | Normalized UPC/GTIN when available              |
| `category`          | string or `null`    | Catalog category                                |
| `tags`              | string array        | Catalog tags, including Spresso family tags     |
| `inventory`         | object              | Inventory data, including `quantity` when known |
| `images`            | array               | Product images                                  |
| `variants`          | array               | Variant snapshots                               |
| `variationSchema`   | object              | Catalog variation definition                    |
| `marketplaces`      | object              | Connected marketplace listing metadata          |
| `promotions`        | array               | Current materialized promotion context          |
| `createdAt`         | timestamp or `null` | OmniCommerce creation time                      |
| `updatedAt`         | timestamp or `null` | Latest OmniCommerce update time                 |

Product price values are JSON numbers in this existing contract. Always pair a
price with `currency`; do not combine amounts across currencies. The versioned
order contract uses decimal strings for monetary precision.

## Example price change

```json theme={null}
{
  "eventId": "evt_61ec5106afc74e0cab8386df8b3980ea",
  "eventType": "product.price.changed",
  "organizationId": "org_123",
  "productId": "prod_456",
  "sku": "LAMP-1",
  "changedFields": ["price", "salePrice"],
  "occurredAt": "2026-08-29T03:10:00.000Z",
  "source": "product.quick-update",
  "data": {
    "product": {
      "id": "prod_456",
      "parentId": null,
      "type": "product",
      "productFamilyId": "prod_456",
      "productFamilyName": "Desk lamps",
      "name": "Desk lamp",
      "description": "Warm adjustable desk lamp",
      "sku": "LAMP-1",
      "brand": "Omni",
      "status": "active",
      "price": 19.95,
      "salePrice": 14.95,
      "compareAtPrice": 24.95,
      "costPrice": 8.5,
      "costPriceCurrency": "SGD",
      "mapPrice": 14,
      "msrpPrice": 24.95,
      "currency": "SGD",
      "upc": "012345678905",
      "category": "Lighting",
      "tags": ["Spresso_Desk_Lamps"],
      "inventory": { "quantity": 7 },
      "images": [{ "url": "https://cdn.example.com/lamp.jpg" }],
      "variants": [],
      "variationSchema": {},
      "marketplaces": {},
      "promotions": [],
      "createdAt": "2026-08-20T02:00:00.000Z",
      "updatedAt": "2026-08-29T03:10:00.000Z"
    }
  }
}
```

## Consumer rules

* Deduplicate by `eventId` or `X-Omni-Event-Id` before applying the snapshot.
* Partition products by `organizationId` and `productId`.
* Replace the integration view from `data.product`; do not merge raw marketplace
  webhook bodies into this resource.
* Treat the specific price and inventory events as additional notifications,
  not as replacements for `product.updated` unless you subscribe that way.
* Remove or tombstone the local catalog record on `product.deleted`.
