Перейти к содержанию
CRM9 ноября 2026 г.7 мин чтения

CRM на один экран: приём лидов, фильтры и отправка в n8n

Как собрать простую CRM под одного пользователя: SQLite, API приёма, дедуп, ИИ-синхронизация Telegram каждые 20 минут, умное распределение по 15 параметрам, задачи из плана и outbox в n8n.

Главное

CRM синхронизирует Telegram через ИИ каждые 20 минут: ИИ читает ответы, отличает клиента от бота, обновляет CRM и раскладывает лидов по 15 параметрам. Задачи рождаются из плана: первое касание → follow-up → разбор ответа → повтор.

ПоделитьсяTelegram
Назад в блог
Содержание

Проблема: лиды в трёх местах

До этой 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:

  1. лид создаётся в SQLite;
  2. рядом появляется запись deliveries со статусом queued;
  3. worker отправляет payload на вебхук;
  4. при 2xx ставит sent, при 5xx — повторяет с backoff;
  5. после восьми попыток — failed и кнопка «Повторить».

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

Фильтры, которые нужны на самом деле

Фильтры в CRM — это не «поле для галочки». Они отвечают на ежедневные вопросы: что новое, что горит, кому не ответили, что готово к отправке, где ошибка n8n.

В текущей версии есть:

  • этап, источник и температура;
  • теги, бюджет и score;
  • дата создания и последняя активность;
  • следующий шаг: просрочен, сегодня, на неделе, не задан;
  • статус отправки в n8n;
  • архив и спам;
  • сохранённые представления и экспорт текущего фильтра в CSV.

Состояние фильтров живёт в URL. Ссылку можно сохранить, отправить себе в Telegram или открыть на телефоне.

Дедуп по контактам

Ключи дедупа идут по приоритету:

  1. source + external_id;
  2. нормализованный email;
  3. Telegram без @;
  4. телефон в E.164;
  5. нечёткое совпадение имени и компании — только флаг, без автослияния.

Нормализованные поля хранятся отдельно. Это позволяет найти одного и того же человека, даже если в одной форме он написал @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 на ближайшем тике, а задача «Разобрать ответ» закроется после того, как сообщение отмечено прочитанным.

CRM: ИИ-синхронизация, лиды и задачи на демо-данных

Распределение: 15 параметров

Лид не просто падает в общий список. Он раскладывается по очередям и приоритетам по параметрам:

  • источник и группа, откуда пришёл запрос;
  • этап: новый, связались, квалифицирован, предложение, переговоры;
  • температура: горячий, тёплый, холодный;
  • score 0–100;
  • бюджет и валюта;
  • язык и часовой пояс;
  • канал связи: Telegram, email, телефон;
  • последняя активность;
  • возраст лида;
  • SLA без ответа;
  • есть ответ или нет;
  • дата follow-up;
  • теги: tier, day, sent, replied, flood, bot-reply;
  • статус доставки в n8n;
  • наличие открытой задачи и следующего шага.

На практике это даёт понятные срезы: «горячие без ответа», «просрочен follow-up», «ответили, но задача не разобрана», «flood — повторить после паузы». Один и тот же лид не теряется между чатом, таблицей и заметками.

Задачи: сначала планирование, потом действие

Автоматические задачи появляются не из хаоса, а из плана:

  1. План по датам. Первое касание — по дате и времени из плана, follow-up — через три рабочих дня, повтор после блокировки — после паузы.
  2. Очередь задач. Из плана рождаются конкретные задачи: первое сообщение, follow-up, разбор ответа, повтор после PEER_FLOOD.
  3. Next action. У лида всегда один ближайший шаг, а не десять равнозначных.
  4. Автозакрытие. Ответ разобран или отправлен — задача закрывается, 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 для лидов.

Валера · webdev07

Senior Systems Engineer. Строю системы, которые работают.

@webdevs07

Нужна такая автоматизация?

Напиши в Telegram — обсудим задачу и формат работы.

Обсудить проект