Skip to main content

UTM Tracking


1. Summary

Goal: Система отслеживания маркетинговых кампаний — позволяет создавать короткие ссылки с UTM-параметрами, отслеживать переходы и конверсии, анализировать эффективность каналов привлечения.

User Value: Маркетолог получает точную аналитику по каждому источнику трафика: сколько кликов, сколько регистраций, какая конверсия. Красивые короткие ссылки (goloot.online/influencer/code) вместо длинных Telegram URL.


2. Business Logic

UTM Parameters

Обязательные параметры:

ПараметрОписаниеПример
sourceИсточник трафикаtelegram, youtube, vk
mediumСпособ размещенияcpc, social, influencer, banner
campaignНазвание кампанииwinter_sale, new_users_2024

Ограничения: 1-30 символов (source/medium), 1-50 символов (campaign)

Формат короткой ссылки: goloot.online/{influencer}/{code}

Внутреннее хранение: {influencer}_{code} (с underscore) Отображение: {influencer}/{code} (со slash)

Генерация shortCode
  1. Базовый код из первых символов параметров: tel_vid_winter
  2. При коллизии: добавляется счётчик tel_vid_winter_1, tel_vid_winter_2
  3. При переполнении (>999): fallback на случайный 6-символьный код

Click Tracking Flow

Два пути редиректа

Существуют два endpoint для UTM shortlink redirect:

  1. Redirect Service (goloot.online/:shortCode) — основной production путь. Инкрементирует linkClicksCount в БД (fire-and-forget UPDATE).
  2. Backend public route (/utm/:influencer/:code в public-utm.routes.ts) — чистый редирект без записи в БД ("NO DATABASE WRITES"). Клики трекаются позже через /auth endpoint при открытии Mini App.

linkClicksCount инкрементируется только в redirect-service.

Три метрики трекинга (v5.0.0)
МетрикаПолеИсточник
Link ClickslinkClicksCountredirect-service (переход по ссылке)
Bot OpensclicksCountAuth API (открытие Mini App)
ConversionsconversionsActivationRewardService (завершение онбординга)

CTR = conversions / clicksCount * 100% (Конверсии / Открытия бота)

InviteSession (Activation Domain)

UTM и Referrals используют сервисы из домена activation:

  • InviteSessionService — создание и поиск сессий
  • ActivationRewardService — активация и распределение наград

Модель InviteSession:

  • type: InviteType — UTM или REFERRAL
  • telegramId — ID пользователя до регистрации
  • metadata: Json — UTM параметры или referralCode
  • state — PENDING → ACTIVATED / EXPIRED
Переиспользуемая архитектура

InviteSession — это общий механизм для трекинга любых "приглашений". UTM и Referrals — два варианта использования. Подробнее см. Activation.

Conversion Tracking

Конверсия считается когда:

  1. Пользователь перешёл по UTM-ссылке (зафиксировано в InviteSession)
  2. Пользователь завершил регистрацию (onboarding completed)
  3. Система устанавливает UTMTracking.converted = true

Три метрики (воронка):

  • utm_campaigns.linkClicksCount — переходы по короткой ссылке (redirect-service)
  • utm_campaigns.clicksCount — открытия Mini App через UTM (bot opens, InviteSession)
  • utm_campaigns.conversions — завершённые регистрации (onboarding completed)

Формула CTR: CTR = conversions / clicksCount * 100% (Конверсии / Открытия бота)

Promo Code Integration

UTM-кампания может быть связана с промокодом (promoCodeId). Это позволяет:

  • Отслеживать активации промокода от пользователей, пришедших по UTM
  • Видеть на графике аналитики две линии: общие активации и активации от UTM
  • Сравнивать эффективность разных кампаний по промокоду

Метрики промокода:

  • totalPromoRedemptions — все активации привязанного промокода
  • utmPromoRedemptions — активации от пользователей с UTM-визитом за последние 7 дней
  • На графике: фиолетовая линия (всего) и розовая линия (от UTM)
Attribution Logic

Активация промокода атрибутируется к UTM-кампании если пользователь посетил Mini App по любой UTM-ссылке в течение 7 дней до активации. Связь устанавливается через source+medium+campaign match.

Admin UI (Promo Code UX):

ЭлементОписание
Колонка "Промокод" в таблицеЗелёный badge с кодом или прочерк "—" для кампаний без промокода
Секция "Промокод" в деталях кампанииВсегда отображается: badge или "Не привязан" (курсив)
Кнопка "+" в форме созданияРядом с полем выбора промокода — открывает мини-диалог быстрого создания нового промокода (код + тип награды + количество)

Normalization

UTM параметры автоматически нормализуются для консистентности:

ВводРезультат
youtube, YOUTUBE, ютубYouTube
tiktok, тиктокTikTok
vk, вконтактеVK
shorts, reelsvideo_vertical

Protection

ДействиеRate LimitAuthValidation
Redirect (public)shortlinks (100/min)none
Create campaigncritical (5/min)adminUTMParametersSchema
List campaignsadmin (~50/min)adminUTMLinksQuerySchema
Toggle/Deletecritical (5/min)admin
Analyticsanalytics (30/min)adminAnalyticsQuerySchema
Trackingtracking (60/min)telegramUTMTrackingSchema
Emergencyemergency (1/min)При подозрительной активности
Rate Limit Configs

Определены в backend/src/domains/utm/routes/middleware/utm-rate-limiting.middleware.ts:

ConfigЛимитНазначение
criticalRateLimitConfig5/minСоздание/изменение кампаний
trackingRateLimitConfig60/minТрекинг активности
analyticsRateLimitConfig30/minАналитика
shortlinksRateLimitConfig100/minПубличные редиректы
adminRateLimitConfig~50/min (GENERAL_REQUESTS_PER_MINUTE / 2)Админские операции
emergencyRateLimitConfig1/minЭкстренная защита
Security Details

См. Security Matrix для полного обзора защит.

Edge Cases

СитуацияUI поведениеКод
❌ Кампания не найденаRedirect на /start (fallback)
❌ Кампания неактивнаRedirect на /start (fallback)
❌ Short link истёкRedirect на /start (fallback)
❌ Дублирующая комбинация UTM"Кампания с такими параметрами уже существует"DUPLICATE
❌ Заблокированное значение"Значение заблокировано"BLOCKED_VALUE
✅ Успешный редирект302 → Telegram

3. ADR (Architectural Decisions)

ADR 1: Lightweight Redirect Architecture (v4.0.0 → v5.0.0)

Проблема: Redirect сервис изначально не записывал клики в БД ("чистый редирект"). Это привело к тому, что clicksCount инкрементировался только при открытии Mini App — без разделения "переход по ссылке" и "открытие бота".

Решение (v5.0.0): Redirect сервис записывает только linkClicksCount (атомарный инкремент) — лёгкая запись без JOIN. Основная нагрузка (создание InviteSession, user lookup) по-прежнему в Auth API.

Альтернативы (отклонены):

  • Async запись через очередь — усложняет архитектуру
  • Redis counter + batch flush — дополнительная инфраструктура

Последствия: Redirect остаётся быстрым (один простой UPDATE). linkClicksCount точно считает переходы по ссылке независимо от открытий Mini App.


ADR 2: Агрегированные счётчики в utm_campaigns

Проблема: Подсчёт кликов через COUNT(*) в utm_tracking дорогой при большом объёме данных.

Решение: Денормализация: clicksCount и conversions хранятся в utm_campaigns и инкрементируются атомарно.

Альтернативы (отклонены):

  • Материализованные представления — сложнее поддерживать
  • Кэширование — inconsistency при инвалидации

Последствия: O(1) для получения статистики кампании. Trade-off: нужно инкрементировать при каждом событии.


ADR 3: Unique constraint (source, medium, campaign)

Проблема: Две кампании с одинаковыми UTM параметрами создают путаницу в аналитике.

Решение: Unique constraint на комбинацию (source, medium, campaign).

Альтернативы (отклонены):

  • Разрешить дубликаты с разными ID — невозможно определить attribution
  • Добавить timestamp в constraint — переусложнение

Последствия: Одна комбинация UTM = одна кампания. При необходимости новой кампании нужно изменить один из параметров.


ADR 4: Soft delete для UTM значений

Проблема: Удалённого инфлюенсера/источник можно создать заново, обойдя блокировку.

Решение: Таблица utm_deleted_values хранит заблокированные значения. При создании кампании проверяется отсутствие в этой таблице.

Последствия: Заблокированный инфлюенсер не сможет создать новую кампанию с тем же именем. Restore возможен через админку.


ADR 5: Разделение метрик кликов (v5.0.0)

Проблема: UTMCampaign.clicksCount инкрементировался дважды за один пользовательский путь:

  1. В redirect-service — при переходе по короткой ссылке
  2. В invite-session.service — при создании InviteSession

Один пользователь = 2 "клика". Это двойной счёт.

Решение: Три отдельные метрики с чёткой семантикой:

МетрикаПолеИнкрементируется в
Link ClickslinkClicksCount (новое)redirect-service (переход по ссылке)
Bot OpensclicksCount (существующее)invite-session.service (открытие Mini App)
Conversionsconversions (существующее)activation-reward.service (онбординг завершён)

CTR считается от Bot Opens: CTR = conversions / clicksCount * 100%

Ключевые решения:

  • Имя clicksCount сохранено для обратной совместимости (семантика "bot opens")
  • linkClicksCount стартует с 0 для всех существующих кампаний
  • Старые данные не мигрируются (исторические данные несущественны)

Альтернативы (отклонены):

  • Переименовать clicksCountbotOpensCount — нарушило бы backward compatibility API
  • Объединить в одну метрику с флагом — усложняет запросы аналитики

Последствия: Маркетологи видят полную воронку: Link Clicks → Bot Opens → Conversions. Диагностируется drop-off на каждом шаге.


4. Architecture

Services Overview

Key Components

КомпонентПутьОписание
UTMServicebackend/src/domains/utm/services/utm.service.tsFacade, делегирует к специализированным сервисам
UTMCampaignServicebackend/src/domains/utm/services/utm-campaign.service.tsCRUD кампаний
UTMShortLinksServicebackend/src/domains/utm/services/utm-short-links.service.tsУправление короткими ссылками
UTMAnalyticsServicebackend/src/domains/utm/services/utm-analytics.service.tsАналитика промокодов для UTM
UTMValidatorbackend/src/domains/utm/services/utils/utm-validator.tsВалидация параметров
UTMNormalizerbackend/src/domains/utm/services/utils/utm-normalizer.tsНормализация значений
UTMAnalyticsUtilsbackend/src/domains/utm/services/utils/utm-analytics-utils.tsРасчёт трендов
UTMHashGeneratorbackend/src/domains/utm/services/utils/utm-hash-generator.tsГенерация уникальных shortCode
UTMUserServicebackend/src/domains/utm/services/utils/utm-user.service.tsУтилиты для работы с пользователями
InviteSessionServicebackend/src/domains/activation/services/invite-session.service.tsСоздание UTM/Referral сессий
ActivationRewardServicebackend/src/domains/activation/services/activation-reward.service.tsАктивация сессий и награды
Admin Controllersbackend/src/domains/utm/controllers/admin-utm-campaign, admin-utm-links, admin-utm-sources, admin-utm-values, admin-utm-analytics, admin-utm-autocomplete
Public Routesbackend/src/domains/utm/routes/public-utm.routes.tsRedirect endpoint
Admin Routesbackend/src/domains/utm/routes/admin-utms.routes.tsAdmin API
Rate Limitsbackend/src/domains/utm/routes/middleware/utm-rate-limiting.middleware.tsUTM-специфичные rate limit конфиги
UTMFormFieldsadmin/src/components/utm/UTMGenerator/components/UTMFormFields.tsxПоля формы UTM-генератора (источник, канал, тип, промокод + кнопка быстрого создания)
UTMTableadmin/src/components/utm/UTMTable.tsxТаблица UTM-кампаний (включает колонку промокода)
UTMDetailsModaladmin/src/components/utm/UTMDetailsModal.tsxМодалка деталей кампании (всегда показывает секцию промокода)

5. Database Schema

Models

МодельОписаниеКлючевые поля
UTMCampaignМаркетинговая кампанияsource, medium, campaign, shortCode, linkClicksCount, clicksCount, conversions, isActive, title, description, comment, term, content, adType, influencer, promoCodeId, imageUrl
ShortLinkКороткая ссылкаcampaignId, shortCode, destinationUrl, redirectType, title, description, imageUrl, expiresAt
UTMTrackingВизит пользователяsource, medium, campaign, term, content, referrer, ip, userAgent, landingPage, userId, converted, conversionDate
UTMDeletedValueЗаблокированные значенияfield, value, deletedBy, reason

Relationships

Indexes

ТаблицаIndexНазначение
UTMCampaign(source, medium, campaign)UNIQUE — одна кампания на комбинацию
UTMCampaignshortCodeБыстрый поиск для redirect
UTMCampaignisActiveФильтрация активных
UTMCampaigninfluencerПоиск кампаний инфлюенсера
UTMCampaigncampaignGroupIdГруппировка кампаний
UTMCampaignlinkClicksCountСортировка по кликам по ссылке
UTMCampaignclicksCountСортировка по открытиям бота
UTMCampaignconversionsСортировка по конверсиям
UTMCampaignstartDateФильтрация по дате начала
UTMCampaignpromoCodeIdБыстрый поиск кампаний по промокоду
ShortLinkshortCodeБыстрый redirect
ShortLinkcampaignIdПоиск ссылок кампании
ShortLinkisActiveФильтрация активных
ShortLinkexpiresAtCron для деактивации
UTMTrackingsourceГруппировка аналитики по источнику
UTMTrackingcampaignГруппировка аналитики по кампании
UTMTrackinguserIdКонверсии пользователя
UTMTrackingconvertedФильтрация конвертированных
UTMTrackingcreatedAtАналитика по временным диапазонам
UTMDeletedValue(field, value)UNIQUE — проверка при создании

6. API Endpoints

МетодЭндпоинтОписаниеDocs
GET/utm/:influencer/:codeRedirect на Telegram

Response: 302 Redirectt.me/goloot_bot?start={shortCode}


  • Activation — домен InviteSession: создание и активация сессий
  • Promo Codes — промокоды атрибутируются к UTM-кампаниям
  • Referrals — реферальная система (также использует activation domain)
  • Glossary — термины: UTM, ShortLink, Conversion, InviteSession
  • Security Matrix — защиты UTM эндпоинтов
Shared Components

UTM и Referrals используют общие компоненты из activation домена:

  • InviteSessionService — создание и поиск сессий
  • ActivationRewardService — активация и награды
  • InviteSession модель — общая модель в Prisma schema