Документація

Повідомлення партнерові (вебхуки)

Як магазин сам повідомляє партнера про замовлення, ціни й залишки: увімкнення у формі ключа, події, підпис, відповіді, повтори й вимкнення надсилання.

На цій сторінці

Зазвичай партнер сам раз на кілька хвилин питає магазин, що змінилося, і забирає замовлення, ціни й залишки через API. Повідомлення партнерові працюють навпаки. Магазин сам надсилає запит на адресу партнера, коли в замовлення змінився статус, менеджер виправив замовлення, ціни могли стати іншими або змінилися залишки. Розробники називають такі повідомлення вебхуками.

Самих даних у повідомленні немає. У ньому лише назва події та номер замовлення або номери артикулів. Свіжі дані партнер забирає через API звичайними запитами, з тими самими правами ключа. Тому повідомлення не відкриває партнерові нічого понад те, що він і так бачить в API.

Повідомлення вмикає й налаштовує власник магазину у формі ключа. Через API їх налаштувати не можна.

Як це працює

  1. Власник вмикає повідомлення у формі ключа, вписує адресу на сайті партнера, позначає події та передає партнерові секрет підпису.
  2. Коли стається позначена подія, магазин записує повідомлення й надсилає його запитом POST на адресу партнера.
  3. Сайт партнера перевіряє підпис, відповідає кодом 2xx і забирає свіжі дані через API.
  4. Якщо сайт партнера не відповів або відповів помилкою, магазин повторює надсилання за графіком, а власник бачить кожну спробу в журналі надсилань.

Про замовлення магазин повідомляє одразу після збереження, без накопичення. Повідомлення стає в чергу надсилання й зазвичай доходить протягом хвилини. Зміни цін і залишків магазин збирає й раз на хвилину надсилає однією пачкою, тому продаж сотні позицій дає одне повідомлення, а не сотню. Таке повідомлення приходить протягом однієї-двох хвилин після зміни.

Повідомлення не замінюють звірку через API. Якщо адреса партнера довго не відповідала, магазин вимикає надсилання, і частина повідомлень не доходить. Крім того, деякі доповнення цін не повідомляють магазин про свої правки. Тому повну звірку залиште, тільки робіть її рідше, наприклад раз на добу.

Як увімкнути повідомлення

Повідомлення можна налаштувати лише у виданого ключа. Відкрийте «Налаштування платформи», у групі «Платформа» оберіть «API магазину» й натисніть на ключ у списку. Блок «Повідомлення партнерові» стоїть у формі ключа після блоків «Каталог», «Замовлення партнера» і «Залишки». У нового ключа він з’явиться після натискання «Видати ключ». Як видати ключ, описано на сторінці «Ключі доступу та права».

Під заголовком блоку написано: «Магазин сам повідомляє партнера про зміни, і йому не потрібно постійно перевіряти їх через API. У повідомленні лише номери замовлень або артикулів, а самі дані партнер забирає через API. Розробники називають такі повідомлення вебхуками.»

  1. Увімкніть перемикач «Надсилати повідомлення». Підказка під ним: «Про замовлення магазин повідомляє одразу, а зміни цін і залишків збирає й надсилає раз на хвилину.»
  2. У полі «Адреса для повідомлень» впишіть адресу, яку дав партнер. Вимоги до адреси описано нижче.
  3. У блоці «Про що повідомляти» позначте потрібні події.
  4. Натисніть «Зберегти» у шапці ключа.
  5. Натисніть «Показати» або «Скопіювати» біля секрету підпису й передайте секрет партнерові.
  6. Натисніть «Надіслати перевірку» й переконайтеся, що партнер її прийняв.

Розділ ключів і всі кнопки блоку доступні співробітникам із правом «Видавати ключі API».

Адреса для повідомлень

Підказка під полем: «Адреса на сайті партнера, що починається з https://. Її дає сам партнер.»

Магазин приймає адресу, лише якщо:

  • вона починається з https://. Адреси http:// не підходять;
  • у ній немає свого порту. Порт можна не вказувати, а якщо його вказано, то лише :443. Адресу на кшталт https://partner.example.ua:8443/hook магазин не прийме;
  • вона веде на сайт в інтернеті. Магазин має знайти ім’я сайту в інтернеті, і всі IP-адреси цього імені, IPv4 та IPv6, мають бути зовнішніми. Адреси внутрішньої мережі (localhost, 127.0.0.1, 10.…, 192.168.…, 172.16.…, службові адреси хмар і хостингів) не підходять, навіть якщо в імені сайту така адреса лише одна з кількох.

Адреса кирилицею, наприклад https://партнер.укр/hook, теж підходить.

Якщо адреса не підходить, під полем з’явиться «Адреса має починатися з https:// і вести на сайт в інтернеті. Номер порту, наприклад :8080, в адресі вказувати не можна, а адреси внутрішньої мережі не підходять.» Якщо увімкнути надсилання з порожньою адресою, магазин відповість «Впишіть адресу, на яку магазин надсилатиме повідомлення.»

Магазин перевіряє адресу й перед кожним надсиланням. Якщо магазин більше не знаходить ім’я сайту або воно почало вести у внутрішню мережу, спроба вважається невдалою, а в журналі буде написано «Магазин не знайшов цю адресу в інтернеті або адреса веде у внутрішню мережу, тому повідомлення не надіслано.» Магазин не переходить за переспрямуваннями, тому відповідь 301 або 302 вважається невдачею.

Адреса в ключа одна. Якщо повідомлення потрібні двом партнерам, видайте кожному свій ключ.

Про що повідомляти

Кожну подію можна позначити, лише якщо в ключа є право бачити ці дані через API.

Прапорець Підказка під прапорцем Подія Потрібне право
«Змінився статус замовлення» «Лише про замовлення контакту, якому видано ключ.» order.status_changed «Бачити замовлення» (orders:read)
«Зміни в замовленні» «Коли в замовленні змінюється склад, суми, покупець, отримувач, доставка, оплата, номер накладної або коментар.» order.updated «Бачити замовлення» (orders:read)
«Могли змінитися ціни» «Магазин повідомляє, у яких товарів ціна могла стати іншою. Іноді ціна при цьому не змінюється.» prices.changed «Бачити ціни» (prices:read)
«Змінилися залишки» «Лише про склади, вибрані для цього ключа. Якщо склади не вибрано, про ті, з яких продає вітрина.» inventory.changed «Бачити залишки» (inventory:read)

Прапорець події без потрібного права позначити не можна, а під ним написано, наприклад, «Спочатку позначте «Бачити замовлення» у блоці «Що дозволено».» Якщо зняти право в блоці «Що дозволено», форма сама знімає події, які від нього залежать. Якщо все ж надіслати подію без права, магазин не збереже ключ і відповість «Щоб партнер отримував це повідомлення, позначте «Бачити замовлення» у блоці «Що дозволено».»

Права магазин перевіряє й перед кожним надсиланням. Якщо в ключа більше немає права або подію більше не позначено, повідомлення не надсилається, а в журналі біля нього написано «Скасовано: ключ більше не бачить цих даних».

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

Секрет підпису

Магазин підписує кожне повідомлення секретом, а партнер за підписом перевіряє, що повідомлення надійшло саме з магазину й дорогою не змінилося. Як перевіряти підпис, описано нижче.

Секрет має такий вигляд: whsec_BY28PQcMYOLVTPAuI7l1GTNbPdeWnMQMUYxQaH3BLKw=. Магазин створює його сам, вписувати адресу для цього не потрібно. У формі секрет приховано крапками.

  • «Показати» показує секрет повністю, «Сховати» ховає його знову. На відміну від ключа API, секрет можна переглянути будь-коли.
  • «Скопіювати» копіює секрет.
  • «Видати новий секрет» замінює секрет. Магазин спитає «Видати новий секрет?» і попередить: «Попередній секрет діятиме ще добу, щоб партнер встиг замінити його в себе. Після цього повідомлення будуть підписані лише новим секретом.» Поки попередній секрет діє, під секретом написано «Попередній секрет теж діє до …».

Цю добу магазин ставить на кожне повідомлення два підписи, новим секретом і попереднім, тому сайт партнера приймає повідомлення і до, і після заміни секрету в себе. Видайте новий секрет, якщо попередній міг потрапити до сторонніх.

Перевірка

Кнопка «Надіслати перевірку» одразу надсилає на адресу з поля повідомлення webhook.test, підписане чинним секретом, а протягом доби після видачі нового секрету ще й попереднім. Адресу можна не зберігати заздалегідь. Перевірка надсилається, навіть якщо надсилання вимкнено або його вимкнув магазин, і на стан надсилання не впливає.

Біля кнопки з’явиться підсумок:

Підсумок Що означає
«Партнер прийняв перевірку. Його сайт відповів за 184 мс.» сайт відповів кодом 2xx
«Перевірка не дійшла: сайт партнера відповів кодом 401.» сайт відповів іншим кодом. Код 401 або 403 зазвичай означає, що партнер не прийняв підпис, тобто в нього записано інший секрет
«Перевірка не дійшла. …» сайт не відповів зовсім, а після крапки названо причину, наприклад «Сайт партнера не відповів за 10 секунд.»

Якщо поле адреси порожнє, під ним з’явиться «Спочатку впишіть адресу.» Перевірка записується в журнал надсилань, як і звичайні повідомлення. Повторити її кнопкою «Надіслати ще раз» не можна, для цього натисніть «Надіслати перевірку» знову.

Стан надсилання

Під кнопкою перевірки видно, чи доходять повідомлення:

Рядок Що означає
«Повідомлення доходять до партнера. Останнє доставлено …» надсилання ввімкнено, невдач немає. Без другої фрази, якщо повідомлень ще не було
«Повідомлення не доходять з …. Магазин і далі надсилає їх повторно, але якщо адреса не відповість до …, надсилання вимкнеться.» адреса перестала приймати повідомлення. Перша дата — коли почалися невдачі. Друга — час, після якого перша ж невдала спроба вимкне надсилання. Цей час буде не раніше ніж через три доби після першої дати
«Магазин вимкнув надсилання …, бо адреса три доби не приймала повідомлень.» адреса не відповідала 72 години, і магазин вимкнув надсилання сам
«Магазин вимкнув надсилання …, бо сайт партнера відповів, що повідомлення більше не потрібні.» сайт партнера відповів кодом 410
«Ключ відкликано, термін його дії минув або його не прив’язано до контакту, тому повідомлення партнерові не надсилаються.» надсилання ввімкнено, але сам ключ не працює. Коли ключ знову запрацює, повідомлення підуть самі

Рядка немає, поки надсилання вимкнено або адресу не вписано.

Коли адреса не приймає повідомлень 12 годин, магазин один раз попереджає про це: «Повідомлення ключа «…» не доходять до партнера». У тексті попередження названо час, з якого адреса не приймає повідомлень, і час, до якого вона має відповісти. Коли магазин вимикає надсилання, приходить «Повідомлення ключа «…» вимкнено». Обидва сповіщення з’являються в центрі сповіщень панелі у співробітників із правом «Видавати ключі API» й надходять листом на пошту для сповіщень магазину, а якщо її немає, то на пошту магазину з основних налаштувань. У листі є посилання на ключ.

Коли партнер налагодить приймання, натисніть «Увімкнути знову» в рядку стану. Якщо вписати іншу адресу, магазин теж увімкне надсилання знову й забуде попередні невдачі.

Якщо ви самі вимкнули перемикач «Надсилати повідомлення», магазин скасовує повідомлення, які ще чекали надсилання, а в журналі біля них написано «Скасовано: надсилання вимкнено».

Після перерви магазин повідомляє, що змінилося все. Поки повідомлення не надсилалися, партнер міг пропустити будь-що. Тому магазин одразу надсилає {"all": true} про ціни й про залишки, якщо ці події позначено, коли:

  • ви натиснули «Увімкнути знову»;
  • ви самі ввімкнули перемикач «Надсилати повідомлення»;
  • ви вписали іншу адресу, коли надсилання вимкнув магазин;
  • ключ, який не працював, знову запрацював: ви натиснули «Видати ключ знову» або продовжили термін дії.

Навіть якщо одне збереження збіглося з кількома такими випадками, партнер отримає по одному повідомленню на кожну подію. Пропущених повідомлень про замовлення магазин не досилає, їх партнер знайде через GET /orders?updated_since=….

Журнал надсилань

Під блоком «Повідомлення партнерові» стоїть блок «Журнал надсилань». У ньому повідомлення цьому партнерові за останні 30 днів, нові згори. Старі записи магазин видаляє щоночі. Список довантажується кнопкою «Показати ще».

У рядку журналу:

  • код відповіді сайту партнера або «—», якщо відповіді не було;
  • що за повідомлення, наприклад «Змінився статус замовлення #1005120», «Зміни в замовленні #1005120: склад, суми», «Могли змінитися ціни для 3 артикулів» або «Змінилися залишки для всіх товарів». Номер замовлення відкриває замовлення в панелі;
  • стан повідомлення;
  • коли магазин записав повідомлення й скільки було спроб.
Стан Що означає
«У черзі» повідомлення ще не надсилалося
«Повтор …» спроба не вдалася, наступна буде у вказаний час
«Доставлено» сайт партнера відповів кодом 2xx
«Не доставлено» усі спроби за графіком закінчилися невдачею, сайт відповів кодом 410, не вдалася спроба, запущена вручну, або не дійшла перевірка
«Скасовано: ключ більше не бачить цих даних» у ключа немає права, подію більше не позначено, ключ відкликано або термін його дії минув, або замовлення більше не належить контакту ключа
«Скасовано: надсилання вимкнено» надсилання вимкнули ви або магазин, поки повідомлення чекало
«Замінено спільним повідомленням про всі товари» адреса не відповідала, і магазин об’єднав повідомлення про ціни чи залишки, що накопичилися, в одне {"all": true}

Натисніть на рядок, щоб розгорнути повідомлення. Там видно його тіло й webhook-id, а під ним кожну спробу: номер, час, код відповіді або причину невдачі словами, скільки мілісекунд тривав запит і позначку «вручну», якщо спробу запустили кнопкою. Якщо сайт партнера щось відповів, видно початок відповіді, до 2 КБ.

Причини невдачі без відповіді:

Текст у журналі Що сталося
«Сайт партнера не відповів за 10 секунд.» з’єднання є, але відповіді немає довше за 10 секунд
«Сайт партнера не прийняв з’єднання за 5 секунд.» сайт не прийняв з’єднання за 5 секунд
«Сайт партнера недоступний. Можливо, адресу вказано з помилкою або сайт зараз не працює.» з’єднання не прийнято або обірвано без відповіді
«У сайту партнера неправильний або прострочений сертифікат https.» помилка шифрованого з’єднання
«Магазин не знайшов цю адресу в інтернеті або адреса веде у внутрішню мережу, тому повідомлення не надіслано.» адреса не пройшла перевірку перед надсиланням
«Не вдалося зв’язатися із сайтом партнера.» інший збій з’єднання

У повідомлення зі станом «Не доставлено» або «Скасовано» є кнопка «Надіслати ще раз». Магазин зробить одну спробу поза графіком протягом хвилини й покаже «Повідомлення буде надіслано протягом хвилини». У повтору той самий webhook-id, тому сайт партнера впізнає його, якщо вже обробляв це повідомлення. Якщо й ця спроба не вдасться, повідомлення знову стане «Не доставлено», повторів за графіком після неї не буде. Поки надсилання вимкнено, кнопка відповідає «Надсилання повідомлень цьому партнерові вимкнено. Увімкніть його на цій сторінці й надішліть повідомлення ще раз.» Якщо ключ на цей час уже не бачить замовлення, повідомлення знову буде скасовано.

Якщо передати ключ іншому контакту

Адреса повідомлень належить попередньому партнерові, тому після зміни контакту в полі «Кому видаєте ключ» магазин:

  • вимикає надсилання;
  • скасовує повідомлення, які чекали надсилання;
  • видає новий секрет підпису. Попередній перестає діяти одразу, без доби запасу.

Адреса й позначені події залишаються у формі. Після збереження магазин покаже «Ви передали ключ іншому контакту, тому магазин вимкнув надсилання повідомлень і видав новий секрет підпису. Перевірте адресу, передайте новий секрет партнерові й увімкніть надсилання знову.» Якщо надсилання й так було вимкнено, повідомлення коротше: «Ви передали ключ іншому контакту, тому магазин видав новий секрет підпису. Передайте його партнерові, перш ніж увімкнути надсилання.»

Сам ключ API під час зміни контакту теж змінюється. Це описано на сторінці «Ключі доступу та права».

Повідомлення

Далі сторінка написана для розробника партнера.

Магазин надсилає запит POST на адресу партнера. Тіло запиту — JSON у кодуванні UTF-8.

POST /cartix/webhook HTTP/1.1
Host: partner.example.ua
Content-Type: application/json
User-Agent: CartiX-Webhooks/2.120.0
webhook-id: msg_01k7xq3m9d2v8w6r4t0y5z1abc
webhook-timestamp: 1792402202
webhook-signature: v1,3SzMRkXefMzwvzMCD1IbStsAfVLji5r4riITbB/82TU=

{"type":"order.updated","timestamp":"2026-10-19T09:30:02Z","api_version":"1","data":{"order_id":5120,"changed":["items","totals"]}}

Заголовки

Формат заголовків і підпису взято з відкритої специфікації Standard Webhooks (standardwebhooks.com), тому підпис можна перевіряти й готовою бібліотекою Standard Webhooks.

Заголовок Що означає
webhook-id номер повідомлення: msg_ і 26 латинських літер і цифр у нижньому регістрі. Він однаковий в усіх спробах доставити одне повідомлення й під час повтору вручну з журналу. За ним відкидайте повтори
webhook-timestamp час цієї спроби в секундах Unix. У кожної спроби свій
webhook-signature підпис: v1, і підпис у base64. Добу після видачі нового секрету тут два підписи через пробіл, спершу новим секретом, потім попереднім
Content-Type завжди application/json
User-Agent CartiX-Webhooks/ і версія магазину

Тіло

{
    "type": "order.updated",
    "timestamp": "2026-10-19T09:30:02Z",
    "api_version": "1",
    "data": {
        "order_id": 5120,
        "changed": ["items", "totals"]
    }
}

Тут тіло показано з відступами, щоб його було зручно читати. Насправді магазин надсилає його одним рядком, без пробілів, а скісну риску й кирилицю не екранує. Підпис перевіряйте за тілом саме в тому вигляді, у якому воно надійшло.

Поле Тип Що означає
type рядок назва події, усі події описано нижче
timestamp рядок коли магазин записав повідомлення, в UTC. Однаковий в усіх спробах, на відміну від заголовка webhook-timestamp
api_version рядок версія формату повідомлень, зараз "1"
data об’єкт номери, у кожної події свої. У перевірки порожній об’єкт {}

Магазин записує тіло один раз і під час повторів надсилає його без змін, байт у байт.

Події

Подія Коли приходить data Що зробити
order.status_changed змінився статус замовлення order_id GET /orders/{id}
order.updated менеджер змінив замовлення, змінилася оплата або номер накладної order_id, changed GET /orders/{id}
prices.changed ціни артикулів могли змінитися sku_ids або all GET /prices?sku_ids=… або всі ціни
inventory.changed змінилися залишки артикулів sku_ids або all GET /inventory?sku_ids=… або всі залишки
webhook.test власник натиснув «Надіслати перевірку» порожній об’єкт відповісти 2xx

Про нове замовлення магазин не повідомляє. Замовлення через API партнер створює сам, а замовлення, які для його контакту оформив менеджер, і замовлення, які партнер сам оформив на вітрині, знайдуться через GET /orders?created_since=….

order.status_changed

{"type":"order.status_changed","timestamp":"2026-10-19T09:30:02Z","api_version":"1","data":{"order_id":5120}}

Змінився статус замовлення. В order_id внутрішній номер замовлення, це поле id замовлення в API, а не номер на кшталт #1005120, який бачать у панелі. Новий статус у повідомленні не приходить, його віддає GET /orders/{id} у полі status.

Повідомлення приходить про всі замовлення контакту, якому видано ключ, хоч би звідки вони надійшли: передані через API, оформлені менеджером, куплені на вітрині або отримані з майданчика. Ті самі замовлення ключ бачить у GET /orders. Якщо в контакту кілька ключів із цією подією, повідомлення отримає кожен.

Статус змінює менеджер, доповнення перевізника або оплата, і повідомлення приходить у кожному з цих випадків. Буває, що одна дія дає два повідомлення. Наприклад, статус, який сам позначає замовлення оплаченим, надішле order.status_changed і order.updated з "changed": ["payment"].

order.updated

{"type":"order.updated","timestamp":"2026-10-19T09:30:02Z","api_version":"1","data":{"order_id":5120,"changed":["shipping","payment"]}}

Замовлення змінилося. У формі ключа прапорець цієї події називається «Зміни в замовленні». У changed перелічено, що змінилося. Назви груп збігаються з блоками відповіді GET /orders/{id}, тож одразу видно, де шукати правку. Самих значень у повідомленні немає.

Група Що змінилося Де дивитися в замовленні
items менеджер додав або прибрав позицію, змінив кількість чи ціну позиції items
totals менеджер змінив знижку замовлення, вартість доставки або підсумок. Сюди ж потрапляє доплата або часткове повернення грошей, якщо стан оплати при цьому не змінився totals
customer ім’я, телефон, пошта або компанія замовника customer
recipient будь-яке поле отримувача: ім’я, телефон, компанія та інші поля форми отримувача recipient
shipping спосіб доставки, адреса доставки (країна, область, індекс, місто, вулиця, будинок, квартира) або відділення чи поштомат shipping
payment спосіб оплати або стан оплати, наприклад замовлення оплатили або гроші повернули payment, payment_status
waybill з’явився або змінився номер накладної перевізника. Його пише менеджер, доповнення перевізника або майданчик shipping.waybill_number
comment коментар до замовлення comment

Групи приходять у тому порядку, у якому їх перелічено в таблиці. Правка в панелі, яка стосується складу, зазвичай змінює й суми, тоді прийде ["items", "totals"]. Номер накладної, оплата й правка в картці замовлення приходять окремими повідомленнями, навіть якщо сталися майже одночасно.

Якщо менеджер натиснув «Зберегти» в замовленні, нічого не змінивши, повідомлення не буде. Зміна статусу приходить своєю подією order.status_changed, а не order.updated.

prices.changed

{"type":"prices.changed","timestamp":"2026-10-19T09:31:00Z","api_version":"1","data":{"sku_ids":[2108,2109,2110]}}
{"type":"prices.changed","timestamp":"2026-10-19T09:31:00Z","api_version":"1","data":{"all":true}}

Ціни цих артикулів могли змінитися. У sku_ids номери артикулів за зростанням, не більше 100 в одному повідомленні. Якщо за хвилину змінилося понад 100 артикулів, прийде кілька повідомлень. Якщо понад 1000, прийде одне повідомлення {"all": true}, і тоді заберіть ціни повністю. До повідомлення потрапляють артикули опублікованих товарів, тобто ті, які видно через API, і артикули, що зникли з продажу.

Магазин повідомляє, що ціна могла змінитися, і не перераховує її для вашого контакту. Тому бувають хибні спрацювання: ціна для вас лишилася попередньою, а повідомлення прийшло. Наприклад, власник змінив звичайну ціну артикула, а ви купуєте за своєю персональною ціною. Звіряйте ціну, яку віддає /prices, із записаною у вас.

Повідомлення приходить, коли:

  • власник або імпорт змінив ціну, стару ціну чи валюту ціни артикула;
  • магазин перерахував ціну зі знижкою, наприклад почалася або закінчилася акція каталогу;
  • змінилися пакування товару, крок продажу, найменша кількість або одиниця продажу, бо від них залежать щаблі ціни tiers;
  • доповнення цін, наприклад «Оптові ціни», повідомило магазин про свою правку;
  • змінився курс валюти, у якій задано ціни артикулів. Прийдуть номери артикулів із ціною в цій валюті;
  • у магазині з’явився новий артикул або товар опублікували, уперше чи знову;
  • артикул зник із продажу, про це нижче.

Повідомлення {"all": true} приходить, коли:

  • змінилася основна валюта магазину, курс валюти якоїсь вітрини або валюта вітрини, бо тоді змінюються ціни всього каталогу;
  • доповнення цін повідомило, що ціни змінилися в усього каталогу. «Оптові ціни» роблять так, наприклад, після правки знижки категорії покупців або своїх налаштувань;
  • ваш контакт перевели в іншу категорію покупців, видалили його категорію або об’єднали його картку з іншою. Таке повідомлення отримують лише ключі цього контакту, і одразу, без хвилинного накопичення;
  • надсилання відновилося після перерви: власник натиснув «Увімкнути знову», сам увімкнув надсилання, вписав нову адресу або знову видав ключ. Усі випадки перелічено вище;
  • адреса не відповідала, і магазин об’єднав повідомлення, що чекали, в одне, про це нижче.

Номери sku_ids можна передати в GET /prices?sku_ids=… як є. Ця адреса приймає до 100 номерів за раз. Отримавши {"all": true}, перечитайте /prices повністю, гортаючи за after_id.

inventory.changed

{"type":"inventory.changed","timestamp":"2026-10-19T09:31:00Z","api_version":"1","data":{"sku_ids":[2108,2115]}}

Залишки цих артикулів змінилися. Номери влаштовано так само, як у prices.changed. Вони йдуть за зростанням, в одному повідомленні їх не більше 100, а якщо за хвилину змінилося понад 1000 артикулів, прийде одне {"all": true}.

Магазин повідомляє лише про залишки, які бачить ваш ключ. Якщо власник вибрав ключу склади в полі «З яких складів», повідомлення прийде лише про рух на цих складах. Якщо склади не вибрано, то про рух на складах, з яких продає хоча б одна вітрина. Як обираються склади, описано на сторінці «Залишки».

Повідомлення приходить, коли:

  • на складі ключа змінилося, скільки лежить або скільки відкладено: надходження, продаж, резерв під замовлення, скасування резерву, переміщення;
  • власник зняв артикул із продажу або повернув у продаж;
  • власник змінив загальний залишок артикула, у якого залишок не розкладено по складах, або перестав вести залишок, або почав його вести;
  • товар опублікували, уперше чи знову;
  • артикул зник із продажу, про це нижче.

Повідомлення {"all": true} приходить, коли:

  • власник перейменував, переставив, увімкнув, вимкнув або видалив склад, зробив його об’єднаним або змінив його вітрини. Таке повідомлення отримує ключ, у якого цей склад вибрано в полі «З яких складів». Ключ без вибраних складів отримує його після правки будь-якого складу, бо вітрина могла почати або перестати продавати з цього складу;
  • власник перемкнув «Дозволити замовлення, коли товару немає», від якого залежить preorder;
  • власник змінив у вашого ключа склади або налаштування «Як віддавати залишки». Його отримує лише ваш ключ, і надходить воно одразу;
  • надсилання відновилося після перерви, як у prices.changed;
  • адреса не відповідала, і магазин об’єднав повідомлення, що чекали, в одне.

Номери sku_ids можна передати в GET /inventory?sku_ids=… як є. Ця адреса, як і /prices, приймає до 100 номерів за раз і віддає їх однією сторінкою. Отримавши {"all": true}, перечитайте /inventory повністю, гортаючи за after_id.

Якщо артикула немає у відповіді

Коли опублікований товар знімають із публікації або видаляють, а також коли видаляють артикул опублікованого товару, магазин надсилає номери його артикулів і в prices.changed, і в inventory.changed. У /prices та /inventory таких артикулів уже немає, бо обидві адреси мовчки пропускають номери, яких не бачать. Якщо номера з повідомлення немає у відповіді, артикул знято з продажу або видалено, і його треба прибрати в себе. Що сталося з товаром, показує /products/ids.

Про товари, які не були опубліковані, наприклад про чернетки, магазин не повідомляє: партнер їх не бачив.

webhook.test

{"type":"webhook.test","timestamp":"2026-10-19T09:32:11Z","api_version":"1","data":{}}

Перевірка, яку власник надіслав кнопкою «Надіслати перевірку». Перевірте підпис і відповідайте 2xx. Більше нічого робити не потрібно.

Перевірка підпису

Підпис — HMAC-SHA256 від рядка {webhook-id}.{webhook-timestamp}.{тіло}. Ключем є секрет без префікса whsec_, розкодований із base64. Підпис кодується в base64 і приходить у заголовку webhook-signature після v1,.

Щоб перевірити повідомлення:

  1. Візьміть тіло запиту саме в тому вигляді, у якому воно надійшло, до розбору JSON. Якщо розібрати JSON і зібрати його знову, підпис не зійдеться.
  2. Перевірте, що webhook-timestamp відрізняється від вашого поточного часу не більше ніж на 5 хвилин у будь-який бік. Так перехоплене повідомлення не вийде надіслати вам ще раз пізніше. Годинник сервера має йти точно.
  3. Порахуйте підпис своїм секретом.
  4. Розбийте заголовок webhook-signature за пробілами. Повідомлення справжнє, якщо ваш підпис збігся хоча б з однією з частин, що починаються з v1,. Порівнюйте функцією з постійним часом порівняння (hash_equals у PHP, crypto.timingSafeEqual у Node.js), а не звичайним ==.

Якщо підпис не зійшовся, відповідайте 401 або 400. Магазин вважатиме спробу невдалою й повторить її за графіком, а власник побачить код у журналі. Не відповідайте на неправильний підпис кодом 410: він вимикає надсилання зовсім.

PHP

<?php

$secret = getenv('CARTIX_WEBHOOK_SECRET'); // whsec_…

$body = file_get_contents('php://input');
$id = $_SERVER['HTTP_WEBHOOK_ID'] ?? '';
$timestamp = $_SERVER['HTTP_WEBHOOK_TIMESTAMP'] ?? '';
$header = $_SERVER['HTTP_WEBHOOK_SIGNATURE'] ?? '';

// Час спроби — не далі 5 хвилин від нашого годинника
if (! ctype_digit($timestamp) || abs(time() - (int) $timestamp) > 300) {
    http_response_code(400);
    exit;
}

$key = base64_decode(substr($secret, strlen('whsec_')));
$expected = base64_encode(hash_hmac('sha256', $id.'.'.$timestamp.'.'.$body, $key, true));

$valid = false;

// Добу після зміни секрету підписів два, через пробіл
foreach (explode(' ', $header) as $part) {
    [$version, $signature] = array_pad(explode(',', $part, 2), 2, '');

    if ($version === 'v1' && hash_equals($expected, $signature)) {
        $valid = true;
        break;
    }
}

if (! $valid) {
    http_response_code(401);
    exit;
}

$event = json_decode($body, true);

// Повтор того самого повідомлення — уже обробили, просто підтверджуємо
if (already_processed($id)) {
    http_response_code(204);
    exit;
}

// Ставимо у свою чергу й одразу відповідаємо, дані забираємо через API пізніше
enqueue($id, $event['type'], $event['data']);
http_response_code(204);

Функції already_processed() і enqueue() у прикладі ваші: перша шукає webhook-id серед оброблених, друга ставить завдання у вашу чергу.

Node.js

Приклад для Node.js 18 і новіших без сторонніх пакетів. Він слухає порт 3000, а перед ним стоїть вебсервер, який приймає https на порту 443 і передає запити сюди.

import { createServer } from 'node:http';
import { createHmac, timingSafeEqual } from 'node:crypto';

const secret = process.env.CARTIX_WEBHOOK_SECRET; // whsec_…
const key = Buffer.from(secret.slice('whsec_'.length), 'base64');

function verify(headers, body) {
    const id = headers['webhook-id'] ?? '';
    const timestamp = headers['webhook-timestamp'] ?? '';
    const header = headers['webhook-signature'] ?? '';

    // Час спроби — не далі 5 хвилин від нашого годинника
    if (! /^\d+$/.test(timestamp) || Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) {
        return false;
    }

    const expected = createHmac('sha256', key)
        .update(`${id}.${timestamp}.`)
        .update(body) // тіло як надійшло, байтами
        .digest();

    // Добу після зміни секрету підписів два, через пробіл
    return header.split(' ').some((part) => {
        const [version, signature] = part.split(',');
        if (version !== 'v1' || ! signature) return false;

        const given = Buffer.from(signature, 'base64');

        return given.length === expected.length && timingSafeEqual(given, expected);
    });
}

createServer((req, res) => {
    const chunks = [];

    req.on('data', (chunk) => chunks.push(chunk));
    req.on('end', () => {
        const body = Buffer.concat(chunks);

        if (! verify(req.headers, body)) {
            res.writeHead(401).end();
            return;
        }

        const event = JSON.parse(body.toString('utf8'));
        // Ставимо у свою чергу за req.headers['webhook-id'] й одразу відповідаємо
        res.writeHead(204).end();
    });
}).listen(3000);

Приклад для перевірки

Перевірити свій код можна на прикладі зі специфікації Standard Webhooks. З такими даними підпис має вийти v1,g0hM9SsE+OTPJTGt/tmIKtSyZlE3uFJELVlNIOLJ1OE=.

Що Значення
секрет whsec_MfKQ9r8GKYqrTwjUPD8ILPZIo2LaLaSw
webhook-id msg_p5jXN8AQM9LWM0D4loKWxJek
webhook-timestamp 1614265330
тіло {"test": 2432232314}

Час у цьому прикладі давно минув, тому для перевірки вимкніть порівняння часу. Підпис у прикладі вище із секретом whsec_BY28PQcMYOLVTPAuI7l1GTNbPdeWnMQMUYxQaH3BLKw= теж справжній, за ним можна звірити свій код.

Як відповідати

Відповідь сайту партнера Що робить магазин
2xx протягом 10 секунд повідомлення доставлено
410 вимикає надсилання одразу й повідомляє власника. Відповідайте так, лише якщо повідомлення цього магазину вам більше зовсім не потрібні
429 або 503 із заголовком Retry-After чекає вказаний час, але не довше доби, і повторює. Retry-After приймається кількістю секунд або датою
3xx невдача. Магазин не переходить за переспрямуваннями, впишіть у форму кінцеву адресу
будь-який інший код, зокрема 4xx і 5xx невдача, повтор за графіком
немає відповіді за 10 секунд, з’єднання не встановлено за 5 секунд, помилка сертифіката невдача, повтор за графіком

Тіло відповіді магазину не потрібне, можна відповідати 204 без тіла. Початок відповіді, до 2 КБ, магазин зберігає в журналі надсилань, і його бачить власник. Не пишіть туди нічого секретного, а в разі помилки напишіть зрозумілу причину, щоб власникові було простіше розібратися.

Відповідайте одразу, а дані забирайте потім. Якщо в обробнику повідомлення ходити в API магазину й чекати відповіді, легко не вкластися в 10 секунд. Тоді магазин вважатиме спробу невдалою й надішле повідомлення ще раз.

Відповідь 429 або 503 магазин теж вважає невдачею. Така спроба зараховується до графіка з 10 спроб, а відлік до вимкнення надсилання починається з першої такої невдачі.

Повтори й вимкнення надсилання

Кожне повідомлення магазин намагається доставити до 10 разів. Якщо спроба не вдалася, наступна буде через:

Після спроби Наступна через
1 5 секунд
2 5 хвилин
3 30 хвилин
4 2 години
5 5 годин
6 10 годин
7 14 годин
8 20 годин
9 24 години

До кожної паузи, крім останньої, магазин додає або віднімає до 10 %, щоб повідомлення на різні адреси не йшли в одну секунду. Повтори магазин запускає раз на хвилину, тому короткі паузи насправді бувають до хвилини довшими. Якщо сайт відповів 429 або 503 з Retry-After, замість паузи з таблиці магазин чекає вказаний час. Якщо не вдалася й десята спроба, повідомлення отримує стан «Не доставлено», і більше магазин його не надсилає. Увесь графік займає трохи більше ніж три доби.

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

Якщо сайт не відповів на повідомлення, решту повідомлень на цю адресу магазин відкладе на хвилину, до наступного надсилання, а не надсилатиме їх поспіль.

Повідомлення про ціни й залишки об’єднуються. Поки адреса не відповідає, вони не накопичуються. Якщо повідомлення про ціни вже чекає повтору, а надходить ще одне, магазин скасовує всі повідомлення про ціни, що чекають, і записує замість них одне {"all": true}. Із залишками так само. У журналі біля скасованих написано «Замінено спільним повідомленням про всі товари». Коли сайт запрацює, ви отримаєте одне повідомлення й звірите ціни чи залишки повністю. Повідомлення про замовлення не об’єднуються, у кожного свій графік.

Попередження й вимкнення. Магазин відлічує час від першої невдачі після останньої вдалої доставки. Будь-яка відповідь 2xx обнуляє відлік. Відлік починається заново й у двох інших випадках:

  • між двома невдачами минуло понад 30 годин. Тому одна випадкова помилка через тиждень після попередньої надсилання не вимкне;
  • після невдалої спроби в адреси не лишилося жодного повідомлення, яке чекає повтору. Наприклад, останнє повідомлення отримало стан «Не доставлено».

Поки відлік триває, магазин робить так:

  • Після 12 годин невдач власник один раз отримує попередження, що повідомлення не доходять і з якого часу. Воно приходить під час першої невдалої спроби після 12 годин.
  • Після 72 годин невдач магазин вимикає надсилання, скасовує всі повідомлення, що чекають, і повідомляє власника. Остання спроба за графіком завжди припадає на час після 72 годин, якщо тільки сайт не попросив зачекати заголовком Retry-After, тому саме вона й вирішує, чи вимикати надсилання.
  • Відповідь 410 вимикає надсилання одразу, без очікування.

Увімкнути надсилання знову може лише власник, ваш сайт цього не може. Власник натискає «Увімкнути знову» у формі ключа або вписує іншу адресу.

Порядок і повтори повідомлень

Порядок не гарантовано. Повтор старого повідомлення може прийти пізніше за нове, а два повідомлення про одне замовлення можуть прийти не в тому порядку, у якому сталися події. Не будуйте логіку на порядку повідомлень. Отримавши повідомлення про замовлення, запитайте GET /orders/{id} і порівняйте поле updated_at з тим, що записано у вас. Якщо у вас запис того самого часу або новіший, нічого робити не потрібно. Ціни й залишки після повідомлення просто читайте заново, API завжди віддає поточні.

Одне повідомлення може прийти кілька разів. Так буває, якщо ваш сайт обробив повідомлення, але відповів більше ніж через 10 секунд, або якщо власник надіслав його ще раз із журналу. В усіх спроб одного повідомлення однаковий webhook-id. Запам’ятовуйте номери оброблених повідомлень і відповідайте 2xx на повтор, не обробляючи його вдруге. Зберігайте номери хоча б 30 днів, стільки живе журнал, з якого власник може надіслати повідомлення ще раз.

Повідомлення може не прийти зовсім. Якщо адреса довго не відповідала, магазин вимкне надсилання, а пропущених повідомлень про замовлення не дошле. Тому регулярна звірка через updated_since і далі потрібна, тільки робити її можна рідше.

Чого немає

  • Налаштування повідомлень через API. Адресу, події й секрет задає лише власник у формі ключа.
  • Повідомлення про нове замовлення.
  • Даних у повідомленні: лише номери.
  • Повідомлень про покупців.
  • Кількох адрес в одного ключа.
  • Адрес http://, адрес зі своїм портом і адрес внутрішньої мережі.
  • Переходу за переспрямуваннями.
Оновлено 19 жовтня 2026