{
  "format": "x-hub-error-catalog/1",
  "url": "https://docs.x-hub.online/reference/errors/",
  "description": "Коды ошибок X-Hub API. Тело ответа: {\"error\": {\"code\": ..., \"message\": ..., \"details\": {}}}. Поля when/action — текст со страницы, разметка маркдауна сохранена.",
  "errors": [
    {
      "code": "VALIDATION_ERROR",
      "http_status": 400,
      "when": "Невалидные параметры запроса. Отдельный случай с HTTP `422` — `client_phone` в `POST /payments` не в формате E.164: ожидается «+» и 10–15 цифр после него. Ещё один случай с `422` — значение в объекте `payer` не прошло правила полей, `path` вида `payer.full_name`; форма самого объекта (не объект, нет обязательного поля, лишний ключ) — обычный `400`, см. [объект `payer`](../payments/index.md#payer-object)",
      "action": "Проверь `details`, исправь запрос. Для телефона — приведи его к E.164 и повтори запрос"
    },
    {
      "code": "INVALID_JSON",
      "http_status": 400,
      "when": "Тело запроса — не валидный JSON. Проверка тела идёт до авторизации, поэтому код приходит и на невалидных ключах",
      "action": "Пошли корректный JSON с заголовком `Content-Type: application/json`"
    },
    {
      "code": "MISSING_IDEMPOTENCY_KEY",
      "http_status": 400,
      "when": "Нет заголовка `Idempotency-Key` на POST",
      "action": "Добавь UUID"
    },
    {
      "code": "KYC_REQUIRED",
      "http_status": 400,
      "when": "Клиент не прошёл KYC (если для аккаунта настроена KYC-проверка)",
      "action": "Отправь клиента на KYC-верификацию"
    },
    {
      "code": "KYC_PROCESSING",
      "http_status": 400,
      "when": "KYC-верификация ещё обрабатывается",
      "action": "Повтори через несколько минут"
    },
    {
      "code": "KYC_FAILED",
      "http_status": 400,
      "when": "KYC-верификация отклонена",
      "action": "Клиент должен пройти KYC заново"
    },
    {
      "code": "KYC_NOT_REQUIRED",
      "http_status": 400,
      "when": "Вызов KYC-эндпоинта, когда KYC не настроен для аккаунта",
      "action": "KYC не нужен, просто создавай платёж"
    },
    {
      "code": "KYC_PROVIDER_ERROR",
      "http_status": 502,
      "when": "Проверяющая сторона не ответила при **создании платежа** (не при вызове `/kyc/*`)",
      "action": "Временная ошибка. Ретрай через 30–60 сек с тем же `Idempotency-Key`"
    },
    {
      "code": "PROVIDER_DISABLED",
      "http_status": 403,
      "when": "KYC для твоего аккаунта отключён. Возвращают **все** `/kyc/*` — до любых других проверок",
      "action": "Ретрай не поможет — свяжись с X-Hub. См. [Ошибки KYC](../exchange/kyc.md#kyc-errors)"
    },
    {
      "code": "QR_PAYMENTS_DISABLED",
      "http_status": 403,
      "when": "Аккаунт настроен **только на рекуррентные списания** — разовые платежи по QR ему запрещены",
      "action": "Не создавай `POST /payments`; работай через `/subscriptions`. Нужен приём разовых — напиши в X-Hub"
    },
    {
      "code": "CARD_NOT_SUPPORTED",
      "http_status": 400,
      "when": "Запрошен `payment_method: \"card\"`, а приём карт твоему аккаунту не подключён. Платёж не создаётся",
      "action": "Создавай платёж с `payment_method: \"sbp\"`. Нужен приём карт — напиши в X-Hub, это настройка аккаунта, флагом в запросе не включается. Подробнее — [оплата картой](../payments/index.md#card-payment)"
    },
    {
      "code": "PAYMENT_ROUTE_UNAVAILABLE",
      "http_status": 400,
      "when": "На `POST /payments`: для запрошенного способа оплаты у твоего аккаунта нет рабочего маршрута — например, способу оплаты не назначен терминал. Платёж не создаётся. Редкий вариант — тот же код с HTTP `503`: маршрут не удалось проверить из-за временного сбоя на нашей стороне",
      "action": "`400` — ретрай не поможет, пока не изменится настройка аккаунта: напиши в X-Hub. `503` — временный сбой: ретрай через 30–60 сек с тем же `Idempotency-Key`. Подробнее — [отказы при создании платежа](../payments/index.md#create-refusals)"
    },
    {
      "code": "PAYMENT_UNAVAILABLE",
      "http_status": 422,
      "when": "На `POST /payments`: приём по этому способу оплаты для твоего аккаунта сейчас недоступен. Платёж не создаётся. Слово «temporarily» в `message` значит «до изменения настройки на стороне X-Hub», а не «через минуту»",
      "action": "Ретрай не поможет — повтор получит тот же ответ. Напиши в X-Hub. Подробнее — [отказы при создании платежа](../payments/index.md#create-refusals)"
    },
    {
      "code": "SANDBOX_NOT_SUPPORTED",
      "http_status": 400,
      "when": "Эндпоинт отключён в sandbox для твоего аккаунта (у части конфигураций — `/kyc/*`)",
      "action": "Тестируй этот флоу в боевом режиме или свяжись с X-Hub"
    },
    {
      "code": "AMOUNT_BELOW_MINIMUM",
      "http_status": 400,
      "when": "Сумма разового платежа (`POST /payments`) ниже минимума — 30 ₽, одинаково для всех аккаунтов. Списания по уже выданному мандату подписки порогу не подчиняются",
      "action": "Увеличь сумму — точное значение минимума приходит в `message`"
    },
    {
      "code": "AMOUNT_TOO_LOW",
      "http_status": 400,
      "when": "Сумма меньше минимальной для операции (пример: обмен < 1000 ₽)",
      "action": "Увеличь сумму"
    },
    {
      "code": "AMOUNT_TOO_HIGH",
      "http_status": 400,
      "when": "Сумма превышает максимум",
      "action": "Разбей на несколько операций или свяжись с X-Hub"
    },
    {
      "code": "CLIENT_NOT_REGISTERED",
      "http_status": 422,
      "when": "Клиент не прошёл KYC-регистрацию для USDT→RUB",
      "action": "Сначала зарегистрируй клиента через `/kyc/register`"
    },
    {
      "code": "CLIENT_BLOCKED",
      "http_status": 403,
      "when": "Аккаунт клиента заблокирован",
      "action": "Свяжись с X-Hub"
    },
    {
      "code": "UNAUTHORIZED",
      "http_status": 401,
      "when": "Неверные `X-Api-Key` / `X-Api-Secret`",
      "action": "Проверь ключи"
    },
    {
      "code": "MODE_MISMATCH",
      "http_status": 401,
      "when": "Ключ не-активного режима (test/live) — активен другой режим",
      "action": "Переключи режим в кабинете или используй ключи активного режима"
    },
    {
      "code": "IP_NOT_WHITELISTED",
      "http_status": 403,
      "when": "IP-адрес не в whitelist мерчанта",
      "action": "Добавь IP в настройках или свяжись с X-Hub"
    },
    {
      "code": "PAYMENT_NOT_FOUND",
      "http_status": 404,
      "when": "Платёж не существует / чужой",
      "action": "Проверь ID"
    },
    {
      "code": "NOT_FOUND",
      "http_status": 404,
      "when": "Обмен не существует / чужой; или такого пути в `/api/v1` нет",
      "action": "Проверь ID и путь"
    },
    {
      "code": "WITHDRAWAL_DISCONTINUED",
      "http_status": 410,
      "when": "Запрос к выведенным из эксплуатации эндпоинтам `/withdrawals`",
      "action": "Ничего не делать — выплаты происходят автоматически, см. [Выплаты](../money/withdrawals.md#vyplaty-avtomaticheskie)"
    },
    {
      "code": "DELIVERY_PROOF_LOCKED",
      "http_status": 409,
      "when": "`POST /payments/:id/delivery-proof`: хэш по платежу уже подтверждён, заменить его через API нельзя",
      "action": "Если хэш передан по ошибке, напиши в X-Hub. См. [хэш перевода клиенту](../exchange-terminal/index.md#step-delivery-proof)"
    },
    {
      "code": "DELIVERY_PROOF_CONFLICT",
      "http_status": 409,
      "when": "`POST /payments/:id/delivery-proof`: хэш изменён параллельным запросом",
      "action": "Прочитай состояние запросом `GET /payments/:id/delivery-proof` и повтори запрос"
    },
    {
      "code": "EXCHANGE_IN_PROGRESS",
      "http_status": 409,
      "when": "Обмен уже в обработке",
      "action": "Дождись финального статуса"
    },
    {
      "code": "INVALID_STATUS",
      "http_status": 409,
      "when": "Операция невалидна для текущего статуса (напр. попытка отменить уже завершённый обмен; хэш перевода клиенту по платежу не в статусе `paid` или `settled`)",
      "action": "Проверь статус перед действием"
    },
    {
      "code": "IDEMPOTENCY_TERMINAL",
      "http_status": 409,
      "when": "Повтор запроса с тем же `idempotency_key` после финального статуса",
      "action": "Используй новый ключ"
    },
    {
      "code": "RATE_INVALID",
      "http_status": 502,
      "when": "Временная ошибка получения курса для обмена",
      "action": "Ретрай через 30-60 сек"
    },
    {
      "code": "PROVIDER_UNAVAILABLE",
      "http_status": 503,
      "when": "Внешняя платёжная система временно недоступна. **Операция могла пройти** — мы не увидели ответ",
      "action": "Ретрай **с тем же `Idempotency-Key`** через 30-60 сек. Второй платёж не создастся"
    },
    {
      "code": "PROVIDER_NOT_CONFIGURED",
      "http_status": 503,
      "when": "Операция для твоего аккаунта сейчас недоступна на стороне X-Hub. Так же сейчас отвечают списания по подпискам в боевом режиме — см. [Подписки](../subscriptions/index.md#subscriptions)",
      "action": "Свяжись с X-Hub. На списаниях по подпискам повтор ответ не изменит"
    },
    {
      "code": "KYC_UNAVAILABLE",
      "http_status": 503,
      "when": "На `/kyc/*`: аккаунту положен KYC, но проверка сейчас недоступна на нашей стороне. Это **не** «KYC не нужен» — не пропускай верификацию клиента",
      "action": "Ретрай через 1–2 мин; если долго — напиши в X-Hub"
    },
    {
      "code": "KYC_PERSIST_FAILED",
      "http_status": 500,
      "when": "На `POST /kyc/register`: проверяющая сторона клиента приняла, но у нас не сохранилась запись об этом (сбой на нашей стороне уже ПОСЛЕ успешного ответа проверяющей стороны)",
      "action": "**Не повторяй запрос с теми же данными** — клиент мог быть создан у проверяющей стороны, повтор для части конфигураций навсегда ломает повторную регистрацию по тому же e-mail/телефону. Напиши в X-Hub с временем запроса и телефоном/e-mail клиента — восстановим вручную"
    },
    {
      "code": "RATE_UNAVAILABLE",
      "http_status": 503,
      "when": "`GET /rates`: источник курса временно недоступен",
      "action": "Ретрай через 30-60 сек"
    },
    {
      "code": "EXCHANGE_RATE_NOT_SUPPORTED",
      "http_status": 404,
      "when": "Запрос курса по способу оплаты: курс не отдаётся ни для одного терминала",
      "action": "Не запрашивай курс — повтор ответ не изменит"
    },
    {
      "code": "FEATURE_NOT_ENABLED",
      "http_status": 400,
      "when": "Опция не подключена **на твоём аккаунте** (например USDT→RUB или KYC; хэш перевода клиенту — по платежу, по которому он не принимается, в том числе в тестовом режиме)",
      "action": "Свяжись с X-Hub для подключения"
    },
    {
      "code": "FEATURE_DISABLED",
      "http_status": 503,
      "when": "Направление временно недоступно **у всех** мерчантов. Твоя интеграция исправна",
      "action": "Подожди и ретрай. В поддержку — если держится долго"
    },
    {
      "code": "RATE_LIMIT_EXCEEDED",
      "http_status": 429,
      "when": "Превышен лимит. Создание платежа: 120 req/min на ключ **или** 120 req/min на IP. Остальные вызовы: 60 req/min на ключ **или** 300 req/min на IP",
      "action": "Жди `Retry-After` секунд, см. [Rate Limiting](../start/auth.md#rate-limiting)"
    },
    {
      "code": "INTERNAL_ERROR",
      "http_status": 500,
      "when": "Серверная ошибка",
      "action": "Ретрай через 30 сек, если повторяется — пиши команде"
    },
    {
      "code": "SUBSCRIPTION_NOT_FOUND",
      "http_status": 404,
      "when": "Подписка не существует / чужая / другого режима",
      "action": "Проверь ID"
    },
    {
      "code": "CHARGE_MANDATE_INACTIVE",
      "http_status": 409,
      "when": "Списание по неактивному мандату (подписка не `active`)",
      "action": "Дождись активации / возобнови подписку"
    },
    {
      "code": "CAP_EXCEEDED",
      "http_status": 409,
      "when": "Списание превышает `cap_amount_rub` или `cap_window_rub`",
      "action": "Уменьши сумму или измени потолки (PATCH)"
    },
    {
      "code": "INVALID_STATE",
      "http_status": 409,
      "when": "Недопустимый lifecycle-переход подписки (pause/resume/cancel)",
      "action": "Проверь текущий `status`"
    },
    {
      "code": "RECURRING_NOT_SUPPORTED",
      "http_status": 501,
      "when": "Подписки недоступны для твоего аккаунта: подписка или списание не создаются",
      "action": "Ретрай не поможет — напиши в X-Hub. См. [Подписки](../subscriptions/index.md#subscriptions)"
    },
    {
      "code": "PROVIDER_ERROR",
      "http_status": 502,
      "when": "Операция отклонена платёжной системой. На `POST /subscriptions` — не удалось инициировать привязку мандата; на `POST /:id/charges` — **отказ по списанию, включая отказ банка**; на `/kyc/*` — проверяющая сторона не ответила",
      "action": "Для привязки и KYC — повтори через 30–60 сек. Для списания это чаще окончательный отказ, см. [Ошибки списания](../subscriptions/api.md#charge-errors)"
    },
    {
      "code": "REFUND_NOT_FOUND",
      "http_status": 404,
      "when": "Заявка на возврат не существует / чужая / из другой среды. Приходит на чтение и на отмену",
      "action": "Проверь ID и режим (test/live)"
    },
    {
      "code": "PAYLOAD_TOO_LARGE",
      "http_status": 413,
      "when": "Тело запроса больше лимита (100 КБ). Проверка тела идёт до авторизации",
      "action": "Уменьши тело запроса, например `metadata`"
    },
    {
      "code": "NOT_REFUNDABLE",
      "http_status": 400,
      "when": "Платёж не в возвратном статусе",
      "action": "Возврат возможен только по оплаченному платежу, см. [Возвраты](../payments/refunds.md#refunds)"
    },
    {
      "code": "CANNOT_CANCEL",
      "http_status": 409,
      "when": "Заявку уже взяли в работу или закрыли",
      "action": "Отменить нельзя — деньги могли уйти. Дождись финального статуса"
    },
    {
      "code": "PARTIAL_DISABLED",
      "http_status": 400,
      "when": "Прислана `amount_rub`, а частичные возвраты выключены",
      "action": "Не присылай сумму — вернётся весь остаток"
    },
    {
      "code": "PARTIAL_BEFORE_SETTLEMENT",
      "http_status": 400,
      "when": "Частичный возврат до ежедневного расчёта по способу оплаты, где начисление идёт по фактическому поступлению",
      "action": "Вернуть полностью или частично после расчёта"
    },
    {
      "code": "OVER_REMAINING",
      "http_status": 400,
      "when": "Сумма больше остатка по платежу",
      "action": "Остаток смотри в `remaining_rub` через `GET /refunds/:id`"
    },
    {
      "code": "INVALID_AMOUNT",
      "http_status": 400,
      "when": "Сумма нулевая или отрицательная",
      "action": "Исправь сумму"
    },
    {
      "code": "LIMIT_PER_PAYMENT",
      "http_status": 409,
      "when": "Больше 10 заявок на один платёж",
      "action": "Разбирайся с уже созданными или пиши команде"
    },
    {
      "code": "SHOP_NOT_FOUND",
      "http_status": 400,
      "when": "Магазина с таким `shop_id` нет в твоём аккаунте (ответ один и для чужого, и для несуществующего)",
      "action": "Проверь `shop_id` в кабинете"
    },
    {
      "code": "SHOP_DISABLED",
      "http_status": 403,
      "when": "Магазин выключен",
      "action": "Включи магазин в кабинете или передай другой"
    },
    {
      "code": "SHOP_MARKUP_NOT_SET",
      "http_status": 422,
      "when": "Магазину ещё не назначена ставка",
      "action": "Напиши в X-Hub"
    },
    {
      "code": "PAYER_DATA_REQUIRED",
      "http_status": 422,
      "when": "На `POST /payments`: выбранный способ оплаты требует данные плательщика (`client_phone` / `client_email` / `description` / `payer`), которых нет в запросе. В `details.missing_fields` — имена недостающих полей; `payer` в списке значит, что объекта `payer` в запросе нет вовсе. Платёж не создаётся. Если `payer` передан, но не прошёл проверку, ответ — `VALIDATION_ERROR` (`400` — форма объекта, `422` — значения полей), а не этот код",
      "action": "Повтори запрос **с тем же `Idempotency-Key`**, добавив недостающие поля: неудачная попытка ключ не расходует. Подробнее — [данные плательщика](../payments/index.md#body) и [объект `payer`](../payments/index.md#payer-object)"
    },
    {
      "code": "IDEMPOTENCY_KEY_REUSED",
      "http_status": 409,
      "when": "Тот же `Idempotency-Key` с другими параметрами. Другой `shop_id` в `POST /subscriptions` даёт этот ответ всегда. На возвратах (`POST /refunds`) действует всегда. На `POST /payments`, `POST /exchanges` и `POST /subscriptions` этот ответ пока не включён: повтор с другими параметрами возвращает исходную операцию. О включении объявим отдельно",
      "action": "Это не ретрай, а ошибка — новая операция требует нового ключа. Используй новый `Idempotency-Key` для каждой новой операции"
    }
  ]
}
