Working with Catalog
About This Section
The Catalog section allows you to manage your venue's products and categories via the Public API. You may create new categories and products, browse existing items, search by names, descriptions, and codes, and filter by specific attributes.
All operations are performed within the company context determined during authentication. You don't need to explicitly pass company_id in requests.
How a Product Is Structured in the API
A product in the system consists of two levels:
- Good — the general card: name, description, category, unit of measure, and VAT rate.
- Variations — specific sellable SKUs within the good. Each variation has its own price (
default_price) and a set of codes — barcodes, SKUs, and other identifiers. A single good may contain from 1 to 150 variations.
If a good has one variation, its name field in the API response will be null — use the good-level name for display. If a good has multiple variations, each variation will have its own non-null name.
Goods available through the Public API always have business_type: "regular".
Main Scenarios
Scenario 1: Creating a Category
To organize your menu, you may consider to create a category. The category has a name, an optional description, and can be nested by providing the parent_id of an existing parent category. Maximum nesting level is 2.
Endpoint:
POST /catalog/categories
Example request body:
{
"name": "Coffee",
"description": "Hot coffee beverages",
"parent_id": null
}
If needed, you may pass external_data.id — your internal identifier from an external system. It must be unique within the company. Reusing an existing ID will result in a 409 Conflict error.
The status field is optional and defaults to active.
Scenario 2: Listing Categories
To retrieve all categories, make a paginated GET request.
Endpoint:
GET /catalog/categories
Key parameters:
| Parameter | Description |
|---|---|
status | Filter by status: active (default), inactive, or all |
parent_id | Filter by parent category |
has_parent | true — subcategories only, false — root categories only. Ignored if parent_id is provided |
text_filter | Search by name and description (minimum 2 characters) |
order_by | Sorting: created_at_asc, name_asc, name_desc, and others |
The response contains an items array and a cursor for retrieving the next page. If cursor is null, the page is the last one.
Scenario 3: Creating a Product
To add a product, use the regular good creation endpoint.
Endpoint:
POST /catalog/goods/regular
Required fields:
name— product nameuom_id— unit of measure identifier (retrieved viaGET /uoms)vat.fee_item_id— VAT rate identifier (retrieved viaGET /fees)variations— array of 1 to 150 variations
Example request body with a single variation:
{
"name": "Cappuccino 300 ml",
"category_id": "11111111-1111-4111-8111-111111111111",
"uom_id": "33333333-3333-4333-8333-333333333333",
"vat": {
"fee_item_id": "66666666-6666-4666-8666-666666666666"
},
"variations": [
{
"status": "active",
"default_price": {
"amount": 350.00,
"currency": "RUB"
},
"codes": [
{
"type": "sku",
"value": "CAP-300-001"
}
]
}
]
}
Note: with a single variation, you can omit its name — the good-level name will be used for display.
Example with multiple variations:
{
"name": "Croissant",
"uom_id": "33333333-3333-4333-8333-333333333333",
"vat": {
"fee_item_id": "66666666-6666-4666-8666-666666666666"
},
"variations": [
{
"name": "Almond Croissant",
"status": "active",
"default_price": {
"amount": 280.00,
"currency": "RUB"
},
"codes": [
{
"type": "sku",
"value": "CRO-ALM-001"
}
]
},
{
"name": "Chocolate Croissant",
"status": "active",
"default_price": {
"amount": 300.00,
"currency": "RUB"
},
"codes": [
{
"type": "sku",
"value": "CRO-CHOC-001"
}
]
}
]
}
Up to 20 codes can be specified per variation. Supported types: ean_8, ean_13, itf_14, upc_a, sku, other.
Scenario 4: Listing Goods
Endpoint:
GET /catalog/goods/regular
Key parameters:
| Parameter | Description |
|---|---|
status | Filter by status: active (default), inactive, all |
category_id | Filter by one or more categories (can be passed multiple times) |
has_category | true — goods with a category, false — goods without a category |
text_filter | Search by name, description of goods and variations, and code values |
order_by | Sorting: created_at_asc, name_asc, name_desc, and others |
Important: you cannot use has_category=false together with category_id — the API will return a 400 error.
The response returns an array of goods, each containing detailed information on variations, unit of measure, and VAT rate.
Scenario 5: Retrieving a Single Good or Category
To get a specific category or good by identifier:
GET /catalog/categories/{category_id}
GET /catalog/goods/regular/{good_id}
Error Codes (Catalog-Specific)
| HTTP Code | Body Code | Description |
|---|---|---|
| 400 | validation_error | Request field validation error |
| 400 | invalid_cursor | Invalid or expired pagination cursor |
| 409 | existing_entity_conflict | Conflict: duplicate external_data.id within the company |