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. У кожного джерела був свій формат, але не було спільної відповіді на питання «що з цим лідом відбувається далі».
Потрібна була не копія великої CRM, а один робочий екран: лід прийшов, отримав статус, наступний крок і пішов у 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 — обговоримо задачу та формат роботи.