К содержанию
ActionPulse

Ошибки

Канонический конверт ошибки ActionPulse API, полный перечень машинных кодов, таблица HTTP-статусов, какие ответы имеет смысл повторять и чего в ответе нет намеренно.

Кому
Разработчикам, обрабатывающим ответы API
Проверено
На этой странице

Любой отказ ActionPulse API — один и тот же JSON-объект с машинным кодом внутри. Эта страница нужна, когда вы пишете обработку ответов: здесь полный перечень кодов, их HTTP-статусы, разбор того, что повторять, а что нет, и список того, чего в ответе принципиально не будет. Какие коды объявлены у конкретного метода — видно на его странице в справочнике API.

Конверт ошибки

Все ошибки — и приёма событий, и основного API — приходят в одной оболочке. Другого формата отказа в продукте нет.

{
  "error": {
    "code": "validation_failed",
    "message": "batch is empty",
    "details": {}
  }
}
Поле Обязательно Что в нём
error.code да Машинный код из перечня ниже. Ветвитесь только по нему.
error.message да Короткая строка для человека и логов. Не стабильна, не для сравнения в коде.
error.details нет Объект произвольной формы. Набор ключей зависит от метода и описан в его ответе.

details не типизирован в спецификации: у одних отказов он пустой, у других несёт полезное. Задокументированные ключи, на которые можно опираться:

Ключ Где приходит Значение
billing_url 402 trial_expired, 402 subscription_required Адрес страницы биллинга в кабинете
retryable 409 privacy_job_active true — операцию можно повторить позже
field 422 validation_failed Имя параметра или поля, которое не прошло проверку
org_name_masked, owner_email_masked 409 domain_has_org Маскированные признаки уже существующей организации
rotation_required 403 credential_rotation_required true — нужен перевыпуск ключа

Машинные коды

Полный перечень кодов, которые может содержать error.code. Порядок — как в спецификации.

Код HTTP Когда возникает Что делать
unauthorized 401 Ключа или токена нет; значение не разбирается, отозвано, истекло либо не того типа для выбранного способа передачи Проверить заголовок и значение, обновить сессию. Повтор с тем же значением не поможет
forbidden 403 Запрос аутентифицирован, но роли не хватает Выполнить под ролью, у которой есть право; роли: owner, admin, analyst, viewer
credential_scope_forbidden 403 Тип ключа или его явный scope не разрешён на этом маршруте приёма Взять ключ нужного типа и с нужным scope — см. типы ключей
credential_origin_forbidden 403 У браузерного токена не совпал точный Origin страницы Выпустить токен со всеми нужными адресами: маски и поддомены не подставляются
credential_rotation_required 403 Ключ выпущен до перехода на привязанную к организации связку личности и не может писать identify/alias Перевыпустить ключ, сохранив профиль
not_found 404 Объекта нет; объект есть, но принадлежит другой организации; маршрут не смонтирован в этом режиме развёртывания Проверить идентификатор и режим развёртывания. Не считать 404 доказательством отсутствия
validation_failed 422, у части методов 400 или 413 Тело или параметры не прошли проверку: неизвестное поле, пустой список, слишком длинный курсор, превышение байтового лимита тела Исправить запрос. Повтор без изменений бесполезен
quota_exceeded 429 Исчерпана квота плана организации Дождаться следующего периода или сменить план
rate_limited 429 Превышен лимит частоты запросов Повторить не раньше, чем через Retry-After секунд
query_timeout 408 или 504 Запрос не успел в отведённое этому методу время Сузить период, фильтры или размер страницы и повторить
license_invalid 403 на записи в on-prem, 422 при установке лицензии Лицензия отсутствует, недействительна, просрочена (режим только чтение) либо её статус временно недоступен Установить действующий ключ лицензии; чтение при этом продолжает работать
license_signer_unavailable 503 В спецификации у кода нет описания ни в одном ответе — он объявлен только в перечне конверта. Возникает на внутренней стороне выпуска лицензий В клиентских маршрутах не появляется
idempotency_conflict 409 Описания в ответах спецификации нет; во внешней части API код не встречается. Конфликт повторно использованного Idempotency-Key у публичного маркера деплоя отвечает validation_failed Ветвиться по статусу 409, а не по этому коду
admin_effect_pending 503 Описания в ответах спецификации нет: код объявлен только в перечне конверта и относится к внутренним служебным операциям В клиентских маршрутах не появляется
dogfood_unavailable 503 Описания в ответах спецификации нет: код объявлен только в перечне конверта и относится к внутреннему служебному проекту В клиентских маршрутах не появляется
demo_project 409 Операция недоступна для демо-проекта: ключи приёма не выдаются, импорты отключены, повторный посев данных уже идёт Работать в обычном проекте
feature_locked 403 Возможность не входит в текущий план или в проверенный набор возможностей лицензии. Тот же код приходит как причина отказа отдельного элемента пакета для управляемых событий диагностики Включить возможность в плане; остальные элементы пакета при этом принимаются
feature_locked_trial 403 Возможность закрыта политикой пробного периода и открывается после оплаты Дождаться оплаты плана
trial_expired 402 Пробный период закончился без оплаты: приём, изменения и закрытые планом чтения приостановлены Оплатить план; адрес — в details.billing_url
subscription_required 402 Нет действующей подписки и нет живого пробного периода Выбрать план; адрес — в details.billing_url
trial_quota_exceeded 429 На приёме событий исчерпан суммарный лимит событий пробного периода — приходит вместо quota_exceeded Повторы не помогают: нужна оплата или конверсия. Retry-After в этом ответе нет
trial_pending_review 403 Аккаунт удерживается анти-фрод проверкой, приём приостановлен до подтверждения Дождаться подтверждения аккаунта
domain_has_org 409 На регистрации: адрес принадлежит корпоративному домену, на котором уже есть организация Продолжить запросом инвайта или заявкой на отдельную компанию — проверка адреса при этом остаётся действующей
disposable_email 422 Домен адреса регистрации принадлежит известному сервису одноразовой почты Зарегистрироваться с рабочим адресом
sms_unavailable 409 Подтверждение по SMS неприменимо к аккаунту в его текущем состоянии Дождаться ручной проверки: активация пойдёт другим путём
phone_already_used 409 Тот же номер уже использован организацией, чей пробный период начался менее 90 дней назад Отправить аккаунт на ручную проверку
sms_send_failed 502 Провайдер не смог отправить код подтверждения Повторить позже; аккаунт остаётся на ручной проверке
confirmation_required 422 У необратимой операции нет обязательного заголовка подтверждения Добавить заголовок подтверждения, указанный в описании метода
privacy_job_active 409 Активное удаление данных субъекта временно ограждает identify и alias Повторить позже: details.retryable=true
picker_token_consumed 409 Одноразовый токен выбора элемента уже использован Выпустить новый токен
internal 500 Внутренняя ошибка сервиса Повторить с задержкой. Тело всегда одинаково и деталей не содержит

Отдельно стоит помнить про два кода, которых в этом перечне нет: credential_event_forbidden и schema_violation — это причины отказа отдельного элемента пакета событий, они приходят внутри rejected в успешном ответе 202, а не в конверте ошибки. Полный список таких причин — в лимитах приёма.

HTTP-статусы

Статусы, которые объявлены в спецификации хотя бы у одного метода.

Статус Что означает
200 Успех с телом
201 Объект создан
202 Запрос принят в обработку, но работа не закончена: приём событий, постановка задач удаления, повторный посев демо-данных, возобновление импорта
204 Успех без тела: удаление объекта и ответ на браузерный preflight
303 Только возврат из внешнего провайдера входа: сессия установлена, продолжайте по адресу из Location
304 Условный GET: присланный If-None-Match совпал, тела нет. Только у неизменяемых файлов браузерного SDK и у конфигурации трекера
400 Те же ошибки проверки с кодом validation_failed, что и 422, — так отвечают захват элемента пикером и правила именованных событий. Две страницы отказа от рассылки отдают на 400 HTML, а не конверт
401 Учётных данных нет или они не приняты
402 Доступ приостановлен по оплате
403 Аутентифицированы, но не разрешено
404 Объекта нет, он не ваш либо маршрут не смонтирован
408 Таймаут запроса, код query_timeout
409 Конфликт состояния
413 Тело больше байтового лимита метода; код в теле — validation_failed
422 Ошибка проверки запроса
429 Лимит частоты или квота плана
501 Возможность не настроена в этом развёртывании — так отвечает элемент-пикер, если для него нет секрета
502 Внешний провайдер не выполнил операцию: только отправка SMS-кода на активации пробного периода
503 Зависимость временно недоступна
504 Тот же query_timeout, что и 408

408 и 504 — один и тот же отказ по времени с одинаковым телом; какой из них придёт, зависит от метода. Обрабатывайте их одной ветвью.

Статус 500 не объявлен ни у одной операции, но существует: код internal приходит именно с ним. Ветвь «любой 5xx» в клиенте обязательна.

Что имеет смысл повторять

Ответ Повтор Как повторять
Ошибка транспорта, обрыв соединения да Экспоненциальная задержка
408, 504 query_timeout да, но иначе Сначала сузьте период, фильтры или размер страницы
429 rate_limited да Не раньше, чем через Retry-After секунд
429 quota_exceeded, trial_quota_exceeded нет Повтор не изменит квоту; Retry-After здесь не приходит
409 privacy_job_active да details.retryable=true, повторяйте с задержкой
500 internal, 503 да, с оговоркой ниже Экспоненциальная задержка
401, 402, 403, 404, 413, 422, остальные 409 нет Нужно менять запрос, ключ, план или состояние объекта

Retry-After приходит в секундах и равен времени до конца окна лимита; у части маршрутов это фиксированное значение. У основного API заголовок открыт браузерному коду через Access-Control-Expose-Headers, а у кросс-доменного приёма событий он наружу не выставлен — код на странице его не прочитает и должен опираться на собственную задержку. В машинной спецификации заголовок не объявлен: читайте его как обычный заголовок ответа и предусмотрите поведение на случай его отсутствия.

У приёма событий отдельного заголовка идемпотентности нет: там роль ключа повтора играет event_id внутри самого события. Подробности — в отправке событий с бэкенда.

Чего в ответе нет

Ответ об отказе сознательно беден. Это не недоработка, а граница безопасности.

Ещё три вещи, которых в ответе не будет:

  • Разных сообщений для разных причин 401. Приём событий различает только «учётных данных нет вообще» и «данные не приняты»: обрезанный, неразбираемый, отозванный, истёкший и не подходящий по типу ключ дают один и тот же текст. Различать эти случаи по ответу нельзя — сверяйтесь со списком ключей проекта.
  • Признака «нет прав» отдельно от «нет объекта». См. оговорку про 404 выше.
  • Внутренних деталей ошибки. У internal тело всегда одинаково, без трассировки стека, имён таблиц и текстов от зависимостей. Для разбора нужен идентификатор запроса из ваших логов, а не тело ответа.

Что проверить в своём обработчике

  1. Ветвление идёт по error.code, а не по message и не только по статусу.
  2. Есть ветвь «неизвестный код»: перечень кодов пополняется, и незнакомый код должен приводить к понятной ошибке, а не к падению разбора.
  3. 408 и 504 обрабатываются одинаково, любой 5xx — включая необъявленный 500.
  4. При 429 читается Retry-After, а не используется фиксированная пауза; на quota_exceeded и trial_quota_exceeded повторов нет.
  5. Изменяющие запросы не повторяются вслепую на 503.
  6. Успешный 202 на приёме событий проверяется на непустой rejected — иначе часть событий потеряется молча. Дерево диагностики такого случая — в решении проблем.
  7. Тело ответа об ошибке не попадает в пользовательский интерфейс целиком: message написан для инженера, а не для клиента.