Ви передаєте click ID у Telegram-бота через ?start=, а ваш postback повертається порожнім для частини Android-встановлень, які всі одностайно вважають реальним трафіком. Бот залогував команду /start. Просто він так і не отримав ваш payload — або отримав, але з пакета, який ваша звітність ніколи не враховувала. Обидва пояснення дають однаковий симптом: розрив, який виглядає як баг трекінгу, а насправді є прогалиною в маршрутизації.
Три способи передати значення в Telegram
t.me-посилання передають значення в Telegram через три різні параметри, і вони не взаємозамінні:
| Параметр | Куди потрапляє значення | Ліміт | Умова |
|---|---|---|---|
?start=<payload> | передається боту як аргумент команди /start | до 64 символів base64url | відвідувач має тапнути START |
?startapp=<payload> | init data Mini App, лише на клієнті | до 512 символів | доходить до вашого сервера, тільки якщо сам Mini App пересилає й валідує значення |
?startchannel | простий флаг | взагалі без payload | тут нічого прикріпити |
?start=: створений саме для цього
Це параметр, який поводиться так, як більшість трекерів очікує від параметра deep link. Він передається боту напряму як аргумент команди /start, тобто логіка вашого бота читає його так само, як будь-який інший аргумент команди — не потрібна окрема обробка init data, не потрібен власний клієнтський код. Обмеження, яке на практиці справді має значення, — ліміт у 64 символи: це base64url, який комфортно вміщує більшість внутрішніх click ID, але достатньо тісний, щоб доповнений нулями або багатослівний ідентифікатор переповнив його раніше, ніж ви помітите. І значення приходить лише тоді, коли відвідувач тапає START — t.me-посилання, яке відкриває чат, але так і не отримує тап, не передає payload нікуди, незалежно від того, що ви поклали в URL.
https://t.me/your_bot?start=dc_9Kx2Qz
доходить до бота як:
/start dc_9Kx2Qz
У цьому циклі нічого не змінюється залежно від платформи чи того, який пакет Telegram обробив тап — payload доходить до бота однаково, чи маршрутизував його Android через Play-збірку, APK з telegram.org, чи Telegram X, і незалежно від того, чи прийшов він через переписування tg://resolve на iOS. Єдина змінна, яка впливає на те, що бачить бот, — сам payload.
?startapp=: зовсім інша тварина
?startapp= виглядає як більша версія ?start= — більше символів дозволено, до 512, — і саме ця схожість і провокує помилку. Він поводиться зовсім по-іншому. Значення передається на клієнті, в init data Mini App, а не в обробник команди бота. Саме по собі це значення не доходить до вашого сервера. Воно доходить до сервера лише тоді, коли код Mini App, яким керує оператор, читає ці init data і пересилає їх, з власною валідацією, туди, де ви збираєте дані. Якщо ви не контролюєте Mini App, або якщо Mini App не побудований так, щоб пересилати значення, payload може лежати прямо в клієнті й жодного разу не торкнутися бекенду, до якого у вас є доступ.
?startchannel: нічого нести
?startchannel — це простий флаг. Він взагалі не приймає значення. Якщо план полягав у тому, щоб передати click ID у deep link каналу так само, як ?start= передає його боту, тут немає механізму для цього — параметр просто не побудований для того, щоб тримати payload, і те, що ви до нього додасте, нічого не змінить.
Що відбувається на iOS: переписування tg://resolve
На iOS t.me-посилання не потребує, щоб ви вручну будували URL із власною схемою. Підтверджено, що працюють два переписування:
https://t.me/<bot>?start=<payload> -> tg://resolve?domain=<bot>&start=<payload>
https://t.me/<channel> -> tg://resolve?domain=<channel>
Випадок з ботом — той, що має значення для трекінгу: payload start виживає при переписуванні цілим, переноситься як власний параметр у URL tg://resolve, а не губиться чи перекодовується по дорозі. Якщо ваше посилання t.me/<bot>?start=<payload> працює на Android, той самий payload доходить до того самого бота на iOS без жодної додаткової обробки з вашого боку. Випадок з каналом переписується так само, тільки без payload start — канали не боти, тож немає нічого еквівалентного, що можна було б нести.
Пастка Android-пакетів: три ID, один прихований на видноті
Саме тут більшість налаштувань тихо втрачають трафік без жодної помилки, яка б це показала. Telegram на Android — це не один пакет, а три:
| Джерело | Пакет |
|---|---|
| Play Store-збірка | org.telegram.messenger |
| APK, який Telegram роздає зі свого сайту | org.telegram.messenger.web |
| Telegram X | org.thunderdog.challegram |
Більшість налаштувань маршрутизації орієнтуються лише на org.telegram.messenger, бо саме він вважається «тим самим» Telegram у Play Store. Але APK, який Telegram роздає безпосередньо з telegram.org — зовсім не через Play, — має зовсім інший package ID. Прочитано прямо з маніфесту цього APK: package=org.telegram.messenger.web, versionName 12.10.0. Будь-хто, хто встановив Telegram із власного сайту Telegram, а не з Play Store, працює саме з цим пакетом — і жодна маршрутизація за пакетами, яка називає лише org.telegram.messenger, ніколи його не побачить.
Це не похибка округлення у ваших Android-цифрах. Це відвідувач, на пристрої якого справді встановлено Telegram, коректно, з джерела, яким керує сам Telegram, — і який невидимий для ланцюжка, побудованого на один package ID.
Telegram X, org.thunderdog.challegram, — третій окремий клієнт понад це. Це окремий застосунок з окремого дистрибутиву, і його варто включати в той самий ланцюжок пакетів, якщо реальна частка вашої аудиторії справді ним користується.
Чому це виглядає як баг трекінгу, а не прогалина в маршрутизації
Пропущений пакет не породжує жодної помилки в жодній точці ланцюжка. Відвідувач тапає ваше посилання, Android передає його тому пакету Telegram, який встановлено, той застосунок відкривається, бот отримує /start з payload, прикріпленим точно так, як задумано, і логіка вашого бота спрацьовує. З боку Telegram усе спрацювало. Прогалина існує лише на вашому боці, у припущенні, закладеному в логіку маршрутизації чи звітності, яка від самого початку вирішила, з яких пакетів очікувати трафік.
Саме тому org.telegram.messenger.web так легко пропустити на місяці: це не зламане встановлення, не помилка в налаштуванні бота і не payload, що не дійшов. Це трафік, який ніколи не рахувався окремим джерелом, бо ніхто не зафіксував, що Telegram випускає другий Android-пакет поза Play. Аргумент /start, ліміт у 64 символи, переписування на iOS — жодна з цих поведінок не відрізняється для цього пакета порівняно з Play-збіркою. Єдине, що відрізняється, — чи знає ваш власний список пакетів, що це джерело взагалі існує.
Що насправді доходить до вашого postback
Складіть три нитки разом, і картина буде послідовною: для трекінгу на основі /start ваш ідентифікатор виживає при передачі аргументу боту, виживає при переписуванні схеми на iOS цілим, а далі повністю залежить від того, які саме Android-пакети називає ваша маршрутизація. Пропустіть org.telegram.messenger.web — і ви не просто не відстежуєте рідкісний крайній випадок, ви не відстежуєте кожного відвідувача, який довірився власній сторінці завантаження Telegram більше, ніж Play Store. Для Mini Apps ?startapp= доходить до сервера взагалі лише тоді, коли пересилання робить ваш власний код Mini App; ніщо зовнішнє щодо цього Mini App саме по собі значення не побачить.
Трекеру, який генерує t.me-посилання для кампанії, потрібен список пакетів, що включає org.telegram.messenger.web поряд з Play-збіркою, не тому що це екзотичний крайній випадок, а тому що це той самий застосунок, виданий через інші двері. Telegram-роутинг DarkCore за замовчуванням несе всі три Android-пакети саме з цієї причини: розподіл трафіку між ними — не шум, який можна усереднити, а реальна популяція встановлень, про яку ланцюжок з одним пакетом просто ніколи не питає.
Що перевірити, перш ніж довіряти цифрам
Більшість цього непомітна, доки ви цілеспрямовано не почнете шукати, бо жодна з проблем не викликає помилку на боці Telegram. Короткий чекліст перед запуском кампанії ловить майже все:
- Переконайтеся, що ваш click ID уміщується в 64 символи base64url, перш ніж він потрапить у
?start=— усе довше або обрізається, або відхиляється, залежно від того, де застосовано ліміт, і жоден із цих результатів не хочеться виявити вже в продакшні. - Не маршрутизуйте payload
?startapp=, вважаючи, що він автоматично з’явиться на сервері. Це станеться, лише якщо контрольований вами Mini App явно його пересилає. - Якщо мета — донести payload у deep link каналу, не покладайтеся на
?startchannel— це флаг, а не контейнер, і жодне значення, яке ви до нього додасте, нікуди не потрапить. - Додайте
org.telegram.messenger.webдо будь-якого Android-ланцюжка пакетів, побудованого для Telegram. Пропуск цього пакета не зменшує ваш трафік — він просто не дає вам побачити частину, яка завжди там була. - Якщо Telegram X має реальну частку серед ваших відвідувачів, включіть
org.thunderdog.challegramу той самий ланцюжок, а не розглядайте його як окремий, другорядний випадок.