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

Схема событий и свойства

Полный состав события в запросе приёма ActionPulse: обязательные и необязательные поля, типы значений свойств, служебные поля приёма, зарезервированные имена и время.

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

Приём событий принимает ровно одну структуру, и она короткая. Ниже — каждое поле, которое существует в запросе, что с ним делает сервер и чего в запросе нет.

Состав запроса

{
  "batch": [
    {
      "event_id": "8b0d3f2e-1c4a-4f0e-9b1d-2a3c4d5e6f70",
      "event": "order_created",
      "user_id": "YOUR_USER_ID",
      "anon_id": "3f1c9d2a-5b6e-4c8f-9a0b-1d2e3f4a5b6c",
      "session_id": "9c7e5b3a-1d2f-4e6a-8b0c-2d4f6a8b0c1e",
      "time": "2026-07-28T09:41:07.512Z",
      "url": "https://example.com/checkout",
      "referrer": "https://example.com/cart",
      "schema_version": 1,
      "props": { "plan": "pro", "total": 990, "trial": false }
    }
  ],
  "context": {
    "sdk": { "name": "my-backend", "version": "1.4.2" },
    "app_version": "2026.07.3",
    "release": "a1b2c3d"
  }
}

На верхнем уровне существует ровно три поля.

Поле Обязательно Назначение
batch да массив событий, до 500 элементов; пустой массив — ошибка
context нет диагностические метаданные всего пакета
credential нет браузерный токен в теле запроса вместо заголовка

context содержит source, sdk с полями name и version, app_version и release. Это метки для диагностики: они не дают никаких прав и не влияют на то, чем событие считается. Настоящий источник события приём выводит из типа ключа — браузерный токен даёт браузерный источник, серверный ключ — серверный.

credential существует для одного случая: браузер отправляет последний пакет при закрытии страницы через sendBeacon, где нельзя поставить заголовок. Принимается только браузерный токен и только когда заголовка Authorization и параметра запроса нет. Серверный ключ в теле не принимается никогда.

Поля события

Поле Обязательно Что делает приём
event да проверяет по ^[a-z0-9_.]{1,128}$; иначе отказ элемента invalid_event
user_id нужно одно из двух до 256 байт, без нулевого байта; становится субъектом события
anon_id нужно одно из двух те же ограничения; используется как субъект, если user_id нет
event_id нет, но нужен разбирает как UUID и приводит к нижнему регистру; если поля нет — генерирует своё значение
session_id нет обрезает до 256 байт; больше ничего не проверяет
time нет разбирает RFC 3339 и переводит в UTC; см. раздел о времени
props нет ограничивает количество и размер, вырезает секреты и персональные данные
url нет обрезает до 2048 байт, вычитывает UTM-метки, затем редактирует
referrer нет обрезает до 2048 байт и редактирует
schema_version нет целое ≥ 0; 0 означает текущую версию из реестра событий

Полей больше нет. В частности, в запросе нет и не может быть: IP-адреса, страны, устройства, браузера, метки согласия, идентификатора проекта. Проект определяется ключом, остальное приём выводит сам.

Если вы подключаетесь браузерным трекером, вручную заполнять почти ничего не нужно: event_id, anon_id, session_id, time, url и referrer он подставляет сам, от вас нужны имя события и свойства. Именно поэтому time в браузерных событиях приходит с часов посетителя — см. раздел о времени.

Ограничения, которые обрезают, и ограничения, которые отклоняют

Это разные вещи, и путать их дорого:

  • session_id, url и referrer обрезаются по длине — событие принимается с укороченным значением, и в ответе об этом ничего не будет;
  • слишком длинный user_id или anon_id отклоняет элемент — событие не принимается;
  • нарушение лимитов props тоже отклоняет весь элемент, а не отдельное свойство: одно тяжёлое значение теряет всё событие.

Полная таблица порогов и кодов — в статье про лимиты приёма.

Типы значений свойств

Значением свойства может быть любое значение JSON: строка, число, логическое значение, null, объект или массив. Вложенность допускается.

Ограничение на значение считается в байтах его JSON-представления, поэтому вложенный объект расходует лимит вместе с кавычками и скобками.

Что происходит с типом, которого в JSON нет

Браузерный трекер получает значения из JavaScript, где типов больше, чем в JSON. Он не пытается их угадывать, а заменяет на "[redacted]":

Значение в JavaScript Что уйдёт на сервер
undefined, функция, Symbol, BigInt "[redacted]"
Date, Map, Set, экземпляр класса "[redacted]"
Значение из геттера "[redacted]"
Вложенность глубже 12 уровней весь объект свойств заменяется на {}
Больше 1000 значений внутри весь объект свойств заменяется на {}
Циклическая ссылка весь объект свойств заменяется на {}

Практический вывод: дату отправляйте строкой (date.toISOString()), а не объектом Date; коллекции разворачивайте в массивы. Обратите внимание, что последние три случая теряют все свойства события, а не одно — это сознательный отказ в безопасную сторону.

Если вы отправляете события по HTTP сами, эти правила к вам не относятся: в теле запроса уже JSON. Но действуют правила размера и правила редакции.

Служебные поля, которые заполняет приём

Ни одно из этих значений нельзя задать из запроса — их вычисляет сервер.

Поле события Откуда берётся
server_time время приёма запроса, UTC
actor_id user_id, если он есть, иначе anon_id
user_id подставляется из связки, если в событии был только anon_id
source тип ключа: браузерный токен или серверный ключ
device_type User-Agent: bot, tablet, mobile или desktop
os семейство операционной системы из User-Agent
browser семейство браузера из User-Agent
country страна по IP-адресу запроса, если в установке подключена база геоданных
utm_source, utm_medium, utm_campaign параметры запроса в url события
event_id генерируется, если в элементе его не было

UTM-метки читаются из url до редакции адреса, поэтому вырезание секретов и персональных данных из ссылки не ломает атрибуцию кампаний.

Зарезервированные имена

Имя Кто может отправлять
pp.* браузерный токен; серверный ключ — нет
registered, subscription_started, trial_started, trial_activated, trial_converted, trial_expired, payment_succeeded, payment_attached, order_created, first_order_created, order_paid, invoice_paid, refund только серверный ключ
first_event_received никто из внешних клиентов

Пространство pp. принадлежит ActionPulse: это встроенные события браузерного сбора — просмотры, клики, прокрутка, поля форм, ошибки JavaScript, метрики загрузки. Серверный ключ такие имена отправить не может, а публичный метод track() в браузерном SDK на имени с префиксом pp. выбрасывает исключение.

Список из второй строки — подтверждённые бизнес-факты. Браузер их отправить не может, потому что браузерный токен виден каждому посетителю страницы: событие об оплате из браузера ничего не подтверждает. Попытка вернётся отказом элемента с кодом credential_event_forbidden.

checkout_started и signup_completed в этот список намеренно не входят: это шаги интерфейса, а не факты оплаты, и их отправляет браузер.

Имя first_event_received зарезервировано за внутренними процессами платформы и на публичном приёме отклоняется с кодом invalid_event.

Свои имена событий подчиняются шаблону ^[a-z0-9_.]{1,128}$: строчные латинские буквы, цифры, подчёркивание и точка, до 128 символов. Order_Created, заказ_создан и имя с пробелом — отказ invalid_event.

Время и часовой пояс

У события два времени, и они разные:

  • event_time — когда событие произошло. Берётся из поля time, приводится к UTC.
  • server_time — когда приём получил запрос. Ставит сервер, всегда UTC.

Поле time разбирается по RFC 3339 и требует указания зоны: Z либо смещение вида +03:00. Строка 2026-07-28T09:41:07 без зоны не разбирается.

Разбор публичного приёма прощающий, и это важно понимать буквально:

Что в поле time Что попадёт в event_time
Корректная метка внутри окна ±48 ч она же, в UTC
Поля нет или пустая строка время приёма
Строка не разбирается время приёма
Метка старше 48 ч время приёма
Метка в будущем дальше 48 ч время приёма

Отказа при этом не будет: приём вернёт 202, событие сохранится, но со временем приёма. Так сделано ради часов клиента, которые нередко выставлены неверно, — но у этого есть прямое следствие: загрузить историю задним числом через обычный приём невозможно. Для этого существует загрузка данных.

Часовой пояс у отчётов свой: сутки, недели и когорты режутся по часовому поясу проекта, который задаётся при его создании и меняется в настройках проекта. Сами события всегда хранятся в UTC, поэтому смена часового пояса проекта меняет границы суток в отчётах, но не сдвигает данные.

Что проверить

  1. Отправьте одно событие и сверьте ответ: accepted равен n, массив rejected пустой.
  2. Найдите событие в списке событий проекта и откройте его целиком. Все свойства на месте, значений "[redacted]" нет — если есть, вы отправили секрет, персональные данные или тип, которого нет в JSON.
  3. Проверьте event_time: если он совпадает с временем приёма, а вы присылали своё time, значит метка не разобралась — проверьте зону.
  4. Сверьте имена и типы свойств с тем, что отправляет вторая сторона (браузер или бэкенд). Расхождение здесь — самая частая причина отчётов, которые «не сходятся».
  5. Зафиксируйте схему в реестре событий и включите режим мониторинга: он покажет нарушения, ничего не отклоняя.

Машиночитаемое описание всех полей и ответов — в справочнике API.