# Catalog Integration

The Commerce API uses a **Pull Model** for catalog synchronization, and ingestion is **asynchronous** —
you notify Gett, then poll for the outcome.

<Mermaid chart={`sequenceDiagram
    participant Partner
    participant Gett
    participant Storage as Your Storage

    Partner->>Storage: 1. Upload catalog.json
    Partner->>Gett: 2. POST /catalogset-notification
    Gett-->>Partner: 202 Accepted { catalogIngestId, statusUrl }
    Gett->>Storage: 3. GET catalog.json (pull)
    Gett->>Gett: 4. Validate & build store catalog
    loop until succeeded / failed
        Partner->>Gett: 5. GET {statusUrl}
        Gett-->>Partner: { status, errors }
    end`} />

## Notify, then poll

1. **Host** your CatalogSet JSON at a publicly reachable URL.
2. **`POST /catalogset-notification?storeId=…`** with `{ "callbackUrl": "…" }`. Gett responds
   `202 Accepted` with a body — `{ "catalogIngestId", "statusUrl" }` — and a `Retry-After` header.
   The `202` only means *accepted*, **not** *ingested*.
3. **Poll `statusUrl`** (`GET /v1/commerce/stores/{storeId}/catalog-ingest/{catalogIngestId}/status`),
   honoring `Retry-After`, until the run reaches a terminal state.

## Ingest status lifecycle

`status` moves through:

| Status | Meaning |
| --- | --- |
| `queued` | Accepted; not yet started. |
| `pulling` | Downloading the CatalogSet from your `callbackUrl`. |
| `processing` | Validating and building the store catalog. |
| `succeeded` | The store now serves the new catalog. |
| `failed` | Ingestion did not complete — see `errors`. |

A run reaches `succeeded` **only when the store actually flips to the new catalog**, so polling to
`succeeded` is a reliable confirmation. On `failed`, `errors[]` carries sanitized, partner-safe
reasons (e.g. an unreachable `callbackUrl`, or a CatalogSet whose references don't resolve).

## Optional: catalog sync webhooks

If you'd rather not poll, configure a **Webhook Base URL** (the same one used for
[Order Webhooks](/commerce-partners/order-webhooks)) and Gett will **push** the terminal
outcome to `{baseUrl}/catalog-sync` as soon as the ingest finishes. The webhook is **optional and
best-effort** — polling the `statusUrl` remains the authoritative signal, so treat the push as a
latency optimization, not a guarantee. With no Webhook Base URL configured, nothing is sent.

Two event types fire, both as a [Standard Webhooks](https://www.standardwebhooks.com) envelope
(`type` / `timestamp` / `data`):

| `type` | Fires when | `data.errors` |
| --- | --- | --- |
| `catalog.sync.succeeded` | The store flipped to your new catalog. | empty |
| `catalog.sync.failed` | A pull / validation / generation stage failed. | the same sanitized reasons as the poll |

`data.catalogIngestId` matches the id from the original `202`, so you can correlate the push to your
notification:

```json
{
  "type": "catalog.sync.succeeded",
  "timestamp": "2026-06-17T12:00:00.000Z",
  "data": {
    "catalogIngestId": "018f4d2a-c5b0-7c4e-9b3a-2d1e8f7a6b5c",
    "storeId": "7c3a1e90-2b4d-4f8a-9c1e-5a6b7c8d9e0f",
    "status": "succeeded",
    "errors": []
  }
}
```

```json
{
  "type": "catalog.sync.failed",
  "timestamp": "2026-06-17T12:00:00.000Z",
  "data": {
    "catalogIngestId": "018f4d2a-c5b0-7c4e-9b3a-2d1e8f7a6b5c",
    "storeId": "7c3a1e90-2b4d-4f8a-9c1e-5a6b7c8d9e0f",
    "status": "failed",
    "errors": [
      { "path": "sections.{id}.itemIds[2]", "message": "Item id not present in Items" }
    ]
  }
}
```

These are signed exactly like the order webhooks — the **same** `Authorization` + Standard Webhooks
headers and the same `whsec_…` signing secret — so verify them with the identical code (see
[Security — Standard Webhooks signing](/commerce-partners/order-webhooks#security--standard-webhooks-signing)).
`webhook-id` (and `Idempotency-Key`) equal `data.catalogIngestId`, so a redelivery is trivially
deduplicated. Acknowledge with any `2xx`. The envelope schema is in the Commerce API Reference
([**CatalogSyncWebhook**](/api/commerce/~schemas#catalogsyncwebhook)); both event types are also
listed under **Webhooks** on the [reference overview](/api/commerce).

## Failure modes to handle

- **Unknown store** — the `POST` returns `404` if the `storeId` isn't registered to your partner
  account.
- **Unreachable / slow `callbackUrl`** — the pull times out and the run ends `failed`. Make sure the
  URL is publicly reachable and returns the catalog JSON promptly.
- **Invalid CatalogSet** — Gett rejects a catalog whose cross-references don't resolve (e.g. an item
  ID referenced by a section but absent from the `items` dictionary). The run ends `failed` and the
  store keeps serving its previous catalog (fail-closed). Validate against the schema before
  notifying.

For the machine-readable contract — request/response schemas, parameters, and a try-it console — see
[**Notify Catalog Update**](/api/commerce/catalog#notify-catalog-update) and
[**Get Catalog Ingest Status**](/api/commerce/catalog#get-catalog-ingest-status) in the Commerce API Reference.
