Kommo + Dialpad: AI-транскрипт и итог звонка в карточке сделки

Dialpad - UCaaS-платформа с встроенным AI: после каждого звонка система автоматически создаёт транскрипт и краткое резюме переговоров (recap_summary). Это сильный инструмент для управления продажами - при условии, что данные попадают в CRM. Без интеграции с Kommo транскрипты живут внутри Dialpad: менеджер должен сам открывать приложение, находить запись и пересказывать содержание руководителю.

В командах, которые используют Dialpad как основной инструмент для sales-звонков, AI-резюме становится ценным активом: оно заменяет ручные заметки после звонка. Но ценность теряется, если резюме не привязано к конкретной сделке в CRM. Менеджер звонит из Dialpad, переговоры там же - а воронка в Kommo остаётся без контекста.

В этой статье покажем, как связать Dialpad с Kommo через webhook и Dialpad Calls API, чтобы каждый звонок с его записью и AI-резюме автоматически появлялся в карточке сделки.

Почему нативной интеграции нет

Dialpad имеет нативные интеграции с Salesforce, HubSpot, Pipedrive и несколькими другими CRM. Kommo в этот список не входит. В Kommo App Marketplace Dialpad отсутствует. Это типичная ситуация для специализированных UCaaS-платформ: поддерживается топ-5 CRM по рыночной доле, остальные - через API.

Zapier-триггеры для Dialpad существуют, но они не передают AI-транскрипт и recap_summary - только базовые мета-данные звонка (длительность, номер, дата). Именно ради AI-данных нужна кастомная реализация.

Ключевая особенность: Dialpad Ai

Dialpad Ai - встроенный движок распознавания речи и генерации инсайтов. Для каждого завершённого звонка он создаёт:

  • Транскрипт - полная расшифровка диалога с разметкой говорящих
  • Recap Summary (recap_summary) - AI-резюме звонка: что обсуждали, какие следующие шаги, открытые вопросы
  • Action Items - задачи, упомянутые в разговоре

Эти данные доступны через Dialpad Calls API через 5-10 минут после завершения звонка. Пушить их в Kommo нужно не в момент call_ended, а после того как AI обработал запись.

Архитектура интеграции

Поток данных:

  1. Звонок завершён - Dialpad отправляет call_ended webhook
  2. Наш сервис ставит задачу с задержкой 10 минут
  3. Через 10 минут - запрос Dialpad Calls API за транскриптом и recap
  4. Параллельно - поиск сделки в Kommo по номеру телефона
  5. Создаём заметку в Kommo с записью, транскриптом и AI-резюме

Реализация

Шаг 1 - настройка Dialpad webhook:

Через Dialpad Developer Portal создаём webhook и подписку на call events. Dialpad поддерживает JWT-кодированные или JSON-кодированные payload.

from flask import Flask, request, abort
import requests, time

app = Flask(__name__)
DIALPAD_API_KEY = "your_dialpad_api_key"  # из Dialpad Developer Portal
KOMMO_DOMAIN    = "yourdomain.kommo.com"
KOMMO_TOKEN     = "your_kommo_token"

DP_BASE = "https://dialpad.com/api/v2"

@app.route("/dialpad/webhook", methods=["POST"])
def dialpad_webhook():
    # Dialpad webhook signature verification через shared secret
    # (опциональна, но рекомендована)
    event = request.json
    state = event.get("state", "")

    if state == "call_ended":
        # Ставим delayed task - AI-обработка занимает 5-15 минут
        call_id      = event.get("call_id")
        phone        = (
            event.get("target", {}).get("phone")
            or event.get("caller_phone_number", "")
        )
        direction    = event.get("direction", "inbound")
        duration_sec = event.get("duration", 0)

        # Планируем обработку через 12 минут
        schedule_ai_processing(call_id, phone, direction, duration_sec, delay=720)

    return "ok", 200

Шаг 2 - получение AI-данных из Dialpad:

def get_dialpad_call_data(call_id: str) -> dict:
    """Get call details, recording URL, and AI recap from Dialpad API."""
    headers = {"Authorization": f"Bearer {DIALPAD_API_KEY}"}

    # Основные данные звонка
    r = requests.get(f"{DP_BASE}/calls/{call_id}", headers=headers)
    r.raise_for_status()
    call = r.json()

    # AI Recap Summary (доступен через ~10 минут)
    recap = ""
    try:
        r2 = requests.get(f"{DP_BASE}/calls/{call_id}/recap", headers=headers)
        if r2.status_code == 200:
            recap_data = r2.json()
            recap = recap_data.get("recap_summary", "")
    except Exception:
        pass  # Recap может быть недоступен для коротких звонков

    # URL записи (требует scope recordings_export)
    recording_url = call.get("recording_url", "")

    return {
        "call_id":       call_id,
        "duration":      call.get("duration", 0),
        "recording_url": recording_url,
        "recap_summary": recap,
        "contact_name":  call.get("contact", {}).get("name", ""),
    }

Шаг 3 - поиск сделки в Kommo и создание заметки:

KOMMO_BASE = f"https://{KOMMO_DOMAIN}/api/v4"

def find_kommo_lead(phone: str) -> dict | None:
    """Find open lead in Kommo by phone number."""
    hs = requests.Session()
    hs.headers.update({
        "Authorization": f"Bearer {KOMMO_TOKEN}",
        "Content-Type":  "application/json"
    })

    # Kommo поиск по телефону через contacts search
    r = hs.get(f"{KOMMO_BASE}/contacts", params={"query": phone, "limit": 1})
    contacts = r.json().get("_embedded", {}).get("contacts", [])
    if not contacts:
        return None

    contact_id = contacts[0]["id"]

    # Найти активные сделки контакта
    r2 = hs.get(f"{KOMMO_BASE}/contacts/{contact_id}/links")
    links = r2.json().get("_embedded", {}).get("links", [])
    lead_ids = [l["to_entity_id"] for l in links if l.get("to_entity_type") == "leads"]

    if not lead_ids:
        return None

    # Берём первую открытую сделку
    for lead_id in lead_ids[:5]:
        r3 = hs.get(f"{KOMMO_BASE}/leads/{lead_id}",
                    params={"with": "contacts"})
        lead = r3.json()
        # Статусы <143 = незакрытые сделки в Kommo
        if lead.get("status_id", 0) not in (142, 143):
            return {"id": lead_id, "contact_id": contact_id}

    return None

def process_call_to_kommo(call_id: str, phone: str, direction: str, duration: int):
    """Main function: fetch AI data from Dialpad, push to Kommo."""
    # Получаем AI-данные
    call_data = get_dialpad_call_data(call_id)

    # Ищем сделку
    lead = find_kommo_lead(phone)
    if not lead:
        return  # нет активной сделки - логируем без привязки

    hs = requests.Session()
    hs.headers.update({
        "Authorization": f"Bearer {KOMMO_TOKEN}",
        "Content-Type":  "application/json"
    })

    duration_min = duration // 60
    direction_ru = "Входящий" if direction == "inbound" else "Исходящий"
    name         = call_data["contact_name"] or phone

    # Формируем текст заметки
    note_parts = [
        f"{direction_ru} звонок: {name}",
        f"Длительность: {duration_min} мин.",
    ]

    if call_data["recording_url"]:
        note_parts.append(f"Запись: {call_data['recording_url']}")

    if call_data["recap_summary"]:
        note_parts.append("")
        note_parts.append("AI-резюме Dialpad:")
        note_parts.append(call_data["recap_summary"])

    note_text = "\n".join(note_parts)

    # Создаём заметку в Kommo
    hs.post(f"{KOMMO_BASE}/leads/notes", json=[{
        "entity_id":  lead["id"],
        "note_type":  "call_in" if direction == "inbound" else "call_out",
        "params":     {
            "duration": duration,
            "phone":    phone,
            "text":     note_text,
            "link":     call_data["recording_url"] or "",
        }
    }])

note_type: "call_in" / "call_out" - специальные типы заметок в Kommo для звонков. Они отображаются в хронологии с иконкой трубки, а не как обычная заметка.

Шаг 4 - delayed processing через очередь:

Для задержки 12 минут используем простую очередь на Redis + Celery, или если нет Redis - встроенный планировщик:

from threading import Timer

# Хранилище pending calls (в продакшене - Redis)
_pending: dict = {}

def schedule_ai_processing(call_id, phone, direction, duration, delay=720):
    def run():
        try:
            process_call_to_kommo(call_id, phone, direction, duration)
        finally:
            _pending.pop(call_id, None)

    t = Timer(delay, run)
    t.daemon = True
    t.start()
    _pending[call_id] = t

В продакшене вместо threading.Timer используйте Celery с Redis-брокером или любую задачную очередь. threading.Timer подойдёт для небольших объёмов (до 100 звонков/день).

Реальный кейс

B2B-компания: команда 5 sales-менеджеров, 60-80 звонков в день через Dialpad. До интеграции: менеджеры открывали Dialpad после каждого важного звонка, копировали AI-резюме в заметку Kommo вручную. Это занимало 3-5 минут на звонок, при этом часть звонков не фиксировалась вообще.

После внедрения кастомного webhook:

  • 100% звонков фиксируются в Kommo без участия менеджера
  • AI-резюме появляется в карточке сделки через 12-15 минут после звонка
  • Запись доступна как ссылка прямо в заметке
  • Руководитель видит полный контекст сделки без обращения к Dialpad

Время разработки: 1-2 дня. Единственная нетривиальная часть - обработка задержки AI-транскрипта.

Для кого актуально

Команды на Kommo, которые используют Dialpad как основной инструмент для исходящих и входящих sales-звонков. Особенно актуально для команд с активным coaching-процессом: руководители регулярно просматривают звонки и используют AI-резюме для обратной связи.

Аналогичные интеграции реализованы для Kommo + Vonage и Kommo + Nextiva. Если вы на Kommo и ищете кастомные интеграции - подход универсальный для любого UCaaS с API.

Часто задаваемые вопросы

Какой OAuth scope нужен для записи звонка?

Для получения recording_url в Dialpad API необходим scope recordings_export. Он запрашивается при OAuth авторизации или при создании API key в Developer Portal. Без этого scope recording_url возвращается пустым.

Доступен ли recap_summary на базовом тарифе Dialpad?

Dialpad Ai функции (транскрипт, recap, action items) включены в планы Pro и Enterprise. На базовом Business-плане AI-функции могут быть ограничены. Проверьте ваш тариф в Dialpad Admin Portal -> Ai features.

Как обрабатывать пропущенные звонки?

Для пропущенных звонков duration = 0 и транскрипта нет. В нашей реализации можно добавить проверку: если duration < 30 секунд - создать заметку типа call_missed в Kommo без AI-данных. Kommo поддерживает note_type: "call_miss" для этого случая.

Как идентифицировать сделку если один номер телефона в нескольких сделках?

Аналогично другим VoIP-интеграциям: в базовой реализации берём последнюю открытую сделку. Для точной привязки используйте Custom Data в Dialpad Contact - добавьте kommo_lead_id как кастомное поле контакта Dialpad. Тогда оно будет в webhook-payload и устранит неоднозначность.

Поддерживается ли Dialpad Meetings (видеозвонки)?

Dialpad Meetings (ранее UberConference) - отдельный продукт с другими API endpoints. Интеграция с Kommo через meetings API строится аналогично, но через GET /conferences/{id} вместо /calls/{id}. Transcript доступен через GET /conferences/{id}/transcripts.

Итог

Kommo + Dialpad интеграция ценна именно AI-данными: recap_summary автоматически документирует каждый звонок без ручной работы менеджера. Схема:

  • Dialpad webhook call_ended -> отложенный запуск через 12 минут
  • GET /calls/{id} + GET /calls/{id}/recap -> транскрипт и AI-резюме
  • Поиск сделки в Kommo по номеру телефона
  • POST /leads/notes с типом call_in/call_out - запись и резюме в хронологии сделки

Если ваша команда работает с Dialpad и хочет видеть AI-транскрипты прямо в Kommo - опишите задачу команде Exceltic.dev. Покажем архитектуру под ваш объём звонков.

Ещё статьи

Все →