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

Помилки та їхні коди

Формат відповіді про помилку, кожен код із причиною і тим, що робити. Ліміт запитів, Retry-After і закритий магазин.

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

Магазин повідомляє про помилку чесним HTTP-статусом. Відповіді 200 і 201 (на створення замовлення) завжди означають успіх, і шукати ознаку помилки всередині «успішної» відповіді не потрібно. Тіло помилки в усіх адрес однакове, за полем code програма розуміє, що сталося.

Формат помилки

Помилка приходить із заголовком Content-Type: application/problem+json у форматі RFC 9457 (Problem Details):

{
    "type": "https://cartixx.com/docs/api/errors#missing-scope",
    "title": "Доступ заборонено",
    "status": 403,
    "detail": "У ключа немає права «shop:read». Додати його може власник магазину в налаштуваннях ключа.",
    "code": "missing_scope",
    "request_id": "C3TOWCVB",
    "scope": "shop:read"
}
Поле Що означає
type адреса опису цієї помилки на цій сторінці. Підкреслення з коду в адресі замінено дефісом: #missing-scope. У кодів http_<статус> адреса веде на цю сторінку цілком
title коротка назва помилки за HTTP-статусом, мовою запиту. Якщо статус не має власної назви, тут повторюється code, наприклад http_413
status HTTP-статус, той самий, що й у відповіді
detail пояснення для людини мовою запиту: що не так і що зробити
code код помилки. Програма має спиратися на нього, а не на текст
request_id код запиту, той самий, що в заголовку X-Request-Id і в журналі магазину

Деякі помилки мають додаткові поля: errors у помилки перевірки даних, scope у помилки браку права, problems у відмови прийняти замовлення. Їх описано біля відповідних кодів.

Тексти title і detail приходять мовою, обраною через ?locale= або Accept-Language (див. «Запити та відповіді»). Текст може змінюватися від випуску до випуску, а code лишається сталим.

Усі коди

HTTP code Коли виникає
400 bad_json тіло запиту на замовлення не є об’єктом JSON
400 bad_idempotency_key заголовок Idempotency-Key неправильного вигляду
401 unauthenticated ключ не передано, передано не повністю або такого ключа немає: його замінили новим, передали іншому контакту чи видалили
401 key_revoked власник відкликав ключ або минув термін дії ключа
403 ip_not_allowed запит надійшов з адреси, якої немає в списку дозволених у ключа
403 missing_scope ключ не має права, потрібного цій адресі
403 forbidden дія ключу недоступна з іншої причини
404 not_found такої адреси в API немає або запис не знайдено
405 method_not_allowed адреса є, але не для цього методу
409 conflict запит суперечить поточному стану даних
409 idempotency_in_progress замовлення з цим Idempotency-Key ще оформлюється
409 idempotency_order_hidden замовлення за цим Idempotency-Key створено, але ключ API відтоді прив’язано до іншого контакту
409 external_id_taken замовлення з цим external_id уже прийнято
409 external_id_in_progress замовлення з цим external_id ще оформлюється
409 total_mismatch підсумок замовлення не збігся з expected_total
409 order_busy магазин саме зараз оформлює інші замовлення з тими самими товарами
422 validation_failed параметри чи дані запиту не пройшли перевірку
422 cannot_place замовлення не можна зібрати точно так, як його надіслали
422 idempotency_key_reused з цим Idempotency-Key уже надійшло замовлення з іншим тілом
423 shop_locked магазин закрито, бо підписка не активна
428 idempotency_key_required у запиті на створення замовлення немає заголовка Idempotency-Key
429 rate_limited перевищено ліміт запитів ключа або ліміт невдалих входів з адреси
500 server_error помилка всередині магазину
503 not_installed магазин ще не встановлено
503 unavailable магазин тимчасово недоступний, наприклад закритий на обслуговування
інший http_<статус> рідкісні відповіді HTTP, які не мають власного коду, наприклад http_413

У якому порядку перевіряється запит

Якщо в запиті кілька проблем, відповідь називає першу за порядком:

  1. Магазин доступний: не закритий на обслуговування (unavailable), установлений (not_installed) і не закритий через підписку (shop_locked).
  2. Ключ передано і він чинний (unauthenticated, key_revoked), запит надійшов з дозволеної адреси (ip_not_allowed).
  3. Не перевищено ліміт запитів ключа (rate_limited).
  4. Текст у тілі запиту записано в UTF-8 (validation_failed). Тіло перевіряється в усіх запитів, крім GET і HEAD.
  5. Адреса й метод існують (not_found, method_not_allowed).
  6. Ключ має потрібне право (missing_scope).
  7. Параметри запиту правильні (validation_failed). Адреси каталогу, цін, залишків і замовлень спочатку шукають невідомі параметри, а далі перевіряють значення решти.

Тому запит без ключа до неіснуючої адреси отримає 401, а не 404, а закритий магазин відповість 423 навіть на запит без ключа.

Далі адреси замовлень перевіряють ключ повтору, тіло й саме замовлення. Цей порядок описано на сторінці «Замовлення: адреси й поля».

Коди по черзі

bad_json

400. Тіло запиту POST /orders чи POST /orders/check не є об’єктом JSON: це непорожній список, рядок, дані форми або JSON із помилкою розмітки. Надішліть замовлення об’єктом, наприклад {"items": [...]}. Порожнє тіло й порожній список [] помилкою не вважаються: магазин перевірить їх як порожній об’єкт і відповість validation_failed з помилкою в полі items: позицій немає.

bad_idempotency_key

400. Заголовок Idempotency-Key довший за 255 знаків або містить знаки поза друкованими ASCII: кирилицю, перенесення рядка, табуляцію. Використовуйте латиницю, цифри й розділові знаки, найзручніше UUID. Докладніше про ключ повтору на сторінці «Замовлення партнера».

unauthenticated

401. Ключа немає в заголовку Authorization, його скопійовано не повністю або такого ключа в магазині немає. Відповідь має заголовок WWW-Authenticate: Bearer.

Перевірте, що заголовок має вигляд Authorization: Bearer 12|ctx_… і ключ передано разом із номером і вертикальною рискою.

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

Сеанс співробітника в панелі API не відкриває. Запит із браузера, де виконано вхід у панель, але без ключа, теж отримає цю відповідь.

key_revoked

401. Магазин упізнав ключ, але ключ більше не працює: власник його відкликав або минув термін, указаний у полі «Діє до». Відповідь має заголовок WWW-Authenticate: Bearer. Попросіть власника магазину видати ключ знову. Працюватиме новий ключ, який він вам передасть, а попередній так і лишиться неробочим.

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

ip_not_allowed

403. Для ключа задано список дозволених IP-адрес, і адреси, з якої надійшов запит, у ньому немає. Повідомте власникові адресу свого сервера, щоб він додав її в обмеження ключа. Якщо сервер виходить в інтернет через різні адреси, зручніше вказати підмережу, наприклад 203.0.113.0/24.

missing_scope

403. Ключу не позначено право, потрібне цій адресі. Код права приходить у додатковому полі scope:

{ "code": "missing_scope", "scope": "shop:read" }

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

forbidden

403. Дія ключу недоступна з причини, яка не має окремого коду. Причину написано в detail.

not_found

404. Такої адреси в API немає або запитаного запису немає в магазині. Відповідь приходить у форматі API, а не сторінкою вітрини. Неопублікованого товару для API немає, тому /products/{id} чернетки, прихованого товару чи товару в архіві теж відповідає 404. Звірте адресу з довідником адрес: слова в адресах розділяються підкресленням (order_statuses), а не дефісом.

method_not_allowed

405. Адреса є, але приймає інший метод. Допустимі методи перелічено в заголовку відповіді Allow, наприклад Allow: GET, HEAD. Усі адреси API приймають GET, крім /orders/check, яка приймає лише POST. Адреса /orders приймає і GET, і POST.

conflict

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

idempotency_in_progress

409. Запит POST /orders з цим Idempotency-Key уже надійшов і ще оформлюється. Так буває, якщо програма повторила запит, не дочекавшись відповіді на перший. Відповідь має заголовок Retry-After: 2. Повторіть той самий запит із тим самим ключем через дві секунди: якщо перший запит створив замовлення, магазин віддасть його.

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

idempotency_order_hidden

409. Замовлення за цим Idempotency-Key уже створено, але потім власник прив’язав ключ API до іншого контакту. Під час зміни контакту магазин видає новий ключ, а попередній відповідає 401. Новому ключу замовлення попереднього контакту не видно, і магазин його не віддає. Не надсилайте замовлення ще раз із новим ключем повтору, інакше в магазині з’явиться друге таке саме. Запитайте у власника, що з ним робити.

external_id_taken

409. Замовлення з таким external_id уже прийнято за цим ключем. Відповідь називає ваш номер у полі external_id, а номер замовлення в магазині — у полі order_id:

{ "code": "external_id_taken", "external_id": "A-1024", "order_id": 5120 }

Найімовірніше, ви надіслали те саме замовлення вдруге. Переглянути його можна в /orders/{order_id}. Номери належать ключу API, тому після прив’язки ключа до іншого контакту номер може виявитися зайнятим замовленням попереднього контакту, і тоді ця адреса відповість 404. Якщо власник видалить замовлення в панелі, номер звільниться.

external_id_in_progress

409. Замовлення з таким external_id саме зараз оформлює інший запит. Відповідь має заголовок Retry-After: 2 і ваш номер у полі external_id. Повторіть запит через дві секунди. Якщо перший запит створить замовлення, відповіддю буде external_id_taken, а номер цього замовлення прийде в полі order_id.

total_mismatch

409. Підсумок замовлення за розрахунком магазину не збігся з expected_total із запиту, і замовлення не створено. Ваш підсумок приходить у полі expected_total, підсумок магазину — у полі actual_total:

{ "code": "total_mismatch", "expected_total": "400.00", "actual_total": "416.64" }

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

order_busy

409. Магазин саме в цю секунду оформлював інші замовлення з тими самими товарами, і оформити ваше не вдалося навіть із третьої спроби. Замовлення не створено. Відповідь має заголовок Retry-After: 2. Повторіть той самий запит із тим самим Idempotency-Key через дві секунди.

validation_failed

422. Параметри чи дані запиту неправильні. У полі errors для кожного неправильного поля приходить список повідомлень:

{
    "type": "https://cartixx.com/docs/api/errors#validation-failed",
    "title": "Дані не пройшли перевірку",
    "status": 422,
    "detail": "Дані не пройшли перевірку. Подробиці в полі errors.",
    "code": "validation_failed",
    "request_id": "C3TOWCVB",
    "errors": {
        "limit": ["limit — ціле число від 1 до 200."],
        "offset": ["offset — ціле число від 0."]
    }
}

Так відповідають усі списки на неправильні limit і offset. Адреси каталогу, цін і залишків так само відповідають на невідомий параметр, неправильний номер вітрини, неправильне значення фільтра, неправильну дату в updated_since і after_id разом з offset. Повідомлення для кожного параметра перелічено в описах адрес, наприклад у /products. Така сама відповідь приходить, якщо в тілі запиту є текст не в кодуванні UTF-8: магазин називає поле, у якому його знайшов.

Адреси замовлень так відповідають на помилки в тілі замовлення. Ключем у errors стоїть шлях до поля, наприклад items.2.qty чи items.0.product_id, а помилки приходять усі одразу. Повідомлення для кожного поля перелічено на сторінці «Замовлення: адреси й поля».

cannot_place

422. Замовлення не можна зібрати точно так, як його надіслали, і його не створено. Причини приходять списком problems, кожна має поле запиту, код і пояснення:

{
    "code": "cannot_place",
    "problems": [
        { "field": "items.0.qty", "code": "out_of_stock", "product_id": 1045, "sku_id": 2108, "available": "2.000", "message": "На складі менше, ніж у замовленні: доступно 2.000." }
    ]
}

Усі причини й що з ними робити описано в розділі «Причини відмови». Заздалегідь ті самі причини показує POST /orders/check.

idempotency_key_reused

422. З цим Idempotency-Key уже надійшло замовлення, але з іншим тілом. Найімовірніше, новому замовленню помилково дістався ключ попереднього. Дайте новому замовленню новий ключ. Якщо ж це повтор того самого замовлення, надішліть те саме тіло, що й першого разу. Що магазин вважає тим самим тілом, описано на сторінці «Замовлення партнера».

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

shop_locked

423. Магазин закрито, бо його підписка не активна. Поки магазин закритий, API не відповідає на жоден запит. Часто повторювати запит немає сенсу: магазин відкриється, коли власник продовжить підписку.

idempotency_key_required

428. У запиті POST /orders немає заголовка Idempotency-Key. Без нього магазин не створює замовлення: якщо відповідь загубиться, повтор створив би друге таке саме замовлення. Придумайте для замовлення унікальний рядок, наприклад UUID, і передавайте його в цьому заголовку під час кожного повтору цього замовлення. POST /orders/check заголовок не потрібен.

rate_limited

429. Запитів забагато. Відповідь має заголовок Retry-After із кількістю секунд, через яку можна повторити запит, і це саме число назване в detail.

Така відповідь буває у двох випадках.

Перевищено ліміт ключа. У кожного ключа свій ліміт запитів за хвилину, типово 120. Власник може змінити його в полі «Запитів за хвилину». Скільки запитів лишилося в поточній хвилині, видно із заголовка X-RateLimit-Remaining кожної успішної відповіді. Відповідь 429 має також X-RateLimit-Limit, X-RateLimit-Remaining: 0 і X-RateLimit-Reset (час у секундах Unix, коли лічильник обнулиться).

Забагато невдалих входів з однієї адреси. Якщо з однієї IP-адреси за хвилину надійшло 30 відмов у вході (unauthenticated, key_revoked, ip_not_allowed), подальші невдалі запити з цієї адреси отримують 429 замість 401 або 403. Така відповідь має лише заголовок Retry-After, без X-RateLimit-*. Адреси IPv6 рахуються за мережею /64. Чинний ключ цей ліміт не зачіпає: навіть коли з тієї самої адреси йде перебір чужих ключів, запити з правильним ключем проходять.

Що робити: дочекатися часу з Retry-After і повторити запит. Не повторюйте запит одразу в циклі, кожен такий повтор теж витрачає ліміт. Якщо ліміту постійно бракує, попросіть власника підняти його для вашого ключа.

server_error

500. Усередині магазину сталася помилка. Подробиць у відповіді немає, їх записано в журнал магазину під кодом з request_id. Повідомте цей код власникові магазину, як описано нижче.

not_installed

503. Магазин за цією адресою ще не встановлено.

unavailable

503. Магазин тимчасово не приймає запити, наприклад закритий на обслуговування. Якщо відповідь має заголовок Retry-After, повторіть запит через указаний час.

Коди http_

Рідкісні відповіді HTTP, які не мають власного коду, приходять із кодом з http_ і номера статусу, наприклад http_413, якщо тіло запиту завелике. Адреса в type у них веде на цю сторінку цілком. У title зазвичай повторюється той самий код, а detail коротко називає причину, наприклад «Тіло запиту завелике.». Програмі краще спиратися на HTTP-статус.

Відповідь http_413 магазин дає ще до того, як запит дійшов до API. Тому вона не має заголовка X-Request-Id і в журнал запитів не потрапляє. Поле request_id у тілі є, але знайти за ним запит у журналі не вийде. Так само влаштована відповідь unavailable, поки магазин закритий на обслуговування.

Як повідомити про проблему

Передайте власникові магазину три речі:

  • код із поля request_id або заголовка X-Request-Id;
  • час запиту;
  • назву ключа, якщо ключів у вас кілька.

Власник відкриє журнал запитів у розділі «API магазину» й знайде запит, вписавши код у поле «Код звернення». У запиту з помилкою там видно тіло запиту й відповідь магазину. Якщо помилка 500, подробиці записано ще й у журнал помилок магазину в розділі «Система → Журнали», де запис знаходиться пошуком за тим самим кодом. Як влаштовано журнал запитів, описано на сторінці «Журнал запитів».

Оновлено 21 жовтня 2026