# Catalog sync

> Push products, sizes and categories from your platform; Nexafon keeps the store's WhatsApp catalog in sync and reports Meta's reviews.

Source: https://docs.nexafone.so/v1/commerce/catalog/

Your platform stays the source of truth: nobody edits products in Nexafon. Whenever a product, price, image, size or
stock changes on your side, push it; Nexafon keeps its copy and mirrors it into the store’s WhatsApp catalog, usually
within a minute.

## Products

```ts title="partner.ts"
// Your platform stays the source of truth: push each change, Nexafon mirrors it into the store's WhatsApp catalog.
export async function syncProducts(workspaceId: string) {
  const store = nexafon.store(workspaceId)
  await store.categories.upsert([{ externalId: 'shoes', name: 'Shoes' }])
  await store.products.batch([
    {
      externalId: 'sneaker-1',
      name: 'Runner sneaker',
      categoryExternalId: 'shoes',
      priceCents: 2700,
      imageUrls: ['https://cdn.example.com/sneaker-1.jpg'],
      variants: [
        { externalId: 'sneaker-1-42', size: '42', stock: 5 },
        { externalId: 'sneaker-1-43', size: '43', stock: 0, availability: 'out_of_stock' },
      ],
    },
  ])
}
```

| Operation | |
| --- | --- |
| `PUT /api/v1/workspaces/{workspaceId}/commerce/products/{externalId}` | Create or update one product (idempotent by your `externalId`) |
| `POST …/commerce/products/batch` | Up to 500 products in one call; results come back in the same order |
| `DELETE …/commerce/products/{externalId}` | Remove it, also from WhatsApp |
| `GET …/commerce/products` | The products with their WhatsApp review status |
| `PUT …/commerce/categories` | Create or update categories (`externalId`, `name`), in their display order |
| `GET …/commerce/categories` | The categories |

A product has a name, description, category, 1 to 10 image URLs, `priceCents`, an optional sale price with a
start and end, availability and stock, and optional **variants** (size, colour, each with its own price and stock).
Each variant, or the product itself when it has none, is one item in WhatsApp, identified by its `externalId` as
WhatsApp’s `retailer_id` (`retailer_id_taken` when another product already uses it).

Images are your own public HTTPS URLs; WhatsApp fetches them and Nexafon does not host them. Private or internal
addresses are refused. The number of products is limited by the plan’s `commerce.products.max`
(`plan_limit_reached`).

## WhatsApp’s review

Meta reviews each item. The products list shows each one’s status, and a `catalog.product_status` webhook reports
every decision: `approved`, or `rejected` with Meta’s `reason`. Only approved items appear in the Shop.

Nexafon also reconciles the whole catalog nightly, so a missed update corrects itself. The store’s **Products** screen
in the embed shows the same list, read-only.
