Проверка живости сервиса и соединения с PostgreSQL.
curl -s "$BASE/health"
# { "ok": true, "service": "sklad-f5-backend" }
REST API для товаров, остатков, резервов в сделках amoCRM, возвратов, аналитики и webhook-интеграций. Документация рассчитана на подключение сторонних сервисов (Make, n8n, МойСклад, собственные бэкенды).
Минимальный сценарий: проверить сервер → получить товары → зарезервировать в сделку.
BASE="https://sklad.metch21.ru"
# 1) Health
curl -s "$BASE/health"
# 2) Список товаров
curl -s "$BASE/api/products" | jq '.items[0]'
# 3) Резерв в сделку amoCRM (dealId = ID сделки)
curl -s -X POST "$BASE/api/deals/12345678/products" \
-H "Content-Type: application/json" \
-d '{"product_id":"UUID-ТОВАРА","quantity":2,"price":1500}'
const BASE = "https://sklad.metch21.ru";
const health = await fetch(`${BASE}/health`).then(r => r.json());
const products = await fetch(`${BASE}/api/products`).then(r => r.json());
const reserved = await fetch(`${BASE}/api/deals/12345678/products`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
product_id: products.items[0].id,
quantity: 2,
price: 1500
})
}).then(async r => {
const data = await r.json();
if (!r.ok) throw data;
return data;
});
import requests
BASE = "https://sklad.metch21.ru"
print(requests.get(f"{BASE}/health").json())
products = requests.get(f"{BASE}/api/products").json()["items"]
product_id = products[0]["id"]
r = requests.post(
f"{BASE}/api/deals/12345678/products",
json={"product_id": product_id, "quantity": 2, "price": 1500},
timeout=20,
)
print(r.status_code, r.json())
Content-Type: application/jsonamo_deal_id| Операция | Лимит |
|---|---|
| Импорт товаров | до 5000 строк за запрос |
| Массовое удаление | до 500 id |
| Движения склада | последние 200 |
| Активные заказы | limit 1–500 (по умолчанию 100) |
quantity — физический остаток на складе. reserved_quantity — сколько зарезервировано в сделках со статусом pending. available_quantity = quantity − reserved_quantity.
Резерв в сделке не уменьшает физический остаток. Физическое списание происходит при отгрузке (shipment) или списании (write_off).
| status | Смысл |
|---|---|
pending | Зарезервировано |
shipped | Отгружено (остаток уменьшен) |
written_off | Списано (брак/убыль) |
returned | Возврат |
cancelled | Отменено |
{
"id": "4cdf80d1-7d68-49a9-a90c-73a4923ad0ea",
"name": "Саморез 4.2×16",
"sku": "SR-4216",
"category": "Крепеж",
"unit": "шт.",
"quantity": 450,
"reserved_quantity": 40,
"available_quantity": 410,
"min_quantity": 50,
"purchase_price": 1.2,
"sale_price": 3.5,
"price": 3.5,
"stock_value": 1575,
"status": "ok",
"comment": null,
"created_at": "...",
"updated_at": "..."
}
Единый формат: { "error": { "message": "..." } }. Для коллизии резерва добавляются поля.
{
"error": {
"message": "Нельзя зарезервировать больше свободного остатка...",
"code": "INSUFFICIENT_STOCK",
"product_id": "uuid",
"product_name": "Кабель ВВГ 3×2.5",
"requested": 20,
"available": 10,
"free_stock": 10,
"stock": 50,
"reserved": 40,
"reserved_others": 40,
"unit": "шт."
}
}
Рекомендация интеграции: при code === "INSUFFICIENT_STOCK" предложить пользователю количество available и повторить запрос.
| HTTP | Когда |
|---|---|
| 400 | Невалидные параметры |
| 404 | Товар / позиция не найдены |
| 409 | Конфликт остатка / резерва / бизнес-правило |
| 500 | Внутренняя ошибка сервера |
Проверка живости сервиса и соединения с PostgreSQL.
curl -s "$BASE/health"
# { "ok": true, "service": "sklad-f5-backend" }
Мультисклад: один каталог товаров, остатки отдельно на каждом складе. Если warehouse_id не передан, API подставляет склад по умолчанию (Основной).
stocks[] (разбивка по складам) и суммарный quantity. В позициях сделки и движениях — warehouse_id, warehouse_name, warehouse_code.| Query | Описание | |
|---|---|---|
include_inactive=1 | opt | Включить отключённые склады |
curl -s "$BASE/api/warehouses"
# { items: [{ id, name, code, is_default, is_active, sort_order }] }
curl -s -X POST "$BASE/api/warehouses" \
-H "Content-Type: application/json" \
-d '{"name":"Склад СПб","code":"spb"}'
Переименовать, сменить code, is_active, назначить is_default: true.
Только если нет ненулевых остатков и pending-резервов. Иначе 409. Нельзя удалить единственный / основной без замены.
Каталог склада. При создании/изменении товар может синхронизироваться в каталог amoCRM.
| Query | Описание | |
|---|---|---|
search | opt | Поиск по названию / артикулу |
status | opt | all | ok | low | out |
category | opt | all или точное имя категории |
warehouse_id | opt | Остатки в разрезе склада; без параметра — сумма + stocks[] |
curl -s "$BASE/api/products?search=кабель&status=ok&warehouse_id=UUID"
Один товар. 404 если не найден.
| Body | Описание | |
|---|---|---|
name | req | Название |
sku | opt | Артикул |
category, unit, description, comment | opt | Метаданные; unit по умолчанию «шт.» |
quantity, min_quantity, warehouse_id | opt | Остаток на складе (по умолчанию — основной) |
purchase_price, sale_price / price | opt | Цены |
curl -s -X POST "$BASE/api/products" \
-H "Content-Type: application/json" \
-d '{
"name": "Кабель ВВГ 3×2.5",
"sku": "KB-001",
"category": "Электрика",
"unit": "м",
"quantity": 100,
"min_quantity": 20,
"purchase_price": 45,
"sale_price": 69
}'
Обновление. Нельзя поставить quantity ниже текущего резерва — ответ 409.
Удаляет товар, связанные движения и позиции сделок; пытается убрать из каталога amoCRM.
curl -s -X POST "$BASE/api/products/bulk-delete" \
-H "Content-Type: application/json" \
-d '{"ids":["uuid-1","uuid-2"]}'
Массовый импорт. До 5000 позиций. При update_quantity: true количество прибавляется к существующему артикулу.
curl -s -X POST "$BASE/api/products/import" \
-H "Content-Type: application/json" \
-d '{
"update_quantity": true,
"items": [
{"name":"Саморез","sku":"SR-1","quantity":100,"sale_price":3.5}
]
}'
Выгрузить весь каталог в список товаров amoCRM.
Шаблоны сопоставления колонок для Excel/CSV-импорта в UI.
Операции меняют физический остаток и пишут запись в журнал. При включённых исходящих webhook — отправляют события наружу.
productId (обяз.), quantity или targetQuantity, warehouseId / warehouse_id (опц., иначе склад по умолчанию), опционально comment, amoDealId, dealName, userId, unitPrice, purchasePrice, accountId.Поступление (+quantity). 201 → { product, movement }.
curl -s -X POST "$BASE/api/stock/incoming" \
-H "Content-Type: application/json" \
-d '{"productId":"UUID","warehouseId":"WAREHOUSE_UUID","quantity":50,"comment":"Приход от поставщика"}'
Списание (−quantity). 409 если не хватает физического остатка.
Отгрузка (−quantity). Аналогично write-off, тип движения shipment.
Инвентаризация: установить абсолютный targetQuantity.
curl -s -X POST "$BASE/api/stock/adjustment" \
-H "Content-Type: application/json" \
-d '{"productId":"UUID","warehouseId":"WAREHOUSE_UUID","targetQuantity":120,"comment":"Инвентаризация"}'
Последние 200 движений. Query: warehouse_id — фильтр по складу. В ответе есть поля склада.
:dealId — ID сделки (лида) в amoCRM.
curl -s "$BASE/api/deals/12345678/products"
# { items, total_amount, positions, total_quantity }
| Body | Описание | |
|---|---|---|
product_id | req | UUID товара |
quantity | req | > 0 |
price | opt | Цена в сделке |
warehouse_id | opt* | Склад резерва; без параметра — склад по умолчанию |
Создаёт/обновляет резерв pending на выбранном складе. Один товар можно резервировать с разных складов отдельными строками. При нехватке — 409 INSUFFICIENT_STOCK.
Пакетное добавление. Транзакция: при ошибке одной позиции откатывается весь пакет.
curl -s -X POST "$BASE/api/deals/12345678/products/batch" \
-H "Content-Type: application/json" \
-d '{
"warehouse_id": "WAREHOUSE_UUID",
"items": [
{"product_id":"uuid-1","quantity":2,"price":1500,"warehouse_id":"WAREHOUSE_UUID"},
{"product_id":"uuid-2","quantity":1,"price":990,"warehouse_id":"WAREHOUSE_UUID"}
]
}'
Изменить количество/цену/статус позиции. Для pending снова проверяется свободный остаток.
curl -s -X PUT "$BASE/api/deals/12345678/products/POSITION_UUID" \
-H "Content-Type: application/json" \
-d '{"quantity":3,"price":1400,"status":"pending"}'
Снять позицию (резерв). Ответ 204.
Возврат. Для shipped увеличивает остаток; для pending снимает резерв. Может создать примечание/задачу/смену этапа по настройкам.
curl -s -X POST "$BASE/api/deals/12345678/return" \
-H "Content-Type: application/json" \
-d '{
"deal_name": "Заказ Иванова",
"items": [{"deal_product_id":"POSITION_UUID","quantity":1}]
}'
| Query | Описание | |
|---|---|---|
scope | opt | current (есть pending) или completed |
limit | opt | 1–500, по умолчанию 100 |
warehouse_id | opt | Только заказы с позициями этого склада |
curl -s "$BASE/api/deals/orders/active?scope=current&limit=50&warehouse_id=UUID"
В заказе: имя сделки, контакт, компания, телефон/ИНН (если синхронизированы), позиции с остатками.
Хранит правила этапов, права, return_rules и widget_settings.webhook.endpoints[] (несколько исходящих URL со своими events/fields).
curl -s -X PUT "$BASE/api/settings" \
-H "Content-Type: application/json" \
-d '{
"account_id": 123,
"write_off_rules": [
{"pipelineId":"111","statusId":"222","action":"shipment"}
],
"widget_settings": {
"webhook": {
"enabled": true,
"endpoints": [{
"id": "ep1",
"name": "Make",
"url": "https://hook.example/in/xxx",
"enabled": true,
"events": ["write_off","shipment","low_stock"],
"fields": ["product","quantity","deal","stock"]
}]
}
}
}'
| Query | Описание |
|---|---|
period | today | week | month | quarter | custom |
date_from, date_to | Для custom |
refresh=1 + accountId | Подтянуть изменения сделок из amoCRM |
warehouse_id | Аналитика по одному складу |
curl -s "$BASE/api/analytics?period=month&warehouse_id=UUID"
Прокси к amoCRM с серверным токеном. Для UI склада / интеграций без собственного OAuth.
Установка: создать каталог товаров в amoCRM, поля, сиды. Используется виджетом при сохранении настроек.
Последние 50 событий установки.
URL для настройки в amoCRM Digital Pipeline / Webhooks. При смене этапа сделки применяет правила склада (отгрузка/списание pending-позиций).
# Укажите в amoCRM:
$BASE/api/webhooks/amocrm
# Ответ:
# { "ok": true, "processed": 1, "results": [ ... ] }
Настраиваются в UI «Исходящий Webhook»: у каждого адреса свои события и поля. Склад шлёт POST JSON, не дожидаясь ответа (таймаут ~8 сек).
incoming, write_off, shipment, adjustment, а также производные low_stock / out_of_stock после изменения остатка.
{
"event": "write_off",
"timestamp": "2026-08-17T10:15:00.000Z",
"endpoint": { "id": "ep1", "name": "Make" },
"product": {
"id": "uuid",
"name": "Кабель ВВГ 3×2.5",
"sku": "KB-001",
"category": "Электрика",
"unit": "м"
},
"quantity": { "delta": -5, "absolute": 5, "before": 100, "after": 95 },
"prices": {
"purchase_price": 45,
"sale_price": 69,
"unit_price": 69,
"total_amount": 345
},
"deal": { "id": 12345678, "name": "Заказ Иванова" },
"manager": { "user_id": null },
"stock": { "quantity": 95, "min_quantity": 20, "status": "ok" },
"warehouse": { "id": "uuid", "name": "Основной склад", "code": "main" }
}
warehouse (id, name, code).write_off / shipment и нужные поля.# Найти товар
curl -s "$BASE/api/products?search=ВВГ" | jq '.items[] | {id,name,available_quantity}'
# Зарезервировать
curl -s -X POST "$BASE/api/deals/$DEAL_ID/products" \
-H "Content-Type: application/json" \
-d '{"product_id":"UUID","quantity":10,"warehouse_id":"WAREHOUSE_UUID"}'
# Если 409 — взять error.available и повторить
Периодически вызывайте GET /api/products или слушайте исходящие webhook с полем stock.