События и конверсии: денежный слой воронки

Клик - это расход, пока событие не докажет, что он окупился. В Leadgram каждая конверсия - старт бота, регистрация, депозит - это событие, привязанное к конкретному клику, и именно эта связка кормит всё, что происходит дальше: выручку в Отчётах, постбэки в партнёрские трекеры, серверные конверсии, которые уходят обратно в Meta и Google, и пуш подписчику, который сконвертился - если это прописано в вашем Флоу. Наладите события правильно - и трекер начнёт показывать правду о вашем спенде.

Типы событий

  • Воронка - click, land, bot_start, channel_join, join_request, first_dm, miniapp_launch. Показывают, как пользователь движется по вашей Telegram-воронке.
  • Конверсии - lead, registration, deposit, ftd, purchase. За них платит рекламодатель.
  • Кастомные - custom_1-custom_8 под любую специфику оффера.

Большинство конверсионных событий также двигают статус клика на странице Клики: Клик → Лендинг → Старт → Регистрация → Лид → Депозит → FTD. Статус двигается только вперёд - если registration придёт уже после ftd, клик назад не откатится. Исключение - purchase: он биллится и попадает в отчётность как любая другая конверсия, но статус клика не трогает. Так же ведут себя channel_join и события custom_* - все три записываются в базу, просто не участвуют в лестнице статусов воронки.

Как события попадают в систему

  1. S2S / API. Ваш бэкенд (или постбэк-скрипт рекламодателя) шлёт POST /api/events с clickId, типом события и опциональными payout и nonce. API-ключ для внешних инструментов вроде Keitaro или собственных скриптов создаётся в Настройках → API ключи. Забрать события обратно можно через GET /api/events (с пагинацией, с фильтром по type) - удобно, когда нужно сверить, что реально сработало, с вашими собственными записями.
  2. Флоу. Поставьте узел «Триггер события» в нужный шаг Флоу - событие сработает автоматически, когда подписчик до него дойдёт. Регистрации и депозиты прямо из Telegram-воронки, без единой строчки кода.
  3. Сам Leadgram. Часть типов трекер пишет сам - из апдейтов Telegram, которые ваш бот и так получает. Следующий раздел говорит, какие именно, и какие останутся пустыми, пока вы их не пришлёте.

Дедупликация детерминированная: повторная отправка того же clickId + типа без nonce считается ретраем той же конверсии и никогда не создаёт вторую строку, второе списание или дубль Purchase в Meta. Настоящая повторная конверсия (второй депозит с того же клика) обязана идти с уникальным nonce.

Какие типы Leadgram пишет сам

То, что тип есть в списке узла «Триггер события», смаплен на событие Meta и принимается API, ещё не значит, что его трекают за вас. Групп три, и разница решает, ждать вам цифру или присылать её самому:

  • Пишет Leadgram - bot_start, channel_join, join_request, purchase, subscription_renewed и subscription_cancelled берутся из апдейтов Telegram, которые ваш бот и так получает; miniapp_launch пишется на сервере, но только после того, как сниппет из гайда Mini Apps дёрнет эндпоинт запуска - сам Telegram о запуске Mini App никому не сообщает. Подключите бота или канал - и типы из апдейтов пойдут сами, с одним условием: событие записывается, только если апдейт удалось связать с кликом. Что связать не удалось, событием не становится вовсе и попадает в «Дропы без атрибуции» на странице Доставка конверсий. Сильнее всего это бьёт по bot_start: кто открыл бота по юзернейму, а не по вашей трекинговой ссылке, старта не создаёт.
  • Только приём извне: click, land - редирект создаёт строку клика на странице Клики, а не событие, и за вашим лендингом никто не наблюдает. Поэтому статус клика никогда не дойдёт до Лендинга, пока вы сами не пришлёте событие land.
  • Только приём извне: first_dm - вебхуки Bot API покрывают только чаты с вашим ботом; чтобы поймать первое сообщение в личку обычного аккаунта, нужна MTProto-сессия пользователя, а её Leadgram не держит. Отправляйте событие узлом «Триггер события» или в POST /api/events.
  • Присылаете вы - lead, registration, deposit, ftd и custom_1-custom_8. Только ваш бэкенд знает, когда рекламодатель подтвердил конверсию.

Присылать тип из первой группы самому можно, и иногда это правильно (например, ваш собственный биллинг репортит purchase), но рядом с автоматическим появится второе событие. Исключение - bot_start: вебхук, узел «Триггер события» и API сходятся в одно событие на пользователя, поэтому повторно отправленный старт ничего не удвоит. Сходятся они по паре «бот + пользователь Telegram», поэтому для этого типа API требует пользователя - передайте telegram_user_id, tg_user_id, from.id или user.id внутри payload. bot_start без него отклоняется с кодом 422 (bot_start_missing_telegram_user_id), а не записывается вторым стартом, который ушёл бы в Meta вторым Lead.

Чем на самом деле оборачивается задвоенный старт. Не деньгами: вы платите один раз за человека в рамках рабочего пространства - списание происходит на его первом платном событии, а Telegram-аккаунт узнаётся без срока давности, - поэтому второй bot_start по уже оплаченному человеку закрывается как free и баланса не касается. Бьёт он дальше по цепочке. У дубля другой event_id, поэтому собственная дедупликация Meta свести его с первым не может, и оптимизатор кампании узнаёт, что один человек сконвертился дважды, - за этот урок вы потом платите на каждом аукционе. На ту же величину разъезжаются цифры воронки в Отчётах. Ради этого и нужны правила пары ниже, а не ради второго счёта.

У этой пары есть два условия, и лучше назвать их прямо:

  • По API бота должно быть чем назвать. Передайте bot_username (@имя) или bot_id внутри payload - либо назовите такой clickId, у клика которого бот есть: клик несёт бота своей кампании, а у клика из обычной редирект-кампании бота нет, как и у клика, чьего бота удалили. Вызов, который не называет бота ни тем, ни другим способом, пару назвать не может, поэтому записывается отдельным событием, а не сходится с общим. Явно названный бот выручает и в случае нескольких ботов: бот клика - это лишь запасная догадка, и она неверна, когда человек запустил другого вашего бота.
  • Узел «Триггер события» попадает в эту пару только для тех проходов Флоу, где записан пользователь Telegram. Проходы, начатые до появления этой механики, его не записали, а восстановить задним числом нельзя. Для них узел по bot_start не создаёт ничего, вместо того чтобы гадать, - этот старт всё равно уже записал вебхук. Молчит узел и по второй причине: проходу Флоу без клика старт не к чему привязать, поэтому вместо события, которое всё равно никуда бы не уехало, потеря попадает в «Дропы без атрибуции» на странице Доставка конверсий.

«Только приём извне» - это про код как он есть, а не про фичу в пути. Если first_dm никто не шлёт, строки first_dm не появится никогда: то, что тип можно выбрать в узле и он смаплен в Meta Contact, автоматическим его не делает.

Ретаргетинг: второй клик того же человека

Повторный клик по объявлению - обычное дело: тот же человек кликает снова, получает новую строку клика и снова стартует бота. bot_start считается один раз на бота и пользователя Telegram, навсегда, поэтому второй старт не создаёт ни нового события, ни нового списания, ни второго Lead в Meta. Это осознанный выбор: иначе на одного человека придётся два Lead, а это портит тот самый оптимизатор, обучение которого вы оплачиваете.

Цена этого выбора видна ровно в одном месте: новый клик остаётся в статусе «Клик» на странице Клики, хотя человек в бота зашёл. Двигать его нечем - на уровне событий ничего нового не произошло. Когда меряете ретаргетинговую кампанию, считайте старты по событиям, а не по статусам кликов.

Биллинг-гейт

Записать событие и доставить его дальше - две разные вещи. У каждой строки события есть billing_status: пока идёт списание, он pending, затем становится charged или free. Отправка в CAPI, постбэки и пуш подписчику по событию срабатывают только после того, как этот статус разрешится в charged или free - у приостановленной организации или при пустом балансе строка остаётся blocked (при сбое списания - error), и событие лежит в базе, так и не дойдя ни до Meta, ни до Google, ни до вашего постбэк-эндпоинта, ни до подписчика.

Если конверсия появилась в списке событий, а на другом конце ничего не сработало - сначала проверьте биллинг, а не интеграцию. Событие в статусе blocked не сломано, оно ведёт себя ровно так, как задумано.

Поле payout

Передавайте payout (USD, до двух знаков после запятой) с каждой платящей конверсией. Оно попадает в два места:

  • Выручка в Отчётах - колонка выручки складывается из payout всех подтверждённых событий.
  • Google Ads - payout становится conversionValue серверной конверсии, и Smart Bidding оптимизируется на реальные деньги.

Постбэки payout не передают. Партнёрским трекерам доступны плейсхолдеры {click_id}, {event_type}, sub-цепочка и нативные click id рекламных площадок - полный список смотрите в Постбэках. Если вашему трекеру нужна сумма конверсии, передавайте её через собственную интеграцию.

Что значит колонка CAPI

В таблице «Последние события» на Дашборде у каждого события есть статус CAPI: Отправлено означает, что все рекламные площадки, привязанные к кампании, приняли конверсию; В ожидании означает, что хотя бы одна доставка ещё в очереди или падает. Детали по площадкам - попытки, последняя ошибка, повторная отправка в один клик - на странице Доставка конверсий.

Безопасное тест-событие

На странице Facebook у каждого CAPI-аккаунта Meta есть кнопка «Отправить тест-событие». Она шлёт синтетический Purchase с вашим кодом Test Events из Meta Events Manager, поэтому событие появляется во вкладке Test Events и никогда не затрагивает оптимизацию рекламы, атрибуцию или списания. Это самый быстрый способ убедиться, что пиксель и токен работают, ещё до того, как потрачен первый доллар. Без кода кнопка не сработает - событие без кода Meta засчитала бы как настоящее.

Частые грабли

  • Пропустить nonce на повторной конверсии - второй депозит молча посчитается ретраем и будет отброшен.
  • Отправлять события без payout - выручка в Отчётах останется нулевой, а Google получит конверсии без ценности.
  • Отправить registration для клика, который уже дошёл до ftd, и ждать, что статус изменится - не изменится, так задумано.
  • Ждать, что purchase сдвинет статус клика по воронке - никогда не сдвинет, хотя биллится как любая другая конверсия.
  • Читать «В ожидании» в колонке CAPI как баг - сначала загляните в Доставку конверсий, там обычно названа конкретная ошибка площадки.
  • Считать, что записанное событие уже доставлено - статусы биллинга blocked, error и pending означают, что оно так и не дошло ни до CAPI, ни до постбэков, ни до пушей.
  • Оценивать ретаргетинговую кампанию по статусам кликов - повторный клик человека, который уже стартовал бота, так и останется в статусе «Клик», и это задумано: старт считается один раз на человека.
  • Ждать, что miniapp_launch появится сам - Telegram апдейта о запуске не шлёт, и цифра будет нулевой, пока не заработает сниппет из гайда Mini Apps.