Коды ошибок

Каталог ошибок PayBot API — структура ответов, коды auth, validation, rate_limit, kaspi_error, HTTP-статусы и рекомендации по обработке.

Все ошибки PayBot возвращаются единообразно: JSON с типом, кодом, человекочитаемым сообщением и request_id для дебага.

Формат

json
Скачать
{
  "error": {
    "type": "validation_error",
    "code": "amount_too_small",
    "message": "Минимальная сумма платежа — 100 тенге",
    "request_id": "req_abc123def456",
    "param": "amount",
    "category": "validation"
  }
}
ПолеОписание
typeГруппа: authentication_error, api_error, validation_error, rate_limit_error, proxy_error
codeМашиночитаемый код для switch/case
messageТекст для логов и (опционально) пользователя
request_idУникальный ID запроса — присылайте в поддержку для расследования
paramПоле, вызвавшее ошибку (только для validation_error)
categoryauth, rate_limit, validation, payment, temporary, permanent

request_id также возвращается в HTTP-заголовке X-Request-Id на каждом ответе, даже успешном.

Категории и стратегия retry

КатегорияRetryСтратегия
authнетПроверить ключи, не повторять
validationнетИсправить запрос на стороне клиента
rate_limitдаExponential backoff, см. Rate Limits
temporaryдаBackoff с тем же Idempotency-Key
paymentусловноКли. ошибка Kaspi — обычно не ретраить
permanentнетОбъект уже в финальном состоянии

Authentication errors (401, 403)

CodeHTTPОписание
missing_api_key401Не передан X-API-Key
invalid_api_key401Ключ не существует или отозван
invalid_token401JWT просрочен или плохо подписан
inactive_account403Аккаунт заблокирован
plan_limit_exceeded403Исчерпан лимит платежей по тарифу
feature_not_in_plan403Функция недоступна на вашем тарифе

Validation errors (400)

CodeОписание
amount_too_smallСумма < 100 тенге
amount_too_largeСумма > 1 000 000 тенге
invalid_phoneТелефон не в формате 77XXXXXXXXX
invalid_emailEmail не прошёл базовую валидацию
invalid_urlURL не HTTPS или некорректен
invalid_cursorCursor подделан или повреждён
invalid_limitlimit <= 0 или > 100
idempotency_key_requiredОбязательный Idempotency-Key не передан
idempotency_conflictТот же ключ с другим телом запроса
metadata_too_largeMetadata > 4 KB
description_too_longОписание > 500 символов
unsupported_event_typeНеизвестный тип в фильтре ?type=

Payment errors (402, 422)

CodeHTTPОписание
kaspi_insufficient_funds402На счету клиента недостаточно денег
kaspi_user_blocked422Клиент заблокирован в Kaspi
kaspi_limit_exceeded422Превышен дневной лимит клиента
operation_not_found404Нет такой operation_id
operation_not_refundable422Прошло 90 дней или статус не Processed
amount_too_large_for_refund422Сумма возврата > доступной
phone_not_in_kaspi422У номера нет Kaspi-аккаунта
link_not_active409Платёжная ссылка не в статусе active
link_expired410Срок действия ссылки истёк

Rate limit errors (429)

CodeОписание
rate_limit_exceededПревышен лимит запросов
concurrent_request_limitСлишком много параллельных запросов
creation_throttledСлишком частое создание QR/инвойса с одного IP

Подробнее: Rate Limits.

Server errors (5xx)

CodeHTTPОписаниеRetry
internal_error500Внутренняя ошибка PayBotда
kaspi_unavailable502Kaspi не отвечаетда, с backoff
kaspi_timeout504Kaspi не ответил за timeoutда, с тем же Idempotency-Key
database_unavailable503Временные проблемы БДда
circuit_breaker_open503PayBot временно отключил вызовы Kaspiда, через 30–60 сек

Обработка в коде

Python

python
Скачать
import requests

def safe_create(amount, idem_key):
    try:
        r = requests.post(
            "https://api.paybot.kz/v2/qr",
            headers={"X-API-Key": "kp_live_xxx", "Idempotency-Key": idem_key},
            json={"amount": amount},
            timeout=10,
        )
    except requests.Timeout:
        return retry_with_backoff(amount, idem_key)

    if r.ok:
        return r.json()

    err = r.json().get("error", {})
    code = err.get("code")
    rid  = err.get("request_id")

    if err.get("category") == "temporary":
        return retry_with_backoff(amount, idem_key)

    if code == "rate_limit_exceeded":
        retry_after = int(r.headers.get("Retry-After", 5))
        time.sleep(retry_after)
        return safe_create(amount, idem_key)

    if code in ("invalid_api_key", "inactive_account"):
        raise FatalError(f"check credentials, request_id={rid}")

    raise APIError(f"{code}: {err.get('message')} (request_id={rid})")

Node.js

javascript
Скачать
async function safeCreate(amount, idemKey) {
  const res = await fetch("https://api.paybot.kz/v2/qr", {
    method: "POST",
    headers: {
      "X-API-Key": "kp_live_xxx",
      "Idempotency-Key": idemKey,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ amount }),
  });

  if (res.ok) return res.json();

  const { error } = await res.json();

  if (error.category === "temporary") {
    return retryWithBackoff(amount, idemKey);
  }
  if (error.code === "rate_limit_exceeded") {
    const wait = Number(res.headers.get("retry-after") || "5") * 1000;
    await new Promise(r =&gt; setTimeout(r, wait));
    return safeCreate(amount, idemKey);
  }
  throw new Error(`${error.code}: ${error.message} (rid=${error.request_id})`);
}

Запрос в поддержку

При обращении в поддержку прикладывайте:

  • request_id из заголовка X-Request-Id или поля error.request_id
  • HTTP-код ответа
  • Время запроса (UTC)
  • Тип API-ключа (live / test), последние 4 символа ключа

Это позволяет за 1–2 минуты найти запрос в логах.

Что дальше