Коды ошибок и ограничения

Что вернёт API, когда баллов не хватает, карта не найдена или ключ отозван. Разбирайте код, а не текст сообщения.

Ошибка API — это всегда JSON одного вида: машинный код, человеческий текст и, где есть что добавить, details. Разбирайте code: текст сообщения может измениться, код — нет.

Любая ошибка

{
  "error": {
    "code": "REDEEM_LIMIT_EXCEEDED",
    "message": "Превышен лимит списания баллов",
    "details": { "maxAllowed": 425, "requested": 500 }
  }
}

Общие

КодHTTPЧто случилось
VALIDATION_ERROR400Запрос не прошёл проверку. Если виновато поле — в details.fields имя поля и что с ним не так. Сюда же: нет заголовка Idempotency-Key, не похоже на номер телефона, неверный код подтверждения, несколько программ без programId
UNAUTHORIZED401Ключ не передан, недействителен или отозван
FORBIDDEN403Ключу не хватает области доступа — недостающие названы в тексте. Или компания приостановлена платформой: ключ работает снова, только когда приостановку снимут
NOT_FOUND404Объекта нет — или он принадлежит другой компании: чек для возврата, карта в истории, программа, сотрудник в staffId. Сюда же попадает чек с чужой или выдуманной точкой продаж: на ней держатся и правила, и выручка в отчётах
CONFLICT409Запись с такими данными уже существует — в details.fields названы поля
RATE_LIMITED429Слишком много запросов. В details.retryAfterSec — через сколько секунд повторить

Лояльность

КодHTTPЧто случилось
CARD_NOT_FOUND404Карты с таким штрихкодом нет, у гостя с таким телефоном нет действующей карты, или вместо номера пришла строка, на номер не похожая
CARD_BLOCKED409Карта не действует — заблокирована или удалена по просьбе гостя. Операции по ней запрещены
REDEEM_LIMIT_EXCEEDED409Списание больше потолка: доли чека по правилам (в том числе правилам этой точки) или баланса карты. В details.maxAllowed — сколько было можно, в details.requested — сколько просили
INSUFFICIENT_POINTS409На карте меньше баллов, чем просят списать; details.available и details.requested. В POST /transactions вместо него приходит REDEEM_LIMIT_EXCEEDED: потолок не бывает выше баланса
COUPON_NOT_FOUND404Кода купона нет или купон выдан другому гостю; код — в details.code
COUPON_UNUSABLE409Купон есть, но погасить его нельзя: погашен, отозван, ещё не начал действовать, срок вышел, гость уже пользовался промокодом. Причина — в details.reason, её можно повторить гостю
PROGRAM_INACTIVE409Программа выключена — чеки по ней не проводятся, карты не выдаются
OPERATOR_NOT_NAMED409Заведение не указало в кабинете юридическое название и ИНН — карты не выдаются, пока оно не назовёт себя оператором персональных данных
CARDS_LIMIT_REACHED409Исчерпан лимит карт по тарифу — новые карты не выдаются и коды новым гостям не отправляются. Уже выданные карты работают
CODE_QUOTA_EXCEEDED429Квота подтверждений заведения перебрана втрое — коды не отправляются
TRANSACTION_ALREADY_REFUNDED409Этот чек уже возвращён целиком; details.transactionId
IDEMPOTENCY_KEY_REUSED422Тот же Idempotency-Key прислан с другим телом запроса

Инфраструктура

КодHTTPЧто случилось
INTERNAL_ERROR500Ошибка на нашей стороне. Повторить можно, но не сразу
SERVICE_UNAVAILABLE503Сервис перегружен или недоступен. В details.retryAfterSec — через сколько секунд повторить

Остальные коды — CAPTCHA_REQUIRED, EMAIL_NOT_VERIFIED, TENANT_ACCESS_REVOKED, PROMO_INVALID, PASS_NOT_FOUND — отдают страница выдачи карты, кабинет и страница гостя; запросам по ключу API они не приходят. CAPTCHA_REQUIRED увидит и тот, кто берёт песочницу.

Ограничения

ЧтоПределЧто будет сверх
Все запросы к API с одного IP-адреса300 в минутуОтказ до конца минуты. Предел общий для всех ключей и методов, включая чтение карт и возвраты: касса за общим адресом делит его с соседями. Остаток — в заголовках x-ratelimit-remaining и x-ratelimit-reset, после отказа — Retry-After
Коды подтверждения на один номерНе чаще раза в минутуНе ошибка: POST /customers/phone-code отвечает sent: false и retryAfterSec
Коды на один номер за суткиПять платных (Telegram, звонок, SMS) и пять писемИсчерпаны платные — остаётся только письмо; исчерпано и оно — RATE_LIMITED
Попытки ввести кодПять, код живёт пять минутVALIDATION_ERROR — запросите новый код
Квота подтверждений заведенияПо тарифу, в месяц и в суткиСверх квоты платные каналы закрываются, письмо остаётся; сверх тройной — CODE_QUOTA_EXCEEDED
Idempotency-KeyУникален в компании; тело сверяется суткиДругое тело в первые сутки — IDEMPOTENCY_KEY_REUSED; повтор всегда отдаёт уже проведённый чек
ЧекСумма до 10 000 000 ₽, до 500 позицийVALIDATION_ERROR

Пределы на номер и квоту подтверждений держат не ради нас, а ради заведения: платные каналы оплачиваются из его квоты и пакета, и перебор чужих номеров через вашу интеграцию тратил бы его деньги. Предела по адресу и проверки «я не робот» у запросов по ключу нет — адрес принадлежит вашему серверу, а не гостю.

  • RATE_LIMITED — сбавьте темп и повторите не раньше details.retryAfterSec; у общего предела по адресу то же число приходит и в заголовке Retry-After. Разумная стратегия — удвоение задержки.
  • Песочница ограничена отдельно: три выдачи в сутки с адреса, 30 в час и 500 живых на всю платформу — см. её страницу.

Что смотреть при разборе

  • Журнал действий в кабинете — что менялось и кто это сделал.
  • «Выпущенные ключи» на вкладке «Интеграции» — когда ключом пользовались последний раз и чей адрес пришёл в User-Agent.
  • «Доставки» у адреса на вкладке «Вебхуки» — код ответа вашего сервера или ошибка, число попыток и время следующей.
  • История карты — GET /cards/:id/history: движение баллов, включая сгорание и ручные правки.
  • status.snovacard.ru — состояние сервисов, если ошибки пошли разом и у всех.

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

Чем отличается 404 «карты нет» от 404 «чужая компания»?

Ничем, и это намеренно: ответ «объект есть, но не ваш» сообщал бы посторонним факт существования карты. Изоляция компаний проверяется на уровне базы, а не кода, поэтому чужая строка просто не видна.

Стоит ли разбирать текст сообщения?

Нет. Текст пишется для человека и может измениться в любой момент — например, стать понятнее. Машинный контракт — это поле code и поля details.

Как отличить временную ошибку от постоянной?

По HTTP-статусу: 5xx — наша сторона, повтор оправдан; 4xx — запрос нужно исправить, повтор ничего не изменит. Исключение — 429: он временный, но требует паузы, а не немедленного повтора.

Первые 20 карт — бесплатно. Программа настраивается за вечер.

Создать программу