CRM на один экран: приём лидов, фильтры и отправка в n8n
Как собрать простую CRM под одного пользователя: SQLite, API приёма, дедуп, ИИ-синхронизация Telegram каждые 20 минут, умное распределение по 15 параметрам, задачи из плана и outbox в n8n.
Главное
CRM синхронизирует Telegram через ИИ каждые 20 минут: ИИ читает ответы, отличает клиента от бота, обновляет CRM и раскладывает лидов по 15 параметрам. Задачи рождаются из плана: первое касание → follow-up → разбор ответа → повтор.
Содержание
Проблема: лиды в трёх местах
До этой CRM заявки жили в трёх несвязанных местах: форма портфолио писала в JSONL и Telegram, AI OPS сохранял демо-заявки в Supabase, а Telegram-лиды лежали в CSV. У каждого источника был свой формат, но не было общего ответа на вопрос «что с этим лидом происходит дальше».
Нужна была не копия amoCRM, а один рабочий экран: лид пришёл, получил статус, следующий шаг и ушёл в n8n. Без ролей, отделов и настроек на сорок вкладок.
Что вошло в MVP
- один пользователь и один вход;
- приём заявок с портфолио и AI OPS через общий API;
- ручное добавление и импорт Telegram-лидов;
- карточка с этапом, температурой, score, бюджетом, тегами, заметками и задачами;
- фильтры по этапу, источнику, температуре, тегам, бюджету, дате и следующему шагу;
- отправка в n8n с журналом доставок и повторами;
- SQLite, systemd, Caddy и ежедневный бэкап.
Один вход вместо трёх интеграций
Все источники отправляют один и тот же формат lead.v1:
{
"schema": "lead.v1",
"external_id": "portfolio:8f3c...",
"source": "portfolio",
"name": "Иван",
"email": "ivan@example.com",
"message": "Нужна CRM для отдела продаж",
"consent": { "granted": true, "at": "2026-11-09T10:00:00Z" }
}
API принимает Bearer-ключ и Idempotency-Key. Повторная отправка с тем же ключом не создаёт второй лид. Если email, Telegram или телефон уже есть в базе, CRM дополняет существующую карточку и пишет событие «повторное обращение».
Это важнее, чем кажется. Формы ретраят запросы, пользователи отправляют заявку дважды, а менеджер не должен разбирать дубли вручную.
SQLite и очередь вместо инфраструктуры
Для одного пользователя и десятков лидов в день SQLite достаточно. WAL, индексы и busy_timeout закрывают чтение, фильтры и фоновую запись. База — один файл, бэкап — копия файла, а не отдельный сервер с Postgres.
Отправка в n8n сделана через outbox:
- лид создаётся в SQLite;
- рядом появляется запись
deliveriesсо статусомqueued; - worker отправляет payload на вебхук;
- при 2xx ставит
sent, при 5xx — повторяет с backoff; - после восьми попыток —
failedи кнопка «Повторить».
Прямой fetch из формы выглядит проще, но если n8n недоступен, лид остаётся без доставки. Очередь переживает рестарт и не блокирует ответ пользователю.
Фильтры, которые нужны на самом деле
Фильтры в CRM — это не «поле для галочки». Они отвечают на ежедневные вопросы: что новое, что горит, кому не ответили, что готово к отправке, где ошибка n8n.
В текущей версии есть:
- этап, источник и температура;
- теги, бюджет и score;
- дата создания и последняя активность;
- следующий шаг: просрочен, сегодня, на неделе, не задан;
- статус отправки в n8n;
- архив и спам;
- сохранённые представления и экспорт текущего фильтра в CSV.
Состояние фильтров живёт в URL. Ссылку можно сохранить, отправить себе в Telegram или открыть на телефоне.
Дедуп по контактам
Ключи дедупа идут по приоритету:
source + external_id;- нормализованный email;
- Telegram без
@; - телефон в E.164;
- нечёткое совпадение имени и компании — только флаг, без автослияния.
Нормализованные поля хранятся отдельно. Это позволяет найти одного и того же человека, даже если в одной форме он написал @Username, а в другой — ссылку https://t.me/username.
Контракт с n8n
n8n получает не «плоский JSON на глаз», а стабильный контракт:
{
"schema": "lead.v1",
"delivery_id": "dlv_...",
"event": "lead.created",
"lead": {
"id": "ld_...",
"name": "Иван",
"source": "portfolio",
"temperature": "hot",
"score": 70
},
"consent": { "granted": true },
"meta": { "attempt": 1 }
}
Запрос подписан HMAC-SHA256:
X-CRM-Signature: sha256=<hmac(secret, timestamp + "." + body)>
В n8n подпись проверяется первым узлом. Дальше workflow сам решает, куда вести лида: в Telegram, таблицу, CRM клиента или почту.
Импорт Telegram-лидов
Сценарий — 120+ прямых контактов из Telegram-выгрузки. Импорт идемпотентный: external_id = telegram-tools:<id>, дедуп по @username.
В карточке сохраняются группа, категория, оффер, score, ссылка на пост и дата поста. Исходная дата остаётся в метаданных, а created_at выставляется на момент импорта — чтобы дашборд показывал, что лиды действительно добавлены.
Умная синхронизация: лид не живёт в двух местах
Каждые 20 минут ИИ-слой читает новые сообщения и обновляет CRM: первое сообщение, ответы, этап, температуру, теги, последнюю активность и ошибки доставки. Модель отличает живого клиента от бота или агрегатора, а детерминированный синк переносит данные без дублей. Не нужно вручную копировать переписку и переносить статусы.
Синк идемпотентный. Повторный запуск обновляет ту же карточку, а не создаёт дубль: дедуп идёт по Telegram, email и телефону. Если ответ пришёл в 16:32, он появится в CRM на ближайшем тике, а задача «Разобрать ответ» закроется после того, как сообщение отмечено прочитанным.

Распределение: 15 параметров
Лид не просто падает в общий список. Он раскладывается по очередям и приоритетам по параметрам:
- источник и группа, откуда пришёл запрос;
- этап: новый, связались, квалифицирован, предложение, переговоры;
- температура: горячий, тёплый, холодный;
- score 0–100;
- бюджет и валюта;
- язык и часовой пояс;
- канал связи: Telegram, email, телефон;
- последняя активность;
- возраст лида;
- SLA без ответа;
- есть ответ или нет;
- дата follow-up;
- теги: tier, day, sent, replied, flood, bot-reply;
- статус доставки в n8n;
- наличие открытой задачи и следующего шага.
На практике это даёт понятные срезы: «горячие без ответа», «просрочен follow-up», «ответили, но задача не разобрана», «flood — повторить после паузы». Один и тот же лид не теряется между чатом, таблицей и заметками.
Задачи: сначала планирование, потом действие
Автоматические задачи появляются не из хаоса, а из плана:
- План по датам. Первое касание — по дате и времени из плана, follow-up — через три рабочих дня, повтор после блокировки — после паузы.
- Очередь задач. Из плана рождаются конкретные задачи: первое сообщение, follow-up, разбор ответа, повтор после PEER_FLOOD.
- Next action. У лида всегда один ближайший шаг, а не десять равнозначных.
- Автозакрытие. Ответ разобран или отправлен — задача закрывается, next action пересчитывается.
Так задачи помогают двигать лида, а не создают видимость работы.
Рабочее место, а не таблица
После первых недель стало понятно: мало просто хранить лиды. Нужно, чтобы список был рабочим местом.
Что добавил:
- 21 колонка — от компании и Telegram до тегов и последнего контакта; набор сохраняется в URL.
- Inline-редактирование — этап, температура, score, бюджет и дата следующего шага меняются прямо в строке.
- Сохранённые виды — фильтр + сортировка + колонки под именем.
- Расширенные фильтры — score, канал связи, давность активности и возраст новых.
- SLA-метка — «N дней без ответа» у новых лидов.
- Глобальный поиск — ⌘K / Alt+K по имени, компании, email, Telegram и тексту.
- Массовые действия и экспорт выбранных — этап, температура, тег, «Связался», архив, n8n.
- Закрепление и j/k — важные лиды сверху, навигация по строкам с клавиатуры.
- Задачи и следующие шаги — отдельный экран, задача закрывается и обновляет next action.
- Сохранение состояния — возврат из карточки открывает тот же отфильтрованный список.
Это не «ещё десять кнопок», а сценарии, которые убирают переходы в карточку и ручную рутину.
Что получилось
В сценарии — 120+ Telegram-контактов и 30 активных диалогов. 10+ функций, один пользователь, одна база, один экран.
Что оказалось важным:
- не делать роли и команды, которых нет;
- не тащить Postgres ради одного пользователя;
- хранить нормализованные контакты, а не надеяться на «чистые» данные;
- отделить приём лида от отправки в n8n;
- сделать фильтры ссылками, а не состоянием в голове.
Что дальше
Следующие шаги — не «больше полей», а меньше ручной работы: CSV-импорт в интерфейсе, склейка дублей, автоматические правила отправки и отчёт по источникам. Но и текущая версия уже закрывает главную боль: лид не теряется между формой, Telegram и n8n.
Полный кейс с архитектурой и деталями: CRM для лидов.
Ещё по теме
DOC OPS: как превратить счёт в проверенную таблицу
Разбор кейса: OCR, извлечение по JSON-схеме, детерминированная валидация, human-in-the-loop и бенчмарк на 44 синтетических документах.
ЧитатьАрбитраж трафика в 2026: дороже, честнее и ближе к инженерии
Состояние арбитража трафика к ноябрю 2026: рост CPM, ограничения cookie, AI-креативы, Telegram Ads, first-party data и unit-экономика. Что делать дальше.
ЧитатьНужна такая автоматизация?
Напиши в Telegram — обсудим задачу и формат работы.