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

# List Products

> Get the product catalog with advanced filtering and pagination.

## Overview

This endpoint allows you to retrieve your branch-based product catalog. It offers a multitude of filtering options such as `type`, `category_id`, and `tags`, and supports five main product types: `GOODS`, `DISH`, `TIMER`, `PREPARATION`, and `INGREDIENT`.

The returned data includes product variants (`modifications`), modifiers (`modificator_groups`), recipes (`recipe`), and all other related data.

### Product Types and Behaviors

While all product types are fundamentally "products," each has its own specific models and behaviors:

* **GOODS:** These can have variants (`modifications`).
  * **With Variants:** If a product has variants, only those variants can be sold. The main product acts as a parent and cannot be sold itself. Each modification behaves like a standard `GOODS` product without variants.
  * **Without Variants:** Standard products that can be sold directly.

* **DISH:** This type can have `modificator_groups` (modifiers).
  * **Modifiers:** Modifiers (`Modificator`) are used exclusively for `DISH` type products. They represent add-on options like "Spice Level" or "Extra Lavash."

* **TIMER:** Represents time-based services (e.g., PS5 rental). Pricing is determined by rules defined in the `setting` field.

* **PREPARATION:** Semi-finished items that have their own recipe and are used in the production of other `DISH` items.

* **INGREDIENT:** Raw materials used in production.

## HTTP Request

```http theme={null}
GET https://integrations.clopos.com/open-api/v2/products
```

<Warning>
  This endpoint requires authentication. Include your JWT in the `x-token` header. See [Authentication](/authentication) for how to obtain a token and [Errors](/errors) for error responses.
</Warning>

## Query Parameters

All parameters are standard URL query parameters. Array and filter values use PHP/Laravel bracket notation — **not** a JSON blob. Arrays are indexed (`with[0]=category&with[1]=station`), and each filter is a tuple under `filters[N]`: field name at `filters[N][0]`, value at `filters[N][1]` (or `filters[N][1][M]` when the value is itself an array).

<ParamField query="page" type="integer" default="1">
  Page number for pagination.
</ParamField>

<ParamField query="limit" type="integer" default="50">
  Products per page. Maximum: 100.
</ParamField>

<ParamField query="with[]" type="array[string]">
  Related resources to include in each product. Repeat with indexed brackets. Common values: `category`, `station`, `modifications`, `modifications.codes`, `taxes`, `codes`, `modificator_groups`, `recipe`, `packages`, `tags`.
  **Example:** `with[0]=category&with[1]=station&with[2]=modifications`
</ParamField>

<ParamField query="selects" type="string">
  Comma-separated list of fields to include in the response. `id`, `name`, and `type` are always returned.
  **Example:** `selects=id,name,type,price,image`
</ParamField>

<ParamField query="filters[N]" type="array">
  Zero or more filter tuples, where `N` is a 0-based index. Each tuple is `[field_name, value]`. `value` may be a scalar (`filters[N][1]=...`) or an array (`filters[N][1][0]=...&filters[N][1][1]=...`). See the **Filtering** section for the full list of supported fields.
</ParamField>

## Filtering

Each filter occupies its own index under `filters[]`. Stack multiple filters by incrementing the outer index — for example `filters[0]` for `type`, `filters[1]` for `inventory_behavior`, and so on. The outer index order does not matter; only uniqueness does.

<ResponseField name="type" type="array[string]">
  Product type. Possible values: `GOODS`, `DISH`, `TIMER`, `PREPARATION`, `INGREDIENT`.
  **Example:** `filters[0][0]=type&filters[0][1][0]=GOODS&filters[0][1][1]=DISH`
</ResponseField>

<ResponseField name="category_id" type="array[integer]">
  Products belonging to the specified category IDs.
  **Example:** `filters[0][0]=category_id&filters[0][1][0]=1&filters[0][1][1]=3`
</ResponseField>

<ResponseField name="station_id" type="array[integer]">
  Products assigned to the specified station IDs.
  **Example:** `filters[0][0]=station_id&filters[0][1][0]=1&filters[0][1][1]=2`
</ResponseField>

<ResponseField name="tags" type="array[integer]">
  Products with the specified tag IDs.
  **Example:** `filters[0][0]=tags&filters[0][1][0]=1&filters[0][1][1]=2`
</ResponseField>

<ResponseField name="giftable" type="string">
  `"1"` = giftable, `"0"` = not giftable.
  **Example:** `filters[0][0]=giftable&filters[0][1]=1`
</ResponseField>

<ResponseField name="discountable" type="string">
  `"1"` = discountable, `"0"` = not discountable.
  **Example:** `filters[0][0]=discountable&filters[0][1]=1`
</ResponseField>

<ResponseField name="inventory_behavior" type="string">
  Inventory tracking mode. Allowed values: `"0"` (`MINUS_INGREDIENTS` — deduct recipe ingredients on sale, typical for `DISH`), `"1"` (`MINUS_SELF` — deduct the product itself from stock, countable `GOODS`/`INGREDIENT`), `"3"` (`PASSIVE` — no inventory tracking, uncountable).
  **Example:** `filters[0][0]=inventory_behavior&filters[0][1]=0`
</ResponseField>

<ResponseField name="haveIngredients" type="string">
  `"1"` = has a recipe/ingredients.
  **Example:** `filters[0][0]=haveIngredients&filters[0][1]=1`
</ResponseField>

<ResponseField name="sold_by_portion" type="string">
  `"1"` = sold by portion.
  **Example:** `filters[0][0]=sold_by_portion&filters[0][1]=1`
</ResponseField>

<ResponseField name="has_variants" type="string">
  `"1"` = has variants (`modifications`).
  **Example:** `filters[0][0]=has_variants&filters[0][1]=1`
</ResponseField>

<ResponseField name="has_modifiers" type="string">
  `"1"` = has a modifier group (`modificator_groups`).
  **Example:** `filters[0][0]=has_modifiers&filters[0][1]=1`
</ResponseField>

<ResponseField name="has_barcode" type="string">
  `"1"` = has at least one barcode. The filter still works, but the top-level `barcode` string on the product is **deprecated** — request `with[]=codes` and read barcodes from the `codes` array instead.
  **Example:** `filters[0][0]=has_barcode&filters[0][1]=1`
</ResponseField>

<ResponseField name="has_service_charge" type="string">
  `"1"` = service charge applies.
  **Example:** `filters[0][0]=has_service_charge&filters[0][1]=1`
</ResponseField>

### Combining filters

Stack filters by incrementing the outer index. Scalar and array values can be mixed freely:

```
?page=1&limit=50
 &filters[0][0]=type&filters[0][1][0]=GOODS&filters[0][1][1]=DISH&filters[0][1][2]=TIMER
 &filters[1][0]=inventory_behavior&filters[1][1]=0
```

(Line breaks shown only for readability — the real URL must be a single string with no whitespace. Brackets should be URL-encoded by your HTTP client; `curl` users can pass `--globoff` to avoid shell interpretation.)

## Request Examples

<CodeGroup>
  ```bash cURL theme={null}
  # Basic request with pagination and selects
  curl --globoff 'https://integrations.clopos.com/open-api/v2/products?page=1&limit=100&selects=id,name,type' \
    -H "x-token: oauth_example_token"
  ```

  ```bash cURL theme={null}
  # Relations + two filters (type IN (GOODS,DISH,TIMER) AND inventory_behavior = 0)
  curl --globoff 'https://integrations.clopos.com/open-api/v2/products?with[0]=category&with[1]=station&with[2]=modifications&with[3]=modifications.codes&with[4]=taxes&with[5]=codes&page=1&limit=50&filters[0][0]=type&filters[0][1][0]=GOODS&filters[0][1][1]=DISH&filters[0][1][2]=TIMER&filters[1][0]=inventory_behavior&filters[1][1]=0' \
    -H "x-token: oauth_example_token"
  ```

  ```javascript javascript theme={null}
  // URLSearchParams handles the bracket encoding for you
  const params = new URLSearchParams({
    page: '1',
    limit: '50',
    'with[0]': 'category',
    'with[1]': 'station',
    'with[2]': 'modifications',
    'with[3]': 'modifications.codes',
    'with[4]': 'taxes',
    'with[5]': 'codes',
    'filters[0][0]': 'type',
    'filters[0][1][0]': 'GOODS',
    'filters[0][1][1]': 'DISH',
    'filters[0][1][2]': 'TIMER',
    'filters[1][0]': 'inventory_behavior',
    'filters[1][1]': '0',
  });

  const response = await fetch(`https://integrations.clopos.com/open-api/v2/products?${params}`, {
    headers: { 'x-token': 'oauth_example_token' },
  });

  const result = await response.json();
  console.log(result);
  ```

  ```python python theme={null}
  import requests

  url = "https://integrations.clopos.com/open-api/v2/products"
  headers = {"x-token": "oauth_example_token"}
  params = {
      "page": 1,
      "limit": 50,
      "with[0]": "category",
      "with[1]": "station",
      "with[2]": "modifications",
      "with[3]": "modifications.codes",
      "with[4]": "taxes",
      "with[5]": "codes",
      "filters[0][0]": "type",
      "filters[0][1][0]": "GOODS",
      "filters[0][1][1]": "DISH",
      "filters[0][1][2]": "TIMER",
      "filters[1][0]": "inventory_behavior",
      "filters[1][1]": 0,
  }

  response = requests.get(url, headers=headers, params=params)
  result = response.json()
  ```
</CodeGroup>

## Response

```json theme={null}
{
  "success": true,
  "data": [
    {
      "id": 1,
      "parent_id": null,
      "station_id": null,
      "category_id": null,
      "unit_id": 1,
      "type": "INGREDIENT",
      "name": "Test_Tomato",
      "parent_name": "",
      "full_name": "Test_Tomato",
      "position": null,
      "barcode": null,
      "gov_code": null,
      "status": 1,
      "hidden": 0,
      "sold_by_weight": false,
      "discountable": true,
      "giftable": false,
      "has_modifications": false,
      "description": null,
      "price": 0,
      "cost_price": 0,
      "cooking_time": 0,
      "inventory_behavior": 0,
      "low_stock": 0,
      "unit_weight": 0,
      "venues": [],
      "media": [],
      "created_at": "2026-01-13 20:04:05",
      "updated_at": "2026-01-13 20:04:05"
    },
    {
      "id": 2,
      "parent_id": null,
      "station_id": null,
      "category_id": null,
      "unit_id": 1,
      "type": "INGREDIENT",
      "name": "Test_Onion",
      "parent_name": "",
      "full_name": "Test_Onion",
      "position": null,
      "barcode": null,
      "gov_code": null,
      "status": 1,
      "hidden": 0,
      "sold_by_weight": false,
      "discountable": true,
      "giftable": false,
      "has_modifications": false,
      "description": null,
      "price": 0,
      "cost_price": 1,
      "cooking_time": 0,
      "inventory_behavior": 0,
      "low_stock": 0,
      "unit_weight": 0,
      "venues": [],
      "media": [],
      "created_at": "2026-01-13 20:04:06",
      "updated_at": "2026-04-01 17:05:36"
    }
  ],
  "total": 284
}
```

## Field Reference

### Product Object

| Field                | Type               | Description                                                                                                                                                                                                                                                                                                                             |
| -------------------- | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                 | integer            | Unique product identifier.                                                                                                                                                                                                                                                                                                              |
| `parent_id`          | integer (nullable) | ID of the parent product for a variant. A row with `type: "MODIFICATION"` is a **variant** of the `GOODS` product referenced here — "modification" and "variant" mean the same thing in this API, and a variant carries the full product schema (same fields as the parent, with its own `price`, `cost_price`, stock, barcodes, etc.). |
| `station_id`         | integer (nullable) | ID of the preparation station assigned to this product.                                                                                                                                                                                                                                                                                 |
| `category_id`        | integer (nullable) | ID of the category this product belongs to.                                                                                                                                                                                                                                                                                             |
| `unit_id`            | integer            | ID of the unit of measurement.                                                                                                                                                                                                                                                                                                          |
| `type`               | string             | Product type: `GOODS`, `DISH`, `TIMER`, `PREPARATION`, `INGREDIENT`, `MODIFICATION`, `MODIFIER`.                                                                                                                                                                                                                                        |
| `name`               | string             | Product name.                                                                                                                                                                                                                                                                                                                           |
| `parent_name`        | string             | Name of the parent product (empty string if none).                                                                                                                                                                                                                                                                                      |
| `full_name`          | string             | Full product name including variant info (e.g., "Fanta 0.5 L").                                                                                                                                                                                                                                                                         |
| `position`           | integer (nullable) | Display order position within the category.                                                                                                                                                                                                                                                                                             |
| `barcode`            | string (nullable)  | **Deprecated.** Legacy single-barcode field, kept for backwards compatibility and not guaranteed to be populated. For current barcodes, request `with[]=codes` and read from the `codes` array.                                                                                                                                         |
| `gov_code`           | string (nullable)  | Government/tax code for the product.                                                                                                                                                                                                                                                                                                    |
| `status`             | integer            | `1` = active, `0` = inactive.                                                                                                                                                                                                                                                                                                           |
| `hidden`             | integer            | `1` = hidden from menus, `0` = visible.                                                                                                                                                                                                                                                                                                 |
| `sold_by_weight`     | boolean            | Whether the product is sold by weight rather than quantity.                                                                                                                                                                                                                                                                             |
| `discountable`       | boolean            | Whether discounts can be applied to this product.                                                                                                                                                                                                                                                                                       |
| `giftable`           | boolean            | Whether this product can be given as a gift/complimentary item.                                                                                                                                                                                                                                                                         |
| `has_modifications`  | boolean            | If `true`, the product has variants in the `modifications` array.                                                                                                                                                                                                                                                                       |
| `description`        | string (nullable)  | Product description text.                                                                                                                                                                                                                                                                                                               |
| `price`              | number             | Base selling price. For parent GOODS with variants, this may be `0` since variants carry their own prices.                                                                                                                                                                                                                              |
| `cost_price`         | number             | Cost price used for margin calculations.                                                                                                                                                                                                                                                                                                |
| `cooking_time`       | integer            | Estimated preparation time in minutes.                                                                                                                                                                                                                                                                                                  |
| `inventory_behavior` | integer            | Inventory tracking mode. `0` = `MINUS_INGREDIENTS` — on sale, deduct the recipe's ingredients from stock (typical for `DISH`). `1` = `MINUS_SELF` — deduct the product itself from stock (countable `GOODS` / `INGREDIENT`). `3` = `PASSIVE` — no inventory tracking (uncountable).                                                     |
| `low_stock`          | integer            | Low stock threshold for alerts.                                                                                                                                                                                                                                                                                                         |
| `unit_weight`        | number             | Physical weight of a single unit, in **kilograms**. For example, if `unit_id` resolves to `pcs`, this is how much one piece weighs (a single packet that weighs 3 kg is stored as `3`). Independent of `sold_by_weight`; used for logistics, shipping, and stock-by-weight calculations, not for pricing mode.                          |
| `venues`             | array              | Venue-specific availability and pricing overrides.                                                                                                                                                                                                                                                                                      |
| `media`              | array              | Image attachments. See [Media object](/common-objects#media).                                                                                                                                                                                                                                                                           |
| `created_at`         | string             | Creation timestamp.                                                                                                                                                                                                                                                                                                                     |
| `updated_at`         | string             | Last update timestamp.                                                                                                                                                                                                                                                                                                                  |

### Variant Object (`modifications`)

Represents different versions (e.g., size, color) of a `GOODS` type product.

A variant has the **same shape as a product** — every field listed in the [Product Object](#field-reference) above (`id`, `parent_id`, `category_id`, `unit_id`, `price`, `cost_price`, `unit_weight`, `inventory_behavior`, `media`, `venues`, `created_at`, `updated_at`, …) is present on each variant. The only differences worth calling out:

* `type` is always `MODIFICATION`.
* `parent_id` points at the parent `GOODS` product instead of being `null`.
* `full_name` combines the parent name with the variant name (e.g. `"Fanta 0.5 L"`).
* The variant carries its own `price`, `cost_price`, `barcode`/`codes`, `status`, stock, etc. — the parent's values are not inherited at sale time.

### Modifier Group (`modificator_groups`)

Defines groups of options that can be added to a `DISH` type product (e.g., "Pizza Toppings").

| Field          | Type    | Description                                             |
| -------------- | ------- | ------------------------------------------------------- |
| `id`           | integer | The group's identifier.                                 |
| `name`         | string  | The name of the group (e.g., "Spice Level").            |
| `type`         | integer | Selection rule (`1`: Single-choice, `0`: Multi-choice). |
| `min_select`   | integer | Minimum number of selections.                           |
| `max_select`   | integer | Maximum number of selections.                           |
| `modificators` | array   | List of selectable items. See **Modifier Object**.      |

### Modifier Object (`modificators`)

| Field        | Type              | Description                                                                  |
| ------------ | ----------------- | ---------------------------------------------------------------------------- |
| `id`         | integer           | The modifier's identifier.                                                   |
| `name`       | string            | The name of the modifier (e.g., "Medium Hot").                               |
| `price`      | number            | The additional price for the option.                                         |
| `ingredient` | object (nullable) | If the modifier is linked to an ingredient, contains ingredient information. |

### Timer Settings (`setting`)

Contains the time-based pricing rules for `TIMER` type products.

| Field      | Type    | Description                                                        |
| ---------- | ------- | ------------------------------------------------------------------ |
| `interval` | integer | The pricing interval in minutes.                                   |
| `prices`   | array   | Prices for different time periods. `[{ "price": 3, "from": 120 }]` |

### Recipe Item (`recipe`)

| Field           | Type    | Description                             |
| --------------- | ------- | --------------------------------------- |
| `ingredient_id` | integer | The product ID of the recipe component. |
| `name`          | string  | The name of the component.              |
| `gross`         | string  | Gross amount.                           |
| `net`           | string  | Net amount.                             |

### Package Object (`packages`)

Specifies the purchasing packages defined for `INGREDIENT` type products.

| Field   | Type    | Description                                        |
| ------- | ------- | -------------------------------------------------- |
| `id`    | integer | The package's identifier.                          |
| `name`  | string  | The name of the package (e.g., "Bundle 10 pcs").   |
| `equal` | integer | The number of base units contained in the package. |
