Sinch - cloud communications platform: SMS, голосовые звонки, WhatsApp Business, email. Конкурент Twilio и Telnyx, ориентированный на EU и скандинавские рынки (штаб-квартира в Стокгольме). Ключевые отличия: собственная SS7-сеть в Европе, SLA 99.99%, прямые роуминговые соглашения с EU-операторами.
Sinch SMS API использует Basic Auth (Service Plan ID + API Token) или Bearer. Голосовые звонки - Sinch Voice REST API. Для Kommo интеграция позволяет: отправлять SMS при смене этапа, получать входящие SMS как note в карточке, инициировать автоматический TTS-звонок (text-to-speech).
Sinch SMS работает через Messaging API: POST /xms/v1/{service_plan_id}/batches - отправить SMS одному или группе. Входящие сообщения - POST /v1/sms/inbounds или через webhook. Delivery reports - отдельный webhook endpoint.
Sinch MO (Mobile Originated) - входящее SMS (от клиента к вашему номеру). MT (Mobile Terminated) - исходящее SMS (от вас к клиенту). Обе направления обрабатываются через один Service Plan.
Реализация: исходящий SMS при смене этапа
import requests, os, hmac, hashlib
from flask import Flask, request, jsonify
app = Flask(__name__)
SINCH_SERVICE_PLAN_ID = os.environ["SINCH_SERVICE_PLAN_ID"]
SINCH_API_TOKEN = os.environ["SINCH_API_TOKEN"]
SINCH_FROM_NUMBER = os.environ["SINCH_FROM_NUMBER"] # +46...
SINCH_BASE = f"https://us.sms.api.sinch.com/xms/v1/{SINCH_SERVICE_PLAN_ID}"
SINCH_AUTH = (SINCH_SERVICE_PLAN_ID, SINCH_API_TOKEN)
KOMMO_SUBDOMAIN = os.environ["KOMMO_SUBDOMAIN"]
KOMMO_TOKEN = os.environ["KOMMO_ACCESS_TOKEN"]
SMS_STAGE_ID = int(os.environ["KOMMO_SMS_STAGE_ID"])
KOMMO_BASE = f"https://{KOMMO_SUBDOMAIN}.kommo.com/api/v4"
KOMMO_HDR = {"Authorization": f"Bearer {KOMMO_TOKEN}", "Content-Type": "application/json"}
def get_contact_phone(lead_id: int) -> tuple[str, str]:
r = requests.get(
f"{KOMMO_BASE}/leads/{lead_id}",
headers=KOMMO_HDR,
params={"with": "contacts"},
)
contacts = r.json().get("_embedded", {}).get("contacts", [])
if not contacts:
return "", ""
rc = requests.get(
f"{KOMMO_BASE}/contacts/{contacts[0]['id']}",
headers=KOMMO_HDR,
params={"with": "custom_fields_values"},
)
c = rc.json()
phone = ""
for cf in c.get("custom_fields_values", []) or []:
if cf.get("field_code") == "PHONE":
vals = cf.get("values", [])
if vals:
phone = vals[0].get("value", "")
break
return c.get("name", ""), phone
def send_sms(to: str, body: str) -> str:
r = requests.post(
f"{SINCH_BASE}/batches",
auth=SINCH_AUTH,
json={
"from": SINCH_FROM_NUMBER,
"to": [to],
"body": body,
},
)
r.raise_for_status()
return r.json().get("id", "")
def add_note(lead_id: int, text: str):
requests.post(
f"{KOMMO_BASE}/notes",
headers=KOMMO_HDR,
json=[{
"entity_id": lead_id,
"entity_type": "leads",
"note_type": "common",
"params": {"text": text},
}],
)
@app.route("/webhooks/kommo", methods=["POST"])
def kommo_webhook():
data = request.json or {}
for lead_data in data.get("leads", {}).get("status", []):
lead_id = lead_data.get("id")
new_status = lead_data.get("status_id")
if new_status != SMS_STAGE_ID:
continue
name, phone = get_contact_phone(lead_id)
if not phone:
continue
if not phone.startswith("+"):
digits = "".join(c for c in phone if c.isdigit())
phone = "+" + digits
first = name.split()[0] if name else ""
text = (
f"Hello{', ' + first if first else ''}! "
f"Your inquiry has been reviewed. Our manager will contact you shortly."
)
batch_id = send_sms(phone, text)
add_note(lead_id, f"Sinch SMS отправлен ({batch_id}) на {phone}.")
return jsonify({"status": "ok"}), 200
Реализация: входящие SMS -> note в Kommo
@app.route("/webhooks/sinch/inbound", methods=["POST"])
def sinch_inbound():
event = request.json or {}
ev_type = event.get("type", "")
if ev_type != "mo_text": # mo_text = Mobile Originated text SMS
return jsonify({"status": "ignored"}), 200
from_num = event.get("from", "")
body = event.get("body", "")
received = event.get("received_at", "")
lead_id = find_lead_by_phone(from_num)
if not lead_id:
return jsonify({"status": "no_lead"}), 200
add_note(lead_id, f"Sinch входящий SMS от {from_num}: {body}")
return jsonify({"status": "ok"}), 200
def find_lead_by_phone(phone: str) -> int | None:
r = requests.get(
f"{KOMMO_BASE}/contacts",
headers=KOMMO_HDR,
params={"query": phone, "limit": 5},
)
contacts = r.json().get("_embedded", {}).get("contacts", []) or []
if not contacts:
return None
r2 = requests.get(
f"{KOMMO_BASE}/leads",
headers=KOMMO_HDR,
params={"filter[contact_id]": contacts[0]["id"], "limit": 1},
)
leads = r2.json().get("_embedded", {}).get("leads", []) or []
return leads[0]["id"] if leads else None
@app.route("/webhooks/sinch/delivery", methods=["POST"])
def sinch_delivery():
# Delivery report для исходящих SMS
event = request.json or {}
status = event.get("status", "") # Delivered, Failed, etc.
batch_id = event.get("batch_id", "")
# Логировать для мониторинга доставки
return jsonify({"status": "ok"}), 200
Голосовые звонки: TTS (Text-to-Speech)
SINCH_APP_KEY = os.environ["SINCH_APP_KEY"]
SINCH_APP_SECRET = os.environ["SINCH_APP_SECRET"]
SINCH_VOICE_BASE = "https://calling.api.sinch.com/calling/v1"
def make_tts_call(to: str, message: str) -> str:
import base64
auth_str = base64.b64encode(f"{SINCH_APP_KEY}:{SINCH_APP_SECRET}".encode()).decode()
r = requests.post(
f"{SINCH_VOICE_BASE}/callouts/tts",
headers={
"Authorization": f"Basic {auth_str}",
"Content-Type": "application/json",
},
json={
"method": "ttsCallout",
"ttsCallout": {
"cli": SINCH_FROM_NUMBER,
"destination": {"type": "number", "endpoint": to},
"locale": "en-US",
"text": message,
},
},
)
r.raise_for_status()
return r.json().get("callId", "")
Регионы и номера
Sinch предоставляет номера в 60+ странах. Для EU: отдельный regional endpoint eu.sms.api.sinch.com (против us.sms.api.sinch.com). Если клиенты преимущественно в Европе - используйте EU endpoint для меньшей задержки и соответствия GDPR.
# EU endpoint
SINCH_BASE = f"https://eu.sms.api.sinch.com/xms/v1/{SINCH_SERVICE_PLAN_ID}"
10DLC регистрация (для США) и TF (Toll-Free) - через Sinch Dashboard. Для EU-номеров отдельная процедура верификации бизнеса.
Для кого актуально
B2B компании с EU-клиентами, которым нужна альтернатива Twilio с европейской инфраструктурой. Sinch особенно хорошо работает для SMS в Скандинавии, Германии, Нидерландах и Великобритании. Собственная SS7-сеть означает меньше промежуточных операторов и более высокую доставляемость.
Сравнение альтернатив: Kommo + Telnyx (низкие цены, Tier-1 сеть), Kommo + Plivo (простой API).
Часто задаваемые вопросы
Как получить Sinch SMS Number для отправки в Европу?
Sinch Dashboard -> Numbers -> Browse -> выбрать страну -> купить. Для SMS в Германии нужен верифицированный sender ID (буквенный, например “MyCompany”). Для Великобритании - числовой shared short code или dedicated long code. Процесс верификации: 1-3 рабочих дня.
Поддерживает ли Sinch WhatsApp Business API?
Да, через Sinch Conversation API - единый API для SMS, WhatsApp, RCS, Viber. Настройка WhatsApp через Sinch занимает 1-2 недели (верификация мета). Для Kommo интеграция с WhatsApp через Sinch - альтернатива прямому подключению через Meta или готовым Kommo-коннекторам.
Как Sinch обеспечивает GDPR-совместимость для EU-SMS?
Sinch GDPR: данные обрабатываются в EU датацентрах при использовании EU endpoint. DPA (Data Processing Agreement) доступно по запросу. Логи сообщений хранятся 7 дней по умолчанию (настраивается). Sinch имеет ISO 27001 сертификацию и регулярные аудиты безопасности.
Итог
Kommo + Sinch - cloud communications с EU-инфраструктурой:
- Basic Auth (Service Plan ID + API Token),
POST /xms/v1/{service_plan_id}/batches - EU endpoint:
eu.sms.api.sinch.comдля GDPR и меньшей задержки - Входящие SMS: webhook
mo_text-> find lead by phone -> note - TTS звонок: Sinch Voice API, Basic Auth с App Key + App Secret
- Delivery reports через отдельный webhook endpoint для мониторинга доставки
Если нужна интеграция Kommo с Sinch или другой облачной платформой коммуникаций - опишите задачу команде Exceltic.dev.