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

# Catalog resources and identifiers

> Understand catalog identity, relationships, products, modifiers, and bundles.

## Parent-scoped identity

Every `id` supplied by your integration is immutable. Resources that already exist in a POS- or dashboard-created catalog keep their provider ID when available; otherwise Chataigne assigns an opaque stable public ID. Private database and SKU IDs are never used as fallbacks.

* A catalog ID is unique inside one location.
* Category, product, modifier-group, and bundle IDs are unique inside one catalog.
* A modifier ID is unique inside one modifier group.
* A bundle-line ID is unique inside one bundle.

The same `delivery-menu`, `burger-classic`, or `sauces` IDs can be used for several restaurant locations. Chataigne always resolves an ID with its complete parent path.

<Warning>
  Never use a display name as a relationship key. Names can change. Use `category_id`,
  `modifier_group_ids`, and `product_ids`.
</Warning>

## Categories

Categories group products and bundles. Required fields are `id` and `name`; `description` and `image_url` are optional.

## Products

A product is the complete public sellable item:

```json theme={null}
{
  "id": "burger-classic",
  "object": "product",
  "name": "Classic Burger",
  "category_id": "burgers",
  "description": null,
  "image_url": null,
  "price": { "amount": 12.9, "currency": "EUR" },
  "modifier_group_ids": ["sauces"],
  "disabled": false,
  "out_of_stock": false,
  "restrictions": [],
  "price_overrides": [],
  "bundle_only": false,
  "order": 10,
  "created_at": "2026-08-06T12:00:00.000Z",
  "updated_at": "2026-08-06T12:00:00.000Z"
}
```

There is no public SKU resource. Chataigne maintains one private primary SKU for internal ordering compatibility, but its ID never appears in this API. Product-level concepts stored internally with that SKU, such as `price_overrides` and `bundle_only`, are flattened onto the public product.

`order` is optional in writes and always present in responses. On creation, an omitted order appends the product to its category. In a complete snapshot, unordered products follow explicitly ordered products in the same category.

## Modifier groups and modifiers

A modifier group defines selection bounds and contains modifiers. `min_selections` defaults to `0`; `max_selections: null` means unlimited. A group with `min_selections > 0` must contain at least one modifier. A modifier has a name and optional price; omitting its price creates a zero-price modifier in the location currency. Modifiers expose the same operational fields as products.

## Bundles

A bundle has a fixed price and one or more selection lines. Each line references public products through `product_ids`. Selection bounds apply to the line. Bundles expose the same operational fields as products, except `bundle_only` and `order`.

## Operational state and restrictions

`disabled` and `out_of_stock` are independent. Disabled resources are hidden and cannot be ordered. Out-of-stock resources remain visible but cannot be selected until stock returns.

Each entry in `restrictions` is an availability window. `days_of_week` contains weekday names, `start_time` and `end_time` use `HH:MM`, `start_date` and `end_date` use ISO 8601 date-times, and `service_types` contains `delivery`, `collection`, or both. A resource is available when any restriction matches. An empty array means unrestricted.

## Prices and conditional prices

Prices use `{ "amount": 12.9, "currency": "EUR" }`. Amounts must be non-negative and have at most two decimal places. Currency must match the location currency.

Each `price_overrides` entry contains a `price` and `conditions`. Conditions may select one `service_type`, one or more `days_of_week`, and a local `start_time`/`end_time` interval. At least one non-null condition is required. When several overrides match, the most specific match wins; the base price is the fallback. An empty array removes every override.
