Ошибки
Канонический конверт ошибки ActionPulse API, полный перечень машинных кодов, таблица HTTP-статусов, какие ответы имеет смысл повторять и чего в ответе нет намеренно.
На этой странице
Любой отказ 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тело всегда одинаково, без трассировки стека, имён таблиц и текстов от зависимостей. Для разбора нужен идентификатор запроса из ваших логов, а не тело ответа.
Что проверить в своём обработчике
- Ветвление идёт по
error.code, а не поmessageи не только по статусу. - Есть ветвь «неизвестный код»: перечень кодов пополняется, и незнакомый код должен приводить к понятной ошибке, а не к падению разбора.
408и504обрабатываются одинаково, любой5xx— включая необъявленный500.- При
429читаетсяRetry-After, а не используется фиксированная пауза; наquota_exceededиtrial_quota_exceededповторов нет. - Изменяющие запросы не повторяются вслепую на
503. - Успешный
202на приёме событий проверяется на непустойrejected— иначе часть событий потеряется молча. Дерево диагностики такого случая — в решении проблем. - Тело ответа об ошибке не попадает в пользовательский интерфейс целиком:
messageнаписан для инженера, а не для клиента.