NurCore API
Guides

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 коды

КодЗначение
200OK
201Created (POST)
204No Content (DELETE)
400Bad Request — нарушение бизнес-правил
401Unauthorized — нет / невалидный API Key
403Forbidden — нет прав на ресурс
404Not Found
409Conflict — race condition (например, места заняты)
410Gone — broня истекла (15 минут на оплату прошли)
422Unprocessable Entity — валидация payload
429Too Many Requests — rate limit
5xxServer 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=2

Round-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.

On this page