Схема событий и свойства
Полный состав события в запросе приёма 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, поэтому смена часового пояса проекта меняет границы суток в отчётах, но не сдвигает данные.
Что проверить
- Отправьте одно событие и сверьте ответ:
acceptedравенn, массивrejectedпустой. - Найдите событие в списке событий проекта и откройте его целиком. Все
свойства на месте, значений
"[redacted]"нет — если есть, вы отправили секрет, персональные данные или тип, которого нет в JSON. - Проверьте
event_time: если он совпадает с временем приёма, а вы присылали своёtime, значит метка не разобралась — проверьте зону. - Сверьте имена и типы свойств с тем, что отправляет вторая сторона (браузер или бэкенд). Расхождение здесь — самая частая причина отчётов, которые «не сходятся».
- Зафиксируйте схему в реестре событий и включите режим мониторинга: он покажет нарушения, ничего не отклоняя.
Машиночитаемое описание всех полей и ответов — в справочнике API.