Error Handling
Коды ошибок, формат, best practices обработки.
Формат ошибок
Все API endpoints возвращают единый формат:
{
"detail": "Человекочитаемое описание"
}Для ошибок валидации (некорректный payload):
{
"detail": [
{
"type": "value_error",
"loc": ["body", "passengers", 0, "first_name"],
"msg": "String should have at least 1 character"
}
]
}HTTP коды
| Код | Значение |
|---|---|
| 200 | OK |
| 201 | Created (POST) |
| 204 | No Content (DELETE) |
| 400 | Bad Request — нарушение бизнес-правил |
| 401 | Unauthorized — нет / невалидный API Key |
| 403 | Forbidden — нет прав на ресурс |
| 404 | Not Found |
| 409 | Conflict — race condition (например, места заняты) |
| 410 | Gone — broня истекла (15 минут на оплату прошли) |
| 422 | Unprocessable Entity — валидация payload |
| 429 | Too Many Requests — rate limit |
| 5xx | Server error — повторите позже |
Часто встречающиеся ошибки
400 — Бронирование уже отменено или завершено
Попытка cancel / confirm на брони в финальном статусе.
400 — Окно регистрации закрыто
OCI попытка вне окна T-24h до T-45m. Возможные сообщения:
- "Регистрация откроется через X часов"
- "Регистрация закрыта (за 45 мин до вылета)"
403 — Нет доступа к этому бронированию
Каждый клиент видит только свои брони. Не пытайтесь работать с бронями, созданными другим API ключом.
403 — access_token не соответствует booking_id
Magic-link token bound к одному booking_id. Попытка использовать на другой брони запрещена.
409 — Места заняты
Concurrent bookings — кто-то занял те же места между шагом просмотра seat-map и созданием. Покажите свежий seat-map.
410 — Бронирование истекло
expiry_date < now(). Места освобождены. Создайте новую броню.
429 — Слишком много запросов
См. Rate limits. При получении — exponential backoff с jitter.
Pricing / SSR / PNR validation errors
С 2026-06-01 включены строгие проверки (IATA Resolution 728/791 + Recommended Practice 1701):
SSR advance-booking-time (Rec 1701):
400: SSR 'WCHC' (Инвалидная коляска (до кресла)) требует запроса минимум
за 48ч до вылета. До вылета осталось 12.5ч — недостаточно времени
для подготовки. Обратитесь в авиакомпанию для индивидуального решения.Решение: запросить SSR заранее (см. Ancillary → Advance booking time) либо обратиться в support.
Min/max stay (ATPCO Cat 6/7):
400: Нет matching rules для route_id=..., pax_type=adult,
days_until_flight=30, stay_days=2Round-trip stay не попадает в окно [min_stay_days, max_stay_days] доступных тарифов. Либо сделайте one-way, либо измените даты return.
Passenger age vs type (Res 728):
422: Пассажир 1: Тип 'infant' допустим только для детей до 2 лет на момент
вылета (текущий возраст: 3.5 лет). Используйте 'child'.Document expiry (Annex 9):
422: Пассажир 1: Документ истекает 2026-08-01 — до даты вылета 2026-08-16.
Обновите документ.Promo per-customer limit (per-email):
{
"applied_rules": [{
"rule_type": "promo_rejected",
"name": "Promo SUMMER25 not applied",
"description": "Reason: per_customer_limit_reached"
}]
}Не 4xx — quote проходит, но без скидки. Клиент уже использовал промо.
Inventory sold out (RBD):
409: {"detail": {"error": "insufficient_availability_no_book_up",
"available": 0, "requested": 1,
"booking_class_code": "Y", "is_open": true}}Все места в bucket'е проданы. Попробуйте другой рейс или premium-class.
Quote expired:
410: {
"error": "quote_not_available",
"message": "Quote ABC123 expired or not found",
"quote_id": "ABC123",
"hint": "Сделайте новый quote через POST /api/v1/fares/quote"
}Quote TTL = 5 минут (300 секунд). Получите новый.
Best practices
Retry policy (TypeScript)
async function withRetry<T>(
fn: () => Promise<T>,
maxRetries = 3
): Promise<T> {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (err: any) {
if (err.status === 429) {
const wait = (err.headers?.["retry-after"] || 1) * 1000 + Math.random() * 200;
await new Promise((r) => setTimeout(r, wait));
continue;
}
if (err.status >= 500 && i < maxRetries - 1) {
await new Promise((r) => setTimeout(r, 2 ** i * 1000));
continue;
}
throw err;
}
}
throw new Error("Max retries exceeded");
}Идемпотентность
Для критичных POST (e.g., refund) используйте уникальный
X-Idempotency-Key заголовок — повторные запросы с тем же ключом
вернут оригинальный ответ без двойного списания.
Логирование
Все 4xx / 5xx ответы содержат заголовок x-request-id — приложите
его при обращении в поддержку, чтобы команда могла найти строки в
логах.
Поддержка
При невозможности понять ошибку:
- B2B:
partners@nurcore.kg - Mobile / Website:
developers@nurcore.kg
Включите в письмо: x-request-id, время запроса, использованный endpoint.