# API InventoryMod — справочник методов для интеграций

> Справочник HTTP API InventoryMod: товары, варианты, комплекты, склады, движения, резервы, заказы покупателей и поставщикам, импорт и отчёты. Быстрый старт, параметры, тело запроса и примеры ответа для каждого метода.

Source: https://inventorymod.com/ru/api-docs/

🔌 API для разработчиков

HTTP API для интеграций: товары, склады, остатки, заказы, закупки и отчёты. Ключ API создаётся в веб-приложении: меню пользователя → «API-ключи».

## Быстрый старт

### 1. Получите ключ

В [веб-приложении](https://app.inventorymod.com/) откройте меню пользователя → «API-ключи» и создайте ключ. Ключ `read` — только чтение, `write` — чтение и запись. Там же указан `campaign_id` — номер кампании, к которой относится ключ; он передаётся в каждом запросе.

### 2. Первый запрос

Каждый метод — это `/api/rpc/<раздел>/<действие>`. Чтение — GET, параметры в query:

```
curl "https://app.inventorymod.com/api/rpc/products/search?campaign_id=1&limit=20&offset=0" \
  -H "Authorization: Bearer sk_test_…"
```

### 3. Запись

Запись — POST с ключом `write` в заголовке (`Authorization: Bearer …` или `X-API-Key`); данные — в JSON-теле:

```
curl -X POST "https://app.inventorymod.com/api/rpc/products/create?campaign_id=1" \
  -H "Authorization: Bearer sk_live_…" \
  -H "Content-Type: application/json" \
  -d '{"product": {"name": "Blue T-shirt", "sku": "TS-BLUE-M"}}'
```

### 4. Ответы и ошибки

Успешный ответ всегда `{ "data": … }`. Методы поиска принимают `limit` и `offset` и возвращают `{ items, totalCount }`:

```
{ "data": { "items": [ … ], "totalCount": 120 } }
```

Ошибка — HTTP-статус и `{ "error": "…" }`, иногда с машинным `code` и `details`:

```
{ "error": "products/create requires a key with write scope" }
```

- **400** — неверные параметры или нарушено бизнес-правило
- **401** — нет ключа или он отозван
- **403** — у ключа нет доступа к кампании или нужен scope `write`
- **404 / 405** — неизвестный метод или не тот HTTP-метод
- **429** — больше 300 запросов в минуту; заголовок `RateLimit` подскажет, когда можно снова

### 5. Спецификация OpenAPI

Полное описание API в формате OpenAPI 3.1: [openapi.json](https://inventorymod.com/openapi.json). Его можно импортировать в Postman, Insomnia или генератор клиентов.

## Товары

Каталог: создание, изменение, поиск и удаление товаров, поиск по SKU и штрихкоду.

### Список товаров

GET `products/list`

Список товаров (опц. фильтр по категории/производителю/валюте/типу). Модели вариантов по умолчанию ИСКЛЮЧЕНЫ (V18): список используется для выбора товара в складских операциях. includeParents: true — для мест, где модели нужны (набор SKU при импорте, экспорт).

`GET https://app.inventorymod.com/api/rpc/products/list?campaign_id=1`

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| api_key | string | нет | для чтения из браузера; для записи — заголовок Authorization |
| categoryId | integer | нет |  |
| manufacturerId | integer | нет |  |
| currencyId | integer | нет |  |
| isArchived | boolean | нет |  |
| kinds | string | нет |  |
| includeParents | boolean | нет |  |

Ответ:

```
{
  "data": [
    {
      "id": 1,
      "campaignId": 1,
      "sku": "ABC-1",
      "barcode": "CODE",
      "name": "Sample",
      "manufacturerId": 1,
      "categoryId": 1,
      "price": 9.99,
      "priceOld": 9.99,
      "rrp": 9.99,
      "currencyId": 1,
      "purchaseCost": 9.99,
      "purchaseCostCurrencyId": 1,
      "url": "string",
      "imageUrl": "string",
      "minQty": 9.99,
      "customFieldValues": {},
      "isArchived": false,
      "kind": "simple",
      "laborCost": 9.99,
      "laborCostCurrencyId": 1,
      "componentsUpdatedAt": "2026-01-01T00:00:00.000Z",
      "parentId": 1,
      "variantKey": "string",
      "nameLocked": false,
      "createdAtUtc": "2026-01-01T00:00:00.000Z",
      "updatedAtUtc": "2026-01-01T00:00:00.000Z"
    }
  ]
}
```

### Поиск товаров

GET `products/search`

Список товаров с фильтрами, поиском, статусом остатка, сортировкой и пагинацией + общее количество.

`GET https://app.inventorymod.com/api/rpc/products/search?campaign_id=1&limit=50&offset=0`

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| api_key | string | нет | для чтения из браузера; для записи — заголовок Authorization |
| search | string | нет |  |
| categoryId | integer | нет |  |
| manufacturerId | integer | нет |  |
| currencyId | integer | нет |  |
| isArchived | boolean | нет |  |
| stockStatus | string all \| in \| out \| low | нет |  |
| limit | integer | **да** |  |
| offset | integer | **да** |  |
| sortBy | string name \| sku \| price \| isArchived \| updatedAtUtc | нет |  |
| sortCustomFieldId | string | нет |  |
| sortOrder | string asc \| desc | нет |  |
| lite | boolean | нет |  |
| kinds | string | нет |  |
| variantMode | string grouped \| flat \| all | нет |  |
| parentId | integer | нет |  |
| parentIsNull | boolean | нет |  |

Ответ:

```
{
  "data": {
    "items": [
      {
        "id": 1,
        "campaignId": 1,
        "sku": "ABC-1",
        "barcode": "CODE",
        "name": "Sample",
        "manufacturerId": 1,
        "categoryId": 1,
        "price": 9.99,
        "priceOld": 9.99,
        "rrp": 9.99,
        "currencyId": 1,
        "purchaseCost": 9.99,
        "purchaseCostCurrencyId": 1,
        "url": "string",
        "imageUrl": "string",
        "minQty": 9.99,
        "customFieldValues": {},
        "isArchived": false,
        "kind": "simple",
        "laborCost": 9.99,
        "laborCostCurrencyId": 1,
        "componentsUpdatedAt": "2026-01-01T00:00:00.000Z",
        "parentId": 1,
        "variantKey": "string",
        "nameLocked": false,
        "createdAtUtc": "2026-01-01T00:00:00.000Z",
        "updatedAtUtc": "2026-01-01T00:00:00.000Z"
      }
    ],
    "totalCount": 1
  }
}
```

### Сменить тип товара

POST `products/setKind`

Сменить тип товара (единственный путь смены kind у существующего товара — V13/D15 планов). updateProduct/bulkUpdateProducts/upsertBySku kind игнорируют.

`POST https://app.inventorymod.com/api/rpc/products/setKind?campaign_id=1&productId=1&kind=x`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| productId | integer | **да** |  |
| kind | string | **да** |  |

Тело запроса (JSON):

```
{
  "opts": {}
}
```

Ответ:

```
{
  "data": {}
}
```

### Данные для смены типа

GET `products/kindFacts`

Метрики товара, нужные правилам смены типа. Последовательно: может вызываться внутри транзакции.

`GET https://app.inventorymod.com/api/rpc/products/kindFacts?campaign_id=1&productId=1`

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| api_key | string | нет | для чтения из браузера; для записи — заголовок Authorization |
| productId | integer | **да** |  |

Ответ:

```
{
  "data": {}
}
```

### Выгрузка товаров

GET `products/exportPage`

Постраничная выгрузка товаров для экспорта — keyset по id (id > afterId). O(n) на весь проход, без деградации глубокого OFFSET; сервер отдаёт по одному батчу, не весь каталог разом. Все товары (включая архивные), порядок по id.

`GET https://app.inventorymod.com/api/rpc/products/exportPage?campaign_id=1&limit=50`

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| api_key | string | нет | для чтения из браузера; для записи — заголовок Authorization |
| afterId | integer | нет |  |
| limit | integer | **да** |  |

Ответ:

```
{
  "data": [
    {
      "id": 1,
      "campaignId": 1,
      "sku": "ABC-1",
      "barcode": "CODE",
      "name": "Sample",
      "manufacturerId": 1,
      "categoryId": 1,
      "price": 9.99,
      "priceOld": 9.99,
      "rrp": 9.99,
      "currencyId": 1,
      "purchaseCost": 9.99,
      "purchaseCostCurrencyId": 1,
      "url": "string",
      "imageUrl": "string",
      "minQty": 9.99,
      "customFieldValues": {},
      "isArchived": false,
      "kind": "simple",
      "laborCost": 9.99,
      "laborCostCurrencyId": 1,
      "componentsUpdatedAt": "2026-01-01T00:00:00.000Z",
      "parentId": 1,
      "variantKey": "string",
      "nameLocked": false,
      "createdAtUtc": "2026-01-01T00:00:00.000Z",
      "updatedAtUtc": "2026-01-01T00:00:00.000Z"
    }
  ]
}
```

### Данные товара для строки заказа

GET `products/orderInfo`

Цена, валюта и доступный остаток товара по складам для строки заказа; для комплекта — расчётный остаток и состав.

`GET https://app.inventorymod.com/api/rpc/products/orderInfo?campaign_id=1&productId=1`

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| api_key | string | нет | для чтения из браузера; для записи — заголовок Authorization |
| productId | integer | **да** |  |

Ответ:

```
{
  "data": {}
}
```

### Получить товар

GET `products/get`

Товар по id (undefined — нет).

`GET https://app.inventorymod.com/api/rpc/products/get?campaign_id=1&id=1`

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| api_key | string | нет | для чтения из браузера; для записи — заголовок Authorization |
| id | integer | **да** |  |

Ответ:

```
{
  "data": {
    "id": 1,
    "campaignId": 1,
    "sku": "ABC-1",
    "barcode": "CODE",
    "name": "Sample",
    "manufacturerId": 1,
    "categoryId": 1,
    "price": 9.99,
    "priceOld": 9.99,
    "rrp": 9.99,
    "currencyId": 1,
    "purchaseCost": 9.99,
    "purchaseCostCurrencyId": 1,
    "url": "string",
    "imageUrl": "string",
    "minQty": 9.99,
    "customFieldValues": {},
    "isArchived": false,
    "kind": "simple",
    "laborCost": 9.99,
    "laborCostCurrencyId": 1,
    "componentsUpdatedAt": "2026-01-01T00:00:00.000Z",
    "parentId": 1,
    "variantKey": "string",
    "nameLocked": false,
    "createdAtUtc": "2026-01-01T00:00:00.000Z",
    "updatedAtUtc": "2026-01-01T00:00:00.000Z"
  }
}
```

### Найти товар по SKU

GET `products/getBySku`

Найти товар по SKU.

`GET https://app.inventorymod.com/api/rpc/products/getBySku?campaign_id=1&sku=ABC-1`

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| api_key | string | нет | для чтения из браузера; для записи — заголовок Authorization |
| sku | string | **да** |  |

Ответ:

```
{
  "data": {
    "id": 1,
    "campaignId": 1,
    "sku": "ABC-1",
    "barcode": "CODE",
    "name": "Sample",
    "manufacturerId": 1,
    "categoryId": 1,
    "price": 9.99,
    "priceOld": 9.99,
    "rrp": 9.99,
    "currencyId": 1,
    "purchaseCost": 9.99,
    "purchaseCostCurrencyId": 1,
    "url": "string",
    "imageUrl": "string",
    "minQty": 9.99,
    "customFieldValues": {},
    "isArchived": false,
    "kind": "simple",
    "laborCost": 9.99,
    "laborCostCurrencyId": 1,
    "componentsUpdatedAt": "2026-01-01T00:00:00.000Z",
    "parentId": 1,
    "variantKey": "string",
    "nameLocked": false,
    "createdAtUtc": "2026-01-01T00:00:00.000Z",
    "updatedAtUtc": "2026-01-01T00:00:00.000Z"
  }
}
```

### Найти товар по SKU или штрихкоду

GET `products/getByCode`

Найти товар по SKU или штрихкоду.

`GET https://app.inventorymod.com/api/rpc/products/getByCode?campaign_id=1&value=ABC-1`

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| api_key | string | нет | для чтения из браузера; для записи — заголовок Authorization |
| value | string | **да** |  |

Ответ:

```
{
  "data": {
    "id": 1,
    "campaignId": 1,
    "sku": "ABC-1",
    "barcode": "CODE",
    "name": "Sample",
    "manufacturerId": 1,
    "categoryId": 1,
    "price": 9.99,
    "priceOld": 9.99,
    "rrp": 9.99,
    "currencyId": 1,
    "purchaseCost": 9.99,
    "purchaseCostCurrencyId": 1,
    "url": "string",
    "imageUrl": "string",
    "minQty": 9.99,
    "customFieldValues": {},
    "isArchived": false,
    "kind": "simple",
    "laborCost": 9.99,
    "laborCostCurrencyId": 1,
    "componentsUpdatedAt": "2026-01-01T00:00:00.000Z",
    "parentId": 1,
    "variantKey": "string",
    "nameLocked": false,
    "createdAtUtc": "2026-01-01T00:00:00.000Z",
    "updatedAtUtc": "2026-01-01T00:00:00.000Z"
  }
}
```

### История цен

GET `products/priceHistory`

История цен товара.

`GET https://app.inventorymod.com/api/rpc/products/priceHistory?campaign_id=1&productId=1`

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| api_key | string | нет | для чтения из браузера; для записи — заголовок Authorization |
| productId | integer | **да** |  |

Ответ:

```
{
  "data": [
    {
      "id": 1,
      "campaignId": 1,
      "productId": 1,
      "price": 9.99,
      "currencyId": 1,
      "createdAtUtc": "2026-01-01T00:00:00.000Z"
    }
  ]
}
```

### Создать товар

POST `products/create`

Создать товар (возвращает id).

`POST https://app.inventorymod.com/api/rpc/products/create?campaign_id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |

Тело запроса (JSON):

```
{
  "product": {
    "sku": "ABC-1",
    "barcode": "CODE",
    "name": "Sample",
    "manufacturerId": 1,
    "categoryId": 1,
    "price": 9.99,
    "priceOld": 9.99,
    "rrp": 9.99,
    "currencyId": 1,
    "purchaseCost": 9.99,
    "purchaseCostCurrencyId": 1,
    "url": "string",
    "imageUrl": "string",
    "minQty": 9.99,
    "customFieldValues": {},
    "isArchived": false,
    "kind": "simple",
    "laborCost": 9.99,
    "laborCostCurrencyId": 1
  }
}
```

Ответ:

```
{
  "data": 1
}
```

### Изменить товар

POST `products/update`

Обновить поля товара.

`POST https://app.inventorymod.com/api/rpc/products/update?campaign_id=1&id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| id | integer | **да** |  |

Тело запроса (JSON):

```
{
  "patch": {
    "sku": "ABC-1",
    "barcode": "CODE",
    "name": "Sample",
    "manufacturerId": 1,
    "categoryId": 1,
    "price": 9.99,
    "priceOld": 9.99,
    "rrp": 9.99,
    "currencyId": 1,
    "purchaseCost": 9.99,
    "purchaseCostCurrencyId": 1,
    "url": "string",
    "imageUrl": "string",
    "minQty": 9.99,
    "customFieldValues": {},
    "isArchived": false,
    "kind": "simple",
    "laborCost": 9.99,
    "laborCostCurrencyId": 1
  }
}
```

Ответ:

```
{
  "data": null
}
```

### Массовое изменение товаров

POST `products/bulkUpdate`

Массовое обновление товаров по списку id.

`POST https://app.inventorymod.com/api/rpc/products/bulkUpdate?campaign_id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |

Тело запроса (JSON):

```
{
  "ids": {},
  "patch": {}
}
```

Ответ:

```
{
  "data": null
}
```

### Создать или обновить товар по SKU

POST `products/upsert`

Создать или обновить товар по SKU.

`POST https://app.inventorymod.com/api/rpc/products/upsert?campaign_id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |

Тело запроса (JSON):

```
{
  "product": {
    "sku": "ABC-1",
    "barcode": "CODE",
    "name": "Sample",
    "manufacturerId": 1,
    "categoryId": 1,
    "price": 9.99,
    "priceOld": 9.99,
    "rrp": 9.99,
    "currencyId": 1,
    "purchaseCost": 9.99,
    "purchaseCostCurrencyId": 1,
    "url": "string",
    "imageUrl": "string",
    "minQty": 9.99,
    "customFieldValues": {},
    "isArchived": false,
    "kind": "simple",
    "laborCost": 9.99,
    "laborCostCurrencyId": 1
  }
}
```

Ответ:

```
{
  "data": 1
}
```

### Связи товара

GET `products/relations`

Посчитать всё, что ссылается на товар (для диалога удаления).

`GET https://app.inventorymod.com/api/rpc/products/relations?campaign_id=1&productId=1`

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| api_key | string | нет | для чтения из браузера; для записи — заголовок Authorization |
| productId | integer | **да** |  |

Ответ:

```
{
  "data": {}
}
```

### Удалить товар

POST `products/delete`

Удалить товар физически. - cascade=false (по умолчанию): удаляет только товар и его историю цен, но ТОЛЬКО если нет движений/заказов/резервов/остатка. Иначе бросает ошибку (UI должен предложить архив). - cascade=true: удаляет товар и ВСЕ связанные строки (движения, позиции заказов, резервы, остатки).

`POST https://app.inventorymod.com/api/rpc/products/delete?campaign_id=1&id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| id | integer | **да** |  |

Тело запроса (JSON):

```
{
  "opts": {}
}
```

Ответ:

```
{
  "data": null
}
```

## Состав изделий

Компоненты изделий и комплектов.

### Состав товара

GET `components/list`

Состав товара с данными компонентов. warehouseId — склад (или набор складов) для колонки «доступно» (без склада — сумма по всем складам, как остаток в списке товаров).

`GET https://app.inventorymod.com/api/rpc/components/list?campaign_id=1&productId=1`

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| api_key | string | нет | для чтения из браузера; для записи — заголовок Authorization |
| productId | integer | **да** |  |
| warehouseId | integer | нет |  |

Ответ:

```
{
  "data": []
}
```

### Добавить или изменить компонент

POST `components/set`

Добавить компонент в состав или обновить его количество (upsert по паре productId+componentId). Проверки: qty > 0; компонент ≠ товар; нет цикла; компонент складской (simple/assembled); у товара тип с составом (assembled/virtual_kit). Новая строка встаёт в конец (sort_order = max+1).

`POST https://app.inventorymod.com/api/rpc/components/set?campaign_id=1&productId=1&componentId=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| productId | integer | **да** |  |
| componentId | integer | **да** |  |

Тело запроса (JSON):

```
{
  "input": {}
}
```

Ответ:

```
{
  "data": 1
}
```

### Удалить компонент

POST `components/delete`

Удалить компонент из состава.

`POST https://app.inventorymod.com/api/rpc/components/delete?campaign_id=1&productId=1&componentId=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| productId | integer | **да** |  |
| componentId | integer | **да** |  |

Ответ:

```
{
  "data": {}
}
```

### Порядок компонентов

POST `components/reorder`

Новый порядок строк состава: componentIds — id товаров-компонентов, ровно текущий набор.

`POST https://app.inventorymod.com/api/rpc/components/reorder?campaign_id=1&productId=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| productId | integer | **да** |  |

Тело запроса (JSON):

```
{
  "componentIds": {}
}
```

Ответ:

```
{
  "data": null
}
```

### Где используется товар

GET `components/whereUsed`

В составе каких изделий/комплектов используется товар.

`GET https://app.inventorymod.com/api/rpc/components/whereUsed?campaign_id=1&productId=1`

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| api_key | string | нет | для чтения из браузера; для записи — заголовок Authorization |
| productId | integer | **да** |  |

Ответ:

```
{
  "data": []
}
```

### Недопустимые компоненты

GET `components/forbidden`

id товаров, которые нельзя добавить в состав productId (сам товар + его «предки» — иначе цикл).

`GET https://app.inventorymod.com/api/rpc/components/forbidden?campaign_id=1&productId=1`

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| api_key | string | нет | для чтения из браузера; для записи — заголовок Authorization |
| productId | integer | **да** |  |

Ответ:

```
{
  "data": {}
}
```

### Себестоимость сборки

GET `components/cost`

Себестоимость единицы изделия по текущим закупочным ценам компонентов (+ стоимость работ).

`GET https://app.inventorymod.com/api/rpc/components/cost?campaign_id=1&productId=1`

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| api_key | string | нет | для чтения из браузера; для записи — заголовок Authorization |
| productId | integer | **да** |  |

Ответ:

```
{
  "data": {}
}
```

## Оси вариантов

Оси модели — например, размер или цвет.

### Список осей

GET `variantOptions/list`

Оси модели со значениями и числом вариантов на каждое значение.

`GET https://app.inventorymod.com/api/rpc/variantOptions/list?campaign_id=1&productId=1`

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| api_key | string | нет | для чтения из браузера; для записи — заголовок Authorization |
| productId | integer | **да** |  |

Ответ:

```
{
  "data": []
}
```

### Создать ось

POST `variantOptions/create`

Добавить ось модели. При существующих вариантах — только с defaultValue (иначе нарушится V6).

`POST https://app.inventorymod.com/api/rpc/variantOptions/create?campaign_id=1&productId=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| productId | integer | **да** |  |

Тело запроса (JSON):

```
{
  "name": {},
  "input": {}
}
```

Ответ:

```
{
  "data": {}
}
```

### Переименовать ось

POST `variantOptions/update`

Переименовать ось (пересчитывает названия вариантов).

`POST https://app.inventorymod.com/api/rpc/variantOptions/update?campaign_id=1&optionId=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| optionId | integer | **да** |  |

Тело запроса (JSON):

```
{
  "patch": {}
}
```

Ответ:

```
{
  "data": {}
}
```

### Удалить ось

POST `variantOptions/delete`

Удалить ось (V10): без вариантов — всегда; с вариантами — если после удаления комбинации остаются уникальными (значения оси у вариантов удаляются, названия пересчитываются). Иначе — ошибка со списком пар.

`POST https://app.inventorymod.com/api/rpc/variantOptions/delete?campaign_id=1&optionId=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| optionId | integer | **да** |  |

Ответ:

```
{
  "data": {}
}
```

### Порядок осей

POST `variantOptions/reorder`

Порядок осей (влияет на порядок значений в названии варианта).

`POST https://app.inventorymod.com/api/rpc/variantOptions/reorder?campaign_id=1&productId=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| productId | integer | **да** |  |

Тело запроса (JSON):

```
{
  "optionIds": {}
}
```

Ответ:

```
{
  "data": null
}
```

## Значения осей

Значения оси модели — например, S, M, L.

### Добавить значение оси

POST `variantValues/create`

Добавить значение оси. code — для SKU (по умолчанию транслит значения).

`POST https://app.inventorymod.com/api/rpc/variantValues/create?campaign_id=1&optionId=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| optionId | integer | **да** |  |

Тело запроса (JSON):

```
{
  "value": {},
  "code": {}
}
```

Ответ:

```
{
  "data": 1
}
```

### Изменить значение оси

POST `variantValues/update`

Изменить значение/код (пересчитывает названия вариантов с этим значением).

`POST https://app.inventorymod.com/api/rpc/variantValues/update?campaign_id=1&valueId=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| valueId | integer | **да** |  |

Тело запроса (JSON):

```
{
  "patch": {}
}
```

Ответ:

```
{
  "data": {}
}
```

### Удалить значение оси

POST `variantValues/delete`

Удалить значение — только если ни один вариант его не использует (V10).

`POST https://app.inventorymod.com/api/rpc/variantValues/delete?campaign_id=1&valueId=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| valueId | integer | **да** |  |

Ответ:

```
{
  "data": null
}
```

### Порядок значений оси

POST `variantValues/reorder`

Порядок значений оси.

`POST https://app.inventorymod.com/api/rpc/variantValues/reorder?campaign_id=1&optionId=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| optionId | integer | **да** |  |

Тело запроса (JSON):

```
{
  "valueIds": {}
}
```

Ответ:

```
{
  "data": null
}
```

## Варианты

Генерация вариантов модели и работа с ними.

### Список вариантов

GET `variants/list`

Варианты модели (или нескольких моделей — пакетно, для печати этикеток) с значениями осей и остатками.

`GET https://app.inventorymod.com/api/rpc/variants/list?campaign_id=1&parentIds=x`

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| api_key | string | нет | для чтения из браузера; для записи — заголовок Authorization |
| parentIds | string | **да** |  |

Ответ:

```
{
  "data": []
}
```

### Сгенерировать варианты

POST `variants/generate`

Сгенерировать варианты модели (V7–V9): копии полей-шаблонов модели, автоназвание, SKU из кодов значений (+ суффикс -2, -3 при занятом SKU). Существующие комбинации пропускаются (в т.ч. архивные — их возвращают в skipped).

`POST https://app.inventorymod.com/api/rpc/variants/generate?campaign_id=1&parentId=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| parentId | integer | **да** |  |

Тело запроса (JSON):

```
{
  "input": {}
}
```

Ответ:

```
{
  "data": {}
}
```

### Сменить значения варианта

POST `variants/setValues`

Сменить комбинацию значений у существующего варианта (проверка V6).

`POST https://app.inventorymod.com/api/rpc/variants/setValues?campaign_id=1&variantId=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| variantId | integer | **да** |  |

Тело запроса (JSON):

```
{
  "values": {}
}
```

Ответ:

```
{
  "data": {}
}
```

### Привязать товар как вариант

POST `variants/attach`

Привязать существующий товар к модели как вариант (переход с «плоских» товаров). Название не меняется.

`POST https://app.inventorymod.com/api/rpc/variants/attach?campaign_id=1&parentId=1&productId=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| parentId | integer | **да** |  |
| productId | integer | **да** |  |

Тело запроса (JSON):

```
{
  "values": {}
}
```

Ответ:

```
{
  "data": {}
}
```

### Отвязать вариант

POST `variants/detach`

Отвязать вариант: станет обычным товаром (значения осей удаляются, название не меняется).

`POST https://app.inventorymod.com/api/rpc/variants/detach?campaign_id=1&variantId=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| variantId | integer | **да** |  |

Ответ:

```
{
  "data": {}
}
```

### Матрица вариантов

GET `variants/matrix`

Данные для матрицы «ось × ось»: остатки по ВСЕМ складам в ячейке (переключение склада без запроса).

`GET https://app.inventorymod.com/api/rpc/variants/matrix?campaign_id=1&parentId=1`

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| api_key | string | нет | для чтения из браузера; для записи — заголовок Authorization |
| parentId | integer | **да** |  |

Ответ:

```
{
  "data": {}
}
```

## Изображения товаров

Галерея товара: ссылки или загруженные файлы.

### Список изображений

GET `images/list`

Список изображений товара (метаданные, БЕЗ байтов), обложка (sortOrder=0) первой.

`GET https://app.inventorymod.com/api/rpc/images/list?campaign_id=1&productId=1`

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| api_key | string | нет | для чтения из браузера; для записи — заголовок Authorization |
| productId | integer | **да** |  |

Ответ:

```
{
  "data": []
}
```

### Получить изображение

GET `images/getData`

Данные одного изображения для показа. Для kind='url' вернёт ссылку (dataBase64=null), для kind='blob' — байты в base64. Единственный метод, читающий колонку data.

`GET https://app.inventorymod.com/api/rpc/images/getData?campaign_id=1&imageId=1`

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| api_key | string | нет | для чтения из браузера; для записи — заголовок Authorization |
| imageId | integer | **да** |  |

Ответ:

```
{
  "data": {}
}
```

### Обложки товаров

POST `images/firstImages`

Первое изображение каждого товара из списка (миниатюры в списках товаров).

`POST https://app.inventorymod.com/api/rpc/images/firstImages?campaign_id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |

Тело запроса (JSON):

```
{
  "productIds": {}
}
```

Ответ:

```
{
  "data": []
}
```

### Добавить изображение

POST `images/add`

Добавить изображение (в конец галереи). Валидирует инвариант гибрида: - kind='url' → нужен непустой url, data не задаётся; - kind='blob' → нужен dataBase64 (+ желательно mimeType), url не задаётся. sortOrder = (max по товару) + 1, т.е. первое добавленное становится обложкой (0 при пустой галерее).

`POST https://app.inventorymod.com/api/rpc/images/add?campaign_id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |

Тело запроса (JSON):

```
{
  "input": {}
}
```

Ответ:

```
{
  "data": 1
}
```

### Удалить изображение

POST `images/delete`

Удалить изображение.

`POST https://app.inventorymod.com/api/rpc/images/delete?campaign_id=1&imageId=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| imageId | integer | **да** |  |

Ответ:

```
{
  "data": null
}
```

### Порядок изображений

POST `images/reorder`

Переупорядочить изображения товара по переданному списку id (индекс = новый sortOrder). Так задаётся обложка (первый id в списке). Неупомянутые id не трогаются.

`POST https://app.inventorymod.com/api/rpc/images/reorder?campaign_id=1&productId=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| productId | integer | **да** |  |

Тело запроса (JSON):

```
{
  "orderedIds": {}
}
```

Ответ:

```
{
  "data": null
}
```

## Штрихкоды

Несколько штрихкодов у товара, один из них основной.

### Список штрихкодов

GET `barcodes/list`

Все штрихкоды товара (основной — первым).

`GET https://app.inventorymod.com/api/rpc/barcodes/list?campaign_id=1&productId=1`

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| api_key | string | нет | для чтения из браузера; для записи — заголовок Authorization |
| productId | integer | **да** |  |

Ответ:

```
{
  "data": []
}
```

### Добавить штрихкод

POST `barcodes/add`

Добавить штрихкод товару. Код нормализуется, тип определяется автоматически (если не задан). Если isPrimary или это первый код товара — становится основным (флаг снимается с прочих, products.barcode синхронизируется).

`POST https://app.inventorymod.com/api/rpc/barcodes/add?campaign_id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |

Тело запроса (JSON):

```
{
  "input": {}
}
```

Ответ:

```
{
  "data": 1
}
```

### Сделать штрихкод основным

POST `barcodes/setPrimary`

Сделать код основным (снять флаг с прочих + синхронизировать products.barcode).

`POST https://app.inventorymod.com/api/rpc/barcodes/setPrimary?campaign_id=1&barcodeId=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| barcodeId | integer | **да** |  |

Ответ:

```
{
  "data": null
}
```

### Удалить штрихкод

POST `barcodes/delete`

Удалить код. Если удалили основной — назначаем основным первый оставшийся (и синхроним product).

`POST https://app.inventorymod.com/api/rpc/barcodes/delete?campaign_id=1&barcodeId=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| barcodeId | integer | **да** |  |

Ответ:

```
{
  "data": null
}
```

## Склады

Склады и места хранения.

### Список складов

GET `warehouses/list`

Склады. Архивные — только с includeArchived (страница «Склады»); в выборе склада их нет (W1).

`GET https://app.inventorymod.com/api/rpc/warehouses/list?campaign_id=1`

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| api_key | string | нет | для чтения из браузера; для записи — заголовок Authorization |
| includeArchived | boolean | нет |  |

Ответ:

```
{
  "data": [
    {
      "id": 1,
      "campaignId": 1,
      "name": "Sample",
      "code": "CODE",
      "type": "string",
      "address": "string",
      "contact": "string",
      "comment": "string",
      "capacity": 9.99,
      "isDefault": false,
      "isArchived": false,
      "createdAtUtc": "2026-01-01T00:00:00.000Z",
      "updatedAtUtc": "2026-01-01T00:00:00.000Z"
    }
  ]
}
```

### Создать склад

POST `warehouses/create`

Создать склад.

`POST https://app.inventorymod.com/api/rpc/warehouses/create?campaign_id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |

Тело запроса (JSON):

```
{
  "warehouse": {
    "name": "Sample",
    "code": "CODE",
    "type": "string",
    "address": "string",
    "contact": "string",
    "comment": "string",
    "capacity": 9.99,
    "isDefault": false,
    "isArchived": false
  }
}
```

Ответ:

```
{
  "data": 1
}
```

### Изменить склад

POST `warehouses/update`

Обновить склад. Смена типа и архив — только у склада без остатков, резервов и открытых заказов (W1/W2).

`POST https://app.inventorymod.com/api/rpc/warehouses/update?campaign_id=1&id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| id | integer | **да** |  |

Тело запроса (JSON):

```
{
  "data": {
    "name": "Sample",
    "code": "CODE",
    "type": "string",
    "address": "string",
    "contact": "string",
    "comment": "string",
    "capacity": 9.99,
    "isDefault": false,
    "isArchived": false
  }
}
```

Ответ:

```
{
  "data": null
}
```

### Удалить склад

POST `warehouses/delete`

Удалить склад (W1). Нельзя: есть ненулевой остаток, активный резерв или открытый заказ на складе. Есть история (движения, строки заказов, резервы) — удалить нельзя, только архивировать (ошибка с кодом, UI предлагает архив). Пустые строки остатка (qty = 0) удаляются вместе со складом.

`POST https://app.inventorymod.com/api/rpc/warehouses/delete?campaign_id=1&id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| id | integer | **да** |  |

Ответ:

```
{
  "data": null
}
```

### Склад по умолчанию

POST `warehouses/setDefault`

Сделать склад складом по умолчанию: существует, не архивный, не витрина поставщика (W3).

`POST https://app.inventorymod.com/api/rpc/warehouses/setDefault?campaign_id=1&id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| id | integer | **да** |  |

Ответ:

```
{
  "data": null
}
```

## Категории

Категории товаров.

### Список категорий

GET `categories/list`

Список категорий.

`GET https://app.inventorymod.com/api/rpc/categories/list?campaign_id=1`

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| api_key | string | нет | для чтения из браузера; для записи — заголовок Authorization |

Ответ:

```
{
  "data": [
    {
      "id": 1,
      "campaignId": 1,
      "name": "Sample",
      "parentId": 1,
      "createdAtUtc": "2026-01-01T00:00:00.000Z",
      "updatedAtUtc": "2026-01-01T00:00:00.000Z"
    }
  ]
}
```

### Создать категорию

POST `categories/create`

Создать категорию.

`POST https://app.inventorymod.com/api/rpc/categories/create?campaign_id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |

Тело запроса (JSON):

```
{
  "category": {
    "name": "Sample",
    "parentId": 1
  }
}
```

Ответ:

```
{
  "data": 1
}
```

### Изменить категорию

POST `categories/update`

Обновить категорию.

`POST https://app.inventorymod.com/api/rpc/categories/update?campaign_id=1&id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| id | integer | **да** |  |

Тело запроса (JSON):

```
{
  "data": {
    "name": "Sample",
    "parentId": 1
  }
}
```

Ответ:

```
{
  "data": null
}
```

### Удалить категорию

POST `categories/delete`

Удалить категорию.

`POST https://app.inventorymod.com/api/rpc/categories/delete?campaign_id=1&id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| id | integer | **да** |  |

Ответ:

```
{
  "data": null
}
```

## Производители

Производители и бренды товаров.

### Список производителей

GET `manufacturers/list`

Список производителей.

`GET https://app.inventorymod.com/api/rpc/manufacturers/list?campaign_id=1`

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| api_key | string | нет | для чтения из браузера; для записи — заголовок Authorization |

Ответ:

```
{
  "data": [
    {
      "id": 1,
      "campaignId": 1,
      "name": "Sample",
      "createdAtUtc": "2026-01-01T00:00:00.000Z",
      "updatedAtUtc": "2026-01-01T00:00:00.000Z"
    }
  ]
}
```

### Создать производителя

POST `manufacturers/create`

Создать производителя.

`POST https://app.inventorymod.com/api/rpc/manufacturers/create?campaign_id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |

Тело запроса (JSON):

```
{
  "manufacturer": {
    "name": "Sample"
  }
}
```

Ответ:

```
{
  "data": 1
}
```

### Изменить производителя

POST `manufacturers/update`

Обновить производителя.

`POST https://app.inventorymod.com/api/rpc/manufacturers/update?campaign_id=1&id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| id | integer | **да** |  |

Тело запроса (JSON):

```
{
  "data": {
    "name": "Sample"
  }
}
```

Ответ:

```
{
  "data": null
}
```

### Удалить производителя

POST `manufacturers/delete`

Удалить производителя.

`POST https://app.inventorymod.com/api/rpc/manufacturers/delete?campaign_id=1&id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| id | integer | **да** |  |

Ответ:

```
{
  "data": null
}
```

## Валюты

Валюты и курсы к основной валюте.

### Список валют

GET `currencies/list`

Список валют.

`GET https://app.inventorymod.com/api/rpc/currencies/list?campaign_id=1`

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| api_key | string | нет | для чтения из браузера; для записи — заголовок Authorization |

Ответ:

```
{
  "data": [
    {
      "id": 1,
      "campaignId": 1,
      "name": "Sample",
      "rate": 9.99,
      "isDefault": false,
      "createdAtUtc": "2026-01-01T00:00:00.000Z",
      "updatedAtUtc": "2026-01-01T00:00:00.000Z"
    }
  ]
}
```

### Создать валюту

POST `currencies/create`

Создать валюту.

`POST https://app.inventorymod.com/api/rpc/currencies/create?campaign_id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |

Тело запроса (JSON):

```
{
  "currency": {
    "name": "Sample",
    "rate": 9.99,
    "isDefault": false
  }
}
```

Ответ:

```
{
  "data": 1
}
```

### Изменить валюту

POST `currencies/update`

Обновить валюту.

`POST https://app.inventorymod.com/api/rpc/currencies/update?campaign_id=1&id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| id | integer | **да** |  |

Тело запроса (JSON):

```
{
  "data": {
    "name": "Sample",
    "rate": 9.99,
    "isDefault": false
  }
}
```

Ответ:

```
{
  "data": null
}
```

### Удалить валюту

POST `currencies/delete`

Удалить валюту.

`POST https://app.inventorymod.com/api/rpc/currencies/delete?campaign_id=1&id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| id | integer | **да** |  |

Ответ:

```
{
  "data": null
}
```

### Сделать валюту основной

POST `currencies/setDefault`

Сделать валюту основной. Курсы всех валют — «к основной», поэтому при смене основной они пересчитываются (R2): курс := курс / курс новой основной. Было RUB=1 (основная), USD=90 → основная USD: USD=1, RUB=1/90. Раньше новой основной ставился курс 1, а остальные оставались прежними — отчёты считали рубли как доллары.

`POST https://app.inventorymod.com/api/rpc/currencies/setDefault?campaign_id=1&id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| id | integer | **да** |  |

Ответ:

```
{
  "data": null
}
```

## Поставщики

Поставщики для закупок.

### Список поставщиков

GET `suppliers/list`

Список поставщиков.

`GET https://app.inventorymod.com/api/rpc/suppliers/list?campaign_id=1`

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| api_key | string | нет | для чтения из браузера; для записи — заголовок Authorization |

Ответ:

```
{
  "data": [
    {
      "id": 1,
      "campaignId": 1,
      "name": "Sample",
      "code": "CODE",
      "contact": "string",
      "email": "user@example.com",
      "phone": "string",
      "address": "string",
      "comment": "string",
      "isArchived": false,
      "createdAtUtc": "2026-01-01T00:00:00.000Z",
      "updatedAtUtc": "2026-01-01T00:00:00.000Z"
    }
  ]
}
```

### Создать поставщика

POST `suppliers/create`

Создать поставщика.

`POST https://app.inventorymod.com/api/rpc/suppliers/create?campaign_id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |

Тело запроса (JSON):

```
{
  "supplier": {
    "name": "Sample",
    "code": "CODE",
    "contact": "string",
    "email": "user@example.com",
    "phone": "string",
    "address": "string",
    "comment": "string",
    "isArchived": false
  }
}
```

Ответ:

```
{
  "data": 1
}
```

### Изменить поставщика

POST `suppliers/update`

Обновить поставщика.

`POST https://app.inventorymod.com/api/rpc/suppliers/update?campaign_id=1&id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| id | integer | **да** |  |

Тело запроса (JSON):

```
{
  "data": {
    "name": "Sample",
    "code": "CODE",
    "contact": "string",
    "email": "user@example.com",
    "phone": "string",
    "address": "string",
    "comment": "string",
    "isArchived": false
  }
}
```

Ответ:

```
{
  "data": null
}
```

### Удалить поставщика

POST `suppliers/delete`

Удалить поставщика: с закупками — нельзя (K1), только архив.

`POST https://app.inventorymod.com/api/rpc/suppliers/delete?campaign_id=1&id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| id | integer | **да** |  |

Ответ:

```
{
  "data": null
}
```

## Клиенты

Клиенты для заказов покупателей.

### Список клиентов

GET `customers/list`

Список клиентов.

`GET https://app.inventorymod.com/api/rpc/customers/list?campaign_id=1`

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| api_key | string | нет | для чтения из браузера; для записи — заголовок Authorization |

Ответ:

```
{
  "data": [
    {
      "id": 1,
      "campaignId": 1,
      "name": "Sample",
      "code": "CODE",
      "contact": "string",
      "email": "user@example.com",
      "phone": "string",
      "address": "string",
      "comment": "string",
      "isArchived": false,
      "createdAtUtc": "2026-01-01T00:00:00.000Z",
      "updatedAtUtc": "2026-01-01T00:00:00.000Z"
    }
  ]
}
```

### Создать клиента

POST `customers/create`

Создать клиента.

`POST https://app.inventorymod.com/api/rpc/customers/create?campaign_id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |

Тело запроса (JSON):

```
{
  "customer": {
    "name": "Sample",
    "code": "CODE",
    "contact": "string",
    "email": "user@example.com",
    "phone": "string",
    "address": "string",
    "comment": "string",
    "isArchived": false
  }
}
```

Ответ:

```
{
  "data": 1
}
```

### Изменить клиента

POST `customers/update`

Обновить клиента.

`POST https://app.inventorymod.com/api/rpc/customers/update?campaign_id=1&id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| id | integer | **да** |  |

Тело запроса (JSON):

```
{
  "data": {
    "name": "Sample",
    "code": "CODE",
    "contact": "string",
    "email": "user@example.com",
    "phone": "string",
    "address": "string",
    "comment": "string",
    "isArchived": false
  }
}
```

Ответ:

```
{
  "data": null
}
```

### Удалить клиента

POST `customers/delete`

Удалить клиента: с заказами — нельзя, только архив.

`POST https://app.inventorymod.com/api/rpc/customers/delete?campaign_id=1&id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| id | integer | **да** |  |

Ответ:

```
{
  "data": null
}
```

## Пользовательские поля

Собственные поля товаров.

### Список пользовательских полей

GET `customFields/list`

Список кастомных полей товара.

`GET https://app.inventorymod.com/api/rpc/customFields/list?campaign_id=1`

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| api_key | string | нет | для чтения из браузера; для записи — заголовок Authorization |

Ответ:

```
{
  "data": [
    {
      "id": 1,
      "campaignId": 1,
      "name": "Sample",
      "type": "string",
      "sortOrder": 1,
      "createdAtUtc": "2026-01-01T00:00:00.000Z"
    }
  ]
}
```

### Создать поле

POST `customFields/create`

Создать кастомное поле товара (возвращает id).

`POST https://app.inventorymod.com/api/rpc/customFields/create?campaign_id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |

Тело запроса (JSON):

```
{
  "data": {}
}
```

Ответ:

```
{
  "data": 1
}
```

### Изменить поле

POST `customFields/update`

Изменить название или тип кастомного поля.

`POST https://app.inventorymod.com/api/rpc/customFields/update?campaign_id=1&id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| id | integer | **да** |  |

Тело запроса (JSON):

```
{
  "data": {}
}
```

Ответ:

```
{
  "data": null
}
```

### Удалить поле

POST `customFields/delete`

Удалить кастомное поле.

`POST https://app.inventorymod.com/api/rpc/customFields/delete?campaign_id=1&id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| id | integer | **да** |  |

Ответ:

```
{
  "data": null
}
```

## Складские операции

Приход, расход, корректировка, перемещение и резервирование; инвентаризация и проверка остатков.

### Приход

POST `stock/in`

Приход товара на склад (возвращает id операции).

`POST https://app.inventorymod.com/api/rpc/stock/in?campaign_id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |

Тело запроса (JSON):

```
{
  "input": {}
}
```

Ответ:

```
{
  "data": 1
}
```

### Расход

POST `stock/out`

Расход/списание товара со склада.

`POST https://app.inventorymod.com/api/rpc/stock/out?campaign_id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |

Тело запроса (JSON):

```
{
  "input": {}
}
```

Ответ:

```
{
  "data": 1
}
```

### Корректировка остатка

POST `stock/adjust`

Корректировка остатка до заданного количества.

`POST https://app.inventorymod.com/api/rpc/stock/adjust?campaign_id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |

Тело запроса (JSON):

```
{
  "input": {}
}
```

Ответ:

```
{
  "data": 1
}
```

### Перемещение

POST `stock/transfer`

Перемещение товара между складами.

`POST https://app.inventorymod.com/api/rpc/stock/transfer?campaign_id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |

Тело запроса (JSON):

```
{
  "input": {}
}
```

Ответ:

```
{
  "data": 1
}
```

### Зарезервировать

POST `stock/reserve`

Зарезервировать товар.

`POST https://app.inventorymod.com/api/rpc/stock/reserve?campaign_id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |

Тело запроса (JSON):

```
{
  "input": {}
}
```

Ответ:

```
{
  "data": 1
}
```

### Массовый резерв

POST `stock/reserveBulk`

Массовый резерв (R5): несколько складов — одной транзакцией (ошибка на любом — ничего не записано).

`POST https://app.inventorymod.com/api/rpc/stock/reserveBulk?campaign_id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |

Тело запроса (JSON):

```
{
  "inputs": {}
}
```

Ответ:

```
{
  "data": {}
}
```

### Снять резерв

POST `stock/unreserve`

Снять резерв.

`POST https://app.inventorymod.com/api/rpc/stock/unreserve?campaign_id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |

Тело запроса (JSON):

```
{
  "input": {}
}
```

Ответ:

```
{
  "data": 1
}
```

### Отгрузить из резерва

POST `stock/consumeReservation`

Списать зарезервированное (отгрузка из резерва).

`POST https://app.inventorymod.com/api/rpc/stock/consumeReservation?campaign_id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |

Тело запроса (JSON):

```
{
  "input": {}
}
```

Ответ:

```
{
  "data": 1
}
```

### Возврат

POST `stock/return`

Возврат товара (приход).

`POST https://app.inventorymod.com/api/rpc/stock/return?campaign_id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |

Тело запроса (JSON):

```
{
  "input": {}
}
```

Ответ:

```
{
  "data": 1
}
```

### Списание

POST `stock/writeOff`

Списание товара.

`POST https://app.inventorymod.com/api/rpc/stock/writeOff?campaign_id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |

Тело запроса (JSON):

```
{
  "input": {}
}
```

Ответ:

```
{
  "data": 1
}
```

### Пакетные операции

POST `stock/bulk`

Пакетная обработка складских операций.

`POST https://app.inventorymod.com/api/rpc/stock/bulk?campaign_id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |

Тело запроса (JSON):

```
{
  "input": {}
}
```

Ответ:

```
{
  "data": 1
}
```

### Отменить движение

POST `stock/undoMovement`

Откатить складское движение (одно). Правила (D11 планов): - только в окне UNDO_WINDOW_MINUTES; - движение сборки/разборки отменяется только всей операцией (undoOperation); - обратное движение (отмену) отменить нельзя; одно движение — не больше одного раза (UNIQUE reverses_movement_id).

`POST https://app.inventorymod.com/api/rpc/stock/undoMovement?campaign_id=1&movementId=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| movementId | integer | **да** |  |

Ответ:

```
{
  "data": null
}
```

### Проверить остатки

GET `stock/integrityCheck`

Отчёт расхождений; ничего не меняет.

`GET https://app.inventorymod.com/api/rpc/stock/integrityCheck?campaign_id=1`

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| api_key | string | нет | для чтения из браузера; для записи — заголовок Authorization |

Ответ:

```
{
  "data": {}
}
```

### Исправить остатки

POST `stock/integrityRepair`

Исправить расхождения: reserved = сумма активных резервов, available = qty − reserved. Физический остаток не меняется; если журнал с ним не сходится — пишется корректировка журнала (движение Adjustment с комментарием), чтобы история совпала с остатком. Строки по удалённому складу/товару здесь не трогаются — для них «Очистка осиротевших данных».

`POST https://app.inventorymod.com/api/rpc/stock/integrityRepair?campaign_id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |

Ответ:

```
{
  "data": {}
}
```

### Сверка с файлом

POST `stock/reconcile`

Серверная сверка: по строкам из CSV ({sku, warehouse, uploadedQty}) резолвит товары/склады и текущие остатки ОДНИМ набором запросов (без выгрузки каталога и без getBalance на строку), считает расхождения. Склад матчится по имени (регистронезависимо) или по id. Возвращает готовую таблицу для UI.

`POST https://app.inventorymod.com/api/rpc/stock/reconcile?campaign_id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |

Тело запроса (JSON):

```
{
  "rows": {}
}
```

Ответ:

```
{
  "data": {}
}
```

### Сверка инвентаризации

POST `stock/countCheck`

Сверка подсчёта с остатками. Повторы (SKU, склад) суммируются; количество — parseNumber (запятая, пробелы), пустое/нечисло/отрицательное — ошибка строки (I6). Витрина поставщика и архивный склад — ошибка строки (I5).

`POST https://app.inventorymod.com/api/rpc/stock/countCheck?campaign_id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |

Тело запроса (JSON):

```
{
  "rows": {},
  "opts": {}
}
```

Ответ:

```
{
  "data": {}
}
```

### Применить инвентаризацию

POST `stock/countApply`

Применить инвентаризацию (I1/I2/I11): ОДНОЙ транзакцией и одной операцией по всем складам. Строка применяется, только если остаток не изменился с момента сверки (иначе — в changed, сверить заново); остаток := подсчитано. Документ — номер инвентаризации (по умолчанию INV-ГГГГММДД-ЧЧММ), тип «Inventory».

`POST https://app.inventorymod.com/api/rpc/stock/countApply?campaign_id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |

Тело запроса (JSON):

```
{
  "input": {}
}
```

Ответ:

```
{
  "data": {}
}
```

## Сборка

Сборка и разборка изделий из компонентов.

### Собрать изделия

POST `assembly/assemble`

Собрать N шт.: одна операция Assembly, AssemblyOut по каждому компоненту (qty × N), AssemblyIn изделия с себестоимостью (purchase_price + валюта; при неполной себестоимости — NULL, D13).

`POST https://app.inventorymod.com/api/rpc/assembly/assemble?campaign_id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |

Тело запроса (JSON):

```
{
  "input": {}
}
```

Ответ:

```
{
  "data": {}
}
```

### Разобрать изделия

POST `assembly/disassemble`

Разобрать N шт. по ТЕКУЩЕМУ составу (D9): DisassemblyOut изделия, DisassemblyIn компонентов (purchase_price = текущая закупочная цена компонента в её валюте).

`POST https://app.inventorymod.com/api/rpc/assembly/disassemble?campaign_id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |

Тело запроса (JSON):

```
{
  "input": {}
}
```

Ответ:

```
{
  "data": {}
}
```

### Отменить сборку

POST `assembly/undo`

Отменить операцию сборки/разборки целиком (D11): обратные движения 'Adjustment' со ссылкой на исходные, операция 'Undo' со ссылкой на исходную операцию. Окно — UNDO_WINDOW_MINUTES, повторная отмена запрещена. Если сборка меняла закупочную цену изделия и с тех пор её не меняли — цена и валюта возвращаются (B3/F9).

`POST https://app.inventorymod.com/api/rpc/assembly/undo?campaign_id=1&operationId=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| operationId | integer | **да** |  |

Ответ:

```
{
  "data": {}
}
```

### Сколько можно собрать

GET `assembly/buildable`

Сколько изделий можно собрать на складе (только прямые компоненты; полуфабрикат — по его остатку).

`GET https://app.inventorymod.com/api/rpc/assembly/buildable?campaign_id=1&productId=1&warehouseId=1`

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| api_key | string | нет | для чтения из браузера; для записи — заголовок Authorization |
| productId | integer | **да** |  |
| warehouseId | integer | **да** |  |

Ответ:

```
{
  "data": {}
}
```

## Остатки

Количество, резерв и доступный остаток по складам.

### Остаток товара

GET `balances/get`

Остаток товара на складе (qty/reserved/available).

`GET https://app.inventorymod.com/api/rpc/balances/get?campaign_id=1&warehouseId=1&productId=1`

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| api_key | string | нет | для чтения из браузера; для записи — заголовок Authorization |
| warehouseId | integer | **да** |  |
| productId | integer | **да** |  |

Ответ:

```
{
  "data": {
    "id": 1,
    "campaignId": 1,
    "warehouseId": 1,
    "productId": 1,
    "qty": 9.99,
    "reservedQty": 9.99,
    "availableQty": 9.99,
    "lastMovementAtUtc": "2026-01-01T00:00:00.000Z",
    "updatedAtUtc": "2026-01-01T00:00:00.000Z"
  }
}
```

### Список остатков

GET `balances/list`

Остатки (опц. фильтр по складу).

`GET https://app.inventorymod.com/api/rpc/balances/list?campaign_id=1`

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| api_key | string | нет | для чтения из браузера; для записи — заголовок Authorization |
| warehouseId | integer | нет |  |

Ответ:

```
{
  "data": [
    {
      "id": 1,
      "campaignId": 1,
      "warehouseId": 1,
      "productId": 1,
      "qty": 9.99,
      "reservedQty": 9.99,
      "availableQty": 9.99,
      "lastMovementAtUtc": "2026-01-01T00:00:00.000Z",
      "updatedAtUtc": "2026-01-01T00:00:00.000Z"
    }
  ]
}
```

### Поиск по остаткам

GET `balances/page`

Постраничные остатки: фильтр по складу/статусу, поиск по имени/SKU и сортировка — всё на стороне БД. Поиск и статус 'low' используют products, поэтому джойним products (и warehouses для сортировки/выдачи).

`GET https://app.inventorymod.com/api/rpc/balances/page?campaign_id=1&limit=50&offset=0`

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| api_key | string | нет | для чтения из браузера; для записи — заголовок Authorization |
| warehouseId | integer | нет |  |
| search | string | нет |  |
| stockStatus | string all \| in \| out \| low | нет |  |
| sortBy | string product \| warehouse \| qty \| reservedQty \| availableQty \| lastMovementAtUtc | нет |  |
| sortOrder | string asc \| desc | нет |  |
| limit | integer | **да** |  |
| offset | integer | **да** |  |
| parentId | integer | нет |  |

Ответ:

```
{
  "data": {
    "items": [
      {
        "id": 1,
        "campaignId": 1,
        "warehouseId": 1,
        "productId": 1,
        "qty": 9.99,
        "reservedQty": 9.99,
        "availableQty": 9.99,
        "lastMovementAtUtc": "2026-01-01T00:00:00.000Z",
        "updatedAtUtc": "2026-01-01T00:00:00.000Z"
      }
    ],
    "totalCount": 1
  }
}
```

### Итоги по складам

GET `balances/warehouseTotals`

Суммарный физический остаток по каждому складу (для индикатора загрузки склада).

`GET https://app.inventorymod.com/api/rpc/balances/warehouseTotals?campaign_id=1`

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| api_key | string | нет | для чтения из браузера; для записи — заголовок Authorization |

Ответ:

```
{
  "data": {}
}
```

### Остатки комплектов

GET `balances/kits`

Страница комплектов с расчётным остатком (C5/F8). Пагинация — по комплектам, расчёт пакетный: компоненты страницы и их остатки — двумя запросами (без N+1 вызовов calcBuildable).

`GET https://app.inventorymod.com/api/rpc/balances/kits?campaign_id=1&limit=50&offset=0`

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| api_key | string | нет | для чтения из браузера; для записи — заголовок Authorization |
| warehouseId | integer | нет |  |
| search | string | нет |  |
| limit | integer | **да** |  |
| offset | integer | **да** |  |
| includeArchived | boolean | нет |  |

Ответ:

```
{
  "data": {
    "items": [],
    "totalCount": 0
  }
}
```

## Движения

История всех складских движений.

### Список движений

GET `movements/list`

Складские движения (опц. фильтры).

`GET https://app.inventorymod.com/api/rpc/movements/list?campaign_id=1`

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| api_key | string | нет | для чтения из браузера; для записи — заголовок Authorization |
| warehouseId | integer | нет |  |
| productId | integer | нет |  |
| movementType | string | нет |  |
| startDate | string | нет |  |
| endDate | string | нет |  |
| operationId | integer | нет |  |

Ответ:

```
{
  "data": [
    {
      "id": 1,
      "campaignId": 1,
      "warehouseId": 1,
      "productId": 1,
      "sku": "ABC-1",
      "movementType": "string",
      "qtyDelta": 9.99,
      "reservedDelta": 9.99,
      "qtyBefore": 9.99,
      "qtyAfter": 9.99,
      "reservedBefore": 9.99,
      "reservedAfter": 9.99,
      "purchasePrice": 9.99,
      "purchaseCurrencyId": 1,
      "documentType": "string",
      "documentNumber": "string",
      "operationId": 1,
      "comment": "string",
      "createdAtUtc": "2026-01-01T00:00:00.000Z",
      "createdBy": "string",
      "createdBySource": "string",
      "reversesMovementId": 1
    }
  ]
}
```

### Поиск движений

GET `movements/page`

Постраничные складские движения.

`GET https://app.inventorymod.com/api/rpc/movements/page?campaign_id=1&limit=50&offset=0`

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| api_key | string | нет | для чтения из браузера; для записи — заголовок Authorization |
| warehouseId | integer | нет |  |
| productId | integer | нет |  |
| movementType | string | нет |  |
| startDate | string | нет |  |
| endDate | string | нет |  |
| operationId | integer | нет |  |
| search | string | нет |  |
| sortBy | string createdAtUtc \| movementType \| product \| warehouse \| qtyDelta \| qtyAfter \| purchasePrice \| documentNumber | нет |  |
| sortOrder | string asc \| desc | нет |  |
| limit | integer | **да** |  |
| offset | integer | **да** |  |

Ответ:

```
{
  "data": {
    "items": [
      {
        "id": 1,
        "campaignId": 1,
        "warehouseId": 1,
        "productId": 1,
        "sku": "ABC-1",
        "movementType": "string",
        "qtyDelta": 9.99,
        "reservedDelta": 9.99,
        "qtyBefore": 9.99,
        "qtyAfter": 9.99,
        "reservedBefore": 9.99,
        "reservedAfter": 9.99,
        "purchasePrice": 9.99,
        "purchaseCurrencyId": 1,
        "documentType": "string",
        "documentNumber": "string",
        "operationId": 1,
        "comment": "string",
        "createdAtUtc": "2026-01-01T00:00:00.000Z",
        "createdBy": "string",
        "createdBySource": "string",
        "reversesMovementId": 1
      }
    ],
    "totalCount": 1
  }
}
```

## Резервы

Товар, зарезервированный под клиентов и заказы.

### Список резервов

GET `reservations/list`

Резервы (опц. фильтр по статусу/датам). Перед чтением прогоняет истечение просроченных.

`GET https://app.inventorymod.com/api/rpc/reservations/list?campaign_id=1`

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| api_key | string | нет | для чтения из браузера; для записи — заголовок Authorization |
| status | string | нет |  |
| startDate | string | нет |  |
| endDate | string | нет |  |
| warehouseId | integer | нет |  |

Ответ:

```
{
  "data": [
    {
      "id": 1,
      "campaignId": 1,
      "warehouseId": 1,
      "productId": 1,
      "qtyReserved": 9.99,
      "status": "Active",
      "reasonType": "string",
      "documentType": "string",
      "documentNumber": "string",
      "expiresAtUtc": "2026-01-01T00:00:00.000Z",
      "comment": "string",
      "createdAtUtc": "2026-01-01T00:00:00.000Z",
      "updatedAtUtc": "2026-01-01T00:00:00.000Z",
      "releasedAtUtc": "2026-01-01T00:00:00.000Z",
      "orderItemId": 1
    }
  ]
}
```

### Поиск резервов

GET `reservations/page`

Страница резервов (R7/R8): фильтры, поиск и сортировка — в SQL; ответ — {items, totalCount}.

`GET https://app.inventorymod.com/api/rpc/reservations/page?campaign_id=1&limit=50&offset=0`

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| api_key | string | нет | для чтения из браузера; для записи — заголовок Authorization |
| status | string | нет |  |
| warehouseId | integer | нет |  |
| startDate | string | нет |  |
| endDate | string | нет |  |
| search | string | нет |  |
| sortBy | string createdAtUtc \| product \| warehouse \| qtyReserved \| status \| expiresAtUtc \| documentNumber | нет |  |
| sortOrder | string asc \| desc | нет |  |
| limit | integer | **да** |  |
| offset | integer | **да** |  |

Ответ:

```
{
  "data": {
    "items": [
      {
        "id": 1,
        "campaignId": 1,
        "warehouseId": 1,
        "productId": 1,
        "qtyReserved": 9.99,
        "status": "Active",
        "reasonType": "string",
        "documentType": "string",
        "documentNumber": "string",
        "expiresAtUtc": "2026-01-01T00:00:00.000Z",
        "comment": "string",
        "createdAtUtc": "2026-01-01T00:00:00.000Z",
        "updatedAtUtc": "2026-01-01T00:00:00.000Z",
        "releasedAtUtc": "2026-01-01T00:00:00.000Z",
        "orderItemId": 1
      }
    ],
    "totalCount": 1
  }
}
```

## Заказы покупателей

Заказы клиентов: создание, резерв, отгрузка и отмена.

### Список заказов

GET `orders/list`

Список заказов покупателей.

`GET https://app.inventorymod.com/api/rpc/orders/list?campaign_id=1`

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| api_key | string | нет | для чтения из браузера; для записи — заголовок Authorization |

Ответ:

```
{
  "data": [
    {
      "id": 1,
      "campaignId": 1,
      "orderNumber": "string",
      "customerId": 1,
      "source": "string",
      "status": "Active",
      "comment": "string",
      "createdAtUtc": "2026-01-01T00:00:00.000Z",
      "updatedAtUtc": "2026-01-01T00:00:00.000Z",
      "cancelledAtUtc": "2026-01-01T00:00:00.000Z",
      "shippedAtUtc": "2026-01-01T00:00:00.000Z"
    }
  ]
}
```

### Поиск заказов

GET `orders/search`

Постраничный список заказов.

`GET https://app.inventorymod.com/api/rpc/orders/search?campaign_id=1&limit=50&offset=0`

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| api_key | string | нет | для чтения из браузера; для записи — заголовок Authorization |
| search | string | нет |  |
| status | string | нет |  |
| limit | integer | **да** |  |
| offset | integer | **да** |  |

Ответ:

```
{
  "data": {
    "items": [
      {
        "id": 1,
        "campaignId": 1,
        "orderNumber": "string",
        "customerId": 1,
        "source": "string",
        "status": "Active",
        "comment": "string",
        "createdAtUtc": "2026-01-01T00:00:00.000Z",
        "updatedAtUtc": "2026-01-01T00:00:00.000Z",
        "cancelledAtUtc": "2026-01-01T00:00:00.000Z",
        "shippedAtUtc": "2026-01-01T00:00:00.000Z"
      }
    ],
    "totalCount": 1
  }
}
```

### Получить заказ

GET `orders/get`

Заказ по id (с позициями).

`GET https://app.inventorymod.com/api/rpc/orders/get?campaign_id=1&id=1`

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| api_key | string | нет | для чтения из браузера; для записи — заголовок Authorization |
| id | integer | **да** |  |

Ответ:

```
{
  "data": {
    "id": 1,
    "campaignId": 1,
    "orderNumber": "string",
    "customerId": 1,
    "source": "string",
    "status": "Active",
    "comment": "string",
    "createdAtUtc": "2026-01-01T00:00:00.000Z",
    "updatedAtUtc": "2026-01-01T00:00:00.000Z",
    "cancelledAtUtc": "2026-01-01T00:00:00.000Z",
    "shippedAtUtc": "2026-01-01T00:00:00.000Z"
  }
}
```

### Создать заказ

POST `orders/create`

Создать заказ.

`POST https://app.inventorymod.com/api/rpc/orders/create?campaign_id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |

Тело запроса (JSON):

```
{
  "input": {}
}
```

Ответ:

```
{
  "data": 1
}
```

### Следующий номер заказа

GET `orders/nextNumber`

Следующий свободный номер документа с префиксом (напр. «ORD-20261006-»): максимум существующих номеров за этот префикс + 1, три цифры (Z6). Раньше форма считала «все документы + 1» и грузила их целиком.

`GET https://app.inventorymod.com/api/rpc/orders/nextNumber?campaign_id=1&prefix=x`

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| api_key | string | нет | для чтения из браузера; для записи — заголовок Authorization |
| prefix | string | **да** |  |

Ответ:

```
{
  "data": {}
}
```

### Изменить заказ

POST `orders/update`

Изменить заказ (Z13). Нельзя: отгруженный (в т.ч. частично) и отменённый. Строки — полный список: id есть — существующая (товар не меняется), нет id — новая, отсутствующая в списке — удаляется. Резервы приводятся в соответствие автоматически: удаление строки или смена склада снимают весь её резерв, уменьшение количества — излишек (у комплекта — по каждому компоненту по снимку состава).

`POST https://app.inventorymod.com/api/rpc/orders/update?campaign_id=1&orderId=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| orderId | integer | **да** |  |

Тело запроса (JSON):

```
{
  "input": {}
}
```

Ответ:

```
{
  "data": {}
}
```

### Зарезервировать заказ

POST `orders/reserve`

Зарезервировать товары под заказ. Обычные строки и комплекты — одинаково (O3): резервируется недостающее, сколько есть на складе (частичный резерв → статус «Частично зарезервирован»). Ошибка — только если резерв нужен, а по заказу не удалось зарезервировать ничего.

`POST https://app.inventorymod.com/api/rpc/orders/reserve?campaign_id=1&orderId=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| orderId | integer | **да** |  |

Ответ:

```
{
  "data": {}
}
```

### Отгрузить заказ

POST `orders/ship`

Отгрузить заказ (списать со склада).

`POST https://app.inventorymod.com/api/rpc/orders/ship?campaign_id=1&orderId=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| orderId | integer | **да** |  |

Ответ:

```
{
  "data": null
}
```

### Отменить заказ

POST `orders/cancel`

Отменить заказ (снять резервы). Атомарно (O2): резервы и статус — в одной транзакции.

`POST https://app.inventorymod.com/api/rpc/orders/cancel?campaign_id=1&orderId=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| orderId | integer | **да** |  |

Ответ:

```
{
  "data": null
}
```

## Заказы поставщикам

Закупки: создание, приёмка и отмена.

### Список закупок

GET `purchaseOrders/list`

Список заказов поставщикам.

`GET https://app.inventorymod.com/api/rpc/purchaseOrders/list?campaign_id=1`

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| api_key | string | нет | для чтения из браузера; для записи — заголовок Authorization |

Ответ:

```
{
  "data": [
    {
      "id": 1,
      "campaignId": 1,
      "orderNumber": "string",
      "supplierId": 1,
      "status": "Active",
      "expectedAtUtc": "2026-01-01T00:00:00.000Z",
      "comment": "string",
      "createdAtUtc": "2026-01-01T00:00:00.000Z",
      "updatedAtUtc": "2026-01-01T00:00:00.000Z",
      "cancelledAtUtc": "2026-01-01T00:00:00.000Z"
    }
  ]
}
```

### Поиск закупок

GET `purchaseOrders/search`

Постраничный список заказов поставщикам: поиск по номеру, фильтр статуса, пагинация — в БД.

`GET https://app.inventorymod.com/api/rpc/purchaseOrders/search?campaign_id=1&limit=50&offset=0`

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| api_key | string | нет | для чтения из браузера; для записи — заголовок Authorization |
| search | string | нет |  |
| status | string | нет |  |
| limit | integer | **да** |  |
| offset | integer | **да** |  |

Ответ:

```
{
  "data": {
    "items": [
      {
        "id": 1,
        "campaignId": 1,
        "orderNumber": "string",
        "supplierId": 1,
        "status": "Active",
        "expectedAtUtc": "2026-01-01T00:00:00.000Z",
        "comment": "string",
        "createdAtUtc": "2026-01-01T00:00:00.000Z",
        "updatedAtUtc": "2026-01-01T00:00:00.000Z",
        "cancelledAtUtc": "2026-01-01T00:00:00.000Z"
      }
    ],
    "totalCount": 1
  }
}
```

### Получить закупку

GET `purchaseOrders/get`

Заказ поставщику по id.

`GET https://app.inventorymod.com/api/rpc/purchaseOrders/get?campaign_id=1&id=1`

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| api_key | string | нет | для чтения из браузера; для записи — заголовок Authorization |
| id | integer | **да** |  |

Ответ:

```
{
  "data": {
    "id": 1,
    "campaignId": 1,
    "orderNumber": "string",
    "supplierId": 1,
    "status": "Active",
    "expectedAtUtc": "2026-01-01T00:00:00.000Z",
    "comment": "string",
    "createdAtUtc": "2026-01-01T00:00:00.000Z",
    "updatedAtUtc": "2026-01-01T00:00:00.000Z",
    "cancelledAtUtc": "2026-01-01T00:00:00.000Z"
  }
}
```

### Создать закупку

POST `purchaseOrders/create`

Создать заказ поставщику.

`POST https://app.inventorymod.com/api/rpc/purchaseOrders/create?campaign_id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |

Тело запроса (JSON):

```
{
  "input": {}
}
```

Ответ:

```
{
  "data": 1
}
```

### Следующий номер закупки

GET `purchaseOrders/nextNumber`

Следующий свободный номер закупки с префиксом (напр. «PO-20261006-»), Z6.

`GET https://app.inventorymod.com/api/rpc/purchaseOrders/nextNumber?campaign_id=1&prefix=x`

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| api_key | string | нет | для чтения из браузера; для записи — заголовок Authorization |
| prefix | string | **да** |  |

Ответ:

```
{
  "data": {}
}
```

### Изменить закупку

POST `purchaseOrders/update`

Изменить закупку (Z13). Нельзя: полностью полученную и отменённую. Строки — полный список (id — существующая, без id — новая, отсутствующая — удаляется). Количество не меньше уже принятого; строку с принятым товаром удалить нельзя. Цена/валюта не переданы (undefined) — остаются прежними (участник со скрытыми ценами).

`POST https://app.inventorymod.com/api/rpc/purchaseOrders/update?campaign_id=1&id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| id | integer | **да** |  |

Тело запроса (JSON):

```
{
  "input": {}
}
```

Ответ:

```
{
  "data": {}
}
```

### Принять поставку

POST `purchaseOrders/receive`

Оприходовать поставку по заказу.

`POST https://app.inventorymod.com/api/rpc/purchaseOrders/receive?campaign_id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |

Тело запроса (JSON):

```
{
  "input": {}
}
```

Ответ:

```
{
  "data": 1
}
```

### Отменить закупку

POST `purchaseOrders/cancel`

Отменить заказ поставщику.

`POST https://app.inventorymod.com/api/rpc/purchaseOrders/cancel?campaign_id=1&id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| id | integer | **да** |  |

Ответ:

```
{
  "data": null
}
```

## Отчёты

Отчёты по запасам, продажам и закупкам, сводка для дашборда.

### Сводка по остаткам и марже

GET `reports/get`

Сводный отчёт по остаткам, себестоимости и марже.

`GET https://app.inventorymod.com/api/rpc/reports/get?campaign_id=1`

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| api_key | string | нет | для чтения из браузера; для записи — заголовок Authorization |

Ответ:

```
{
  "data": {}
}
```

### Отчёты за период

GET `reports/bundle`

Сводный набор отчётов за период: инвентарь, продажи, закупки, движения, ABC, оборачиваемость; фильтр по складу.

`GET https://app.inventorymod.com/api/rpc/reports/bundle?campaign_id=1`

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| api_key | string | нет | для чтения из браузера; для записи — заголовок Authorization |
| from | string | нет |  |
| to | string | нет |  |
| warehouseId | integer | нет |  |

Ответ:

```
{
  "data": {}
}
```

### Сводка для дашборда

GET `reports/dashboard`

Сводка для дашборда: число товаров, суммы остатков, «нет в наличии», «ниже минимума», графики по категориям и складам.

`GET https://app.inventorymod.com/api/rpc/reports/dashboard?campaign_id=1`

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| api_key | string | нет | для чтения из браузера; для записи — заголовок Authorization |

Ответ:

```
{
  "data": {}
}
```

## Настройки

Настройки кампании.

### Получить настройки

GET `settings/get`

Получить настройки кампании.

`GET https://app.inventorymod.com/api/rpc/settings/get?campaign_id=1`

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| api_key | string | нет | для чтения из браузера; для записи — заголовок Authorization |

Ответ:

```
{
  "data": {
    "id": 1,
    "campaignId": 1,
    "locale": "string",
    "allowNegativeStock": false,
    "allowOverReservation": false,
    "defaultExportQuantityMode": "string",
    "dateFormat": "2026-01-01T00:00:00.000Z",
    "timeFormat": "string",
    "decimalSeparator": "string",
    "thousandsSeparator": "string",
    "lastWarehouseId": 1,
    "lastBackupReminderDate": "2026-01-01T00:00:00.000Z",
    "lastBackupAtUtc": "2026-01-01T00:00:00.000Z",
    "defaultBackupTarget": "string",
    "featureKits": false,
    "featureVariants": false,
    "featuresRolloutDone": false,
    "timeZone": "string"
  }
}
```

### Изменить настройки

POST `settings/update`

Обновить настройки кампании.

`POST https://app.inventorymod.com/api/rpc/settings/update?campaign_id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |

Тело запроса (JSON):

```
{
  "updates": {
    "locale": "string",
    "allowNegativeStock": false,
    "allowOverReservation": false,
    "defaultExportQuantityMode": "string",
    "dateFormat": "2026-01-01T00:00:00.000Z",
    "timeFormat": "string",
    "decimalSeparator": "string",
    "thousandsSeparator": "string",
    "lastWarehouseId": 1,
    "lastBackupReminderDate": "2026-01-01T00:00:00.000Z",
    "lastBackupAtUtc": "2026-01-01T00:00:00.000Z",
    "defaultBackupTarget": "string",
    "featureKits": false,
    "featureVariants": false,
    "featuresRolloutDone": false,
    "timeZone": "string"
  }
}
```

Ответ:

```
{
  "data": null
}
```

## Импорт

Массовый импорт товаров, остатков, документов и справочников.

### История импорта

GET `imports/history`

История задач импорта/экспорта.

`GET https://app.inventorymod.com/api/rpc/imports/history?campaign_id=1`

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| api_key | string | нет | для чтения из браузера; для записи — заголовок Authorization |
| updateMode | string | нет |  |
| type | string IMPORT \| EXPORT | нет |  |

Ответ:

```
{
  "data": [
    {
      "id": 1,
      "campaignId": 1,
      "type": "string",
      "fileName": "string",
      "fileType": "string",
      "format": "string",
      "profileId": 1,
      "updateMode": "2026-01-01T00:00:00.000Z",
      "stockImportMode": "string",
      "defaultWarehouseId": 1,
      "status": "Active",
      "rowsTotal": 1,
      "rowsProcessed": 1,
      "productsCreated": 1,
      "productsUpdated": 1,
      "categoriesCreated": 1,
      "manufacturersCreated": 1,
      "currenciesCreated": 1,
      "stockBalancesUpdated": 1,
      "rowsSkipped": 1,
      "errorsCount": 1,
      "warningsCount": 1,
      "messages": {},
      "startedAtUtc": "2026-01-01T00:00:00.000Z",
      "finishedAtUtc": "2026-01-01T00:00:00.000Z",
      "resultBlob": "string",
      "undoData": {}
    }
  ]
}
```

### Создать задачу импорта

POST `imports/createJob`

Создать задачу импорта.

`POST https://app.inventorymod.com/api/rpc/imports/createJob?campaign_id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |

Тело запроса (JSON):

```
{
  "job": {
    "type": "string",
    "fileName": "string",
    "fileType": "string",
    "format": "string",
    "profileId": 1,
    "updateMode": "2026-01-01T00:00:00.000Z",
    "stockImportMode": "string",
    "defaultWarehouseId": 1,
    "status": "Active",
    "rowsTotal": 1,
    "rowsProcessed": 1,
    "productsCreated": 1,
    "productsUpdated": 1,
    "categoriesCreated": 1,
    "manufacturersCreated": 1,
    "currenciesCreated": 1,
    "stockBalancesUpdated": 1,
    "rowsSkipped": 1,
    "errorsCount": 1,
    "warningsCount": 1,
    "messages": {},
    "startedAtUtc": "2026-01-01T00:00:00.000Z",
    "finishedAtUtc": "2026-01-01T00:00:00.000Z",
    "resultBlob": "string",
    "undoData": {}
  }
}
```

Ответ:

```
{
  "data": 1
}
```

### Изменить задачу импорта

POST `imports/updateJob`

Обновить задачу импорта.

`POST https://app.inventorymod.com/api/rpc/imports/updateJob?campaign_id=1&id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| id | integer | **да** |  |

Тело запроса (JSON):

```
{
  "updates": {
    "type": "string",
    "fileName": "string",
    "fileType": "string",
    "format": "string",
    "profileId": 1,
    "updateMode": "2026-01-01T00:00:00.000Z",
    "stockImportMode": "string",
    "defaultWarehouseId": 1,
    "status": "Active",
    "rowsTotal": 1,
    "rowsProcessed": 1,
    "productsCreated": 1,
    "productsUpdated": 1,
    "categoriesCreated": 1,
    "manufacturersCreated": 1,
    "currenciesCreated": 1,
    "stockBalancesUpdated": 1,
    "rowsSkipped": 1,
    "errorsCount": 1,
    "warningsCount": 1,
    "messages": {},
    "startedAtUtc": "2026-01-01T00:00:00.000Z",
    "finishedAtUtc": "2026-01-01T00:00:00.000Z",
    "resultBlob": "string",
    "undoData": {}
  }
}
```

Ответ:

```
{
  "data": null
}
```

### Отменить импорт

POST `imports/undo`

Отменить импорт: удалить записи, СОЗДАННЫЕ этой задачей (из job.undoData). Возвращает число удалённых.

`POST https://app.inventorymod.com/api/rpc/imports/undo?campaign_id=1&jobId=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| jobId | integer | **да** |  |

Ответ:

```
{
  "data": {}
}
```

### Импорт товаров

POST `imports/execProducts`

Импорт товаров: все смапленные поля, числа через parseNumber, refs по имени (createMissing), тип и варианты.

`POST https://app.inventorymod.com/api/rpc/imports/execProducts?campaign_id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |

Тело запроса (JSON):

```
{
  "items": {},
  "opts": {}
}
```

Ответ:

```
{
  "data": {}
}
```

### Импорт остатков

POST `imports/execStock`

Импорт остатков/движений по SKU. Неизвестные SKU — в отчёт (или создаются при createMissingProducts).

`POST https://app.inventorymod.com/api/rpc/imports/execStock?campaign_id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |

Тело запроса (JSON):

```
{
  "items": {},
  "opts": {}
}
```

Ответ:

```
{
  "data": {}
}
```

### Импорт клиентов

POST `imports/execCustomers`

Импорт клиентов.

`POST https://app.inventorymod.com/api/rpc/imports/execCustomers?campaign_id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |

Тело запроса (JSON):

```
{
  "items": {},
  "opts": {}
}
```

Ответ:

```
{
  "data": {}
}
```

### Импорт поставщиков

POST `imports/execSuppliers`

Импорт поставщиков.

`POST https://app.inventorymod.com/api/rpc/imports/execSuppliers?campaign_id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |

Тело запроса (JSON):

```
{
  "items": {},
  "opts": {}
}
```

Ответ:

```
{
  "data": {}
}
```

### Импорт категорий

POST `imports/execCategories`

Импорт категорий (с поддержкой иерархии через ->).

`POST https://app.inventorymod.com/api/rpc/imports/execCategories?campaign_id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |

Тело запроса (JSON):

```
{
  "items": {},
  "opts": {}
}
```

Ответ:

```
{
  "data": {}
}
```

### Импорт производителей

POST `imports/execManufacturers`

Импорт производителей.

`POST https://app.inventorymod.com/api/rpc/imports/execManufacturers?campaign_id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |

Тело запроса (JSON):

```
{
  "items": {},
  "opts": {}
}
```

Ответ:

```
{
  "data": {}
}
```

### Импорт складов

POST `imports/execWarehouses`

Импорт складов.

`POST https://app.inventorymod.com/api/rpc/imports/execWarehouses?campaign_id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |

Тело запроса (JSON):

```
{
  "items": {},
  "opts": {}
}
```

Ответ:

```
{
  "data": {}
}
```

### Импорт валют

POST `imports/execCurrencies`

Импорт валют.

`POST https://app.inventorymod.com/api/rpc/imports/execCurrencies?campaign_id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |

Тело запроса (JSON):

```
{
  "items": {},
  "opts": {}
}
```

Ответ:

```
{
  "data": {}
}
```

### Импорт заказов

POST `imports/execOrders`

Импорт заказов покупателей (строки группируются по номеру заказа).

`POST https://app.inventorymod.com/api/rpc/imports/execOrders?campaign_id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |

Тело запроса (JSON):

```
{
  "items": {},
  "opts": {}
}
```

Ответ:

```
{
  "data": {}
}
```

### Импорт закупок

POST `imports/execPurchaseOrders`

Импорт заказов поставщикам / закупок (строки группируются по номеру).

`POST https://app.inventorymod.com/api/rpc/imports/execPurchaseOrders?campaign_id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |

Тело запроса (JSON):

```
{
  "items": {},
  "opts": {}
}
```

Ответ:

```
{
  "data": {}
}
```

### Импорт движений

POST `imports/execMovements`

Импорт движений: каждая строка — приход (qty>0) или списание (qty<0) по складу; если заполнен «На склад» — перемещение со склада строки на этот склад (qty>0, T5).

`POST https://app.inventorymod.com/api/rpc/imports/execMovements?campaign_id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |

Тело запроса (JSON):

```
{
  "items": {},
  "opts": {}
}
```

Ответ:

```
{
  "data": {}
}
```

### Импорт резервов

POST `imports/execReservations`

Импорт резервов: каждая строка — резерв товара на складе.

`POST https://app.inventorymod.com/api/rpc/imports/execReservations?campaign_id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |

Тело запроса (JSON):

```
{
  "items": {},
  "opts": {}
}
```

Ответ:

```
{
  "data": {}
}
```

### Импорт составов

POST `imports/execComponents`

Импорт составов: строки «SKU изделия, SKU компонента, кол-во»; режим «заменить состав», авто-смена типа на «Изделие».

`POST https://app.inventorymod.com/api/rpc/imports/execComponents?campaign_id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |

Тело запроса (JSON):

```
{
  "items": {},
  "opts": {}
}
```

Ответ:

```
{
  "data": {}
}
```

## Обслуживание

Очистка данных.

### Удалить все данные

POST `backup/wipeAll`

Полная очистка БД: удаляет ВСЕ данные (товары, склад, документы, справочники, история импорта). Настройки (settings) сохраняются, чтобы уцелели язык/форматы. Работает на всех адаптерах (Dexie/SQLite), т.к. использует тот же Drizzle-инстанс. Возвращает число очищенных таблиц.

`POST https://app.inventorymod.com/api/rpc/backup/wipeAll?campaign_id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |

Ответ:

```
{
  "data": {}
}
```

### Очистка осиротевших данных

POST `backup/cleanupOrphans`

Очистка «осиротевших» складских строк — тех, что ссылаются на уже удалённый товар (product_id, которого нет в products). Такие появляются, если товар удалили, а связанные строки остались. В списках они выглядят как «Unknown Product / SKU: N/A». Возвращает число удалённых строк по каждой таблице.

`POST https://app.inventorymod.com/api/rpc/backup/cleanupOrphans?campaign_id=1`

Запись: ключ со scope `write` в заголовке `Authorization: Bearer <key>`.

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |

Ответ:

```
{
  "data": {}
}
```

## Журнал действий

Кто, что и когда сделал.

### Журнал действий

GET `activity/list`

Действия, последние сверху. search — по описанию и имени метода.

`GET https://app.inventorymod.com/api/rpc/activity/list?campaign_id=1&limit=50&offset=0`

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| api_key | string | нет | для чтения из браузера; для записи — заголовок Authorization |
| limit | integer | **да** |  |
| offset | integer | **да** |  |
| module | string | нет |  |
| search | string | нет |  |

Ответ:

```
{
  "data": {}
}
```

### Сводка действий

GET `activity/summary`

userId — только действия этого пользователя (участник с ограничением по складам; ставит сервер).

`GET https://app.inventorymod.com/api/rpc/activity/summary?campaign_id=1`

| Параметр (query) | Тип | Обяз. |  |
| --- | --- | --- | --- |
| campaign_id | integer | **да** | кампания ключа |
| api_key | string | нет | для чтения из браузера; для записи — заголовок Authorization |

Ответ:

```
{
  "data": {}
}
```
