Kommo + Aircall: Automatic Call Saving and Notes to the Deal Card

Aircall is a cloud telephony platform popular in EU and US markets, with a full REST API and webhooks for every call event. Out of the box, Aircall integrates with HubSpot, Salesforce, and Intercom — but not with Kommo. A custom integration is built via the Aircall Public API: an inbound call triggers creation or update of a contact and deal in Kommo, and after the call the recording and agent notes automatically appear in the deal card.

What the Kommo + Aircall integration provides

Without integration:
— Agent makes a call from Aircall, then manually opens Kommo
— Enters the call outcome as a Note
— Attaches the Aircall recording link — manually
— Updates the deal stage — manually

With integration:
— Inbound call from a number in Kommo -> deal card opens automatically
— Call ends -> Note with duration, outcome, and recording link created automatically
— Agent adds a note in Aircall -> synced to Kommo
— Missed call -> Task for manager with a callback reminder

What gets synced

Aircall -> Kommo:
call.ended -> Note on the deal: date, duration, outcome (answered/voicemail/missed)
— Recording link -> custom field or Note
call.commented -> agent note from Aircall -> Note in Kommo
call.tagged -> call tags -> tags or custom deal field
— Missed call -> Task “Call back” with deadline

Kommo -> Aircall:
— Create/update Contact in Aircall when a Kommo contact changes
— Client phone number -> Contact in Aircall for inbound caller identification

Architecture

Aircall Webhook: call.created (inbound call)
  ↓ Backend
  1. Search contact by phone: GET /api/v4/contacts
     params: filter[phone]={caller_number}
     -> found: retrieve linked deal
     -> not found: create deal at first pipeline stage
  2. Save aircall_call_id -> custom deal field
  3. (optional) Push pop-up to agent with client data

Aircall Webhook: call.ended
  ↓ Backend
  1. Get call_id from payload
  2. Aircall API: GET /v1/calls/{call_id}
     -> duration, status, recording link, agent user_id
  3. Find deal_id by aircall_call_id from storage
  4. Kommo: POST /leads/{deal_id}/notes
     -> Note with result: duration, status, recording link
  5. If missed -> Kommo: POST /tasks
     -> task "Call back" with deadline +2 hours

Aircall Webhook: call.commented
  ↓ Backend
  1. Get comment and call_id
  2. Find deal_id
  3. Kommo: POST /leads/{deal_id}/notes
     -> agent note from Aircall

Aircall REST API: key requests

Base URL: https://api.aircall.io/v1/. Authentication: Basic Auth (api_id:api_token) or OAuth 2.0.

Retrieving call details:

import requests
from requests.auth import HTTPBasicAuth

AIRCALL_ID = 'your_api_id'
AIRCALL_TOKEN = 'your_api_token'
AUTH = HTTPBasicAuth(AIRCALL_ID, AIRCALL_TOKEN)

def get_call_details(call_id: int) -> dict:
    resp = requests.get(
        f'https://api.aircall.io/v1/calls/{call_id}',
        auth=AUTH
    )
    call = resp.json()['call']
    return {
        'duration': call.get('duration'),               # seconds
        'status': call.get('status'),                   # answered, missed, voicemail
        'recording': call.get('recording'),              # audio URL
        'user_id': call.get('user', {}).get('id'),
        'user_name': call.get('user', {}).get('name'),
        'started_at': call.get('started_at'),
        'tags': [t['name'] for t in call.get('tags', [])]
    }

Creating a Note in Kommo with call data:

import requests
from datetime import datetime

def create_call_note(deal_id: int, call: dict, kommo_token: str):
    duration_min = call['duration'] // 60
    duration_sec = call['duration'] % 60
    status_label = {'answered': 'Answered', 'missed': 'Missed',
                    'voicemail': 'Voicemail'}.get(call['status'], call['status'])

    note_text = (
        f'📞 Call {status_label}\n'
        f'Duration: {duration_min}:{duration_sec:02d}\n'
        f'Agent: {call["user_name"]}\n'
    )
    if call.get('recording'):
        note_text += f'Recording: {call["recording"]}\n'
    if call.get('tags'):
        note_text += f'Tags: {", ".join(call["tags"])}'

    requests.post(
        f'https://{KOMMO_DOMAIN}.kommo.com/api/v4/leads/{deal_id}/notes',
        headers={'Authorization': f'Bearer {kommo_token}'},
        json=[{
            'note_type': 'common',
            'params': {'text': note_text}
        }]
    )

Handling webhooks:

from flask import Flask, request

app = Flask(__name__)

@app.route('/webhooks/aircall', methods=['POST'])
def aircall_webhook():
    data = request.json
    event = data.get('event')
    call_id = data.get('data', {}).get('id')

    if event == 'call.ended':
        call = get_call_details(call_id)
        deal_id = get_deal_by_call_id(call_id)  # from your storage

        if deal_id:
            create_call_note(deal_id, call, KOMMO_TOKEN)
            if call['status'] == 'missed':
                create_kommo_task(deal_id, 'Call the client back', KOMMO_TOKEN)

    elif event == 'call.commented':
        comment = data.get('data', {}).get('comment', '')
        deal_id = get_deal_by_call_id(call_id)
        if deal_id and comment:
            create_simple_note(deal_id, f'Call note: {comment}', KOMMO_TOKEN)

    return '', 200

Identifying the caller by phone number

On an inbound call (call.created) you need to quickly find the contact in Kommo:

def find_kommo_contact_by_phone(phone: str) -> dict | None:
    # Normalize the number (remove +, spaces)
    clean_phone = ''.join(filter(str.isdigit, phone))

    resp = requests.get(
        f'https://{KOMMO_DOMAIN}.kommo.com/api/v4/contacts',
        headers={'Authorization': f'Bearer {KOMMO_TOKEN}'},
        params={
            'query': clean_phone,
            'with': 'leads'
        }
    )
    contacts = resp.json().get('_embedded', {}).get('contacts', [])
    return contacts[0] if contacts else None

If the contact is found — retrieve their active deal. If not — create a new deal at the first pipeline stage.

Syncing Kommo contacts to Aircall

For accurate inbound caller identification, Aircall needs to know client phone numbers from Kommo:

def upsert_aircall_contact(name: str, phone: str, email: str,
                           kommo_contact_id: int):
    # Search for existing contact
    resp = requests.get(
        'https://api.aircall.io/v1/contacts',
        auth=AUTH,
        params={'phone_number': phone}
    )
    contacts = resp.json().get('contacts', [])

    payload = {
        'first_name': name.split()[0] if name else '',
        'last_name': ' '.join(name.split()[1:]) if len(name.split()) > 1 else '',
        'phone_numbers': [{'label': 'Work', 'value': phone}],
        'emails': [{'label': 'Work', 'value': email}] if email else []
    }

    if contacts:
        requests.put(f'https://api.aircall.io/v1/contacts/{contacts[0]["id"]}',
                     auth=AUTH, json=payload)
    else:
        requests.post('https://api.aircall.io/v1/contacts',
                      auth=AUTH, json=payload)

Real-world case study

SaaS company (EU market, 8 agents, 150–200 calls per day):

  • Before: after each call, the agent manually opened Kommo, wrote a Note, and copied the recording link. 3–5 minutes per call — across 200 calls, that’s up to 15 hours of team time per week spent just on CRM maintenance.
  • After: all call data appears in Kommo automatically within 30 seconds of the call ending. The agent only adds a note about the outcome — which also syncs from Aircall.
  • Additional benefit: the team lead sees a call summary directly in Kommo without switching to Aircall. Missed calls automatically become tasks — none are lost.

A similar integration is available for Twilio and Kommo — that uses programmable telephony via API, while Aircall is a turnkey SaaS solution for sales teams.

Who benefits from this integration

  • Team uses Aircall as their primary telephony
  • 50+ calls per day — manual CRM maintenance is not cost-effective
  • Full call history is needed directly in the Kommo client card
  • Visibility matters: the manager needs to see who called, how often, and with what result
  • Missed calls must automatically become tasks

Frequently asked questions

Is there a native Aircall and Kommo integration?

There is no native integration — Aircall supports native connectors for HubSpot, Salesforce, Pipedrive, and Intercom. For Kommo, integration is built via the Aircall Public API and Kommo Webhooks. This takes 2–3 weeks of development, but the result is fully customized to your pipeline logic.

Aircall API — Basic Auth or OAuth?

Both are available. Basic Auth with api_id:api_token is simpler for server-side integrations and suitable for most use cases. OAuth 2.0 is needed if each manager should authenticate individually (acting as a specific user). For CRM integration — Basic Auth is sufficient.

Via GET /v1/calls/{id} — the recording field contains a direct link to the audio file. Recordings are only available if recording is enabled in the Aircall number settings. Links are temporary (they expire after N days) — it’s better to download and store them in your own S3/R2.

Which Aircall plans include API access?

The API is available on the Professional plan ($50/user/month) and above. The Essential plan ($40/user/month) does not include API access. Webhooks are available from Professional.

Summary

  • Aircall Webhooks: call.created, call.ended, call.commented, call.tagged — the foundation of the integration
  • Aircall API: GET /v1/calls/{id} for call details, POST /v1/contacts for contact sync
  • Kommo: Note with call outcome, Task for missed calls, custom fields with metadata
  • Typical timeline — 2 weeks development + 1 week testing on real calls

If you use Aircall and Kommo — describe your current call-handling workflow: what agents do manually today. Exceltic.dev will design an automated workflow covering all scenarios (inbound, outbound, missed, voicemail).

More articles

All →