Ошибки
Каждый ответ 4xx/5xx несёт один и тот же JSON-конверт:
{
"code": 400,
"message": "invalid_input",
"details": [
{ "field": "items[0].unit_price", "message": "must be a positive decimal string" }
]
}code— числовой HTTP-статус (400,401,404, …). Полезен для общей обработки на HTTP-уровне, но для логики ветвления переключайтесь поmessage—codeне разделит, например,invalid_inputиpayment_method_not_supported(оба 400).message— sentinel-имя в нижнем snake_case (например,INVALID_INPUTпревращается в"invalid_input"). Стабильно между релизами — переключайтесь по нему.details— заполняется при ошибках валидации. Массив объектов{ field, message }, чтобы клиент мог привязать ошибки к полям ввода. В остальных случаях не указывается.
Тела ошибок не содержат trace ID и timestamp. Чтобы сообщить о проблеме,
отправьте в поддержку заголовок Date ответа, заголовки X-RateLimit-*,
ваш merchant ID, endpoint и приблизительное время запроса.
Ошибки аутентификации используют другую форму. Конверт выше — то, что
API возвращает для большинства ошибок. Запросы, отклонённые до обработки
(отсутствующая/некорректная X-Signature, неизвестный X-Client-ID или
устаревший X-Timestamp на вызове /b2b/v1/*), приходят как
{ "error": "...", "message": "..." }, где error — грубый slug
(unauthorized / bad_request / service_unavailable), а message
несёт конкретику. Числового code и details нет. Сначала ветвитесь по
HTTP-статусу, потом читайте message. Пример (401):
{ "error": "unauthorized", "message": "invalid signature" }HTTP-статус → типичные коды
| HTTP | Типичные значения code | Что значит |
|---|---|---|
| 400 | INVALID_INPUT, MISSING_REQUIRED, INVALID_FORMAT, INVALID_LENGTH, INVALID_VALUE, PAYMENT_METHOD_NOT_SUPPORTED, AMOUNT_BELOW_MINIMUM | Некорректный запрос — смотрите details |
| 401 | INVALID_CREDENTIALS, INVALID_TOKEN, TOKEN_EXPIRED, INVALID_SIGNATURE (B2B); SESSION_EXPIRED (только панель) | Аутентификация не прошла — неверный ключ, истёкший timestamp, неправильная подпись. Коды входа в панель к B2B-интеграциям не относятся. |
| 402 | INSUFFICIENT_CREDIT | Предоплаченный баланс мерчанта закончился — пополните перед повтором |
| 403 | FORBIDDEN, IP_BLOCKED | Ключ валиден, но у него нет scope’а / разрешения по IP для этого вызова |
| 404 | NOT_FOUND, RECORD_NOT_FOUND | Ресурс не существует (или не существует для этого мерчанта) |
| 409 | ALREADY_EXISTS | Idempotency-replay с другим телом, либо state-машина отказала в переходе |
| 429 | TOO_MANY_REQUESTS, TOO_MANY_ATTEMPTS | Достигнут rate-limit; сделайте отступ и повторите |
| 500 | INTERNAL_SERVER_ERROR, EXTERNAL_SERVICE_ERROR | Наша вина; безопасно повторить с отступом. |
| 503 | SERVICE_UNAVAILABLE | Downstream-зависимость не работает. Повторите с отступом |
Полный справочник кодов
Значения code, которые вы можете увидеть:
Аутентификация (401)
INVALID_CREDENTIALS— комбинация API-ключа отклоненаINVALID_TOKEN— токен непарсится или повреждёнTOKEN_EXPIRED— срок действия токена истёкSESSION_EXPIRED— сессия панели истеклаINVALID_SIGNATURE— несовпадение HMAC-подписи на B2B / webhook-вызовах
Авторизация (403)
FORBIDDEN— аутентификация пройдена, но выполнять это действие вам нельзяIP_BLOCKED— запросы с этого IP-адреса заблокированы
Не найдено (404)
NOT_FOUND— общаяRECORD_NOT_FOUND— строка отсутствует по указанному ID
Конфликт (409)
ALREADY_EXISTS— общая
Валидация (400)
INVALID_INPUT— общая; смотритеdetailsMISSING_REQUIRED— обязательное поле отсутствовалоINVALID_FORMAT— значение не соответствовало ожидаемому формату (например, UUID, URL, email)INVALID_LENGTH— значение слишком короткое или слишком длинноеINVALID_VALUE— значение вне допустимого enum/диапазонаPAYMENT_METHOD_NOT_SUPPORTED— комбинация provider/asset не включена для мерчантаAMOUNT_BELOW_MINIMUM— сумма заказа ниже минимального порога (заданного на уровне сети или окружения). Полеdetails.floor_usdв ответе несёт настроенный порог (в USD), так что вы можете показать его напрямую; текстmessageтоже его называет.INSUFFICIENT_BALANCE— на кошельке покупателя недостаточно платёжного актива, чтобы покрыть перевод.INSUFFICIENT_GAS— на кошельке покупателя не хватает нативного газа, чтобы отправить перевод.
Платежи / биллинг (402)
INSUFFICIENT_CREDIT— предоплаченный баланс мерчанта не покрывает комиссию сети (gas) / комиссию платформы. Пополните через панель и повторите
Rate-limit (429)
TOO_MANY_REQUESTS— превышен rate-limit по IPTOO_MANY_ATTEMPTS— повторные неудачные попытки на одном ресурсе (например, OTP) сработали как троттл
Серверные ошибки (500)
EXTERNAL_SERVICE_ERROR— сбой стороннего провайдераINTERNAL_SERVER_ERROR— неожиданная ошибка; обратитесь в поддержку, указав заголовокDateответа и заголовкиX-RateLimit-*
Доступность (503)
SERVICE_UNAVAILABLE— сервис временно недоступен; повторите с отступом
Ошибки валидации (400)
Когда проблема в некорректном теле запроса, details — это массив,
чтобы вы могли сопоставить ошибки с полями:
{
"code": 400,
"message": "invalid_input",
"details": [
{ "field": "items[0].unit_price", "message": "must be a positive decimal string" },
{ "field": "success_url", "message": "must be a valid https URL" }
]
}Ошибки аутентификации (401)
У INVALID_SIGNATURE три типичные причины:
- Неправильный secret-ключ (перепроверьте env var)
- Расхождение по времени > 5 мин (синхронизируйте NTP)
- Неправильная каноническая строка (чаще всего: забыли
\n-разделители или подписали распарсенное-и-пересериализованное тело, отличающееся от отправленного)
См. Аутентификация для точного алгоритма подписи.
Rate-limit’ы (429)
| Уровень | Лимит |
|---|---|
Все маршруты API (включая /b2b/v1/*) | По IP-адресу — 500 запросов/мин, общая корзина. Не per-merchant. |
Публичный checkout (/checkout/*) | Более строгая под-корзина — 20 запросов/мин на IP |
X-RateLimit-Limit и X-RateLimit-Remaining включаются в каждый
rate-limited ответ, а не только в успешные. Считайте их живым бюджетом
для вашего IP.
Rate-limited ответы содержат заголовок Retry-After (секунды до сброса
окна) — учитывайте его. В качестве fallback отступайте с jitter — 1с
базы + экспонента до 30с.
Rate-limit’ы могут меняться. Если вы упираетесь в них при легитимном трафике (например, согласуете большой исторический диапазон), обратитесь в поддержку.
Недостаточный кредит (402)
INSUFFICIENT_CREDIT (HTTP 402 Payment Required) означает, что ваш
предоплаченный баланс не покрывает комиссию сети (gas) и комиссию платформы
для операции, которую вы пытались выполнить, — обычно расчёт крипто-платежа
или on-chain действия с оплатой комиссии сети платформой.
Пополните из панели мерчанта (Billing → Add credit) и повторите
операцию. Уже запущенные операции продолжатся автоматически после
пополнения баланса.
Серверные ошибки (5xx)
500 означает, что мы не смогли обработать запрос. Повторите с отступом
— ваш idempotency_key гарантирует, что вы не получите двойное
списание, если оригинальный запрос частично выполнился.
Если повторы не восстанавливаются в течение минуты, покажите покупателю
обобщённое «Платёж временно недоступен» и обратитесь в поддержку с
проблемным endpoint’ом, вашим merchant ID, заголовком Date ответа и
значениями X-RateLimit-*, а также приблизительным временем запроса —
этого достаточно, чтобы мы нашли запрос.
Ошибки доставки webhook
Доставки webhook — отдельный канал отказов — они не всплывают как ошибки API, потому что вызывает не ваш сервер. Когда доставка возвращает non-2xx (или истекает по времени), она повторяется с экспоненциальным отступом 0с, 1мин, 5мин, 15мин, 1ч, 6ч (всего шесть попыток, то же расписание, что и на Webhooks → Обзор). Полная история по каждой доставке доступна в Developers → Webhooks → [endpoint] → Delivery log в панели. После шестой попытки доставка помечается в панели как «Failed», и вы можете вручную повторить её, когда ваш сервер восстановится.