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

Идентификация пользователей

Как ActionPulse отличает посетителей: анонимный идентификатор и где он хранится, вызов identify, сброс при выходе, границы сессии, ограничения на user_id.

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

У события всегда есть субъект: либо анонимный посетитель, либо конкретный пользователь. Эта страница — про то, как один превращается в другого и что при этом происходит с уже собранными данными.

Анонимный идентификатор

anon_id — случайный UUID, который трекер создаёт для посетителя браузера при первом собранном событии. Он ничего не знает о человеке: это метка устройства и браузера, а не личности.

Где он лежит и сколько живёт:

Что Значение
Хранилище localStorage, ключ pp_anon_id
Срок жизни без срока — пока хранилище не очистят
Переживает перезагрузку страницы да
Переживает закрытие браузера да
Область действия один origin: схема, домен и порт
Резервный cookie нет

Практические следствия, о которых чаще всего забывают:

  • Cookie не используется вовсе. Трекер работает только с localStorage и дополнительно удаляет одноимённые cookie, если они остались от старых версий. Если localStorage в браузере недоступен — заблокирован настройками или режимом приватного просмотра, — идентификатор существует только в памяти страницы, и каждая загрузка страницы будет новым посетителем.
  • www.example.com и example.com — разные origin, а значит и разные анонимные посетители. То же касается поддоменов и перехода с http на https. Один домен для всего трафика — самая дешёвая профилактика двоящихся посетителей.
  • До получения согласия идентификатора нет. Если сбор требует согласия, трекер не создаёт anon_id, не пишет в хранилище и не отправляет запросы, пока согласие не получено. Подробнее — в статье про согласие.

Рядом с pp_anon_id трекер хранит pp_user_id — последний известный user_id, pp_session — текущую сессию, и pp_buffer_v2 — очередь событий, которые ещё не удалось отправить. Больше трекер в хранилище ничего не пишет; в частности, браузерный токен там не лежит.

identify: связать события с человеком

Вызывайте identify, как только узнали, кто перед вами: после входа, после успешной регистрации и один раз при загрузке приложения, если сессия уже открыта.

pp.identify('YOUR_USER_ID', { plan: 'pro' });

Что происходит по этому вызову:

  1. Трекер запоминает user_id и сохраняет его в localStorage. Все последующие события уходят уже с этим user_id.
  2. Трекер отправляет POST /v1/identify с телом {"anon_id": …, "user_id": …, "traits": {…}}. Оба идентификатора обязательны.
  3. Приём сохраняет связку «этот анонимный идентификатор — этот пользователь» и создаёт событие с именем identify, свойствами которого становятся переданные traits. Успешный ответ — 202 с телом {"accepted": 1, "n": 1}.

Что происходит с ранее собранными событиями

Ничего не теряется, но происходит это в два разных момента:

  • Последующие события связываются с user_id сразу.
  • История — события, собранные до identify под анонимным идентификатором, — привязывается к пользователю задним числом фоновой задачей. Это не мгновенная операция: сразу после первого identify отчёты по user_id могут показывать меньше событий, чем их было собрано под анонимным идентификатором.

Повторный identify

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

traits подчиняются тем же ограничениям, что и свойства событий: до 64 ключей, ключ до 64 байт, значение до 8 КБ. Если ограничение нарушено, связка всё равно сохранится, а вот событие identify будет отклонено — при этом ответ останется 202. Держите traits небольшими: это атрибуты пользователя, а не выгрузка его профиля.

Выход из аккаунта: reset

При выходе вызывайте reset():

pp.reset();

reset() удаляет pp_user_id, pp_anon_id и pp_session и сразу выдаёт новый анонимный идентификатор. Дальше устройство считается новым посетителем.

Что важно понимать:

  • Без reset() следующий пользователь на том же устройстве продолжит историю предыдущего. Причём не только из-за сохранённого user_id: связка «анонимный идентификатор — пользователь» уже существует на сервере, поэтому события старого anon_id будут по-прежнему приписываться прежнему человеку. Новый анонимный идентификатор нужен именно для того, чтобы разорвать эту связь.
  • reset() не отменяет связку на сервере и не удаляет данные. Это смена субъекта дальнейшего сбора, а не удаление истории. Удаление данных пользователя — отдельная операция, см. приватность.
  • reset() не выбрасывает события, которые уже лежат в очереди отправки: они уйдут с теми идентификаторами, с которыми были созданы. Отдельный вызов clearQueue() очищает только очередь и не трогает идентификаторы.
  • reset() ничего не делает, пока сбор не разрешён согласием.

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

Сессия

Сессия — непрерывный отрезок активности одного посетителя. Трекер ведёт её сам и передаёт в поле session_id.

Свойство сессии Поведение
Идентификатор UUID, хранится в localStorage под ключом pp_session
Продление при каждом собранном событии
Завершение 30 минут без событий
Переход между страницами сессия продолжается
Перезагрузка и закрытие вкладки сессия продолжается, если уложились в 30 минут
reset() сессия завершается принудительно

Сессия не заканчивается ни в полночь, ни при закрытии браузера — только по бездействию. Обратное тоже верно: человек, вернувшийся через час, начнёт новую сессию с тем же anon_id.

Текущий идентификатор сессии можно прочитать: pp.getSessionId(). До того как сбор разрешён, метод возвращает undefined — идентификатор ещё не создан.

На приёме session_id необязателен и ничем не проверяется, кроме длины: он обрезается до 256 байт. Если вы отправляете события с бэкенда и хотите видеть их в одной сессии с браузерными, передайте туда значение getSessionId() сами — приём не умеет сшивать сессии по догадке.

Ограничения идентификаторов

Ограничение Значение
Длина user_id и anon_id до 256 байт
Нулевой байт внутри запрещён
Обязательность нужен хотя бы один из двух

Длина считается в байтах, а не в символах: строка из русских букв в UTF-8 занимает по два байта на букву. Слишком длинный идентификатор или идентификатор с нулевым байтом — отказ элемента с кодом invalid_actor, отсутствие обоих — missing_actor.

Значения сравниваются точно, как есть. User-42 и user-42 — два разных человека, а " user-42" с пробелом — третий. Приводите идентификатор к одной форме на бэкенде до отправки.

Что нельзя использовать как user_id

Годится: первичный ключ пользователя из вашей базы, UUID, внутренний непубличный номер аккаунта.

Не годится: e-mail, телефон, номер документа, логин, который человек может поменять.

Три причины, и все три практические:

  1. Идентификатор не редактируется PII-фильтрами. Приём вырезает распознанные секреты и персональные данные из свойств события, а также из url и referrer — но не из user_id. То, что вы положили в идентификатор, сохранится как есть и будет видно в списке событий, в профилях, в выгрузках и в ссылках, которыми делятся коллеги.
  2. Такие значения меняются. Человек сменил почту — и с точки зрения аналитики это два разных человека с разорванной историей. Сшить их обратно будет уже нечем.
  3. Ответственность за персональные данные. Чем меньше персональных данных уехало в аналитику, тем меньше объём обязательств по их защите и удалению.

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

  1. Откройте вкладку «Сеть» и войдите в аккаунт. Должен уйти запрос на /v1/identify с кодом 202.
  2. В localStorage появились pp_anon_id, pp_user_id и pp_session. Значение pp_user_id совпадает с идентификатором из вашей базы.
  3. Выйдите из аккаунта. pp_user_id пропал, pp_anon_id изменился.
  4. В списке событий проекта найдите событие identify и убедитесь, что его свойства — это ожидаемые traits без лишнего.
  5. Отправьте одно серверное событие для того же пользователя и проверьте в профиле, что браузерные и серверные события собрались под одним человеком.

Если что-то из этого не сходится, дальше — почему события попали не к тому пользователю.