База знаний Brandmaker

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

Готовый JavaScript показывает короткий код истории рекламных источников. Клиент называет его оператору, а оператор расшифровывает переходы по заранее заданным правилам.

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

Что решает скрипт

Иногда нужно понять, после какой рекламы человек позвонил, но полноценный коллтрекинг пока не подключён. Простой вариант — показать на сайте код источников и попросить клиента назвать его во время разговора. Оператор записывает код в CRM вместе с обращением и сверяется с таблицей источников.

Код не является уникальным номером клиента: одинаковая история даёт одинаковые цифры у разных людей. Скрипт сам не фиксирует звонок, не передаёт его в аналитику и не связывает устройства одного человека. Для автоматического учёта звонков нужен коллтрекинг и передача событий в CRM или систему аналитики.

Как формируется история

Первая цифра — источник первого посещения, которое смог увидеть этот скрипт в данном браузере. Далее сохраняются до семи последних изменений источника. Это история смены источников, а не перечень всех кликов или посещений.

Например, Яндекс → Google → Google → прямой заход даёт 12. Повторный Google не добавляет цифру, а заход без utm_source сохраняет уже известный код независимо от прошедших суток. Первый прямой заход получает 0. Явно размеченный, но неизвестный источник тоже обозначается 0.

Сохраняются прежние соответствия цифр, чтобы не ломать существующую расшифровку:

  • 1 — utm_source=yandex; 2 — utm_source=google.
  • 3 — vk.com или vk с utm_medium=cpm; 6 — тот же источник с utm_medium=social.
  • 4 — mytarget с cpm; 5 — mytarget с social. Это исторические значения разметки, не рекомендация выбирать рекламную площадку.
  • 7 — social с social; 8 — kirovchanka; 9 — youtube с social.
  • 0 — первый неразмеченный заход или явный источник, для которого нет правила.

По умолчанию скрипт различает источники и некоторые каналы, а не отдельные рекламные кампании. Одной utm_campaign=vk.com недостаточно: в ссылке должен быть utm_source.

Подключение

Скачайте JS и CSS, разместите их на своём сайте и подключите в шаблоне. Контейнер utm-code-place поставьте рядом с телефоном или контактами, чтобы оператору было удобно объяснить, где найти код. Если контейнера нет, код появится в конце страницы. Он не перекрывает контент фиксированным блоком.

<!doctype html>
<html lang="ru">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Контакты</title>
  <link rel="stylesheet" href="/downloads/utm-to-digits.css">
</head>
<body>
  <p>Назовите оператору код рядом с телефоном.</p>
  <div id="utm-code-place"></div>
  <script src="/downloads/utm-to-digits.js" defer></script>
</body>
</html>

Полный код скрипта

/* Brandmaker: код истории рекламных источников. Версия 2026-09-30. */
(() => {
  'use strict';
  const cookieName = 'utmCookieDigit';
  const rules = [
    { source: 'yandex', digit: '1' },
    { source: 'google', digit: '2' },
    { source: 'vk.com', medium: 'cpm', digit: '3' },
    { source: 'mytarget', medium: 'cpm', digit: '4' },
    { source: 'mytarget', medium: 'social', digit: '5' },
    { source: 'vk.com', medium: 'social', digit: '6' },
    { source: 'social', medium: 'social', digit: '7' },
    { source: 'kirovchanka', digit: '8' },
    { source: 'youtube', medium: 'social', digit: '9' },
  ];
  function parameter(params, name) {
    const values = params.getAll(name).map(value => value.trim().toLowerCase());
    if (!values.length) return undefined;
    if (values.some(value => !value || value.length > 128 || /[\u0000-\u001f\u007f]/.test(value))) return null;
    return values.every(value => value === values[0]) ? values[0] : null;
  }
  function sourceDigit() {
    const params = new URLSearchParams(window.location.search);
    let source = parameter(params, 'utm_source');
    const medium = parameter(params, 'utm_medium');
    const campaign = parameter(params, 'utm_campaign');
    if (source == null || medium === null || campaign === null) return null;
    if (source === 'vk') source = 'vk.com';
    const rule = rules.find(item => item.source === source &&
      (item.medium === undefined || item.medium === medium) &&
      (item.campaign === undefined || item.campaign === campaign));
    return rule ? rule.digit : '0';
  }
  function readHistory() {
    try {
      const item = document.cookie.split(';').map(value => value.trim())
        .find(value => value.startsWith(cookieName + '='));
      const value = item ? decodeURIComponent(item.slice(cookieName.length + 1)) : '';
      return /^\d{1,8}$/.test(value) ? value : '';
    } catch { return ''; }
  }
  function init() {
    if (document.getElementById('utmScriptElement')) return;
    let history = readHistory();
    const digit = sourceDigit();
    if (!history) history = digit ?? '0';
    else if (digit !== null && history.slice(-1) !== digit) {
      history = history[0] + (history.slice(1) + digit).slice(-7);
    }
    try {
      document.cookie = cookieName + '=' + history + '; Max-Age=7776000; Path=/; SameSite=Lax' +
        (window.location.protocol === 'https:' ? '; Secure' : '');
    } catch { /* При запрете cookie показываем код только этого посещения. */ }
    const element = document.createElement('p');
    element.id = 'utmScriptElement';
    element.dataset.utmCode = history;
    element.textContent = 'Код источника: ' + history;
    (document.getElementById('utm-code-place') || document.body).appendChild(element);
  }
  if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init, { once: true });
  else init();
})();

Как адаптировать правила

Измените массив rules под принятую в вашей компании разметку. Каждому правилу задайте одну цифру от 0 до 9. Более конкретные правила разместите перед общими: поиск использует первое совпадение. Для различения кампаний добавьте campaign, например { source: 'yandex', campaign: 'brand_search', digit: '1' }. Значения в правилах должны быть в нижнем регистре.

Девяти отдельных цифр плюс 0 быстро перестаёт хватать для большой структуры кампаний. В этом случае лучше хранить полноценные параметры и использовать серверный идентификатор обращения, а не усложнять ручную расшифровку.

Cookie и ограничения

Код хранится в first-party cookie utmCookieDigit с запрашиваемым сроком 90 дней после последнего посещения. Браузер может сократить срок или запретить сохранение; при недоступных cookie показывается код только текущего посещения. В другом браузере, приватном окне или после очистки cookie история начинается заново.

Скрипт принимает существующие корректные коды длиной от одной до восьми цифр. Повреждённая cookie безопасно сбрасывается. Пустые, чрезмерно длинные и противоречащие друг другу повторные параметры не меняют известную историю. Некорректное URL-кодирование не останавливает выполнение.

Если ваш сайт разрешает аналитическое сохранение только после выбора пользователя, подключайте этот пример через существующий механизм управления cookie. В UTM и код нельзя закладывать телефоны, email и другие данные клиентов.

Что проверили и исправили

В старом тексте потерялись переносы строк: однострочный комментарий скрывал остаток JavaScript и код не запускался после копирования. Старый парсер мог падать на ошибочном URL-кодировании. Через сутки прямой возврат добавлял ложный ноль; также перезаписывался обработчик body.onload.

Новая версия сохраняет переносы, использует URLSearchParams, поддерживает раннюю и позднюю загрузку и не заменяет чужие обработчики. Автотесты проверяют все цифры, прямые возвраты, историю из восьми цифр, повреждённые данные и запрет cookie.

Автор исходного материала: Каракчиев Денис, веб-технолог Brandmaker. Редакция и код обновлены Brandmaker в 2026 году.

Обновлено 30 сентября 2026 года. Рекомендации и примеры пересмотрены; дата первой публикации сохранена в конце статьи.

Материал опубликован: 11 авг. 2017 г.