Skip to main content
Все ответы API возвращаются в едином конверте (envelope). Ошибки имеют машинно-читаемый error.code — именно на него опирайтесь в коде интеграции, а не на текст message.

Формат конверта

{
  "ok": true,
  "data": { /* полезная нагрузка эндпоинта */ }
}
ok
boolean
false для любой ошибки. Проверяйте это поле перед чтением data.
error.code
string
Стабильный машинно-читаемый код. Ветвите логику обработки по нему.
error.message
string
Человекочитаемое пояснение. Текст может меняться между релизами — не парсите его.
error.details
object
Опциональный объект с контекстом. Например, для STATUS_TRANSITION_NOT_ALLOWED{ entity, from, to }; для VALIDATION_FAILED{ errors: [...] }.
error.traceId
string
Идентификатор трассировки. Присутствует, когда запрос дошёл до платформы. Указывайте его при обращении в поддержку — по нему оператор найдёт запрос в логах.
Для ошибок 5xx в production-режиме платформа намеренно скрывает внутренний message ("Internal server error") и не возвращает details — это защита от утечки внутренней информации. Опирайтесь на error.code и traceId.

HTTP-статус и код ошибки

HTTP-статус задаёт семейство ошибки, а error.code уточняет причину. Базовый маппинг, когда конкретный код не выставлен явно:
HTTPКод по умолчаниюСемантика
400 Bad RequestVALIDATION_FAILEDзапрос некорректен
401 UnauthorizedUNAUTHORIZEDне аутентифицирован
403 ForbiddenPERMISSION_DENIEDаутентифицирован, но доступ запрещён
404 Not FoundNOT_FOUNDресурс не найден
409 ConflictALREADY_PROCESSEDконфликт состояния
422 Unprocessable EntityVALIDATION_FAILEDбизнес-правило не выполнено
429 Too Many RequestsRATE_LIMITEDпревышен лимит запросов
503 Service UnavailableMAINTENANCE_MODEсервис временно недоступен
500 Internal Server ErrorINTERNAL_ERRORвнутренняя ошибка

Справочник кодов

Ниже — полный каталог кодов, которые может вернуть Public API, сгруппированный по семейству.

Аутентификация и подпись (400 / 401 / 403)

КодHTTPЗначениеКак устранить
UNAUTHORIZED401Нет X-Api-Id / X-Signature / X-Timestamp, ключ не найден, неактивен или сайт не в статусе activeПроверьте, что присланы все три заголовка и ключ активен в личном кабинете
INVALID_SIGNATURE401X-Signature не совпала с ожидаемойЧаще всего подписаны не те байты тела. См. Аутентификация
TIMESTAMP_SKEW401X-Timestamp вне диапазона ±300 секунд от времени сервераСинхронизируйте часы (NTP) и берите время непосредственно перед запросом
IP_NOT_WHITELISTED403IP запроса не входит в белый список сайтаДобавьте исходящий IP сервера обменника в whitelist
API_KEY_REVOKED403Ключ отозван операторомВыпустите новый ключ в личном кабинете
PERMISSION_DENIED403У ключа недостаточно прав для операции (например, ключ со scope deposit_only пытается создать выплату)Используйте ключ с подходящим scope
LICENSE_REQUIRED403Лицензия инстанса не активированаОбратитесь к оператору инстанса (затрагивает создание сущностей, не уже идущие операции)

Идемпотентность и конфликты (409)

КодHTTPЗначениеКак устранить
DUPLICATE_ORDER_ID409order_id уже использован в рамках сайта (строгий режим по умолчанию)Используйте новый order_id для новой заявки; для безопасных повторов — X-Idempotency-Key. См. Идемпотентность
IDEMPOTENCY_CONFLICT409Конфликт идемпотентного ключа (зарезервированный код). На стандартном создании депозита/выплаты НЕ возникает: повтор с тем же X-Idempotency-Key возвращает первый сохранённый результат без сверки тела. См. ИдемпотентностьНе переиспользуйте один ключ для разных запросов
ALREADY_PROCESSED409Операция уже обработана / ресурс уже существуетЗапросите текущее состояние ресурса и не повторяйте операцию
STATUS_TRANSITION_NOT_ALLOWED409Недопустимый переход статуса (details: { entity, from, to })Сверьтесь со схемой статусов депозита/выплаты
ROTATION_IN_PROGRESS409Идёт ротация ключа/секретаПовторите запрос после завершения ротации
APPROVAL_QUORUM_NOT_MET409Не набран кворум подтверждений (внутренний апрув выплаты)Дождитесь подтверждения операторами на стороне инстанса

Не найдено (404)

КодHTTPЗначениеКак устранить
NOT_FOUND404Ресурс не найденПроверьте uuid / путь
DEPOSIT_NOT_FOUND404Депозит не найден (в т.ч. принадлежащий другому сайту)Проверьте uuid депозита и что он создан этим сайтом
PAYOUT_NOT_FOUND404Выплата не найденаПроверьте uuid выплаты
SITE_NOT_FOUND404Сайт не найденСверьтесь с оператором
PROVIDER_NOT_FOUND404Провайдер не найденВнутренняя ошибка конфигурации — обратитесь к оператору
SECRET_NOT_FOUND404Секрет/ключ не найденПроверьте конфигурацию ключа

Валидация входных данных (400 / 404 / 422)

КодHTTPЗначениеКак устранить
VALIDATION_FAILED400/422Тело запроса не прошло валидацию (details.errors содержит список полей)Исправьте перечисленные поля
INVALID_AMOUNT400Некорректная сумма (нечисловая, отрицательная, ноль)Передавайте сумму строкой, положительным числом
AMOUNT_TOO_MANY_DECIMALS400Знаков после запятой больше, чем поддерживает актив (asset.decimals)Округлите до точности актива (например, 6 для USDT_TRC20, 8 для BTC/LTC)
INVALID_ADDRESS400Некорректный адрес получателя для выбранной сетиПроверьте формат адреса сети
INVALID_MEMO400Некорректный memo/comment (memo-based сети, например TON)Проверьте формат memo
INVALID_NETWORK400Неизвестная или отключённая сетьБерите code сети из GET /v1/public/networks
INVALID_ASSET404Неизвестный актив (не найден в каталоге)Берите code актива из GET /v1/public/assets
INVALID_SYSTEM_WALLET400Некорректная ссылка на системный кошелёкВнутренняя ошибка конфигурации — к оператору

Бизнес-лимиты: суммы, баланс, комиссии (409 / 422)

КодHTTPЗначениеКак устранить
AMOUNT_BELOW_MINIMUM409Сумма меньше минимума по активуСверьтесь с minDeposit / minPayout из GET /v1/public/assets
AMOUNT_ABOVE_MAXIMUM422Сумма больше максимумаУменьшите сумму или разбейте на несколько операций
INSUFFICIENT_HOT_WALLET_BALANCE422Не хватает средств на hot-кошельке для выплатыСверьтесь с GET /v1/public/balances перед выплатой
INSUFFICIENT_NETWORK_FEE_BUDGET422Не хватает нативной валюты на комиссию сетиПополните бюджет комиссии (на стороне инстанса)
INSUFFICIENT_TRON_ENERGY422Не хватает energy для TRC-20 переводаПлатформа управляет energy сама; при повторе обратитесь к оператору
FEE_EXCEEDS_CEILING422Оценённая комиссия сети превысила настроенный потолокПовторите позже (комиссия сети спадёт) или согласуйте потолок с оператором

Политики выплат (400 / 422)

КодHTTPЗначениеКак устранить
PAYOUT_NOT_ALLOWED422Выплата запрещена политикой инстансаУточните политику выплат у оператора
PAYOUT_DESTINATION_BLACKLISTED400Адрес получателя в чёрном списке (санкции/мошенничество) — выплата запрещенаАдрес заблокирован оператором; используйте другой адрес получателя
PAYOUT_WHITELIST_REJECT400Адрес получателя не в whitelist выплат (режим enforced)Добавьте адрес в whitelist выплат (если он используется)
PAYOUT_VELOCITY_REJECT400Сработал лимит частоты/объёма выплатСнизьте темп; повторите позже
PAYOUT_DUPLICATE_SUSPECTED422Похоже на случайный дубль выплаты (та же пара адрес+сумма+актив за короткое окно) без согласованного order_idДля намеренного повтора задайте уникальный order_id или X-Idempotency-Key

Платформа и доступность (429 / 503 / 5xx)

КодHTTPЗначениеКак устранить
RATE_LIMITED429Превышен лимит запросовУважайте Retry-After, применяйте backoff. См. Лимиты запросов
MAINTENANCE_MODE503Инстанс на обслуживанииПовторите позже
READ_ONLY_MODE422Включён режим «только чтение» (write-операции заблокированы)Повторите создание позже; чтение доступно
NETWORK_DISABLED409Сеть временно отключена операторомВыберите другую сеть/актив или дождитесь включения
NO_ACTIVE_PROVIDERS422Нет доступных блокчейн-провайдеров для сетиПовторите позже; при стойком повторе — к оператору
KEYSTORE_LOCKED503Keystore временно заперт (ожидает разблокировки оператором)Повторите позже
PROVIDER_UNAVAILABLE5xxБлокчейн-провайдер недоступенПовторите с backoff
PROVIDER_DEGRADED5xxПровайдер деградировалПовторите позже
ALL_PROVIDERS_FAILED5xxВсе провайдеры сети недоступныПовторите позже; при стойком повторе — к оператору
INTERNAL_ERROR500Внутренняя ошибкаПовторите позже, сохраните traceId для поддержки
DATABASE_ERROR500Ошибка БДПовторите позже, сохраните traceId
KEYSTORE_ERROR500Ошибка keystoreПовторите позже, сохраните traceId

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

Ветвитесь по error.code, а 5xx и 429 — повторяйте с экспоненциальным backoff.
async function callApi(req) {
  const res = await fetch(req.url, req.options);
  const env = await res.json();

  if (env.ok) return env.data;

  const { code, message, traceId } = env.error;
  switch (code) {
    case 'TIMESTAMP_SKEW':
      throw new Error('Часы рассинхронизированы — синхронизируйте NTP');
    case 'DUPLICATE_ORDER_ID':
      // Заявка с таким order_id уже создана — найдите её
      return findByOrderId(req.orderId);
    case 'RATE_LIMITED':
      // Уважаем Retry-After и повторяем
      await sleep(Number(res.headers.get('Retry-After') ?? 1) * 1000);
      return callApi(req);
    case 'INTERNAL_ERROR':
    case 'DATABASE_ERROR':
    case 'PROVIDER_UNAVAILABLE':
    case 'ALL_PROVIDERS_FAILED':
      // Транзиентные — повтор с backoff (см. ниже)
      throw new RetryableError(code, traceId);
    default:
      // Клиентские ошибки — чинить запрос, не повторять
      throw new Error(`${code}: ${message}`);
  }
}
Транзиентные коды (INTERNAL_ERROR, DATABASE_ERROR, PROVIDER_UNAVAILABLE, PROVIDER_DEGRADED, ALL_PROVIDERS_FAILED, RATE_LIMITED, MAINTENANCE_MODE) безопасно повторять — особенно если вы прислали X-Idempotency-Key, повтор не создаст дубликат. Клиентские коды (VALIDATION_FAILED, INVALID_*, DUPLICATE_ORDER_ID, AMOUNT_*) повторять бессмысленно — сначала исправьте запрос.

Частые вопросы

Подписывается сырое тело запроса — ровно те байты, что уходят на сервер. Если вы сериализуете JSON один раз для подписи, а второй раз (с другими пробелами/порядком ключей) для отправки — подпись не сойдётся. Сериализуйте один раз и подписывайте именно эту строку. Подробнее — Аутентификация.
DUPLICATE_ORDER_ID — вы повторно использовали order_id, уже занятый другой заявкой (строгий режим). IDEMPOTENCY_CONFLICT — вы прислали тот же X-Idempotency-Key, но с другим телом запроса. Первое лечится новым order_id, второе — новым ключом либо повтором ровно того же тела. См. Идемпотентность.
Повторяйте с экспоненциальным backoff. Если вы прислали X-Idempotency-Key, повтор не создаст дубликат. Сохраните error.traceId — по нему оператор найдёт запрос в логах инстанса.
Нет. message предназначен для людей и может меняться. Ветвите логику только по error.code — он стабилен.