На странице
Коды ошибок
Обрабатывайте ошибки Merchant API по стабильным машинным кодам, деталям валидации полей и документированным сценариям восстановления.
Каталог кодов
Стабильные машинно-читаемые идентификаторы возвращаются в code (а при ошибке по конкретному полю — в errors[].code). Список — фактически выдаваемый набор: каждый код ниже реально отдаёт хотя бы один обработчик, валидатор или middleware в API. Семантика существующих кодов не меняется, новые коды добавляются под новые сценарии. Переименование кода — редкий случай, и о каждом мы пишем в чейнджлоге.
Используйте code для программной обработки. detail приходит только на английском — для логов и запасного UI; локализуйте на клиенте по code.
401 и 403 — не две ступени одной ошибки. 401 значит, что запрос не прошёл проверку учётных данных: заголовка нет, он повреждён, подпись сделана не тем секретом или слишком давно. Повторить тот же запрос — получить тот же ответ. Чинить нужно на стороне ключа: сам ключ, его настройки в дашборде или часы, по которым вы подписываете.
403 значит, что ключ приняли, а дальше запрос не пропустили. Под одним статусом живут две несвязанные ситуации:
- Ключ не тот —
forbidden,payment_key_required,payout_key_required. Это настройка интеграции: подпишите запрос ключом нужного типа и окружения, и вызов пройдёт. - Ключ в порядке, дело в состоянии аккаунта или проекта —
whitelist_required,merchant_suspended,widget_inactive,terminal_not_enabled,embed_not_enabled,not_sandbox,payment_channel_simulation_sandbox_only. Ключ исправен, менять его бесполезно. Самый частый случай —whitelist_required: адрес получателя ещё не добавлен в белый список для выводов. Покажите это тому, кто ждёт вывод, вместо того чтобы поднимать дежурного из-за рабочего ключа.
HTTP-статус говорит только о том, на каком уровне запрос остановили. Что делать — смотрите в code: ни он сам, ни класс ошибки из вашего SDK этого не скажут.
Коды поля
Возвращаются при валидации запроса. У кодов уровня поля (ниже) field содержит имя поля, не прошедшего проверку, в snake_case. Когда сразу несколько полей не проходят проверку, код верхнего уровня — validation_failed, а разбивка по полям лежит в errors[].
| Code | Когда | Что делать |
|---|---|---|
validation_failed |
Сразу несколько полей не прошли валидацию. Это код верхнего уровня; разбивка по полям — у каждого свой code и field — лежит в errors[]. |
Разберите errors[] и поправьте каждое перечисленное field. |
field_required |
Обязательное поле пустое, null или состоит только из пробелов. |
Передайте значение в field. |
field_too_long |
Поле длиннее допустимого максимума. | Обрежьте до лимита, указанного для этого эндпоинта. |
field_out_of_range |
Числовое значение вне допустимого диапазона. | Приведите значение к документированному диапазону. |
field_invalid_enum |
Значение не входит в набор допустимых значений enum (например, незнакомая currency / network, или токен не включён в проекте). |
Сверьтесь со справочником допустимых значений; для токенов — убедитесь, что пара токен+сеть включена в проекте. |
field_invalid_format |
Значение не соответствует формату поля: либо не разбирается как id (например, invoice_id без префикса inv_…), либо amount передан не простой decimal-строкой вида "10.00". |
Передавайте поле в документированном формате; id — ровно в том виде, в каком их вернул API. |
entity_id_empty |
Переданный id — это нулевой (пустой) GUID (00000000-0000-…). field указывает на конкретный id — например, invoice_id или withdrawal_id. |
Передавайте id ровно в том виде, в каком его вернул API; не отправляйте пустой или обнулённый id. |
field_must_be_positive |
Десятичное или целое число должно быть больше 0. | Передайте положительное значение. |
field_percentage_out_of_range |
Процент должен быть в диапазоне [0, 100] включительно. |
Приведите значение к диапазону [0, 100]. |
query_parameter_unknown |
В запросе списка передан параметр строки запроса, которого нет у этого эндпоинта. | Удалите параметр или используйте его документированное имя в snake_case. |
pagination_cursor_invalid |
Курсор повреждён, истёк, подделан, относится к другому ресурсу или совмещён с другими фильтрами. | Начните пагинацию без cursor, затем сохраняйте фильтры при переходе по next_cursor. |
amount_too_many_decimals |
Для инвойса с суммой в фиатной валюте amount содержит больше знаков после запятой, чем допускает минимальная единица валюты (например, 23.345 для USD, 23.01 для JPY). |
Округлите до минимальной единицы валюты. |
amount_too_large |
amount при создании инвойса или вывода выходит за поддерживаемый диапазон — выше потолка в 20 целых знаков, который хранит API. |
Передайте сумму в пределах поддерживаемого диапазона. |
Резервные коды по HTTP-статусу
Возвращаются, когда у ошибки нет своего бизнес-кода. Каждый код — резервный вариант для HTTP-статуса: запрос не прошёл, а описать причину точнее было нечем.
| Code | HTTP | Когда | Что делать |
|---|---|---|---|
forbidden |
403 | Аутентификация прошла, но этому ключу операция не разрешена: не хватает возможностей (scope) или не то окружение — например, тестовый ключ на эндпоинте только для рабочего режима. | Используйте ключ с нужными возможностями и в нужном окружении. Владение никогда не даёт 403: чужой ресурс возвращает not_found (404), а не forbidden. |
payment_key_required |
403 | Эндпоинту нужен Payment-ключ (pk_), а запрос пришёл с Payout-ключом (rk_) — например, получение списка инвойсов. |
Вызовите его Payment-ключом. |
payout_key_required |
403 | Эндпоинту нужен Payout-ключ (rk_), а запрос пришёл с Payment-ключом (pk_). Так отвечает любая операция с выводами и чтение балансов. |
Вызовите его Payout-ключом. Учтите: Payout-ключу нужен ещё и белый список IP, иначе он не работает. |
internal_error |
500 | Непредвиденная ошибка или баг на нашей стороне. | Повторите один раз. Если не проходит — обратитесь в поддержку и укажите время ответа. |
validation_failed |
400 | Запрос не прошёл валидацию, но кода под конкретное поле не нашлось. | Смотрите поле detail — там написано, что отклонено. |
Аутентификация
Возвращается только на 401 Unauthorized от обработчика HMAC. Каждый код указывает на конкретную точку отказа — клиент может показать осмысленное сообщение вместо общего «auth failed».
| Code | HTTP | Когда | Что делать |
|---|---|---|---|
unauthorized |
401 | Запасной код, когда не подошёл ни один из кодов ниже — обычно запрос к эндпоинту с обязательной аутентификацией без заголовка Authorization (или с нераспознанной схемой). |
Отправьте корректный заголовок HMAC Authorization; проверьте всю сборку подписи. |
authorization_missing |
401 | Заголовок Authorization есть, но его значение пустое. |
Отправьте полный заголовок Authorization: HMAC-SHA256 {apiKeyId}:{base64signature}. |
authorization_malformed |
401 | Заголовок есть, но его формат нарушен: схема распознана, но в учётных данных нет двоеточия, не разбирается id API-ключа или пустая подпись. | Соберите заголовок по разделу «Аутентификация»; чаще всего — опечатка в id API-ключа. |
timestamp_missing |
401 | Заголовок X-Request-Timestamp отсутствует или не разбирается как положительное целое. |
Передайте текущую Unix-метку в секундах. |
timestamp_expired |
401 | Метка вышла за допустимое окно расхождения часов (по умолчанию ±5 минут — слишком в прошлом или слишком в будущем). | Синхронизируйте часы по NTP; пересчитайте подпись со свежей меткой. |
invalid_credentials |
401 | Ключ использовать нельзя: id не найден, ключ отозван, префикс окружения или типа не совпадает с сохранённым ключом либо подпись не сходится. Один код на все четыре случая — намеренно: разные ответы позволили бы тому, у кого нет секрета, выяснить, какие id ключей существуют. | Проверьте, что id ключа верный и ключ активен, а подпись считается секретом того же ключа. Чаще всего причина в подписи: сверьте сборку строки для подписи ({ts}\n{METHOD}\n{path}\n{query}\n{bodyHash}) и то, что для пустого тела bodyHash — пустая строка (не хешируйте пустую строку). |
ip_not_allowed |
401 | Для Payout-ключа задан белый список IP, и адрес клиента в него не входит. Проверка по IP есть только у Payout-ключей — Payment-ключи вызываются с любого адреса. Идёт после успешной подписи, поэтому корректная подпись с «не той» сети всё равно вернёт 401. | Добавьте IP клиента (или его CIDR-блок) в белый список Payout-ключа в дашборде. |
payout_whitelist_required |
401 | У только что созданного Payout-ключа ещё не задан белый список IP. Payout-ключи остаются неактивными, пока мерчант не добавит хотя бы один разрешённый IP — код отделён от ip_not_allowed, чтобы клиент мог отличить «не настроены параметры в дашборде» от «не тот адрес-источник». |
Откройте дашборд, перейдите на страницу API-ключей и добавьте хотя бы один IP или CIDR в белый список Payout-ключа. |
Лимит запросов
Возвращается при 429 Too Many Requests, когда запрос превышает лимит для мерчанта. Окно — одна секунда, и лимит общий для всех ваших API-ключей: своего бюджета у ключа нет. Перед API работает ещё одно ограничение, по IP-адресу; здесь описан лимит мерчанта.
| Code | HTTP | Когда | Что делать |
|---|---|---|---|
rate_limited |
429 | Превышен лимит запросов для мерчанта. Действуют два лимита: общий на все эндпоинты (по умолчанию 30 запросов в секунду) и строже — на создание инвойса, POST /v1/invoices (по умолчанию 5 запросов в секунду). Какой из двух лимитов сработал, ответ не сообщает — в нём приходит только пауза до следующей попытки. |
Прочитайте заголовок Retry-After (пауза в секундах) и повторите запрос после неё. Большинство HTTP-клиентов с повторами учитывают Retry-After автоматически. |
Транспорт и системные ошибки
Возвращаются системным обработчиком ошибок, когда запрос не дошёл до обработчика самого эндпоинта.
| Code | HTTP | Когда | Что делать |
|---|---|---|---|
malformed_request |
400 | Тело запроса не удалось разобрать — повреждённый JSON. | Проверьте, что тело — валидный JSON. |
payload_too_large |
413 | Тело запроса превышает лимит в 1 MiB. | Отправьте тело меньшего размера; разбейте крупные объёмы на отдельные запросы. |
unsupported_media_type |
415 | Content-Type не application/json. |
Выставьте Content-Type: application/json. |
method_not_allowed |
405 | Путь существует, но не для этого HTTP-метода. | Используйте метод, указанный для эндпоинта (например, POST для создания). |
route_not_found |
404 | Ни один эндпоинт не подходит под этот путь. | Сверьте URL со справочником API. |
Инвойсы
| Code | HTTP | Когда | Что делать |
|---|---|---|---|
invoice_not_found |
404 | Инвойс с таким id не существует или принадлежит другому мерчанту. | Используйте id, возвращённый POST /v1/invoices, без изменений. |
project_not_found |
404 | project_id в запросе на создание инвойса не существует или не виден вызывающему. |
Используйте project id из дашборда (проект должен быть активен и принадлежать тому же мерчанту, что и API-ключ). |
invoice_terminal |
410 | confirm-payment вызван на инвойсе со статусом expired или cancelled. (Инвойс paid возвращает invoice_not_awaiting_client, 409, а не этот код.) |
Обновите статус инвойса; делать больше нечего. |
invoice_deadline_passed |
410 | confirm-payment вызван после истечения срока инвойса. |
Создайте новый инвойс. |
requested_amount_invalid |
400 | amount в запросе на создание инвойса отсутствует, не разбирается, выходит за поддерживаемый диапазон десятичных значений или не положителен. |
Передайте amount положительной десятичной строкой вместе с выбранной currency и при необходимости — network. |
invoice_amount_below_minimum |
400 | USD-эквивалент инвойса ниже минимума для токена/сети из каталога. | Увеличьте сумму выше минимума, возвращённого для выбранного токена. |
deposit_amount_exceeds_limit |
400 | Сумма превышает максимум, допустимый для этого платежа. Проверяется рассчитанная сумма инвойса в момент подтверждения токена и сети, а не фактический приход в блокчейне. | Уменьшите сумму инвойса; если так задача не решается, напишите в поддержку. |
currency_not_enabled_for_project |
400 | Запрошенная криптовалюта не входит в enabled_tokens проекта (ни в одной сети). |
Включите валюту в проекте либо выберите из enabled_tokens. |
tokens_required |
409 | Создание инвойса или подтверждение платежа для проекта, где не включён ни один токен. | Включите в проекте хотя бы один токен через дашборд. |
acceptance_disabled |
503 | Запрошенный токен сейчас не принимается. | Используйте другой токен или попробуйте позже. |
exchange_rate_unavailable |
503 | Нет свежего курса для пары токен/фиат. | Повторите запрос; курсы обновляются часто. |
network_unavailable |
503 | При автоподтверждении не удалось использовать выбранную сеть. | Откажитесь от автоподтверждения; дайте клиенту выбрать токен на странице оплаты Paymos. |
payment_method_unavailable |
503 | Выбранный токен или сеть сейчас не принимают платёж. С 23.08.2026 заменяет no_available_address, address_lease_failed и bridge_unavailable. |
Повторите запрос или дайте клиенту выбрать другой токен либо сеть. |
client_confirmation_failed |
409 | Подтверждение со стороны клиента (выбор токена, автоподтверждение) отклонено. detail содержит причину. |
Смотрите detail; типично — инвойс уже не в awaiting_client или токен не разрешён. |
invoice_not_awaiting_client |
409 | confirm-payment вызван на инвойсе, уже вышедшем из awaiting_client. |
Обновите статус инвойса; клиент уже прошёл выбор. |
invoice_cannot_be_cancelled |
409 | Инвойс уже не в awaiting_client — клиент выбрал токен либо инвойс уже paid или expired. Повторная отмена уже отменённого инвойса возвращает 200, а не эту ошибку. |
Проверьте статус; отмена работает только в awaiting_client. |
simulate_payment_failed |
409 | Вызов simulate_payment для тестового режима отклонён. |
Смотрите detail; обычно — несоответствие статуса или суммы. |
not_sandbox |
403 | simulate-payment вызван для инвойса рабочего режима. |
Симуляция доступна только для тестового инвойса. По рабочему отправьте реальный перевод в выбранной сети. |
Инвойсы через виджет
Эти коды возвращает эндпоинт POST /public/v1/sdk — создание инвойса через Low-Code SDK.
| Code | HTTP | Когда | Что делать |
|---|---|---|---|
widget_key_missing |
401 | Заголовок X-Sdk-Key отсутствует или пуст на эндпоинте, где нужна авторизация виджета. |
Передайте публичный SDK-ключ в заголовке X-Sdk-Key. |
widget_key_invalid |
401 | X-Sdk-Key передан, но не распознаётся как идентификатор ключа Paymos. |
Скопируйте ключ из дашборда; проверьте префикс и что в значение не попали лишние пробелы или URL-кодирование. |
widget_key_not_found |
401 | Ключ распознан, но активной записи за ним нет: он отозван, удалён или это не Payment-ключ (ключи Payout виджетом не управляют). | Возьмите активный Payment-ключ из дашборда. |
widget_inactive |
403 | Виджет проекта выключен или ещё ни разу не был инициализирован. | Включите виджет в настройках проекта. |
terminal_not_enabled |
403 | Запрос пришёл с source=terminal, но способ подключения проекта — не «Терминал». |
Создайте проект типа «Терминал» либо передавайте source=embed из Low-Code-проекта. |
embed_not_enabled |
403 | Запрос пришёл с source=embed, но способ подключения проекта — не Low-Code. |
Используйте способ подключения самого проекта либо создайте Low-Code-проект. |
origin_not_allowed |
401 | Браузерный Origin не входит в список разрешённых доменов. Проверок две — на уровне авторизации origin сверяется с объединённым списком всех проектов ключа с активным виджетом, затем обработчик сверяет его с конкретным проектом запроса, — и обе отвечают одинаково: тот же 401, тот же код, тот же detail. Это сделано намеренно. Другой статус на второй проверке подсказал бы владельцу ключа pk_ (он публичный, его нетрудно вытащить со страницы), что домен зарегистрирован в другом проекте того же мерчанта, и тестовые хосты и невыпущенные бренды можно было бы перебрать по одному запросу за раз. |
Впишите домен в поле Разрешённый origin проекта. Значение одно, не список: под второй домен нужен второй проект. Запросы с того же домена, откуда отдаётся платёжная страница — например, с POS-терминала на том же хосте, — проверку пропускают. |
Платёжные каналы
| Code | HTTP | Когда | Что делать |
|---|---|---|---|
payment_channels_disabled |
503 | Платёжные каналы не включены для вашего аккаунта в этой среде. Так отвечают все методы каналов и депозитов, включая симулятор песочницы. | Обратитесь в поддержку для подключения. Повтор запроса ответ не изменит. |
payment_channel_not_found |
404 | Идентификатор pc_ не соответствует ничему, доступному этому ключу: канала нет, он принадлежит другому мерчанту, находится в другой среде или в проекте вне области видимости ключа. Все четыре случая намеренно неразличимы. |
Используйте id, возвращённый POST /v1/payment-channels, без изменений и подписывайте ключом той же среды и того же проекта. |
payment_channel_deposit_not_found |
404 | Идентификатор pcd_ не соответствует ничему, доступному этому ключу. Причины те же четыре. |
Используйте id из payload вебхука или из ленты без изменений. |
payment_channel_external_id_invalid |
400 | external_id пуст, состоит из пробелов или длиннее 128 символов — при создании либо как фильтр списка. |
Передайте непустой идентификатор длиной не более 128 символов. |
payment_channel_project_has_no_supported_tokens |
409 | В проекте не включён ни один токен, который платёжный канал мог бы принимать, поэтому у канала не будет ни одного маршрута. | Включите в проекте хотя бы один поддерживаемый токен и создайте канал заново. |
payment_channel_simulation_sandbox_only |
403 | simulate-deposit вызван для канала рабочей среды. |
Симуляция существует только для каналов песочницы. Канал рабочей среды проверяется настоящим переводом. |
payment_key_required |
403 | Запрос подписан ключом выплат (rk_). Доступ к каналам даёт ключ Payment — так же, как выплаты работают через ключ Payout. |
Подпишите запрос ключом pk_ той же среды. |
pagination_cursor_invalid |
400 | Курсор списка или ленты повреждён, старше 24 часов или выдан для других фильтров; для ленты — также для другого мерчанта, среды или набора проектов. | Начните с первой страницы. Дедупликация по pcd_ делает полную пересинхронизацию безвредной. |
currency_not_enabled_for_project |
400 | Токен, переданный в simulate-deposit, не включён в проекте. |
Включите его в проекте или выберите тот, который канал уже возвращает в networks[].tokens. |
acceptance_disabled |
503 | Приём указанного токена сейчас отключён на уровне платформы. | Используйте другой токен или дождитесь включения приёма. |
amount_too_many_decimals |
400 | В сумме симуляции больше знаков после запятой, чем поддерживает токен. Деньги мерчанта не округляются молча до другого числа. | Округлите сумму до точности токена перед отправкой. |
Выводы
| Code | HTTP | Когда | Что делать |
|---|---|---|---|
withdrawal_not_found |
404 | Вывода с таким id нет либо он принадлежит другому мерчанту. | Используйте id, который вернул POST /v1/withdrawals, без изменений. |
insufficient_balance |
409 | Доступный баланс мерчанта ниже amount + fee. |
Пополните баланс или уменьшите сумму вывода. |
whitelist_required |
403 | Адрес назначения не добавлен в белый список для выбранной сети. (Сеть белый список поддерживает — просто этот адрес ещё не внесён.) | Добавьте адрес в белый список и повторите запрос. |
destination_address_invalid |
400 | Адрес не проходит проверку формата или контрольной суммы для этой сети. | Сверьте адрес со спецификацией сети. |
withdrawal_amount_below_minimum |
400 | Сумма ниже минимума для выбранного токена. | Увеличьте сумму выше минимума (возвращается в detail). |
withdrawal_quota_exceeded |
409 | Достигнут лимит активных выводов. | Дождитесь завершения текущих или запросите повышение лимита. |
withdrawal_amount_exceeds_limit |
400 | USD-эквивалент тела вывода превышает лимит на одну выплату по вашему аккаунту. Считается по телу вывода, комиссия сети в эту сумму не входит. | Уменьшите сумму или запросите повышение лимита в поддержке. |
exchange_rate_unavailable |
503 | Чтобы сверить сумму вывода с лимитом аккаунта, нужен свежий курс токен/USD, а его в этот момент нет. Лимит не подтвердить, поэтому запрос отклоняется. | Повторите запрос; курсы обновляются часто. |
withdrawal_not_enabled |
400 | Paymos не выводит запрошенную пару токен/сеть. Пара названа в detail — например Withdrawals are not available for USDT on TRC20. Набор пар, доступных для вывода, задаётся на стороне Paymos. |
Выведите токен в сети, по которой Paymos делает выплаты, либо спросите в поддержке про нужную пару. |
withdrawal_cannot_be_cancelled |
409 | Вывод уже вышел из состояния, в котором его можно отменить. | Проверьте текущий статус. Окно закрывается, когда начинается исполнение, — ещё внутри created, до всякого подписания. |
withdrawal_simulation_failed |
409 | Вызов simulate_completion для тестового режима отклонён. |
Смотрите detail. |
merchant_suspended |
403 | Мерчант приостановлен для исходящих операций, поэтому создать вывод нельзя. | Обратитесь в поддержку, чтобы снять приостановку. |
outbound_frozen |
503 | Исходящие операции заморожены — либо на всей платформе, либо для той сети и токена, в которых вы выводите. | Дождитесь снятия, попробуйте другую сеть или токен либо обратитесь в поддержку. |
withdrawal_network_unavailable |
503 | Вывод этого токена в этой сети сейчас провести нельзя. | Попробуйте другую сеть назначения или повторите позже. |
Состояние проекта
Возвращается при создании инвойса или подтверждении платежа, если статус проекта не позволяет их выпускать.
| Code | HTTP | Когда | Что делать |
|---|---|---|---|
project_state_invalid |
409 | Проект не в статусе Active — он Suspended или Archived. Какой именно, ответ не сообщает. |
Посмотрите статус проекта в дашборде: архивный — восстановите, приостановленный — активируйте. Либо отправьте запрос в уже активный проект. |