Перейти до змісту

Ресурси

Усі шляхи відносні до https://crm.h-profit.com/bapi-v2. Кожен ендпоінт потребує заголовка Authorization та відповідного scope.

Багато списків підтримують ?include= для вбудовування пов'язаних об'єктів — див. таблицю зв'язків. Щоб отримати кілька ресурсів за раз, використайте composite /query.


Reference (довідник)

Усе, що потрібно інтегратору для побудови пов'язаних об'єктів (рахунки, валюти, категорії, бренди, склади, канали, служби доставки) одним викликом.

Метод Шлях Scope
GET /reference reference:read

?types=accounts,currencies,warehouses звужує відповідь до конкретних секцій: accounts, currencies, product_categories, expense_categories, brands, warehouses, sales_channels, carrier_accounts, write_off_categories.

carrier_accounts — підключені служби доставки акаунта; id — це account_id для пошуку в Delivery:

{ "id": 427, "carrier": "meest", "name": "Основний відправник" }

carrier — nova_poshta, ukrposhta або meest. Акаунт Нової Пошти також містить senders — відправників, для яких у HugeProfit налаштовані місто й відділення; id — це sender_id для створення ТТН:

{ "id": 431, "carrier": "nova_poshta", "name": "Магазин",
  "senders": [ { "id": "3f6c1a20-…_3f71b5c8-…", "name": "Іван Петренко",
                 "warehouse_name": "Київ, Відділення №1" } ] }

Warehouses (склади)

Метод Шлях Scope
GET /warehouses warehouses:read
{ "id": 8549, "name": "Основний", "type": "main", "position": 0,
  "created_at": "2024-11-24T15:59:10" }

type — одне з main, dropshipping, negative_stock.


Suppliers (постачальники)

Метод Шлях Scope
GET /suppliers suppliers:read
POST /suppliers suppliers:write

Тіло POST: { "name", "phone"?, "address"?, "comment"? } (створення постачальника з наявним іменем повертає існуючого). unpaid — заборгованість перед постачальником.

{ "id": 30, "name": "Acme Ltd", "phone": "380...", "address": "…",
  "comment": null, "unpaid": "69665.00", "created_at": "2023-09-11T10:45:23" }

Products (товари)

Метод Шлях Scope Примітки
GET /products products:read список
GET /products/{id} products:read один (завжди з images)
GET /products/{id}/similar products:read id схожих товарів
POST /products products:write масове створення, до 500
PUT /products products:write масова часткова зміна, до 500: { "products": [{ "id": 1, ... }] }
PUT /products/{id} products:write часткова зміна одного товару
DELETE /products products:write тіло { "ids": [...] }
DELETE /products/{id} products:write один
POST /product_stock products:write швидке оновлення залишку/ціни/собівартості

Фільтри GET: product_id, pids (csv), sku (csv), barcode (csv), category_id, warehouse_id, modify_start/modify_end (unix). ?count=1 повертає лише total. ?include=images вбудовує URL зображень. URL зображення закінчується на ?c=<версія>, яка змінюється, коли фото замінюють, — не відкидайте її, щоб кеш підхопив нове зображення.

Пошук за артикулом або штрихкодом: ?sku=TS-001 і ?barcode=4820000000017 знаходять товари за вашими кодами — точний збіг, без урахування регістру. До 500 значень через кому за раз (більше — 422), тож каталог можна зіставляти пачками. Разом одне з одним або з pids/product_id вони звужують результат; нічого не знайдено — порожній список. У /query: "filter": { "sku": ["TS-001", "TS-002"] }.

У stock по кожному складу: in_stock — скільки доступно для продажу (без зарезервованого), quantity — скільки є фізично.

Акції: stock[].promotion — акція, яку HugeProfit підставить у формі продажу для цього товару на цьому складі, або null: з найвищим пріоритетом серед акцій на основну ціну, а якщо таких немає — серед акцій на додаткові ціни. Акція на додаткову ціну рахується від її значення в товарі; якщо в товару цієї ціни немає, акції теж немає. Акція потрапляє сюди, лише якщо знижує ціну; зміни акцій видно із затримкою до хвилини. Щоб продаж зарахувався до акції, передайте її id у продаж — див. Продаж за акцією.

Поле Опис
id, name Акція
discount_type, discount_value percent — знижка у відсотках, fixed — на суму, new_price — нова ціна
price_type Від якої ціни акція: null — основна ціна, інакше назва додаткової ціни ("Опт")
price_before Ціна без акції: основна або ця додаткова ціна товару
price Ціна за акцією: її показуйте покупцеві й передавайте в продаж
ends_at Кінець акції (UTC); null — безстрокова

sale_price — ціна зі знижкою: окремо задана ціна продажу, а якщо її немає — ціна акції на основну ціну; null — знижки немає.

type — simple, variation (один рядок варіативного товару — групуйте їх за parent_id), bundle або service. У комплекту також є bundle_items — склад по складах: для комплекту на складі bundle_warehouse_id береться quantity товару product_id зі складу warehouse_id.

GET /products/{id}/similar повертає { "product_ids": [...] } — інші товари з тієї ж групи «схожих товарів», налаштованої в HugeProfit; порожній список, якщо товар не в групі.

Об'єкт товару
{
  "id": 111237, "name": "Футболка", "sku": "TS-001", "barcode": "7836...",
  "unit": "units", "type": "simple", "parent_id": null,
  "brand": null, "category_id": 18726,
  "category_path": [ { "id": 18726, "name": "Одяг" } ],
  "attributes": [], "dimensions": null, "min_stock": 0, "uktzed": null,
  "extra": {},
  "stock": [
    { "warehouse_id": 8645, "warehouse_name": "Основний", "in_stock": 4,
      "quantity": 4, "cost_price": "5.00", "price": "6.00", "sale_price": "5.40",
      "promotion": { "id": 12, "name": "Осінній розпродаж", "discount_type": "percent",
                     "discount_value": 10, "price_type": null, "price_before": "6.00", "price": "5.40",
                     "ends_at": "2026-10-31T21:59:59" },
      "sku": "TS-001" }
  ]
}

Створення товарів

POST /products з { "products": [...] } лише створює. Передайте name (щонайменше 2 символи) і за потреби sku, barcode, description, category (назва або шлях ["Одяг", "Футболки"]), brand, unit, images (URL), extra (див. нижче) і stock — [{ "warehouse_id", "quantity", "price", "cost_price" }]. Без stock або без warehouse_id у рядку товар створюється на першому складі зі списку GET /warehouses.

  • id у тілі — 422: наявний товар змінюють через PUT.
  • Товар із таким самим sku або barcode уже є (без sku — також із такою самою назвою name) — 409 з details.product_id. Той самий товар двічі в одному запиті — 422.
  • Помилка вказує товар за його позицією: details.index. Тоді не створюється нічого.

Відповідь — 201 зі створеними товарами. Якщо частину зберегти не вдалося, meta.failed — їхня кількість.

Зміна товарів

PUT /products/{id} (або PUT /products з { "products": [{ "id": 1, ... }] }) змінює лише передані поля, решта лишається як є. Змінити тільки опис:

{ "description": "Новий опис" }
Поле Опис
name Щонайменше 2 символи
description, unit Як в об'єкті товару
category Назва або шлях ["Одяг", "Футболки"]; відсутні категорії створюються
brand Назва бренду; null або "" прибирає бренд
barcode До 30 символів; якщо він в іншого товару — 409 з details.product_id
extra Додаткові поля, зливаються з полями товару, див. нижче
stock [{ "warehouse_id", "quantity", "price", "cost_price", "comment" }] — не передане значення не змінюється

extra — { "Матеріал": "бавовна" }: ключ збігається з наявним додатковим полем без урахування регістру, новий ключ додає поле, null або "" видаляє його; не передані поля лишаються. Додаткові ціни — в extra.custom_price: { "custom_price": { "Опт": 120 } }, лише числа.

stock — { "warehouse_id": 8645, "price": 199 } змінює там лише ціну: кількість і собівартість лишаються. comment — примітка для історії залишків. Склад, де товару ще немає, додається. cost_price має бути більше 0, і його не можна задати, поки HugeProfit рахує собівартість із поставок або складових собівартості (422).

Для варіації name, description, category і brand змінюються для всієї групи; ключ extra, що збігається з назвою атрибута варіації, — 422. sku, attributes і images через PUT не змінюються: вони й будь-які інші поля — 422.

Запит перевіряється повністю до збереження: за будь-якої помилки не зберігається нічого, а details.id вказує товар. Невідомий або видалений id — 404 з details.ids. Відповідь — змінені товари.

Швидке оновлення залишків

POST /product_stock приймає масив { product_id, warehouse_id, quantity?, price?, cost_price?, comment? } — ті самі значення, що й stock у PUT, і не передане значення теж не змінюється: [{ "product_id": 111237, "warehouse_id": 8645, "price": 199 }] змінює лише ціну. Кожен елемент застосовується окремо; відповідь — { "updated": [id], "errors": [{ "product_id", "error" }] }.


Зв'язок товар ↔ маркетплейс

Прив'язує локальний product_id до remote_id маркетплейса в межах інтеграції, щоб синк залишків/цін знав, якому лістингу відповідає товар.

Метод Шлях Scope
GET /product_sync product_sync:read
POST /product_sync product_sync:write
DELETE /product_sync product_sync:write

Фільтри GET: integration_id, product_id (кожен застосовується лише якщо переданий). Тіло POST/DELETE: { "bulk": [ ... ] } — елементи POST потребують product_id+remote_id+integration_id; DELETE — integration_id+remote_id. POST повертає результати по елементах та статистику.

{ "product_id": 111230, "integration_id": 359, "remote_id": "SKU-1",
  "created_at": "2026-07-08T08:20:10" }

Categories (категорії)

Метод Шлях Scope
GET /categories categories:read
GET /categories/{id} categories:read
POST /categories categories:write
DELETE /categories categories:write
DELETE /categories/{id} categories:write

?category_id= фільтрує до однієї; ?tree=1 вкладає дочірні. Тіло POST: { "categories": [ { "name", "parent_id"?, "id"? } ] } (id оновлює замість створення). Назва унікальна серед дочірніх однієї категорії без урахування регістру («Худі» може бути і в «Чоловіче», і в «Жіноче»): створення вже зайнятої назви повертає id наявної, а оновлення в зайняту назву чи у власну підкатегорію — 400.

{ "id": 17497, "name": "Іграшки", "parent_id": null, "created_at": "2024-09-02T06:42:20" }

Clients (клієнти)

Метод Шлях Scope
GET /clients clients:read
GET /clients/{id} clients:read
POST /clients clients:write
DELETE /clients clients:write
DELETE /clients/{id} clients:write
GET/POST/PUT/DELETE /clients/tags clients:read/clients:write

Фільтри GET: id, ids (csv), phone, phone_contains, updated_at (unix — клієнти, змінені після цього), with_debt=1 (вбудовує заборгованість), with_chats=telegram (вбудовує чати в месенджерах). Тіло POST: { "clients": [ ... ] } (для створення потрібен телефон; id оновлює). Теги повертаються вкладеним масивом tags.

Пошук за телефоном: phone знаходить клієнтів за повним номером у будь-якому записі — 380671234567, +380671234567, 0671234567 і +38 (067) 123-45-67 знаходять того самого клієнта. phone_contains знаходить клієнтів, у номері яких є вказані цифри (останні цифри або будь-яка інша частина, напр. ?phone_contains=4567); потрібно щонайменше 3 цифри, менше — 422. Фільтри можна поєднувати. Один номер може бути в кількох клієнтів, тому результат — завжди список. У /query передавайте "filter": { "phone": "0671234567" } або "filter": { "phone_contains": "4567" }.

Чати в месенджерах (GET /clients і GET /clients/{id}): with_chats приймає типи месенджерів через кому — поки що підтримується лише telegram, будь-що інше дає 422. Тоді кожен клієнт отримує масив chats (порожній, якщо чатів немає), від нових до старих: type, integration_id (підключений бот, якому належить чат) і chat_id (рядок; для Telegram — chat_id для Bot API). Бот може написати клієнту, лише якщо той уже писав саме цьому боту, тому використовуйте chat_id разом з його integration_id. У /query передавайте "filter": { "with_chats": "telegram" } (або ["telegram"]).

Об'єкт клієнта
{
  "id": 105424, "is_active": true, "type": "person",
  "first_name": "Світлана", "last_name": "П.", "phone": "380...",
  "email": null, "company": null, "company_code": null,
  "discount": 0, "balance": "0.00", "total_amount": "0.00",
  "birth_day": "1995-12-16T00:00:00", "city": null, "region": null,
  "extra": {}, "tags": [ { "id": 5, "name": "VIP", "color": "#ff0000" } ],
  "chats": [ { "type": "telegram", "integration_id": 512, "chat_id": "123456789" } ],
  "created_at": "2023-06-17T05:59:19", "updated_at": "2026-04-23T10:21:58"
}

Теги (/clients/tags): GET — список тегів (або теги клієнта з ?client_id=); POST { name, color } створює; PUT прив'язує { client_id, tag_id } або редагує { tag_id, name?, color? }; DELETE відв'язує { client_id, tag_id } або видаляє { tag_id }.


Sales (продажі)

Продажа — це один об'єкт із вкладеним масивом items. Записи проходять через справжній рушій продажів (залишки, гроші, фіскальні чеки) — чек і новий баланс повертаються в meta.

Метод Шлях Scope
GET /sales sales:read
GET /sales/{id} sales:read
POST /sales sales:write
PUT /sales sales:write
DELETE /sales або /sales/{id} sales:write
POST /sales/{id}/waybill delivery:write + sales:write
GET /sales/{id}/waybill/label delivery:read + sales:read

Фільтри GET: date_from/date_to (unix; дефолт — останні 90 днів), client_id, warehouse_id, status, refund=1 (лише повернення), external_id (ваш номер замовлення, див. нижче), updated_from/updated_to (unix, за останньою зміною), after_id (разом з updated_from, див. нижче). ?include=client вбудовує клієнта.

Опитування змін: ?updated_from=<unix> повертає продажі, змінені відтоді, — нові, відредаговані й ті, у яких змінився статус (зокрема від відстеження доставки), від найстарішої зміни. Запам'ятайте updated_at (як unix) і id останнього отриманого продажу й передайте їх наступного разу як updated_from і after_id: список продовжиться одразу після цього продажу, навіть якщо багато продажів змінилися в ту саму секунду. Без after_id межа включна. З updated_from або external_id і без date_from вікно 90 днів не діє. Видалений продаж зі списку зникає — для таких є вебхук order_delete.

Об'єкт продажі
{
  "id": 4605, "external_id": "site-1001", "order_number": "20072602", "status": "saled",
  "sold_at": "2026-07-20T08:28:59", "amount": "100.00", "sale_amount": "100.00",
  "prepaid": "20.00", "discount": "0.00", "currency": "2", "fee": "5.00",
  "account_id": 2, "client_id": null, "channel_ids": [867],
  "expense_ids": [],
  "items": [
    { "id": 5436, "product_id": 107720, "warehouse_id": 8562,
      "sku": null, "barcode": "7739...", "product_name": "Товар",
      "quantity": 1, "price": "100.00", "cost_price": "1.00",
      "amount": "100.00", "profit": "94.00", "is_refund": false }
  ]
}

Тіло створення/оновлення містить заголовок продажу та масив items. Обов'язкові: account_id (рахунок, на який надходять гроші) і для кожної позиції product_id, warehouse_id, quantity (> 0), price. meta відповіді може містити фіскальний чек і новий баланс рахунку.

{ "external_id": "site-1001", "account_id": 2, "channel_ids": [867], "discount": 10,
  "items": [{ "product_id": 107720, "warehouse_id": 8562, "quantity": 2, "price": "100.00" }] }

PUT приймає те саме тіло плюс id продажу й замінює продаж повністю, як збереження форми продажу: непередане поле скидається (без status продаж стає saled). Лишаються тільки external_id, order_number і витрати, що вже є в продажу.

Усі поля запиту
Поле Тип Обов'язкове Опис
id int лише PUT Продаж, що редагується (приймається й oid)
items array так Позиції продажу, див. items[] нижче
account_id int так Рахунок, на який надходять гроші (accounts у довіднику); валюта продажу — валюта цього рахунку. На нього надходить sale_amount мінус передоплата й комісія рахунку, незалежно від статусу
external_id string ні Ваш номер замовлення, до 100 символів; лише POST — див. Безпечний повтор запиту
status string ні Статус продажу, до 20 символів, див. статуси нижче. Дефолт — saled
custom_status string ні Лише з status: "delivery": продаж отримує цей статус замість delivery
client_id int ні Клієнт (Clients). Не передано — продаж без клієнта
channel_ids int[] ні Канали продажу (sales_channels у довіднику); приймається й один channel_id: 867. Не передано — технічний канал 1
amount number ні Сума quantity × price позицій; рахується сама, див. нижче
discount number ні Знижка на продаж. Дефолт 0
discount_type string ні % (дефолт) — відсоток, stable — фіксована сума у валюті продажу
sale_amount number ні Скільки сплачує клієнт. Дефолт — amount мінус знижка
prepaid number ні Передоплата. Дефолт 0
prepaid_account_id int з prepaid Рахунок, на який надходить передоплата. Без нього передоплата лише записується в продаж, а весь sale_amount іде на account_id
fee number ні Комісія маркетплейсу / каналу за цей продаж у валюті продажу; зменшує прибуток. З налаштувань каналу не рахується — передавайте суму самі
cash_from_client number ні Готівка, отримана від покупця; з неї рахується решта у фіскальному чеку
comment string ні Коментар, до 300 символів
extra object ні Додаткові поля продажу: { "Джерело": "Instagram" }. Ключі — назви додаткових полів продажу з ваших налаштувань
delivery object ні Адреса Нової Пошти клієнта — відділення або адреса для кур'єра, див. поля доставки нижче. Потрібен client_id
client_delivery_id int ні Уже збережена адреса клієнта замість delivery; невідомий id ігнорується
delivery_info string ні Служба доставки, до 200 символів: 1 — Нова Пошта, 3 — Укрпошта, 6 — Rozetka Delivery, 24 — Meest, justin, delivery_auto, pickup, city-delivery, outcity-delivery, international; поза Україною — UPS, DHL, post office, other. Після двокрапки можна додати примітку: "1: Відділення 5"
tracking_number string ні ТТН, створена поза HugeProfit, до 20 символів. Прив'язується до перевізника, лише якщо в delivery_info тільки код ("1"), без примітки
expenses array ні Витрати, що створюються разом із продажем, див. expenses[] нижче

items[]

Поле Тип Обов'язкове Опис
product_id int так Товар
warehouse_id int так Склад, з якого списується товар (warehouses у довіднику). Один товар із двох складів — дві позиції
quantity number так Кількість, більше 0, можна дробову (0.5)
price number так, крім позиції з promotion_id Ціна за одиницю, за якою продається позиція
promotion_id int ні Акція, за якою продається позиція, див. Продаж за акцією
discount number ні Знижка позиції у відсотках, для відображення: позиція все одно продається за price. Знижка продажу % на таку позицію не діє
default_price number ні Ціна за одиницю до знижки позиції, показується поруч із discount. Дефолт — price
price_type string ні Підпис ціни, наприклад назва додаткової ціни ("Опт"), до 255 символів. З promotion_id за замовчуванням — назва акції
uktzed string ні Код УКТ ЗЕД товару для фіскального чека

expenses[] — кожна витрата зменшує прибуток продажу. У PUT передавайте лише нові витрати.

Поле Тип Обов'язкове Опис
amount number так Сума витрати
category int ні Категорія витрат (expense_categories у довіднику)
account_id int ні Рахунок, з якого сплачується витрата. Дефолт — account_id продажу
expense_name string ні Назва витрати
mid int ні Склад, до якого належить витрата

Статуси

status Що відбувається
saled (дефолт) Завершений продаж: товар списується зі складу
reserve_sale Резерв: in_stock зменшується, quantity лишається. Залишок не перевіряється
delivery Продаж у доставці; з custom_status продаж отримує цей статус
будь-який інший рядок Зберігається як є: delivering, cod, arrived або ваш власний статус

Суми рахуються так само, як у формі продажу HugeProfit:

Поле Опис
amount Сума продажу — сума quantity × price позицій. Не передано — порахуємо самі; якщо передаєте, вона має збігатися з цією сумою до копійки, інакше 422 з правильною сумою в details.expected
discount, discount_type Знижка на продаж: % (дефолт) або stable — фіксована сума. Позиції зі своєю discount до бази % не входять
sale_amount Скільки сплачує клієнт. Дефолт — amount мінус знижка

Гроші й кількості можна передавати числом або рядком ("100.50", "100,50"); будь-що інше, від'ємне число, nan чи inf — 422.

Якщо позиції не вистачає на складі, де не дозволено мінус, продаж не створюється, а відповідь — 422 з тим, чого бракує, як і для запуску рецепта:

{ "success": false, "error": { "code": "validation_error", "message": "not enough stock",
  "details": { "shortages": [ { "product_id": 107720, "warehouse_id": 8562,
                                "required": 2, "in_stock": 1 } ] } } }

Після редагування продаж отримує новий id

PUT /sales (і редагування продажу в HugeProfit) створює його заново під новим id; відповідь містить новий, а GET /sales/{старий id} відтоді — 404. Знаходьте продаж за external_id або беріть новий id з вебхука order_update — у ньому є previous_id.

items[].warehouse_id — склад, з якого списується товар. Поля доставки (усі необов'язкові):

Поле Опис
status Статус продажу: saled (дефолт), reserve_sale, delivery, delivering, cod, arrived або ваш власний статус
delivery Адреса Нової Пошти клієнта — відділення { "carrier": "nova_poshta", "warehouse_ref": "…" } (ref з /delivery/warehouses) або адреса для кур'єра { "carrier": "nova_poshta", "settlement_ref": "…", "street": "Київська", "house": "14", "flat": "285" } (flat необов'язковий; settlement_ref і street — з /delivery/settlements і /delivery/streets). Потрібен client_id; адреса зберігається клієнту (наявна використовується повторно), а продаж отримує delivery_info: "1"
client_delivery_id Уже збережена адреса клієнта замість delivery
delivery_info Код служби доставки: 1 — Нова Пошта, 3 — Укрпошта, 24 — Meest, …
tracking_number ТТН, створена поза HugeProfit. Поки продаж у статусі доставки (delivery, delivering, arrived), статус посилки відстежується автоматично й переводить продаж далі

Продаж за акцією

Щоб продаж зарахувався до акції — її лімітів, бюджету й статистики в HugeProfit, — передайте в позиції promotion_id зі stock[].promotion.id товару або іншої акції, що діє на цей товар на цьому складі (як у списку цін форми продажу). Ціну рахує HugeProfit — від основної ціни або, для акції на додаткову ціну, від неї:

  • price можна не передавати — підставимо promotion.price;
  • переданий price має збігатися з ціною акції до копійки, інакше 422 promotion_price_mismatch з правильною ціною в details.expected;
  • amount (якщо передаєте) рахуйте з ціни акції.
{ "account_id": 2, "items": [{ "product_id": 107720, "warehouse_id": 8562, "quantity": 2, "promotion_id": 12 }] }

Без promotion_id позиція продається за price, і акція її не враховує, навіть якщо ціна збігається з акційною. Відмова — 422 з одним із кодів нижче; якщо помилку можна прив'язати до позиції, в details є її product_id, warehouse_id і promotion_id.

code Причина Що робити
promotion_inactive Акцію вимкнули, вона закінчилась або ще не почалась Перечитайте товар і покажіть покупцеві актуальну ціну
promotion_not_applicable Акція не діє на цей товар на цьому складі, у товару немає її додаткової ціни або вона не знижує ціну Те саме
promotion_price_mismatch price не збігається з ціною акції Візьміть ціну з details.expected
promotion_limit_usage Вичерпано ліміт використань акції Продайте без акції
promotion_limit_client Клієнт client_id вичерпав свій ліміт за акцією Продайте без акції

PUT /sales перевіряє акцію так само: якщо вона вже закінчилась, передайте позицію без promotion_id — ціна збережеться, але продаж вийде з обліку акції.

Безпечний повтор запиту (external_id)

Якщо відповідь на POST /sales загубилася (таймаут, обрив з'єднання), невідомо, чи створився продаж, а повтор наосліп створить другий продаж і вдруге спише залишок. Передавайте свій номер замовлення в external_id (рядок до 100 символів):

  • перший запит створює продаж, і external_id повертається в ньому;
  • повтор із тим самим external_id нічого не створює й відповідає 409 з продажем, який уже є, — заберіть його через GET /sales/{details.sale_id} і працюйте далі:
{ "success": false, "error": { "code": "conflict", "message": "a sale with this external_id already exists",
  "details": { "sale_id": 4605, "external_id": "site-1001" } } }

external_id унікальний у межах API-ключа: два ключі (наприклад, два сайти) можуть кожен надіслати свій 1001. Він лишається за продажем після редагування, приходить в об'єкті продажу й у вебхуках order_*, а GET /sales?external_id=site-1001 знаходить продаж за ним. Після видалення продажу номер знову вільний. Задається лише під час створення — PUT його не змінює.

ТТН Нової Пошти

POST /sales/{id}/waybill створює ТТН Нової Пошти для продажу через підключений акаунт Нової Пошти — те саме, що Створити ТТН у картці продажу. Номер ТТН зберігається в продажі, продаж переходить у delivering, а далі HugeProfit відстежує статус посилки. Продажу потрібні client_id (з ім'ям, прізвищем і телефоном) та адреса Нової Пошти (delivery під час створення продажу): відділення або адреса для кур'єра — тоді посилка йде з відділення відправника до дверей отримувача.

Поле Обов'язкове Опис
carrier_account_id так Акаунт Нової Пошти: id з carrier_accounts у Reference
sender_id якщо в акаунта кілька відправників senders[].id цього акаунта
cod_amount ні Накладений платіж, який сплачує отримувач. Не передано — без накладеного платежу
cod_type ні cash_on_delivery (дефолт) або payment_control («Контроль оплати» Нової Пошти, потрібен договір з Новою Поштою)
declared_value ні Оголошена вартість. Дефолт — sale_amount
payer ні Хто платить за доставку: recipient (дефолт) або sender
payment_method ні cash (дефолт) або non_cash
weight, length, width, height ні Вага посилки в кг і розміри в см. Дефолт — 1 кг, 10×10×10
seats ні Кілька місць: [{ "weight", "length", "width", "height" }, …] замість полів вище
description ні Опис відправлення, до 120 символів. Дефолт — назви товарів продажу
additional_info ні Додаткова інформація, до 100 символів
{ "carrier_account_id": 431, "cod_amount": "185.00", "weight": 0.5 }

Відповідь 201:

{ "tracking_number": "20450000000001", "sale_id": 4605, "carrier": "nova_poshta",
  "carrier_account_id": 431, "sender_id": "3f6c1a20-…_3f71b5c8-…",
  "cost": "70", "estimated_delivery_date": "2026-10-18" }

cost — вартість доставки, розрахована Новою Поштою. Помилки:

  • 404 — продажу немає або це чек повернення.
  • 409 — у продажу вже є ТТН, він повернений або ТТН для нього саме створюється: повтор запиту не створить другу ТТН.
  • 422 validation_error — у продажу немає клієнта чи адреси Нової Пошти, акаунт або відправника не знайдено чи не налаштовано, некоректне поле.
  • 422 carrier_error — Нова Пошта відхилила ТТН; у message її текст.
  • 500 з details.tracking_number — ТТН створено в Новій Пошті, але не збережено в продажу. Не повторюйте запит (це створить ще одну ТТН) — зверніться до підтримки.

Наклейка ТТН

GET /sales/{id}/waybill/label повертає наклейку ТТН продажу (маркування 100×100) у PDF (Content-Type: application/pdf) — те саме, що Друк у HugeProfit. Працює для будь-якої ТТН продажу, створеної через підключений акаунт доставки, а не лише через API. Помилки — у звичайному JSON-конверті:

  • 404 — продажу немає або в нього немає ТТН;
  • 422 carrier_error — служба доставки не віддала наклейку: ТТН не з підключеного акаунта.
curl https://crm.h-profit.com/bapi-v2/sales/4605/waybill/label \
  -H "Authorization: hp_live_your_key_here" -o label.pdf

Orders (замовлення з маркетплейсів)

Вхідні замовлення із зовнішніх каналів (remote_orders) до перетворення на продаж. sale_id з'являється після створення продажі (is_linked).

Метод Шлях Scope
GET /orders orders:read
GET /orders/{id} orders:read
POST /orders orders:write
PUT /orders/{id} orders:write

Фільтри GET: order_id, integration_id, status, local_status, start/end (unix), linked (unlinked [дефолт] | linked | all). ?include=client,sale вбудовує клієнта і, якщо пов'язана, продаж. POST приймає зовнішнє замовлення і резервує залишки.

GET /orders/{id} також повертає tracking_number і sale_status — номер ТТН і статус продажу, на який перетворилося замовлення (null, поки воно не пов'язане).

PUT /orders/{id} змінює замовлення, створене цим самим ключем (будь-яке інше — 404):

  • { "status": "cancelled" } скасовує його і знімає резерв залишків. Повторний виклик безпечний — залишок двічі не повертається. Продаж, уже створений із замовлення, не змінюється.
  • { "order_text": "1024" } задає номер замовлення, який показується в списку замовлень HugeProfit (зберігається в info.order_text).

Обидва повертають оновлене замовлення; інший status або порожнє тіло — 422.


Delivery (доставка)

Пошук міст і відділень Нової Пошти та Meest — наприклад, щоб вибрати місто й відділення для адреси доставки, — а також населених пунктів і вулиць Нової Пошти для адреси кур'єра.

Метод Шлях Scope
GET /delivery/cities delivery:read
GET /delivery/warehouses delivery:read
GET /delivery/settlements delivery:read
GET /delivery/streets delivery:read
  • GET /delivery/cities?carrier=nova_poshta&q=Київ — до 20 міст { ref, name }, у назві яких є q; без q — перші 20 за абеткою. В однойменних населених пунктів область або район уже є в назві: Олександрівка (Миколаївська обл.).
  • GET /delivery/warehouses?carrier=nova_poshta&city_ref=… — відділення міста { ref, number, name } за зростанням номера: number=5 знаходить саме №5 (а не №50), q фільтрує за частиною назви, limit — до 500 (дефолт 50).
  • Meest — ті самі два виклики з carrier=meest і account_id підключеного акаунта Meest (з carrier_accounts у Reference); пошук іде наживо через цей акаунт. GET /delivery/cities?carrier=meest&account_id=…&q=Київ повертає { ref, name }, GET /delivery/warehouses?carrier=meest&account_id=…&city_ref=… — { ref, number: null, name }. account_id, що не є вашим акаунтом Meest, дає порожній список.

Невідомий carrier, відсутній city_ref або account_id для Meest — 422.

{ "ref": "8d5a980d-391c-11dd-90d9-001a92567626", "name": "Київ" }
{ "ref": "1ec09d88-e1c2-11e3-8c4a-0050568002cf", "number": 1,
  "name": "Відділення №1: вул. Пирогівський шлях, 135" }

Адреса для кур'єра (лише Нова Пошта, пошук наживо в Новій Пошті, limit — до 500, дефолт 20):

  • GET /delivery/settlements?carrier=nova_poshta&q=Кременчук — населені пункти { ref, name, city_ref, address_delivery }, зокрема села без відділення. ref передається в delivery.settlement_ref продажу; address_delivery: false — Нова Пошта не доставляє туди на адресу; city_ref підходить для /delivery/warehouses.
  • GET /delivery/streets?carrier=nova_poshta&settlement_ref=…&q=Київ — вулиці { name, street }: у delivery.street передайте street, name (з типом вулиці) — для показу.

Відсутній q чи settlement_ref або невідомий settlement_ref — 422; Нова Пошта не відповіла — 502 carrier_error, повторіть пізніше.

{ "ref": "e71a26ea-4b33-11e4-ab6d-005056801329", "name": "м. Кременчук, Кременчуцький р-н, Полтавська обл.",
  "city_ref": "8d5a9813-391c-11dd-90d9-001a92567626", "address_delivery": true }
{ "name": "вул. Київська", "street": "Київська" }

Media (хостинг зображень)

Завантажуйте зображення в HugeProfit і отримуйте публічні посилання, щоб використовувати їх будь-де.

Метод Шлях Scope
GET /media media:read
POST /media media:write

POST /media приймає multipart/form-data, до 5 файлів; лише PNG, JPEG, WebP і GIF (тип перевіряється за вмістом, а не за розширенням; SVG не приймається). Повертає 201 і масив завантажених файлів. Інший тип файлу, понад 5 файлів або жодного — 422. Файли враховуються в ліміті сховища акаунта.

GET /media повертає файли акаунта (зокрема завантажені в HugeProfit), від найновіших; limit/offset, загальна кількість — у meta.total.

{ "id": 5121, "url": "https://…/bfiles/public/…", "name": "banner", "ext": "png",
  "size": 48213, "created_at": "2026-10-06 12:00:00" }
Об'єкт замовлення
{
  "id": 3510949, "order_id": "9231", "integration_id": 621,
  "status": "completed", "name": "Order ID: 9231", "price": "3101.00",
  "currency": "UAH", "created_at": "2026-07-07T10:19:55",
  "first_name": "Ліна", "phone": "+380...", "email": "...",
  "address": { "city": "…", "address_1": "…" },
  "items": [ { "id": 7759, "name": "…", "quantity": 1, "price": 518 } ],
  "client_id": 36247, "sale_id": null, "is_linked": false
}

Shipments (поставки)

Лише читання. Поставка/приймання — один об'єкт із вкладеним масивом items.

Метод Шлях Scope
GET /shipments shipments:read
GET /shipments/{id} shipments:read

Фільтри GET: date_from/date_to (unix, за датою доставки), status, warehouse_id. ?include=supplier,expenses вбудовує постачальника та пов'язані витрати.

payments — усі оплати за товар, від найстарішої, зокрема відкладені та часткові (оплата доставки сюди не входить, див. delivery_expense_id). expense_id веде до Expenses; усі вони також доступні через GET /expenses?shipment_id=….

Об'єкт поставки
{
  "id": 11191, "name": "Поставка №…", "status": "complete",
  "warehouse_id": 8549, "warehouse_name": "Основний", "supplier_id": null,
  "amount": "100.00", "quantity": 10, "currency": "2",
  "delivered_date": "2026-05-21T07:42:00", "expense_id": 1115,
  "payments": [
    { "expense_id": 1115, "date": "2026-06-16T09:10:00", "amount": "100.00",
      "account_id": 14459, "category_id": 11, "name": "Оплата поставки" }
  ],
  "items": [
    { "id": 5020, "product_id": 104641, "product_name": "…", "sku": "c2b-6i3",
      "quantity_shipped": 10, "quantity_received": 10, "amount": "100.00",
      "cost_price": "10.00" }
  ]
}

Expenses (витрати)

Метод Шлях Scope
GET /expenses expenses:read
GET /expenses/{id} expenses:read
POST /expenses expenses:write

Фільтри GET: date_from/date_to (unix; дефолт — останній 31 день), category_id, shipment_id (оплати поставки; без дат — усі, незалежно від дати). ?include=warehouse вбудовує склад. Тіло POST: { account_id, category_id, amount, name, comment?, warehouse_id?, product_id?, shipment_id?, order_id?, type?, date? }; новий баланс рахунку повертається в meta.

{ "id": 1115, "account_id": 14459, "category_id": 2, "category_name": "Доставка",
  "warehouse_id": 8549, "name": "…", "amount": "30.00", "balance": "116721.08",
  "type": "onetime", "created_at": "2026-06-29T08:32:56" }

Productions (виробництво)

Виробництво — це рецепт: components (спожиті товари) + expenses → results (вироблені товари).

Метод Шлях Scope
GET /productions productions:read
GET /productions/{id} productions:read
POST / PUT /productions productions:write
DELETE /productions або /productions/{id} productions:write
POST /productions/{id}/run productions:write
GET /productions/history productions:read
GET/POST/DELETE /productions/categories productions:read/productions:write

Фільтри GET: start/end (unix), category_id, is_deleted. ?include=category вбудовує категорію.

Об'єкт виробництва
{
  "id": 49, "name": "Рецепт 787", "status": "created", "category_id": null,
  "amount": "0.00", "quantity": 50, "result_product_id": 107511,
  "result_warehouse_id": 8614, "use_real_cost": true,
  "components": [ { "id": 115, "product_id": 107513, "warehouse_id": 8614,
                    "name": "Сировина", "quantity": 1, "cost": "0.00" } ],
  "results": [], "expenses": []
}

Запуск рецепта

POST /productions/{id}/run з { "quantity": 2.5, "account_id": 14459 } запускає рецепт quantity разів — так само, як кнопка Виробити в HugeProfit: списує компоненти зі складів, оприбутковує результати й записує витрати рецепта.

  • quantity — скільки разів запустити рецепт (можна дробове, > 0).
  • account_id — рахунок, з якого сплачуються витрати рецепта (з accounts у Reference); потрібен, лише якщо в рецепті є витрати.

Повертає 201 із запуском (той самий об'єкт, що в /productions/history):

{ "id": 812, "production_id": 49, "product_id": 107511, "warehouse_id": 8614, "category_id": null,
  "quantity": 2.5, "net_price": "20.00", "products_prices": "50.00", "expenses_amount": "0.00",
  "total": "50.00", "type": "production", "created_at": "2026-10-06T12:00:00" }

Якщо компонента не вистачає на складі, де мінус заборонений, нічого не запускається, а відповідь — 422 з тим, чого бракує:

{ "success": false, "error": { "code": "validation_error", "message": "not enough stock",
  "details": { "shortages": [ { "product_id": 107513, "warehouse_id": 8614,
                                "required": 120, "in_stock": 100 } ] } } }

Інші 422: quantity відсутня або не додатна, немає account_id для рецепта з витратами, рецепт без результатів. Невідомий або видалений рецепт — 404.


Write-offs (списання)

Списання товару зі складу (брак, втрати, власні потреби) — як документ Списання в HugeProfit, створюється й проводиться одним викликом.

Метод Шлях Scope
POST /write_offs write_offs:write
{ "warehouse_id": 8614, "category_id": 2, "comment": "Пошкоджено при доставці",
  "items": [ { "product_id": 107513, "quantity": 3 } ] }

category_id — категорія списання (write_off_categories у Reference); кожен товар — один раз. Сума рахується за собівартістю на складі. Повертає 201:

{ "id": 77, "warehouse_id": 8614, "category_id": 2, "status": "completed", "amount": "30.00",
  "quantity": 3, "comment": "Пошкоджено при доставці", "created_at": "2026-10-06T12:00:00",
  "items": [ { "product_id": 107513, "quantity": 3, "quantity_was": 100, "quantity_became": 97 } ] }

Залишку не вистачає (на складі без мінуса) — 422 з details.shortages, як у запуску виробництва, і нічого не списується. Порожній items, повтор товару або не додатна кількість — 422; невідома категорія — 404.