Kommo + Dialpad: AI Call Transcript and Summary in the Deal Card

Dialpad is a UCaaS platform with built-in AI: after every call the system automatically generates a transcript and a call summary (recap_summary). This is a powerful tool for sales management - as long as the data lands in your CRM. Without a Kommo integration, transcripts live inside Dialpad: reps have to open the app themselves, find the recording, and relay the content to their manager.

In teams that use Dialpad as their primary tool for sales calls, AI summaries become a valuable asset - replacing manual post-call notes. But the value is lost if the summary is not tied to a specific deal in the CRM. The rep makes the call in Dialpad, the conversation stays there - while the pipeline in Kommo remains without context.

This article shows how to connect Dialpad with Kommo via webhook and the Dialpad Calls API so that every call, along with its recording and AI summary, automatically appears in the deal card.

Why there is no native integration

Dialpad has native integrations with Salesforce, HubSpot, Pipedrive, and a few other CRMs. Kommo is not on that list, and Dialpad is absent from the Kommo App Marketplace. This is a typical situation for specialized UCaaS platforms: the top 5 CRMs by market share are supported natively; everyone else goes through the API.

Zapier triggers for Dialpad exist, but they do not pass the AI transcript or recap_summary - only basic call metadata (duration, phone number, date). The AI data is exactly why a custom implementation is needed.

Key feature: Dialpad Ai

Dialpad Ai is the built-in speech recognition and insights engine. For every completed call it produces:

  • Transcript - a full dialogue transcription with speaker labels
  • Recap Summary (recap_summary) - an AI summary of the call: what was discussed, next steps, open questions
  • Action Items - tasks mentioned during the conversation

These data points are available via the Dialpad Calls API 5-10 minutes after the call ends. They should be pushed to Kommo not at the call_ended moment, but after the AI has finished processing the recording.

Integration architecture

Data flow:

  1. Call ends - Dialpad sends a call_ended webhook
  2. Our service schedules a delayed task 10 minutes out
  3. After 10 minutes - query the Dialpad Calls API for the transcript and recap
  4. In parallel - search for the deal in Kommo by phone number
  5. Create a note in Kommo with the recording, transcript, and AI summary

Implementation

Step 1 - configure the Dialpad webhook:

Use the Dialpad Developer Portal to create a webhook and subscribe to call events. Dialpad supports JWT-encoded or JSON-encoded payloads.

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

app = Flask(__name__)
DIALPAD_API_KEY = "your_dialpad_api_key"  # from 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 via shared secret
    # (optional but recommended)
    event = request.json
    state = event.get("state", "")

    if state == "call_ended":
        # Schedule a delayed task - AI processing takes 5-15 minutes
        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)

        # Schedule processing in 12 minutes
        schedule_ai_processing(call_id, phone, direction, duration_sec, delay=720)

    return "ok", 200

Step 2 - retrieve AI data from 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}"}

    # Core call data
    r = requests.get(f"{DP_BASE}/calls/{call_id}", headers=headers)
    r.raise_for_status()
    call = r.json()

    # AI Recap Summary (available after ~10 minutes)
    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 may be unavailable for very short calls

    # Recording URL (requires recordings_export scope)
    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", ""),
    }

Step 3 - find the deal in Kommo and create a note:

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 search by phone via 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"]

    # Find active deals for the contact
    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

    # Take the first open deal
    for lead_id in lead_ids[:5]:
        r3 = hs.get(f"{KOMMO_BASE}/leads/{lead_id}",
                    params={"with": "contacts"})
        lead = r3.json()
        # Status IDs <143 = open deals in 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."""
    # Get AI data
    call_data = get_dialpad_call_data(call_id)

    # Find the deal
    lead = find_kommo_lead(phone)
    if not lead:
        return  # no active deal - log without association

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

    duration_min = duration // 60
    direction_label = "Inbound" if direction == "inbound" else "Outbound"
    name         = call_data["contact_name"] or phone

    # Build note text
    note_parts = [
        f"{direction_label} call: {name}",
        f"Duration: {duration_min} min.",
    ]

    if call_data["recording_url"]:
        note_parts.append(f"Recording: {call_data['recording_url']}")

    if call_data["recap_summary"]:
        note_parts.append("")
        note_parts.append("Dialpad AI Summary:")
        note_parts.append(call_data["recap_summary"])

    note_text = "\n".join(note_parts)

    # Create note in 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" are special note types in Kommo for calls. They appear in the timeline with a phone icon rather than as a plain note.

Step 4 - delayed processing via a queue:

For the 12-minute delay, use a simple Redis + Celery queue, or if Redis is not available - a built-in scheduler:

from threading import Timer

# Pending calls storage (use Redis in production)
_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

In production, replace threading.Timer with Celery backed by a Redis broker, or any other task queue. threading.Timer is sufficient for low volumes (up to 100 calls per day).

Real-world case

A B2B company: a team of 5 sales reps making 60-80 calls per day through Dialpad. Before the integration, reps would open Dialpad after every important call, copy the AI summary, and paste it into a Kommo note manually. This took 3-5 minutes per call, and a portion of calls were never logged at all.

After deploying the custom webhook:

  • 100% of calls are logged in Kommo without any rep involvement
  • The AI summary appears in the deal card 12-15 minutes after the call ends
  • The recording is available as a link directly in the note
  • Managers have full deal context without ever opening Dialpad

Development time: 1-2 days. The only non-trivial part is handling the AI transcript delay.

Who this is relevant for

Teams on Kommo that use Dialpad as their primary tool for outbound and inbound sales calls. Especially relevant for teams with an active coaching process: managers regularly review calls and use AI summaries for feedback sessions.

Similar integrations have been built for Kommo + Vonage and Kommo + Nextiva. If you are on Kommo and looking for custom integrations, the approach is universal for any UCaaS platform with an API.

Frequently asked questions

What OAuth scope is needed to access call recordings?

To retrieve recording_url from the Dialpad API you need the recordings_export scope. It is requested during OAuth authorization or when creating an API key in the Developer Portal. Without this scope, recording_url is returned empty.

Is recap_summary available on the base Dialpad plan?

Dialpad Ai features (transcript, recap, action items) are included in the Pro and Enterprise plans. On the base Business plan, AI features may be restricted. Check your plan in the Dialpad Admin Portal -> Ai features.

How should missed calls be handled?

For missed calls, duration = 0 and there is no transcript. In this implementation you can add a check: if duration < 30 seconds - create a call_missed note in Kommo without any AI data. Kommo supports note_type: "call_miss" for this case.

How to identify a deal when one phone number appears in multiple deals?

Same approach as other VoIP integrations: the base implementation picks the most recent open deal. For precise matching, use Custom Data in the Dialpad Contact - add kommo_lead_id as a custom field on the Dialpad contact. It will then appear in the webhook payload and eliminate ambiguity.

Is Dialpad Meetings (video calls) supported?

Dialpad Meetings (formerly UberConference) is a separate product with different API endpoints. Integration with Kommo via the meetings API follows the same pattern, but uses GET /conferences/{id} instead of /calls/{id}. The transcript is available via GET /conferences/{id}/transcripts.

Summary

The value of the Kommo + Dialpad integration lies specifically in the AI data: recap_summary automatically documents every call with zero manual effort from the rep. The flow:

  • Dialpad webhook call_ended -> deferred execution after 12 minutes
  • GET /calls/{id} + GET /calls/{id}/recap -> transcript and AI summary
  • Search for the deal in Kommo by phone number
  • POST /leads/notes with type call_in/call_out - recording and summary in the deal timeline

If your team uses Dialpad and wants to see AI transcripts directly in Kommo - describe the task to the Exceltic.dev team. We will walk you through an architecture sized for your call volume.

More articles

All →