# Каталог ошибок — что возвращает API и что делать

> Все основные коды ошибок Qut Pay API по группам: авторизация, привязка Kaspi, счета, возвраты, лимиты тарифа, вебхуки, подписки. Причина и решение для каждой.

## Коротко

Каждая ошибка приходит в виде `{ "error": "код", "message": "пояснение" }`. HTTP-статус говорит о типе: **4xx** — что-то не так в запросе, нужно исправить и повторить; **429** — лимит, нужно подождать; **5xx** — временный сбой у нас или на стороне Kaspi, повтор допустим.

Пишите интеграцию всегда по коду `error`, а не по тексту `message`: текст может измениться, код — нет.

## Авторизация и ключ

| Код | HTTP | Что случилось | Что делать |
|---|---|---|---|
| `unauthorized` | 401 | Ключ не передан или недействителен | Проверьте заголовок `X-API-Key` |
| `invalid_api_key` | 422 | Неверный формат ключа | Ключ должен быть `qp_live_…` или `qp_test_…` |
| `insufficient_scope` | 403 | У ключа нет прав на это действие | Добавьте нужный scope ключу в кабинете |
| `forbidden` | 403 | Ресурс принадлежит не вашей организации | Проверьте, что ключ и счёт из одной организации |
| `account_blocked` | 403 | Аккаунт заблокирован | Напишите в поддержку |
| `organization_inactive` | 410 | Организация в архиве или удалена | Напишите в поддержку |
| `not_sandbox` | 403 | Действие доступно только в песочнице | Например, симуляция оплаты не работает в боевом режиме |

`forbidden` и `invoice_not_found` связаны и с привязкой ключа к кассиру: привязанный ключ не видит счета другого кассира. Подробно: [Привязка API-ключа к кассиру](/kb/ru/api-key-connection).

## Привязка Kaspi

| Код | HTTP | Что случилось | Что делать |
|---|---|---|---|
| `kaspi_session_expired` | 409 | Привязка кассира оборвалась | Переподключите: [Привязка оборвалась](/kb/ru/connection-lost) |
| `kaspi_session_not_configured` | 409 | Кассир ещё не подключён | Кабинет → Kaspi → Добавить кассира |
| `connection_not_active` | 400 | Подключение есть, но неактивно | Переподключите этого кассира |
| `connection_not_found` | 404 | Указанный `connection_id` не найден | Проверьте идентификатор |
| `no_provider` | 409 | В организации нет ни одного рабочего кассира | Подключите кассира или перейдите в песочницу |
| `connection_has_keys` | 409 | К кассиру привязан API-ключ, удалить нельзя | Сначала переведите ключ на другого кассира |
| `cashier_number_rejected` | 502 | Kaspi не показал экран ввода кода | Номер не подходит: [Три условия](/kb/ru/cashier-number-requirements) |

## Создание счёта

| Код | HTTP | Что случилось | Что делать |
|---|---|---|---|
| `invalid_amount` | 422 | Сумма отсутствует или не число | Передайте положительное число |
| `amount_too_small` | 422 | Сумма меньше минимума | Увеличьте сумму |
| `amount_too_large` | 422 | Сумма больше максимума | Уменьшите или разбейте на части |
| `amount_must_be_whole_tenge` | 422 | Переданы тиыны | Передавайте целые тенге, Kaspi не принимает копейки |
| `invalid_phone` | 422 | Неверный формат телефона | Формат `7XXXXXXXXXX`, 11 цифр |
| `invalid_kind` | 422 | Неизвестный тип счёта | `qr` или `phone` |
| `invoice_create_failed` | 502 | Kaspi не принял счёт | Повторите; если повторяется — в поддержку |
| `invoice_not_found` | 404 | Счёта нет или он вам не виден | Проверьте идентификатор и кассира у ключа |

## Статус счёта, отмена, возврат

| Код | HTTP | Что случилось | Что делать |
|---|---|---|---|
| `invoice_not_open` | 409 | Счёт не открыт: оплачен, истёк или отменён | Прочитайте статус через `GET /invoices/{id}` |
| `invoice_changed` | 409 | Статус счёта изменился, пока вы работали | Перечитайте статус и решите заново |
| `invoice_not_refundable` | 409 | Этот счёт вернуть нельзя | Проверьте, оплачен ли он и не вышел ли срок |
| `invalid_refund_amount` | 422 | Неверная сумма возврата | Не должна превышать оплаченную |
| `refund_failed` | 502 | Kaspi не выполнил возврат | Проверьте, хватает ли средств на счёте Kaspi |
| `refund_unknown` | 502 | Kaspi не ответил, результат неизвестен | Не повторяйте, прочитайте статус счёта |
| `refund_pending_unknown` | 409 | Результат прошлого возврата ещё неизвестен | Подождите, не отправляйте второй раз |
| `refund_state_conflict` | 409 | Состояние возврата отличается от ожидаемого | Перечитайте статус счёта |
| `cancel_failed` | 502 | Kaspi не отменил счёт | Повторите или дождитесь, пока счёт истечёт сам |

Если при возврате пришёл `refund_unknown` или `refund_pending_unknown`, **не повторяйте запрос** — можно вернуть деньги дважды. Прочитайте статус счёта и узнайте результат оттуда.

## Тариф и лимиты

| Код | HTTP | Что случилось | Что делать |
|---|---|---|---|
| `tariff_limit_reached` | 429 | Месячный лимит счетов исчерпан | Поднимите тариф или дождитесь нового месяца |
| `tariff_daily_burst` | 429 | Слишком много счетов за сутки | Это не бизнес-лимит, а защита от зациклившейся интеграции. Проверьте код |
| `tariff_inactive` | 403 | Тариф неактивен или пробный период закончился | Кабинет → Тариф |
| `rate_limited` | 429 | Превышена частота запросов | Смотрите заголовок `Retry-After` и повторите |
| `request_rate_limited` | 429 | Превышена общая частота запросов | Снизьте частоту |
| `too_many_attempts` | 429 | Слишком частые повторы одного действия | Подождите минуту |

`tariff_daily_burst` и `tariff_limit_reached` — разные вещи. Первое суточная защита, обычно это цикл в коде. Второе — месячный лимит вашего тарифа. Подробно: [Тарифы и лимиты](/kb/ru/tariff-limits).

## Вебхуки

| Код | HTTP | Что случилось | Что делать |
|---|---|---|---|
| `invalid_url` | 422 | Неверный адрес | Укажите полный адрес |
| `webhook_url_requires_https` | 422 | HTTP-адрес не принимается | Используйте HTTPS |
| `webhook_url_requires_domain` | 422 | IP или адрес без домена | Укажите настоящий домен |
| `webhook_url_tunnel_forbidden` | 422 | Адрес временного туннеля | Используйте постоянный домен |
| `invalid_events` | 422 | Неверный список событий | Сверьтесь со списком поддерживаемых событий |
| `too_many_endpoints` | 422 | Превышено число вебхуков | Удалите ненужные |
| `endpoint_not_found` | 404 | Вебхук не найден | Проверьте идентификатор |
| `hook_paused` | 410 | Форм-хук остановлен | Включите заново в кабинете |

Если вебхук не доходит, причина чаще не в коде ошибки: [Вебхук не приходит](/kb/ru/webhook-not-arriving).

## Подписки

| Код | HTTP | Что случилось | Что делать |
|---|---|---|---|
| `invalid_interval` | 422 | Неверный интервал | `day`, `week` или `month` |
| `invalid_every` | 422 | Неверная кратность | Целое положительное число |
| `invalid_retry` | 422 | Неверная лестница повторов | Не больше 5 значений, каждое положительное |
| `invalid_misfire` | 422 | Неверная политика пропуска | `run_once` или `skip` |
| `invalid_max_runs` | 422 | Неверное максимальное число запусков | Целое положительное число |
| `subscription_closed` | 409 | Подписка завершена или остановлена | Создайте новую |
| `subscription_not_found` | 404 | Подписка не найдена | Проверьте идентификатор |

## Ссылки на оплату

| Код | HTTP | Что случилось | Что делать |
|---|---|---|---|
| `slug_taken` | 409 | Такой адрес уже занят | Выберите другое имя |
| `link_invalid` | 410 | Ссылка недействительна | Создайте новую |
| `link_paused` | 410 | Ссылка остановлена | Включите заново в кабинете |
| `too_many_links` | 409 | Превышено число ссылок | Удалите ненужные |

## Вход в кабинет

| Код | HTTP | Что случилось | Что делать |
|---|---|---|---|
| `invalid_code` | 400 | Неверный код | Введите заново |
| `challenge_expired` | 410 | Срок кода истёк | Запросите новый |
| `challenge_used` | 409 | Код уже использован | Запросите новый |
| `no_channel` | 503 | Нет канала для отправки кода | Напишите в поддержку |
| `channel_forbidden` | 403 | Этот канал вам недоступен | Выберите другой |

## Можно ли повторять

| Тип | Повтор |
|---|---|
| 4xx (422, 400, 403, 404, 409) | Нет. Повторять без исправления запроса бессмысленно |
| 429 | Да, но после времени из `Retry-After` |
| 502, 503 | Да, с растущей паузой (например 1, 2, 4, 8 секунд) |
| `refund_unknown`, `refund_pending_unknown` | **Нет.** Сначала прочитайте статус счёта |

Чтобы повтор при создании счёта не создал дубль, передавайте `idempotencyKey`: при повторной отправке с тем же ключом вернётся прежний счёт, а не новый.

## Вопросы и ответы

**Текст ошибки приходит на русском?** Поле `message` приходит на казахском. Пишите интеграцию по коду `error`.

**Пришёл код, которого нет в списке.** Смотрите полную спецификацию на [api.qut.kz/docs](https://api.qut.kz/docs) или напишите в поддержку.

**Какую ошибку можно показать покупателю?** Никакую напрямую. Покажите клиенту «оплата не прошла, попробуйте ещё раз», а технический текст запишите в свой журнал.
