Помилки та їхні коди
Формат відповіді про помилку, кожен код із причиною і тим, що робити. Ліміт запитів, Retry-After і закритий магазин.
На цій сторінці
- Формат помилки
- Усі коди
- У якому порядку перевіряється запит
- Коди по черзі
- bad_json
- bad_idempotency_key
- unauthenticated
- key_revoked
- ip_not_allowed
- missing_scope
- forbidden
- not_found
- method_not_allowed
- conflict
- idempotency_in_progress
- idempotency_order_hidden
- external_id_taken
- external_id_in_progress
- total_mismatch
- order_busy
- validation_failed
- cannot_place
- idempotency_key_reused
- shop_locked
- idempotency_key_required
- rate_limited
- server_error
- not_installed
- unavailable
- Коди http_
- Як повідомити про проблему
Магазин повідомляє про помилку чесним 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 |
У якому порядку перевіряється запит
Якщо в запиті кілька проблем, відповідь називає першу за порядком:
- Магазин доступний: не закритий на обслуговування (
unavailable), установлений (not_installed) і не закритий через підписку (shop_locked). - Ключ передано і він чинний (
unauthenticated,key_revoked), запит надійшов з дозволеної адреси (ip_not_allowed). - Не перевищено ліміт запитів ключа (
rate_limited). - Текст у тілі запиту записано в UTF-8 (
validation_failed). Тіло перевіряється в усіх запитів, крімGETіHEAD. - Адреса й метод існують (
not_found,method_not_allowed). - Ключ має потрібне право (
missing_scope). - Параметри запиту правильні (
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, подробиці записано ще й у журнал помилок магазину в розділі «Система → Журнали», де запис знаходиться пошуком за тим самим кодом. Як влаштовано журнал запитів, описано на сторінці «Журнал запитів».