Legan Studio
Все статьи
~ 13 мин чтения

Автоворонка в Telegram: как поднять конверсию х2

Разбираем, как собрать автоворонку в Telegram-боте: сегментация, триггерные цепочки, реактивация и метрики, на которые реально стоит смотреть.

  • Telegram
  • продажи
  • конверсия
  • автоматизация

Автоворонка в Telegram — это сценарий, который сам ведёт клиента от первого касания до покупки и повторных заказов. Грамотно собранная цепочка из 5–10 сообщений нередко поднимает конверсию из подписчика в оплату с 2–3% до 6–10%. Главное — не путать её с примитивной «капельной» рассылкой по таймеру: автоворонка реагирует на поведение, ветвится и умеет вовремя остановиться, чтобы не сжечь подписчика репортом.

Ниже — рабочий каркас: стадии, триггеры, архитектура шедулера, шаблоны сообщений, A/B-тесты, связка с CRM и рекламой, метрики и анти-паттерны. С кусками кода, которые можно положить в продакшн почти как есть.

Чем автоворонка (drip-кампания) отличается от рассылки

Рассылка — это «всем подписчикам в субботу шлём акцию». Автоворонка — это поведенческий сценарий, серия касаний по триггеру с задержкой, где следующее сообщение зависит от того, что человек сделал на предыдущем шаге. Открыл? Кликнул? Заплатил? Не среагировал 24 часа? Каждый исход — отдельная ветка.

Это меняет всё: вместо «1 текст на всех» появляется граф из 10–30 узлов, в каждом из которых по 2–4 ветки. Drip-кампания (от англ. drip — «капля») доставляет ценность дозированно: подписчик не утопает в простыне текста, а получает короткие, своевременные касания с понятным CTA на каждом шаге.

Стадии классической воронки: TOFU → MOFU → BOFU → активация → upsell → возврат

Воронка делится на шесть стадий, каждая решает свою задачу:

  • TOFU (top of funnel) — бесплатный лид-магнит: чек-лист, мини-курс, шаблон, калькулятор. Цель — обменять полезность на подписку и первый контакт.
  • MOFU (middle of funnel) — прогрев контентом: кейсы, разбор возражений, истории клиентов, мини-уроки. Цель — построить доверие и показать экспертизу.
  • BOFU (bottom of funnel) — оффер с дедлайном или ограничением (количество мест, бонус только сегодня). Цель — конверсия в покупку.
  • Активация — после оплаты помогаем быстро получить первый результат: онбординг, чек-лист первого шага, ссылка на закрытый чат.
  • Upsell / cross-sell — через 7–30 дней после активации: расширение тарифа, дополнительный модуль, сопутствующий товар.
  • Возврат (reactivation) — для тех, кто остыл: 14–60 дней молчания → серия из 2–3 касаний с новым углом. Не реактивировался — архив.

Каждая стадия — отдельный сценарий со своей метрикой: TOFU мерим по CR подписки, BOFU — по CR в оплату, upsell — по среднему чеку, реактивация — по % возвращённых.

Триггеры старта воронки

Воронка не запускается «сама». Её запускает явное событие. Самые частые:

  • /start с UTM-меткой/start utm_source=yandex_direct_search_brand. Бот парсит payload, кладёт в профиль источник, кампанию, объявление и стартует сценарий, привязанный к источнику.
  • Подписка на канал — webhook от chat_member события: новый участник → приветственное сообщение в личку → начало воронки.
  • Заполнение квиза — пользователь прошёл 5 вопросов «какой у вас бизнес», на основе ответов попадает в один из 3–4 сегментов с разной серией.
  • Callback из контекстной рекламы — Яндекс.Директ ведёт на t.me/yourbot?start=ad_42, бот узнаёт кампанию и запускает соответствующую цепочку.
  • Внешний webhook от CRM — менеджер сменил статус сделки на «не дозвонились» → бот сам пишет лиду.
  • Действие в продукте — пользователь зарегистрировался на сайте, но не активировал тариф 3 дня → старт цепочки активации.

Чем точнее триггер — тем релевантнее первое касание и тем выше open rate всей серии.

Сценарии: три рабочих примера

Три сценария, которые покрывают 80% задач студии:

  1. 3-дневный прогрев перед вебинаром. Подписался → сразу подтверждение и календарь. День -2: история эксперта и польза вебинара. День -1: программа + кейс выпускника. За 3 часа: напоминание + ссылка. За 15 минут: «начинаем». Через час после вебинара: запись + оффер. Через 24 часа: дедлайн оффера.
  2. 7-дневная серия «обучаемся боту». День 0: приветствие + первый шаг. Дни 1–6: одно короткое письмо в день — концепция, пример, упражнение, кейс, FAQ, чек-лист, итог. День 7: оффер платного тарифа с бонусом «только для тех, кто прошёл серию».
  3. Reactivation спящих. Молчание 30 дней → касание 1: «вернулись с обновлением, вот что нового». Через 3 дня без реакции → касание 2: персональный промокод. Через 7 дней → касание 3: «уходим из вашей подписки, нажмите если интересно». Молчание → архив сегмента, не пишем больше.

Архитектура: scheduler, события, worker

Технический контур автоворонки — три кубика, не считая самого бота:

  • Scheduler — Celery beat, arq cron, временный APScheduler или собственный воркер, который раз в N секунд опрашивает таблицу events и забирает всё, что пора отправить.
  • Таблица events — единая очередь запланированных шагов с полями user_id, scenario_id, step, sent_at, due_at, status. Это источник правды: что когда отправили, что в очереди, что упало.
  • Worker — берёт пачку событий с due_at <= now() AND status = 'pending', рендерит шаблон шага, отправляет в Bot API, пишет результат обратно. Идемпотентен: повторное взятие того же события не приведёт к повторной отправке.

Минимальная схема таблицы:

CREATE TABLE funnel_events (
    id           BIGSERIAL PRIMARY KEY,
    user_id      BIGINT NOT NULL,
    scenario_id  TEXT   NOT NULL,
    step         INT    NOT NULL,
    due_at       TIMESTAMPTZ NOT NULL,
    sent_at      TIMESTAMPTZ,
    status       TEXT   NOT NULL DEFAULT 'pending', -- pending|sent|skipped|failed
    payload      JSONB,
    attempts     INT    NOT NULL DEFAULT 0,
    last_error   TEXT,
    UNIQUE (user_id, scenario_id, step)
);

CREATE INDEX idx_funnel_due ON funnel_events (status, due_at)
    WHERE status = 'pending';

Уникальный ключ (user_id, scenario_id, step) гарантирует, что одна и та же ступень не запланируется дважды — даже если webhook прилетит повторно.

Разбор очереди в воркере (arq-стиль, Python):

async def tick(ctx):
    """Запускается раз в 30 секунд. Берёт пачку готовых шагов и отправляет."""
    rows = await db.fetch(
        """
        UPDATE funnel_events
           SET status = 'sending', attempts = attempts + 1
         WHERE id IN (
            SELECT id FROM funnel_events
             WHERE status = 'pending' AND due_at <= now()
             ORDER BY due_at
             LIMIT 200
             FOR UPDATE SKIP LOCKED
         )
         RETURNING id, user_id, scenario_id, step, payload;
        """
    )
    for ev in rows:
        try:
            await send_step(ev)
            await db.execute(
                "UPDATE funnel_events SET status='sent', sent_at=now() WHERE id=$1",
                ev["id"],
            )
        except RetryableError as e:
            await db.execute(
                "UPDATE funnel_events SET status='pending', last_error=$2 WHERE id=$1",
                ev["id"], str(e),
            )
        except Exception as e:
            await db.execute(
                "UPDATE funnel_events SET status='failed', last_error=$2 WHERE id=$1",
                ev["id"], str(e),
            )

FOR UPDATE SKIP LOCKED позволяет горизонтально масштабировать воркеров — два инстанса не возьмут одно и то же событие. Без этого на пиках получите дубль-отправки и репорты.

Шаблоны сообщений с переменными

Шаблон шага — отдельная сущность, не зашитая в код. Хранится в БД или YAML, меняется маркетологом без релиза:

scenario: warmup_webinar
step: 2
title: "Кейс выпускника"
text: |
  {имя}, рассказываю короткую историю.

  Анна пришла к нам полгода назад с теми же вопросами, что и вы.
  За 8 недель — рост выручки на 42%.

  Подробный разбор — на вебинаре завтра в 19:00 МСК.
  Промокод на закрытый тариф: {промокод}

buttons:
  - text: "Программа вебинара"
    url:  "{dl_link}"
  - text: "Не интересно"
    callback: "unsubscribe:warmup_webinar"
delay: "24h"
quiet_hours: true

Рендер шаблона на отправке:

from string import Template

def render(template: str, ctx: dict) -> str:
    # ctx собирается из profiles + scenario_state + utm
    return Template(template).safe_substitute(ctx)

ctx = {
    "имя": user.first_name or "коллега",
    "промокод": user.promo or "WEBINAR15",
    "dl_link": short_link(user.id, "webinar_program"),
}
text = render(step.text, ctx)

safe_substitute не упадёт на отсутствующей переменной, а оставит плейсхолдер — это спасает прод от рассыпавшегося шаблона.

Условные ветвления

После каждого шага бот ждёт реакцию N часов. Не дождался — путь A. Дождался клика — путь B. Дождался ответа текстом — путь C (передача оператору).

async def schedule_next(user_id: int, scenario: str, current_step: int):
    profile = await get_profile(user_id)
    step_def = SCENARIOS[scenario][current_step]

    # Условие на следующий шаг
    if profile.last_event == "click" and step_def.on_click:
        next_step = step_def.on_click
    elif profile.last_event == "open" and step_def.on_open:
        next_step = step_def.on_open
    else:
        next_step = step_def.on_silent  # реактивация

    if next_step is None:
        return  # выход из воронки

    due_at = compute_due(step_def.delay, profile.timezone, quiet_hours=True)
    await db.execute(
        """
        INSERT INTO funnel_events (user_id, scenario_id, step, due_at)
        VALUES ($1, $2, $3, $4)
        ON CONFLICT (user_id, scenario_id, step) DO NOTHING
        """,
        user_id, scenario, next_step, due_at,
    )

ON CONFLICT DO NOTHING — защита от двойной планировки при повторных webhook'ах от Bot API.

Сегментация на входе и в процессе

Воронка без сегментов — это та же массовая рассылка. Минимальные срезы:

  • Источник трафика — реклама, реферал, органика, конкретная кампания.
  • Этап жизненного цикла — новый, прогретый, купивший, отвалившийся.
  • Интерес — категория продукта или сценарий использования (вычисляется по ответам в квизе).
  • LTV-сегмент — низкий чек / высокий чек.
  • Поведение в воронке — открыл/не открыл, кликнул/не кликнул, ответил/молчал.

Чем больше срезов — тем точнее коммуникация. Но и сложнее поддерживать: оптимально стартовать с 3–4 сегментов и наращивать. Сегмент назначается на входе (по UTM или ответу в квизе) и пересчитывается в процессе (открыл первое письмо — переход в «активные», молчит 14 дней — переход в «спящие»).

A/B-тесты

Без тестов вы оптимизируете на ощущениях. Что разумно тестировать в воронке:

  • Тема первого касания — два варианта приветствия, метрика CR в шаг 2.
  • Время отправки — 9:00 vs 19:00 vs «по часовому поясу пользователя».
  • Длина серии — 5 касаний vs 8 vs 12. Часто оказывается, что короче лучше.
  • Формат CTA — кнопка «Купить» vs «Узнать подробнее» vs «Записаться на разбор».
  • Тон — формальный vs дружеский. На B2C почти всегда выигрывает второй.

Реализация: в funnel_events.payload пишем variant: "A" или "B", распределение 50/50 хеш-функцией от user_id. Не меняем variant в середине эксперимента — иначе данные неинтерпретируемы. Минимальная выборка для значимости — 300–500 событий на ветку.

Окна тишины

Не слать ночью, в выходные, в религиозные праздники — настраивается в профиле. Простая реализация:

def compute_due(delay: timedelta, tz: str, quiet_hours: bool = True) -> datetime:
    due = datetime.now(ZoneInfo(tz)) + delay
    if not quiet_hours:
        return due
    # Сдвигаем в окно 09:00–21:00 локального времени
    if due.hour < 9:
        due = due.replace(hour=9, minute=random.randint(0, 30))
    elif due.hour >= 21:
        due = (due + timedelta(days=1)).replace(hour=10, minute=random.randint(0, 30))
    return due.astimezone(ZoneInfo("UTC"))

Случайный сдвиг минут 0–30 спасает от пиков «ровно в 9:00 ушло 2000 сообщений» — и от лимитов Bot API, и от подозрительно одинакового поведения бота.

Stop-условия

Воронка обязана уметь останавливаться. Минимальный набор условий:

  • Купил — выход из активной серии, переход в активацию.
  • Отписался / нажал «не интересно»halt всех будущих шагов сценария.
  • Ответил менеджеру — передача в живой чат, бот молчит 24 часа, потом возвращается осторожно.
  • Заблокировал бота — Bot API вернул 403 — halt всех сценариев, перевод в архив.
  • Превышен лимит репортов — 2+ репорта за 30 дней — halt и red flag в CRM.

Реализация — общий halt(user_id, scenario_id?), который выставляет status='skipped' на всех будущих funnel_events пользователя:

async def halt(user_id: int, scenario_id: str | None = None, reason: str = ""):
    q = """
        UPDATE funnel_events
           SET status='skipped', last_error=$3
         WHERE user_id=$1
           AND status='pending'
           AND ($2::text IS NULL OR scenario_id=$2)
    """
    await db.execute(q, user_id, scenario_id, f"halt:{reason}")

Связка с CRM (amoCRM, Битрикс24)

Бот — канал, CRM — источник правды по сделке. Связка работает в обе стороны:

  • Бот → CRM: на каждое значимое событие (заявка, оплата, отказ) дёргаем webhook CRM, сделка двигается по воронке менеджера параллельно.
  • CRM → бот: когда менеджер двигает сделку («не дозвонились», «выставлен счёт»), CRM шлёт webhook боту — тот стартует или продолжает соответствующую цепочку.

Маппинг этапов воронки бота и воронки CRM делается явно и хранится в конфиге. Без этого менеджер видит «горячий лид» в CRM, а в боте человек уже отписался неделю назад — звонок будет неловким.

Интеграция с рекламой

Яндекс.Директ → start link с UTM → автоквалификация. Цепочка такая:

  1. В объявлении ссылка вида t.me/yourbot?start=ydirect_search_calc_msk_2026q1.
  2. Бот парсит payload, разбирает компоненты (source=ydirect, type=search, creative=calc, geo=msk, period=2026q1).
  3. Записывает в профиль и стартует сценарий, специфичный для этой кампании.
  4. После первой целевой реакции дёргает Метрику с offline-конверсией: yandex_metrika.reach_goal('lead_qualified', client_id=user.metrika_cid).
  5. Директ оптимизирует кампанию по реальным квалификациям, а не по кликам.

Эта связка сама по себе даёт −20–40% к стоимости лида за 2–3 недели обучения автостратегии.

Метрики

Тщеславные метрики типа «количество подписчиков» обманывают. Реально полезные:

МетрикаЧто показываетНорма
Open rate (≈ delivery в Telegram)Доставлено и не заблокировано90–98%
CTR кнопокКликабельность CTA15–35%
CR шаг → шагДоходимость до следующего касания60–85%
CR в офферДошли до BOFU30–50% от старта
CR в оплатуКупили из увидевших оффер5–15%
Drop-offНа каком шаге теряем больше всего< 30% на шаг
% отписокИндикатор давления< 2% на шаг
ROIВыручка / стоимость трафика> 1.5 окупаемо
LTV / CACЮнит-экономика≥ 3 здоровая

Когда отписки на одном из узлов прыгают выше 5%, это сигнал переписать сообщение, а не докрутить «ещё одно касание».

Конструктор vs кастом

КритерийSenler / SaleBot / BotHelpКастомная разработка
Время до запуска1–3 дня3–8 недель
Стоимость старта2 000–10 000 ₽/мес300 000 ₽+ единоразово
Гибкость сценариевЛинейные + простые ветвленияЛюбая логика
ИнтеграцииГотовые коннекторы (CRM, оплата)Любые API
Лимиты на подписчиковДо 10–50k комфортноБез лимитов
АналитикаБазовая, в интерфейсеСвой dashboard, BI
A/B-тестыЧасто отсутствуют или примитивныПолноценные
СегментацияТегиЛюбые правила на SQL
Владение даннымиНа стороне платформыНа вашей инфраструктуре
Когда выбиратьMVP, < 5k подписчиков, простой сценарийПродукт, масштаб, сложная логика

Гибрид тоже работает: конструктор для быстрого теста гипотезы, миграция на кастом — после выручки в 1 млн ₽/мес и осознанного потолка платформы.

Схема воронки (упрощённо)

[/start utm=...]
      ↓
   [сегмент?]
   /    |    \
[A]   [B]   [C]
  ↓     ↓     ↓
TOFU  TOFU  TOFU (lead-magnet)
  ↓     ↓     ↓
 — wait 1d, открыл? —
  ├─ да → MOFU кейсы
  │       ↓
  │    — wait 2d, кликнул? —
  │       ├─ да → BOFU оффер с дедлайном
  │       │        ↓
  │       │     — оплатил? —
  │       │        ├─ да → активация → upsell
  │       │        └─ нет → дожим (1 касание) → архив/реактивация
  │       └─ нет → реактивация (3 касания) → архив
  └─ нет → реактивация (2 касания) → архив

Каждая стрелка — отдельный funnel_events с due_at. Каждая развилка — отдельный schedule_next с условием.

Анти-паттерны

  • Слишком навязчиво. Касание каждые 2–3 часа — раздражает и провоцирует репорты, репорты — shadow-ban для бота.
  • Нет отписки. Без явной кнопки «больше не писать» нарушаете 38-ФЗ о рекламе и теряете доверие.
  • Единый сценарий на всех. Один и тот же текст и студенту, и B2B-директору — обоих теряете.
  • Длинные простыни. В Telegram читают по диагонали, абзац длиннее 4 строк — проматывают.
  • Одинаковый CTA на каждом шаге. Если каждое сообщение зовёт «купить» — это рассылка, а не воронка.
  • Воронка «вечной жизни». Без выхода в архив накапливаете мёртвых подписчиков и разрушаете метрики.
  • Игнор окон тишины. Сообщение в 03:00 — гарантированный mute и репорт.
  • Нет идемпотентности. Webhook прилетел дважды → пользователь получил два сообщения подряд → отписка.

Итого

Автоворонка в Telegram — это поведенческий сценарий, а не рассылка по расписанию. Связка из welcome-серии, прогрева, оффера, дожима и реактивации с правильной сегментацией стабильно даёт х1.5–х2 к конверсии относительно «голой» рассылки. Технически нужны хранение состояний, идемпотентный шедулер с FOR UPDATE SKIP LOCKED, шаблоны с переменными, условные ветвления, окна тишины, stop-условия и связка с CRM. На разработку реалистично закладывать 3–6 недель в зависимости от количества веток и интеграций. На конструкторе можно стартовать за 2–3 дня — но с осознанным потолком в 5–10k активных подписчиков и слабой аналитикой.

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

Чем автоворонка в Telegram отличается от обычной рассылки?

Рассылка — это «всем подписчикам в субботу шлём акцию». Автоворонка — это поведенческий сценарий, где следующее сообщение зависит от того, что человек сделал на предыдущем шаге. Открыл? Кликнул? Заплатил? Не среагировал 24 часа? Каждый исход — отдельная ветка. Это меняет всё: вместо «1 текст на всех» появляется граф из 10–30 узлов, в каждом из которых по 2–4 ветки. Грамотно собранная цепочка из 5–10 сообщений нередко поднимает конверсию из подписчика в оплату с 2–3% до 6–10%.

Из каких стадий состоит классическая автоворонка?

Из шести. TOFU — бесплатный лид-магнит (чек-лист, мини-курс, шаблон), цель обменять полезность на подписку. MOFU — прогрев контентом (кейсы, разбор возражений, мини-уроки), цель построить доверие. BOFU — оффер с дедлайном или ограничением, цель конверсия в покупку. Активация — после оплаты помогаем быстро получить первый результат (онбординг, чек-лист первого шага). Upsell или cross-sell через 7–30 дней после активации. Реактивация для тех, кто остыл на 14–60 дней — серия из 2–3 касаний с новым углом, не сработало — архив.

Какие триггеры запускают воронку в Telegram-боте?

Самые частые. Команда start с UTM-меткой в payload — бот парсит источник, кампанию, объявление и стартует сценарий. Подписка на канал — webhook от события chat_member: новый участник запускает приветствие в личке. Заполнение квиза — на основе ответов попадает в один из 3–4 сегментов с разной серией. Callback из контекстной рекламы — Яндекс.Директ ведёт на ссылку с payload, бот узнаёт кампанию. Внешний webhook от CRM — менеджер сменил статус сделки и бот сам пишет лиду. Действие в продукте — пользователь зарегистрировался, но не активировал тариф 3 дня — старт цепочки активации.

Как технически устроен шедулер автоворонки?

Три кубика: scheduler, таблица событий, worker. Scheduler — Celery beat, arq cron или собственный воркер раз в 30 секунд опрашивает таблицу событий. Таблица funnel_events — единая очередь со схемой user_id, scenario_id, step, due_at, sent_at, status, payload, attempts, last_error и уникальным ключом (user_id, scenario_id, step) для защиты от двойной планировки. Worker берёт пачку готовых шагов через UPDATE с FOR UPDATE SKIP LOCKED, рендерит шаблон, отправляет в Bot API, пишет результат. SKIP LOCKED позволяет горизонтально масштабировать воркеров — два инстанса не возьмут одно событие.

Какие stop-условия должны быть у автоворонки?

Минимум пять. Купил — выход из активной серии, переход в активацию. Отписался или нажал «не интересно» — halt всех будущих шагов сценария. Ответил менеджеру — передача в живой чат, бот молчит 24 часа и потом возвращается осторожно. Заблокировал бота — Bot API вернул 403 — halt всех сценариев и перевод в архив. Превышен лимит репортов (2+ за 30 дней) — halt и red flag в CRM. Реализация — общий halt(user_id, scenario_id), который выставляет status=skipped на всех будущих funnel_events пользователя одним UPDATE по таблице.

На какие метрики воронки реально стоит смотреть?

Open rate (в Telegram это почти equivalent delivery, норма 90–98%). CTR кнопок (15–35% хорошо). CR шаг в шаг — доходимость до следующего касания (60–85%). CR в оффер — доля дошедших до BOFU (30–50% от старта). CR в оплату из увидевших оффер (5–15%). Drop-off — на каком шаге теряем больше всего, норма меньше 30% на шаг. Процент отписок — индикатор давления, выше 2% на шаг — переписывать. ROI = выручка / стоимость трафика, больше 1.5 окупаемо. LTV / CAC — юнит-экономика, здоровое соотношение от 3.

Что выбрать: конструктор (Senler, SaleBot, BotHelp) или кастомную разработку?

Конструктор — для MVP, до 5k подписчиков и простого линейного сценария: запуск за 1–3 дня, абонентка 2–10 тыс. ₽/мес, базовая аналитика, готовые коннекторы к CRM и оплате. Минусы — лимиты на масштаб, слабые A/B-тесты, тегированная сегментация вместо SQL, данные на стороне платформы. Кастом — для продукта, масштаба и сложной логики: 3–8 недель и от 300 тыс. ₽ единоразово, любая логика, любые API, без лимитов на подписчиков, свой dashboard и BI, полноценные тесты. Гибрид тоже работает — конструктор для быстрого теста гипотезы, миграция на кастом после выручки от 1 млн ₽/мес и осознанного потолка платформы.