Захоплення і прокидання UTM з landing
https://besocial.studio · https://besocial.studio/shift2 — фронт-логіка на besocial.studio
Суть
Лід заходить на besocial.studio за рекламним посиланням з UTM-параметрами в query string. Лендінг зберігає усі параметри в localStorage і прокидає їх далі у дві точки: при кліку на Telegram-бот @BeSocialHR_bot і при сабміті контактної форми. Це єдиний шлях, яким UTM рекламних кампаній доходять до stack-беку.
Use cases
- Лід приходить з Facebook/TikTok-реклами → клікає кнопку «Пройти співбесіду з HR-котом» → попадає у Telegram-бот вже з UTM.
- Лід заповнює форму «залиш свої контакти» (зараз прихована,
display:none, лишається як заглушка) → рекрутер отримує заявку з UTM-контекстом.
Захоплення параметрів
На DOMContentLoaded:
- Читаємо усі query-параметри через
URLSearchParams— без whitelist, generic key/value. Усе що в URL після?потрапляє в payload. - Додаємо
userAgent—navigator.userAgent || navigator.vendor || window.opera. - Merge у
localStorage['urlParams'](ключ —urlParams, значення — JSON-об’єкт). Новий захід зливається з попереднім: нові значення перезаписують старі, відсутні не обнуляються — параметри накопичуються між візитами.
Whitelist навмисно не використовується — бек сам відбирає потрібні поля. Тобто gclid, t, або будь-яке нестандартне значення з URL так само потрапить у сесію.
Прокидання → Telegram бот
- Відправляється подія FB Pixel конверсія
fbq('track', 'ViewContent')(спрацює тільки якщо Pixel ініціалізований — див. trackers). - Щоб обійти нюанс обмеження query параметра при /start telegram бота (ліміт 64 символа!!!), була добавленна логіка сесій. При кліку на старт бота - ми відправляємо на сайт utm мітки, отримуємо
sessionId.
POST https://api.besocial.tech/lead-session/createSessionз body = вмістуlocalStorage['urlParams'].- Якщо
urlParamsпорожній — запит не відправляється взагалі,sessionId = null.
- Якщо
- Редірект на
https://t.me/BeSocialHR_bot?start=<sessionId>. ЯкщоsessionIdнема — на голийhttps://t.me/BeSocialHR_botбез параметраstart.
Прокидання → контактна форма
Зараз форма прихована (
#contactмаєdisplay:none). Код лишається як заглушка для майбутнього сценарію — якщо вмикнути блок, логіка нижче працює одразу.
- Валідація трьох полів:
name— не порожнє, ≥ 2 символи післяtrim().phoneNumber— regex/^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$/(попередньо вирізаються пробіли).telegram— regex/^@?[a-zA-Z0-9_]{5,32}$/(опційний@).
- Сабміт:
POST https://api.besocial.tech/recruit/operatorCard/createOperatorBesocialStudioFormз body{ name, phoneNumber, telegram, params }, деparams— вмістlocalStorage['urlParams']. - Успіх — форма ховається, показується блок
#form-success. Помилка — показується#form-error, кнопка розблоковується для повторного сабміту.
Нюанси
- Накопичення в localStorage. Якщо лід заходив двічі з різних кампаній — параметри злиті, переможе останнє значення по кожному ключу.
- sessionId одноразовий. Бек видаляє сесію одразу після того як бот її прочитав за
/start <sessionId>. - Атрибуція ламається при «переході через буфер». Якщо лід зайшов на besocial.studio, скопіював посилання на бот вручну і відкрив у іншому пристрої — UTM втрачаються.
pixel_idяк UTM.pixel_idпотрапляє вlocalStorageнарівні з іншими параметрами і прокидається вcreateSession— він потрібен не тільки для client-side FB Pixel, а й для server-side подій (див. Facebook Pixel — динамічна логіка).- Дублі лендінгу (shift1 / shift2). Логіка ідентична. Якщо змінюєш — синхронізуй обидва файли. Спільної бібліотеки нема, скрипти живуть інлайном у
<script>блоках.