Kommo + Vonage: SMS and calls from the sales pipeline

Vonage (acquired by Ericsson) is a Communications API platform for SMS, voice calls, and messaging. Coverage: 200+ countries, EU number support (Germany, Netherlands, UK). There is no native integration with Kommo - we build via Vonage Messages API v1 and Voice API.

What we’re building

SMS scenarios:

  • When a deal moves to “Proposal Sent” -> SMS to the client with a reminder
  • Inbound SMS from the client -> Note in Kommo + task for the manager

Voice scenarios:

  • Outbound call from Kommo -> Vonage Voice API -> call recording as a Note

Vonage authentication

Vonage uses two methods: API Key + Secret (for SMS) and JWT (for Voice API).

API Key + Secret - for Messages API:

import requests, base64

VONAGE_API_KEY    = "your_api_key"
VONAGE_API_SECRET = "your_api_secret"

# Messages API uses Basic Auth
credentials = base64.b64encode(f"{VONAGE_API_KEY}:{VONAGE_API_SECRET}".encode()).decode()
msg_headers = {
    "Authorization": f"Basic {credentials}",
    "Content-Type": "application/json",
}

JWT - for Voice API:

import jwt, time, uuid

VONAGE_APPLICATION_ID  = "your_app_id"
VONAGE_PRIVATE_KEY_PATH = "private.key"  # download from Vonage Dashboard

def generate_jwt() -> str:
    private_key = open(VONAGE_PRIVATE_KEY_PATH, "rb").read()
    payload = {
        "application_id": VONAGE_APPLICATION_ID,
        "iat": int(time.time()),
        "jti": str(uuid.uuid4()),
        "exp": int(time.time()) + 3600,
    }
    return jwt.encode(payload, private_key, algorithm="RS256")

Voice API requires an RSA private key from a Vonage Application. Messages API works only with API Key + Secret.

Sending an SMS to a client

def send_sms(to_number: str, text: str, from_number: str = "EXCELTIC") -> dict:
    """Send SMS via Vonage Messages API v1."""
    payload = {
        "from": from_number,     # alphanumeric sender or your Vonage number
        "to":   to_number,       # E.164 format: +49301234567
        "channel": "sms",
        "message_type": "text",
        "text": text,
    }
    r = requests.post(
        "https://api.nexmo.com/v1/messages",
        headers=msg_headers,
        json=payload,
    )
    r.raise_for_status()
    return r.json()  # {"message_uuid": "..."}

Alphanumeric sender IDs (e.g. EXCELTIC) are not supported in some countries - in the US a registered 10DLC number is required, in Germany a German virtual number is needed.

Trigger from Kommo webhook:

from flask import Flask, request

app = Flask(__name__)

@app.route("/kommo/webhook", methods=["POST"])
def kommo_webhook():
    data = request.json
    if "leads" not in data:
        return "ok", 200

    for event_type in ["update"]:
        leads = data.get("leads", {}).get(event_type, [])
        for lead in leads:
            old_status = lead.get("old_status_id")
            new_status = lead.get("status_id")

            if new_status == PROPOSAL_STAGE_ID and old_status != PROPOSAL_STAGE_ID:
                contact_phone = get_kommo_contact_phone(lead["id"])
                if contact_phone:
                    deal_name = lead.get("name", "")
                    send_sms(
                        to_number=contact_phone,
                        text=f"Your proposal from Exceltic is ready. Check your email or reply to this message.",
                    )
    return "ok", 200

Inbound SMS - Note in Kommo

Vonage sends inbound SMS to your Inbound Webhook URL.

@app.route("/vonage/inbound-sms", methods=["POST", "GET"])
def inbound_sms():
    # Vonage sends GET or POST depending on configuration
    data = request.values if request.method == "GET" else request.json or request.form

    from_number = data.get("msisdn", "")  # sender number
    text        = data.get("text", "")
    message_id  = data.get("messageId", "")

    # Find contact by phone number in Kommo
    contact = find_kommo_contact_by_phone(from_number)
    if contact:
        deal_id = get_active_deal(contact["id"])
        if deal_id:
            add_kommo_note(
                deal_id=deal_id,
                text=f"SMS from {from_number}: {text}",
                note_type="sms",
            )

    return "ok", 200

Important: Vonage expects HTTP 200 in response to an inbound SMS. If the server returns an error, Vonage will retry for up to 72 hours.

Outbound call via Voice API

NCCO_BASE_URL = "https://your-service.com"  # public URL for NCCO

def initiate_call(to_number: str, from_number: str, deal_id: int) -> str:
    """Start outbound call. Returns call UUID."""
    payload = {
        "to": [{"type": "phone", "number": to_number}],
        "from": {"type": "phone", "number": from_number},
        "answer_url": [f"{NCCO_BASE_URL}/vonage/ncco/{deal_id}"],
        "event_url":  [f"{NCCO_BASE_URL}/vonage/call-events/{deal_id}"],
        "record": True,
        "recording_channels": "split",  # separate tracks for agent and client
    }
    r = requests.post(
        "https://api.nexmo.com/v1/calls",
        headers={"Authorization": f"Bearer {generate_jwt()}", "Content-Type": "application/json"},
        json=payload,
    )
    r.raise_for_status()
    return r.json()["uuid"]

@app.route("/vonage/ncco/<int:deal_id>")
def ncco(deal_id: int):
    """Vonage Call Control Object - call instructions."""
    return jsonify([
        {"action": "talk", "text": "Connecting you to an Exceltic manager."},
        {"action": "connect", "from": VONAGE_FROM_NUMBER, "endpoint": [
            {"type": "phone", "number": AGENT_NUMBER}
        ]},
        {"action": "record", "eventUrl": [f"{NCCO_BASE_URL}/vonage/recording/{deal_id}"]},
    ])

Handling the recording event:

@app.route("/vonage/recording/<int:deal_id>", methods=["POST"])
def call_recording(deal_id: int):
    data = request.json
    recording_url = data.get("recording_url", "")
    duration_sec  = data.get("duration", 0)

    note = f"Vonage call. Duration: {duration_sec}s. Recording: {recording_url}"
    add_kommo_note(deal_id, note)
    return "ok", 200

Call recordings are stored on Vonage servers for 30 days. The URL is available immediately after the call ends - authentication uses the same JWT.

Real case

A B2B distributor in the Netherlands was using email for initial contact, but the response rate was 18%. After adding an SMS reminder when sending a proposal:

  • Response rate rose to 34% within the first 2 weeks
  • Average client response time dropped from 2.3 days to 14 hours
  • Inbound SMS from clients appear in Kommo without manual copying

A typical project setup takes 3-5 business days including testing EU numbers.

Who this is for

Companies with EU clients who already use or are considering SMS as a communication channel. Vonage is particularly useful for teams in DACH and Benelux - registering German and Dutch numbers goes through Vonage without complex local procedures.

For IP telephony with call recording - see also Kommo + CloudTalk and Kommo + RingCentral.

Frequently asked questions

Can WhatsApp messages be sent via Vonage?

Yes. Vonage Messages API supports WhatsApp Business as a channel. Change "channel": "sms" to "channel": "whatsapp" and pass the WhatsApp number. A WhatsApp Business Account and an approved template are required for outbound messages outside the 24-hour window.

How does alphanumeric sender ID work in Europe?

In most EU countries, alphanumeric senders (e.g. a company name instead of a number) are allowed for outbound SMS. Exceptions: France, Finland, some Baltic countries - a registered number is required there. Vonage automatically falls back to a numeric number if alphanumeric is not supported in the recipient’s country.

How does Vonage differ from Twilio?

The APIs are similar but there are differences: Vonage SMS API uses msisdn instead of from/to, and authentication is split (API Key+Secret for Messages, JWT for Voice). Twilio has broader documentation and ecosystem. Vonage has better coverage in DACH and Nordics for SMS delivery rates.

How to ensure GDPR compliance when storing SMS?

SMS content logged in Kommo as a Note falls under GDPR as personal data. Set a retention policy in Kommo: by default Notes are stored indefinitely. Vonage does not store SMS content after delivery. Delivery report logs are stored for 72 hours.

Summary

Kommo + Vonage integration covers SMS and voice calls in a single stack:

  • Messages API: Basic Auth, alphanumeric sender, two-way SMS
  • Voice API: JWT with RSA private key, NCCO for call control, recording with split tracks
  • Inbound SMS: webhook 200 OK within 72-hour retry window

If your team works with EU clients and needs SMS as a communication tool - describe the task to Exceltic.dev. We’ll set up the integration for your stack and set of numbers.

More articles

All →