Tilda · 6 мин чтения

Переадресация с Тильды на Телеграм бот: deep link и параметры

Переадресация с Тильды на Телеграм бот - место, где я чаще всего вижу потерянные заявки у клиентов. Кнопка стоит, ссылка ведёт на бота, а дальше бот получает голый /start и не понимает, кто пришёл: с лендинга по рекламе, с блока цен на второй странице или из письма рассылки. За практику с Тильдой я настраивал такой переход для лендингов с оплатой через T‑Bank, для интернет-магазинов с доставкой СДЭК и для обычных лид-форм, где нужно было просто передать боту имя и телефон из формы. Разберу рабочую схему: кнопка на Тильде, deep link со start-параметром, разбор параметра в боте на aiogram и учёт источника трафика без сторонних сервисов аналитики.

Зачем нужна переадресация с Тильды в Телеграм-бота

Обычная кнопка Тильды со ссылкой вида t.me/username открывает диалог с ботом, но не передаёт ничего, кроме факта перехода. Deep link устроен иначе: ссылка t.me/botusername?start=payload прокидывает произвольную строку до 64 символов прямо в событие /start, и бот получает её как аргумент команды. Разница между этими двумя вариантами - это разница между «кто-то написал в бота» и «написал клиент с карточки товара X, который увидел баннер с промокодом Y».

На одном проекте с оплатой через T‑Bank кнопка «Оформить в боте» стояла на трёх разных блоках лендинга: в шапке, в блоке с ценами и в попапе с брошенной корзиной. Без deep link в статистике бота эти три источника были неотличимы друг от друга, и заказчик не мог понять, какой блок вообще приносит заявки. После того как каждая кнопка стала передавать свой payload, стало видно, что попап с брошенной корзиной даёт втрое больше стартов бота, чем кнопка в шапке, хотя дизайнер был уверен в обратном.

Кнопка на Тильде: html-блок и переход в бота

Встроенный виджет «Telegram» в Тильде подходит только для ссылки на канал или профиль без параметров - для deep link он не годится, потому что поле принимает голый username. Рабочий вариант - кастомный HTML-блок с обычной ссылкой на t.me и своей вёрсткой кнопки:

<a href="https://t.me/kalinkindev_bot?start=lp_header" class="tg-btn" target="_blank" rel="noopener">
  Написать в Telegram
</a>

<style>
.tg-btn {
  display: inline-block;
  padding: 14px 28px;
  border-radius: 8px;
  background: #229ED9;
  color: #fff;
  text-decoration: none;
  font-weight: 600;
}
</style>

Здесь payload lp_header зашит статически прямо в вёрстке - для страницы с одной кнопкой этого достаточно. Если кнопок несколько и нужно собирать источник динамически, например подставлять utm-метки посетителя, payload собирают скриптом при загрузке страницы и подставляют в href перед показом блока.

Бесплатный материал

🎁 Полезный скрипт в подарок

Подпишитесь на Telegram - пришлю готовый скрипт по этой теме.

Без спама. Отписка в 1 клик.

Telegram жёстко ограничивает содержимое start-параметра: только латинские буквы, цифры, подчёркивание и дефис, максимум 64 символа. Кириллицу, знаки равенства и амперсанд передать нельзя, значит строку вида utm_source=yandex&utm_medium=cpc в чистом виде в ссылку не вставить.

На практике я собираю компактную строку из нужных полей, кодирую её в base64url (это тот же base64, но без символов + и /, которые в start запрещены) и обрезаю до лимита. Если полей много и они не помещаются в 64 символа, лучше сохранить исходные utm-метки в свою базу или таблицу заявок на сервере, а в start-параметр положить только короткий идентификатор записи - тогда бот всегда получает валидный payload независимо от длины исходных данных.

function buildStartPayload() {
  const params = new URLSearchParams(window.location.search);
  const raw = [
    params.get('utm_source') || 'direct',
    params.get('utm_campaign') || 'none'
  ].join('|');

  const encoded = btoa(unescape(encodeURIComponent(raw)))
    .replace(/\+/g, '-')
    .replace(/\//g, '_')
    .replace(/=+$/, '')
    .slice(0, 64);

  document.querySelectorAll('.tg-btn').forEach((btn) => {
    const url = new URL(btn.href);
    url.searchParams.set('start', encoded);
    btn.href = url.toString();
  });
}

document.addEventListener('DOMContentLoaded', buildStartPayload);

Скрипт добавляется в код страницы Тильды через настройки «Ещё» и «Вставка кода на страницу», в зависимости от того, нужен он на одной странице или на всём проекте.

Обработка start-параметра на стороне бота

На стороне бота payload нужно раскодировать обратно и достать поля. Пример для aiogram 3.x:

import base64
from aiogram import Router
from aiogram.filters import CommandStart, CommandObject
from aiogram.types import Message

router = Router()


def decode_payload(payload: str) -> dict:
    padded = payload + "=" * (-len(payload) % 4)
    raw = base64.urlsafe_b64decode(padded).decode("utf-8", errors="ignore")
    source, campaign = (raw.split("|") + ["none", "none"])[:2]
    return {"source": source, "campaign": campaign}


@router.message(CommandStart())
async def on_start(message: Message, command: CommandObject):
    if command.args:
        data = decode_payload(command.args)
    else:
        data = {"source": "direct", "campaign": "none"}

    await save_lead(user_id=message.from_user.id, **data)
    await message.answer("Здравствуйте! Чем можем помочь?")

Важный момент: если пользователь открыл бота вручную, без ссылки, command.args будет пустым - обязательно нужна ветка на этот случай, иначе бот упадёт на распаковке payload. Если нужна интеграция с CRM или сложная логика распределения заявок по менеджерам, такую разработку Telegram-бота под конкретный процесс продаж обычно делают отдельным проектом, а не одной кнопкой на лендинге.

Учёт источника трафика: способы, которые я использую на практике

Есть три рабочих подхода, выбор зависит от того, сколько у сайта точек входа и насколько глубоко нужна аналитика.

Способ Что передаёт Когда использую
Статический payload на каждой кнопке Только метку блока (шапка, попап, футер) Одна-две страницы, источники известны заранее
JS-скрипт с автосбором utm-меток utm_source, utm_campaign, иногда referrer Много страниц входа, трафик из разных рекламных каналов
Короткий id записи и база на сервере Ссылку на полную запись с любым числом полей Нужно передать больше двух-трёх параметров или данные из формы

Для интернет-магазинов с доставкой через СДЭК я обычно комбинирую второй и третий способ: utm-метки летят в payload напрямую, а привязку к конкретному заказу или брошенной корзине бот получает по короткому id, у которого в базе уже лежит адрес, сумма и статус доставки.

Отдельно скажу про хранение данных, которые бот получает из start-параметра вместе с именем и телефоном из формы Тильды. Это персональные данные, и держать их стоит на своей базе или в отечественной CRM, а не в Google Таблицах или Airtable, требование локализации по 152-ФЗ никуда не девается от того, что заявка пришла через Telegram, а не через сайт напрямую. Если заявки нужно автоматически раскладывать по менеджерам или прогревать цепочкой сообщений, эту логику удобно собрать в n8n, подключив вебхук бота как триггер сценария.

Частые ошибки при настройке перехода с Тильды в Телеграм

  • Использовать встроенный виджет «Telegram» вместо кастомной ссылки - он не поддерживает start-параметр в принципе.
  • Передавать в payload кириллицу или пробелы - Telegram либо обрежет параметр, либо бот получит пустой command.args.
  • Превышать лимит в 64 символа - лишние символы отбрасываются, и часть данных теряется без явной ошибки.
  • Не проверять сценарий на iOS Safari - если Telegram не установлен, ссылка может увести в App Store вместо ожидаемого перехода в чат.
  • Забыть про пользователей, которые уже писали боту раньше - для них тоже нужно свежее декодирование payload при каждом новом /start.

Настройка кнопки и deep link без дополнительной логики - это доработка скрипта на Тильде, от 3 000 ₽. Если переход должен завязываться на CRM, эквайринг и разные боты под разные продукты, это уже комплексная интеграция, от 40 000 ₽. Разработка самого бота с обработкой параметров, хранением заявок и рассылками - от 30 000 ₽.

Частые вопросы

Как передать телефон клиента из формы Тильды сразу в Telegram-бота?

Форма на Тильде сама по себе не делает редирект с параметрами - она отправляет данные через вебхук в свою CRM или на внешний адрес. Чтобы связать заявку с ботом, я перехватываю успешную отправку формы через колбэк Тильды, генерирую короткий id записи на сервере и уже после этого показываю пользователю кнопку с deep link, где payload содержит этот id.

Можно ли передать в start-параметре кириллицу или спецсимволы?

Нет, Telegram разрешает только латинские буквы, цифры, подчёркивание и дефис, до 64 символов. Кириллицу и любые другие символы нужно кодировать, обычно через base64url, либо заменять на короткий идентификатор записи в базе.

Что если пользователь уже переписывался с ботом раньше, deep link всё равно сработает?

Да, каждый переход по ссылке с start заново вызывает команду /start с новым payload, и aiogram-обработчик получает свежие параметры при каждом клике, даже если история переписки с ботом уже есть.

Как понять, какая кнопка на сайте реально приводит клиентов в бота?

Присваивайте каждой кнопке или каждому источнику трафика свой payload и логируйте его при получении /start в базу или CRM. Через одну-две недели набирается достаточно данных, чтобы сравнить блоки лендинга или рекламные каналы по числу реальных стартов бота, а не только по кликам на кнопку.

Есть задача?

Обсудим в мессенджере

Расскажите, что нужно сделать — отвечу в течение 4 часов в рабочее время. Первая консультация бесплатно.

Продолжая пользование настоящим сайтом Вы выражаете своё согласие на обработку Ваших персональных данных (файлов куки) с использованием Yandex.Metrika.
Понятно