Salesmsg is a business SMS and calling platform popular with sales teams in the US and Canada - local numbers, two-way SMS, CRM integration. Kommo is a CRM with a sales pipeline. Without an integration, your SMS conversations with clients live inside Salesmsg while your reps constantly switch between two interfaces. With the integration, every SMS and call from Salesmsg automatically appears as a note in the Kommo deal card.
SMS remains an underrated channel in B2B: open rates are around 98% versus 20-30% for email. In the North American market, an SMS from a local number is the standard way to confirm a meeting, follow up on a decision, or share a document link. Salesmsg provides exactly this infrastructure: a local number, business messaging compliance, and A2P 10DLC registration.
Salesmsg is a two-way business SMS and calling platform with a REST API, webhook notifications for inbound messages and calls, and the ability to send SMS programmatically from any system.
Why This Matters for Your Kommo Pipeline
Kommo supports messengers (WhatsApp, Telegram, Instagram) but does not natively support SMS on US/CA numbers. Salesmsg fills this gap for companies working with the North American market.
Two directions of integration:
- Kommo -> Salesmsg: when a deal changes stage, automatically send the client an SMS reminder
- Salesmsg -> Kommo: log inbound SMS and calls as notes in the deal card
Technical Architecture
Kommo CRM
-> deal.status_changed (stage "Meeting Scheduled")
-> POST https://app.salesmsg.com/api/v2/messages/send
{to: client_phone, body: "Meeting reminder..."}
Client
-> Replies to SMS / calls back
Salesmsg
-> POST /your-server/webhooks/salesmsg
{type: "message.received", contact_phone, body, lead_number}
Your server
-> Find deal in Kommo by phone number
-> POST /api/v4/leads/{id}/notes {text: "SMS from client: ..."}
Implementation: Sending SMS from Kommo
import requests, os
from flask import Flask, request, jsonify
app = Flask(__name__)
KOMMO_DOMAIN = os.environ["KOMMO_DOMAIN"]
KOMMO_TOKEN = os.environ["KOMMO_TOKEN"]
SALESMSG_KEY = os.environ["SALESMSG_API_KEY"]
SALESMSG_SECRET = os.environ["SALESMSG_WEBHOOK_SECRET"]
SMS_FROM_NUMBER = os.environ["SMS_FROM_NUMBER"] # your Salesmsg number
KOMMO_BASE = f"https://{KOMMO_DOMAIN}/api/v4"
KOMMO_HDR = {"Authorization": f"Bearer {KOMMO_TOKEN}"}
SM_BASE = "https://app.salesmsg.com/api/v2"
SM_HDR = {"Authorization": f"Bearer {SALESMSG_KEY}"}
MEETING_STATUS_ID = 12345 # ID of the "Meeting Scheduled" stage
@app.route("/webhooks/kommo", methods=["POST"])
def kommo_event():
data = request.json or {}
for lead in data.get("leads", {}).get("status", []):
if lead.get("status_id") == MEETING_STATUS_ID:
send_meeting_reminder(lead["id"])
return jsonify({"ok": True}), 200
def get_client_phone(lead_id: int) -> str | None:
r = requests.get(
f"{KOMMO_BASE}/leads/{lead_id}",
headers=KOMMO_HDR,
params={"with": "contacts"},
)
if not r.ok:
return None
contacts = r.json().get("_embedded", {}).get("contacts", [])
if not contacts:
return None
contact_id = contacts[0]["id"]
cr = requests.get(f"{KOMMO_BASE}/contacts/{contact_id}", headers=KOMMO_HDR)
if not cr.ok:
return None
for f in cr.json().get("custom_fields_values") or []:
if f.get("field_code") == "PHONE":
vals = f.get("values", [])
if vals:
return str(vals[0]["value"])
return None
def send_meeting_reminder(lead_id: int):
phone = get_client_phone(lead_id)
if not phone:
return
body = (
"Hi! Just a reminder about our upcoming meeting. "
"If you have any questions, feel free to reply to this message."
)
r = requests.post(
f"{SM_BASE}/messages/send",
headers=SM_HDR,
json={
"to": phone,
"from": SMS_FROM_NUMBER,
"body": body,
},
timeout=10,
)
if r.ok:
requests.post(
f"{KOMMO_BASE}/leads/{lead_id}/notes",
headers=KOMMO_HDR,
json=[{"note_type": "common", "params": {"text": f"SMS sent: {body}"}}],
)
Implementation: Logging Inbound Messages in Kommo
import hmac, hashlib
def verify_salesmsg_webhook(raw_body: bytes, sig_header: str) -> bool:
expected = hmac.new(
SALESMSG_SECRET.encode(), raw_body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, sig_header)
@app.route("/webhooks/salesmsg", methods=["POST"])
def salesmsg_event():
sig = request.headers.get("X-Salesmsg-Signature", "")
if not verify_salesmsg_webhook(request.data, sig):
return jsonify({"error": "unauthorized"}), 401
data = request.json or {}
event_type = data.get("type", "")
from_phone = data.get("contact_phone", "")
body_text = data.get("body", "")
if event_type == "message.received" and from_phone:
log_sms_to_kommo(from_phone, body_text, direction="in")
elif event_type == "call.completed":
duration = data.get("duration", 0)
recording = data.get("recording_url", "")
log_call_to_kommo(from_phone, duration, recording)
return jsonify({"ok": True}), 200
def find_lead_by_phone(phone: str) -> int | None:
# Search Kommo contacts by phone number.
r = requests.get(
f"{KOMMO_BASE}/contacts",
headers=KOMMO_HDR,
params={"query": phone, "limit": 1},
)
if not r.ok:
return None
contacts = r.json().get("_embedded", {}).get("contacts", [])
if not contacts:
return None
# Find linked deals
contact_id = contacts[0]["id"]
lr = requests.get(
f"{KOMMO_BASE}/contacts/{contact_id}/links",
headers=KOMMO_HDR,
)
if not lr.ok:
return None
leads = [l for l in lr.json().get("_embedded", {}).get("links", []) if l.get("to_entity_type") == "leads"]
return leads[0]["to_entity_id"] if leads else None
def log_sms_to_kommo(phone: str, text: str, direction: str):
lead_id = find_lead_by_phone(phone)
if not lead_id:
return
direction_label = "Inbound SMS" if direction == "in" else "Outbound SMS"
requests.post(
f"{KOMMO_BASE}/leads/{lead_id}/notes",
headers=KOMMO_HDR,
json=[{"note_type": "common", "params": {"text": f"{direction_label} ({phone}): {text}"}}],
)
def log_call_to_kommo(phone: str, duration: int, recording_url: str):
lead_id = find_lead_by_phone(phone)
if not lead_id:
return
note_text = f"Salesmsg call ({phone}): {duration}s."
if recording_url:
note_text += f" Recording: {recording_url}"
requests.post(
f"{KOMMO_BASE}/leads/{lead_id}/notes",
headers=KOMMO_HDR,
json=[{"note_type": "call_in", "params": {"text": note_text, "duration": duration}}],
)
Use Cases
Automated SMS by pipeline stage:
- “Meeting Scheduled” -> reminder the day before the meeting
- “Proposal Sent” -> “Did you receive our proposal? Happy to answer any questions.”
- “Invoice Sent” -> payment reminder after 3 days
Inbound SMS in Kommo:
- Client replies to an SMS -> note added to the deal card with the message text
- Rep sees the full SMS thread in the deal timeline
Calls:
call.completedwebhook -> note with call duration and a link to the recording
Real-World Case
A SaaS company with US clients and 6 sales reps. Before the integration: SMS was handled on reps’ personal phones with no log in the CRM - when a rep left, the conversation history was lost. After the integration: all SMS and calls go through the company’s Salesmsg number, and every message appears in the deal card. Onboarding a new rep with the full conversation history took 15 minutes instead of several hours of context-gathering.
Who Needs This
Companies with US/CA clients that use SMS as their primary follow-up channel. If your reps are actively texting clients from personal phones, that’s the first signal: communication is happening outside the CRM and will be lost when that employee leaves.
A related task - phone calls with transcripts - is covered in the article on VoIP integration with Kommo.
Frequently Asked Questions
How do I find a deal in Kommo if the client writes from a new number?
If the number does not match any contact in Kommo, create a new contact and a new deal via the API. Apply a tag like “Salesmsg inbound” so reps can filter these leads.
Does Salesmsg support MMS and file attachments?
Yes, Salesmsg supports MMS (photos, PDFs). In the webhook event, the media_urls field contains an array of attachment URLs. Add those links to the Kommo note along with the message text.
How do I ensure A2P 10DLC compliance?
A2P 10DLC (Application-to-Person 10-Digit Long Code) is a regulatory requirement for business SMS in the US. Salesmsg walks you through the Brand and Campaign registration process. Without registration, carriers filter your messages. Average approval time is 3-5 business days.
Summary
Kommo + Salesmsg connects SMS/calls and your CRM:
- Kommo webhook -> automated SMS triggered by pipeline events
- Inbound SMS -> notes in the deal card via find-by-phone
- Completed calls -> note with duration and recording link
- Webhook verification via HMAC-SHA256 header
If your team works with the US/CA market and wants to bring SMS communication into the CRM context - reach out to Exceltic.dev. We will set up the integration for your specific pipeline stack.