Автоворонка в 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% задач студии:
- 3-дневный прогрев перед вебинаром. Подписался → сразу подтверждение и календарь. День -2: история эксперта и польза вебинара. День -1: программа + кейс выпускника. За 3 часа: напоминание + ссылка. За 15 минут: «начинаем». Через час после вебинара: запись + оффер. Через 24 часа: дедлайн оффера.
- 7-дневная серия «обучаемся боту». День 0: приветствие + первый шаг. Дни 1–6: одно короткое письмо в день — концепция, пример, упражнение, кейс, FAQ, чек-лист, итог. День 7: оффер платного тарифа с бонусом «только для тех, кто прошёл серию».
- Reactivation спящих. Молчание 30 дней → касание 1: «вернулись с обновлением, вот что нового». Через 3 дня без реакции → касание 2: персональный промокод. Через 7 дней → касание 3: «уходим из вашей подписки, нажмите если интересно». Молчание → архив сегмента, не пишем больше.
Архитектура: scheduler, события, worker
Технический контур автоворонки — три кубика, не считая самого бота:
- Scheduler — Celery beat,
arqcron, временный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 → автоквалификация. Цепочка такая:
- В объявлении ссылка вида
t.me/yourbot?start=ydirect_search_calc_msk_2026q1. - Бот парсит payload, разбирает компоненты (
source=ydirect,type=search,creative=calc,geo=msk,period=2026q1). - Записывает в профиль и стартует сценарий, специфичный для этой кампании.
- После первой целевой реакции дёргает Метрику с offline-конверсией:
yandex_metrika.reach_goal('lead_qualified', client_id=user.metrika_cid). - Директ оптимизирует кампанию по реальным квалификациям, а не по кликам.
Эта связка сама по себе даёт −20–40% к стоимости лида за 2–3 недели обучения автостратегии.
Метрики
Тщеславные метрики типа «количество подписчиков» обманывают. Реально полезные:
| Метрика | Что показывает | Норма |
|---|---|---|
| Open rate (≈ delivery в Telegram) | Доставлено и не заблокировано | 90–98% |
| CTR кнопок | Кликабельность CTA | 15–35% |
| CR шаг → шаг | Доходимость до следующего касания | 60–85% |
| CR в оффер | Дошли до BOFU | 30–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 млн ₽/мес и осознанного потолка платформы.