惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Jina AI
Jina AI
O
OpenAI News
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Security Archives - TechRepublic
Security Archives - TechRepublic
Google Online Security Blog
Google Online Security Blog
Google DeepMind News
Google DeepMind News
酷 壳 – CoolShell
酷 壳 – CoolShell
W
WeLiveSecurity
博客园 - 叶小钗
博客园 - 聂微东
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
小众软件
小众软件
Forbes - Security
Forbes - Security
宝玉的分享
宝玉的分享
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
美团技术团队
S
SegmentFault 最新的问题
N
News and Events Feed by Topic
V2EX - 技术
V2EX - 技术
量子位
H
Heimdal Security Blog
S
Security @ Cisco Blogs
WordPress大学
WordPress大学
Help Net Security
Help Net Security
人人都是产品经理
人人都是产品经理
博客园 - 三生石上(FineUI控件)
L
LangChain Blog
Last Week in AI
Last Week in AI
Hugging Face - Blog
Hugging Face - Blog
aimingoo的专栏
aimingoo的专栏
月光博客
月光博客
N
News | PayPal Newsroom
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
The GitHub Blog
The GitHub Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
阮一峰的网络日志
阮一峰的网络日志
Hacker News - Newest:
Hacker News - Newest: "LLM"
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
www.infosecurity-magazine.com
www.infosecurity-magazine.com
雷峰网
雷峰网
博客园 - 【当耐特】
S
Schneier on Security
S
Securelist
C
Cisco Blogs
Engineering at Meta
Engineering at Meta
Cisco Talos Blog
Cisco Talos Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
T
Tor Project blog
C
Cyber Attacks, Cyber Crime and Cyber Security
博客园_首页

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Автодозвон по лидам из Марквиз с отметкой в CRM
Екатерина Саяпина · 2026-06-18 · via Все публикации подряд на Хабре

9 мин

8

Привет, Хабр! В B2C-продажах — недвижимость, страхование, медицина — каждые 30 секунд задержки с первым звонком стоят процентов конверсии. Покупатель оставляет контакты в момент максимального интереса. Но импульс гаснет быстрее, чем менеджер успевает открыть CRM и набрать номер.

Почта и СМС не спасают: письмо пропустят, сообщение не заменит живого разговора. А ручной перенос данных из квиза в базу и набор номера — это десятки потерянных секунд на каждой заявке.

Вот здесь и кроется главная сложность: можно ли сделать так, чтобы лид сам создавался в CRM, а звонок запускался автоматически, без участия человека?

Оказывается, да. Мы сделали сервис, который соединяет квиз, CRM и телефонию в один сценарий. Он принимает вебхук от Марквиз, создает лид в amoCRM и запускает автоматический звонок через МТС Exolve. После разговора сервис получает событие от Exolve и сохраняет результат в CRM.

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

Стек: FastAPI, SQLite, requests, API Марквиз, amoCRM и МТС Exolve.

Общая схема работы

Пользователь прошел квиз — Марквиз отправляет в наш сервис вебхук с контактами и ответами. Сервис проверяет данные, отсеивает дубликаты и сохраняет заявку в журнале.

Если заявка новая, сервис создает карточку в CRM и запускает звонок между менеджером и клиентом. После разговора результат улетает в amoCRM, а статус заявки в SQLite обновляется.

Забегая вперед: даже в случае сбоя сервис восстановится сам, без ручного вмешательства. 

Архитектура: как мы разделили обязанности

Сервис состоит из двух частей: прием вебхуков (сохраняет входящее событие в базу) и фоновая обработка (доводит задачу до конца — создает лид в CRM, ставит звонок и записывает итог).

Разберем, кто с кем общается. Марквиз выступает источником данных и присылает JSON с результатами опроса. FastAPI — центральный узел, он принимает вебхуки и распределяет задачи в фоне. SQLite хранит состояния заявок и токены. amoCRM — система для ведения продаж, где сервис фиксирует сделки и заметки. А МТС Exolve обеспечивает обратный вызов и присылает отчеты о звонках.

Система работает как конечный автомат. Каждая заявка проходит цепочку статусов: от NEW до финальных ANSWERED или NO_ANSWER. Если внешнее API вернуло ошибку, заявка уходит в RETRY_WAIT.

Такой подход делает систему отказоустойчивой. Вместо сложных транзакций — простая стейт-машина в SQLite. После сбоя сервис восстанавливается с того шага, на котором остановился.

Что понадобится для запуска

Вам понадобится Python версии 3.10 или выше. Ключи доступа к API МТС Exolve и amoCRM нужно вынести в переменные окружения. Для Exolve получите API-ключ и идентификатор ресурса колбэка в личном кабинете API Платформы. Для amoCRM создайте приватное приложение и получите начальные токены.

Настройка окружения выглядит так:

bash
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

Файл .env собираем по примеру из .env.example:

text
AMO_SUBDOMAIN=...
AMO_CLIENT_ID=...
AMO_CLIENT_SECRET=...
AMO_REDIRECT_URI=...
AMO_INITIAL_ACCESS_TOKEN=...
AMO_INITIAL_REFRESH_TOKEN=...

EXOLVE_API_KEY=...
EXOLVE_NUMBER=7800XXXXXXX
EXOLVE_CALLBACK_RESOURCE_ID=12345
MANAGER_PHONE=7999XXXXXXX

Поясним параметры. EXOLVE_NUMBER — ваш системный номер в МТС Exolve, он будет виден менеджеру и клиенту. MANAGER_PHONE определяет, на чей телефон поступит первый вызов при колбэке.

Шаг 1. Принимаем вебхук от Марквиз

Здесь мы получаем запрос от Марквиз, проверяем номер телефона клиента и решаем — запускать обработку или завершиться.

Чтобы исключить повторы, мы создаем уникальный ключ (это называется идемпотентностью — повторный вызов с тем же ключом не создает дубликата). Если в вебхуке есть result.id — берем его. Если нет — собираем из идентификатора квиза, телефона и времени создания. При обнаружении дубликата в базе система прекращает обработку. Новые заявки уходят в фоновую задачу.

main.py:

Код
python
@app.post("/webhook/marquiz")
async def marquiz_webhook(request: Request, background_tasks: BackgroundTasks):
    payload = await request.json()

    if payload.get("test") == "marquiz_route_check":
        return {"status": "ok", "message": "Marquiz test passed"}

    raw_phone = payload.get("contacts", {}).get("phone")
    if not raw_phone:
        return {"status": "skipped", "reason": "No phone"}

    phone = normalize_phone(raw_phone)
    idempotency_key = build_idempotency_key(payload, phone)

    db_id = save_new_lead(idempotency_key, phone, payload)
    if db_id is None:
        return {"status": "ok", "message": "Already processed (Duplicate)"}

    background_tasks.add_task(process_quiz_lead, db_id)
    return {"status": "ok", "message": "Lead accepted"}

Функция сборки ключа идемпотентности:

Код

utils.py:

python
def build_idempotency_key(payload: dict, phone: str) -> str:
    res_id = payload.get("result", {}).get("id")
    if res_id:
        return f"marquiz_result:{res_id}"

    quiz_id = (
        payload.get("quiz", {}).get("id")
        or payload.get("quiz_id")
        or payload.get("form_id")
        or "unknown_quiz"
    )
    created = payload.get("created") or payload.get("created_at") or "unknown_time"

    raw_str = f"{quiz_id}_{phone}_{created}"
    return "marquiz_hash:" + hashlib.sha256(raw_str.encode()).hexdigest()

Результат шага: новая запись в журнале заявок или ответ о дубликате.
Ограничение сейчас: вебхук не проверяется по подписи. Скорее всего, для боевого сценария это стоит добавить.

Шаг 2. Сохраняем состояние заявки

Сервис пишет данные в локальную SQLite. Это помогает повторять запросы, отлаживать и связывать заявки с внешними системами.

Первая таблица хранит статусы заявок и связи с внешними действиями, вторая — токены amoCRM. После перезапуска сервис продолжает работу из базы, а не начинает с нуля.

database.py:

Код
python
cursor.execute("""
    CREATE TABLE IF NOT EXISTS leads_journal (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        idempotency_key TEXT UNIQUE,
        phone TEXT,
        amocrm_lead_id INTEGER,
        exolve_call_id TEXT,
        status TEXT DEFAULT 'NEW',
        attempts INTEGER DEFAULT 0,
        last_error TEXT,
        raw_payload TEXT,
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    )
""")

cursor.execute("""
    CREATE TABLE IF NOT EXISTS amocrm_tokens (
        id INTEGER PRIMARY KEY CHECK (id = 1),
        access_token TEXT,
        refresh_token TEXT
    )
""")

 Фактически таблица с лидами хранит конечный автомат: NEW → PROCESSING → CRM_CREATED → CALL_STARTED → финал. Отдельной истории событий сейчас нет, но для небольшого проекта это не критично.

Шаг 3. Создаем лид в amoCRM

После сохранения лида в SQLite добавляем его в воронку продаж. Система создает контакт со сделкой и прикрепляет заметку с ответами квиза.

Защита от повторной обработки работает так: если запись уже в статусе PROCESSING, обработчик пропускает ее. Заявки с финальным статусом (ANSWERED, NO_ANSWER, FAILED) повторно в API не отправляются.

main.py:

python
if not amo_lead_id:
    amo_lead_id = amocrm_client.create_lead_with_contact(phone)
    update_lead(db_id, amocrm_lead_id=amo_lead_id, status="CRM_CREATED", last_error="")

    answers = "\n".join([f"{q.get('q', '')}: {q.get('a', '')}" for q in raw_payload.get("answers", [])])
    amocrm_client.add_note(amo_lead_id, f"Ответы квиза:\n{answers}")

amocrm.py:

python
resp = requests.request(method, url, headers=headers, json=json_data, timeout=15)

if resp.status_code == 401:
    new_token = self.refresh_access_token()
    headers["Authorization"] = f"Bearer {new_token}"
    resp = requests.request(method, url, headers=headers, json=json_data, timeout=15)

Результат: в базе появляется номер сделки и заметка с ответами. Если что-то пошло не так — заявка уходит в RETRY_WAIT. На этом этапе внешние вызовы синхронные.

Шаг 4. Запускаем колбэк

Теперь сам звонок. Через Callback API МТС Exolve сервис запускает соединение менеджера и клиента, сохраняет идентификатор звонка и связывает его с заявкой.

main.py:

python
exolve_call_id = lead_record["exolve_call_id"]
if not exolve_call_id:
    exolve_call_id = initiate_callback(phone)
    update_lead(db_id, exolve_call_id=exolve_call_id, status="CALL_STARTED", attempts=attempts, last_error="")

Для запуска обратного вызова сервис берет из окружения номер Exolve, идентификатор ресурса и телефоны менеджера с клиентом. API Платформы возвращает идентификатор звонка в ответе на MakeCallback. Если его нет — считаем ошибкой.

exolve.py:

python
payload = {
    "request_description": "quiz_callback",
    "number_code": int(exolve_number),
    "callback_resource_id": callback_resource_id,
    "line_1": {"destinations": [{"number": manager_phone, "timeout": 30}], "ring_logic": 1, "display_number": exolve_number},
    "line_2": {"destinations": [{"number": client_phone, "timeout": 30}], "ring_logic": 1, "display_number": exolve_number}
}

resp = requests.post(url, headers=headers, json=payload, timeout=20)

После этого заявка переходит в CALL_STARTED. Если идентификатор не получен — запись уходит в RETRY_WAIT.

На этом этапе звонок только передан в Exolve. Ждем вебхук от API Платформы — по нему поймем, состоялся разговор или нет.

Шаг 5. Подчищаем сбойные и зависшие заявки

Раз в минуту отдельный цикл ищет записи, которые еще не дошли до звонка или финального статуса. Это страховка: если фоновая задача не стартовала или сервис перезапустился, заявка все равно попадет в обработку.

Для статусов RETRY_WAIT, CRM_CREATED и зависших PROCESSING (не обновлялись дольше пяти минут) цикл работает как восстановление после сбоя. Количество повторов — не больше пяти.

main.py:

python
pending_leads = conn.execute("""
    SELECT id FROM leads_journal 
    WHERE (
        status IN ('NEW', 'RETRY_WAIT', 'CRM_CREATED')
        OR (
            status = 'PROCESSING'
            AND datetime(updated_at) < datetime('now', '-5 minutes')
        )
    )
    AND attempts < 5
""").fetchall()

Сервис отдельно проверяет заявки, которые зависли в обработке, и возвращает их на повторную обработку, если они слишком долго не обновлялись.

python
def is_stale_processing(updated_at: str) -> bool:
    try:
        updated_dt = datetime.fromisoformat(updated_at)
        return updated_dt < datetime.utcnow() - timedelta(minutes=5)
    except Exception:
        return True

CALL_STARTED в эту выборку не попадает — после старта звонка сервис ждет только вебхук от Exolve.

Шаг 6. Завершаем работу с заявкой

Когда звонок закончился, Exolve присылает вебхук с его идентификатором. Сервис находит запись в базе и обновляет статус в зависимости от результата.

Если разговор состоялся — ставим ANSWERED. Если стороны не соединились — статус может быть NO_ANSWER, CANCELLED, COMPLETED или FAILED в зависимости от причины.

main.py:

Код
python
if event_type != "d":
    return {"status": "ok", "message": f"Event {event_type} ignored"}

if talk_time_ms > 0:
    final_status = "ANSWERED"
elif hangup_cause in {"NO_ANSWER", "NO_USER_RESPONSE"}:
    final_status = "NO_ANSWER"
elif "CANCEL" in hangup_cause:
    final_status = "CANCELLED"
elif hangup_cause == "NORMAL_CLEARING":
    final_status = "COMPLETED"
else:
    final_status = "FAILED"

update_lead(lead["id"], status=final_status)

После этого сервис добавляет заметку в карточку лида — менеджер видит итог звонка, не заходя в телефонию.

python
note_text = (
    f"Результат Callback-звонка: {final_status}\n"
    f"call_id: {call_id}\n"
    f"Причина завершения: {hangup_cause}\n"
    f"Время разговора: {talk_time_sec} сек.\n"
    f"Общая длительность: {duration_sec} сек."
)
amocrm_client.add_note(lead["amocrm_lead_id"], note_text)

Если заметку добавить не удалось, заявка получает статус NOTE_FAILED. Автоматического повтора для этого статуса нет.

Ограничения решения

У решения есть несколько ограничений, о которых важно знать.

  • Вебхуки от Марквиз и Exolve принимаются без проверки подписи. Это упрощает запуск, но повышает риск получить левые данные.

  • Сервис обновляет базу, CRM и телефонию последовательно, а не одной транзакцией. Иногда это приводит к рассинхрону: лид в CRM уже есть, а звонок еще не начался.

  • После старта звонка сервис ждет только обратный вебхук от Exolve. Если событие не приходит, заявка навсегда остается в промежуточном статусе.

  • SQLite хранит только текущее состояние, а не полную цепочку обработки. Вы видите статус, но восстановить, как заявка дошла до этого этапа, сложно.

  • SQLite нормально работает при небольшом потоке заявок. Но при сотнях одновременных вебхуков база начнет блокировать запись. Для промышленной эксплуатации стоит брать PostgreSQL и очередь задач.

Запуск и проверка

Настроили .env — запускаем:

bash
uvicorn main:app --host 0.0.0.0 --port 8000

Сначала отправляем тестовый пинг от Марквиз, чтобы убедиться, что маршрут доступен:

bash
curl -X POST http://localhost:8000/webhook/marquiz \
  -H "Content-Type: application/json" \
  -d '{"test":"marquiz_route_check"}'

Затем отправляем тестовый лид. В ответ ждем Lead accepted, а в journal.db — новую запись с ключом защиты от дублей.

bash
curl -X POST http://localhost:8000/webhook/marquiz \
  -H "Content-Type: application/json" \
  -d '{
    "contacts": {"phone": "+7 (999) 123-45-67"},
    "result": {"id": "r_001"},
    "answers": [{"q":"Услуга","a":"Логистика"}]
  }'

Финальная проверка — событие от МТС Exolve с type: d и валидным call_id из вашей записи. После него заявка должна получить финальный статус.

bash
curl -X POST http://localhost:8000/webhook/exolve \
  -H "Content-Type: application/json" \
  -d '{
    "type":"d",
    "call_id":"123456789",
    "hangup_cause":"NO_ANSWER",
    "talk_time":0,
    "duration":23000
  }'

Потенциал для развития

У нашего решения хороший потенциал для роста. Например, можно добавить повторные касания: если клиент не ответил, система сама запланирует следующий звонок. Лиды не выпадают, а нагрузка на команду не растет.

Полезна будет и приоритизация лидов — настроить очередь так, чтобы менеджер сначала получал заявки с высоким шансом на сделку. Тогда выручка ускоряется без увеличения потока клиентов.

Стоит внедрить контроль зависших заявок: система отслеживает лиды, которые застряли между этапами, и возвращает их в обработку. Это снижает скрытые потери в воронке.

И наконец, речевая аналитика. Анализ первого разговора помогает находить причины отказов и улучшать скрипты. Как результат — растут конверсия и качество продаж.

Что из этого внедрить в первую очередь — решать вам, исходя из текущих болевых точек.

Что в итоге?

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

Наш сценарий закрывает разрыв между квизом и первым разговором. Заявка проходит путь: Марквиз → amoCRM → вызов в Exolve → статус в CRM. Бизнес получает меньше потерянных лидов и полную прозрачность по каждому контакту.

Главные метрики, которые стоит считать регулярно, — время до первого звонка и итоговый статус: дозвонились, нет или ошибка. На их основе легко принимать решения по улучшению продаж.

Код, как обычно, в репозитории.