Plivo - облачная коммуникационная платформа (CPaaS): программируемые SMS, голосовые звонки, WhatsApp через API. Работает в 190+ странах. Популярна как более дешёвая альтернатива Twilio - тарифы на SMS и звонки ниже на 30-50% при сопоставимом покрытии. Для Kommo кастомная интеграция позволяет: отправлять SMS при смене этапа сделки, логировать входящие SMS в карточку, инициировать исходящий звонок через Kommo.
Plivo работает через REST API с Basic Auth (Auth ID + Auth Token). Исходящие SMS: POST /v1/Account/{auth_id}/Message/. Исходящие звонки: POST /v1/Account/{auth_id}/Call/ с Answer URL, где отдаётся XML (Plivo Markup Language, PHML) с инструкциями для звонка.
Plivo Markup Language (PHML) - XML-формат инструкций для управления звонком: проиграть сообщение, соединить с агентом, записать разговор. Аналог TwiML у Twilio.
Ключевые сценарии интеграции
Сценарий 1: При переводе сделки в этап “Отправить КП” - автоматическое SMS клиенту с ссылкой на предложение.
Сценарий 2: Входящее SMS от клиента -> Plivo шлёт webhook -> создать заметку в Kommo.
Сценарий 3: Менеджер нажимает кнопку “Позвонить” в Kommo -> Plivo поднимает звонок через браузер или мобильный.
Реализация: исходящие SMS при смене этапа
import requests, os, hmac, hashlib
from flask import Flask, request, jsonify
app = Flask(__name__)
PLIVO_AUTH_ID = os.environ["PLIVO_AUTH_ID"]
PLIVO_AUTH_TOKEN = os.environ["PLIVO_AUTH_TOKEN"]
PLIVO_FROM_NUMBER = os.environ["PLIVO_FROM_NUMBER"] # например +12025551234
KOMMO_SUBDOMAIN = os.environ["KOMMO_SUBDOMAIN"]
KOMMO_TOKEN = os.environ["KOMMO_ACCESS_TOKEN"]
KP_STAGE_ID = int(os.environ["KOMMO_KP_STAGE_ID"]) # этап "Отправить КП"
PLIVO_BASE = f"https://api.plivo.com/v1/Account/{PLIVO_AUTH_ID}"
PLIVO_AUTH = (PLIVO_AUTH_ID, PLIVO_AUTH_TOKEN)
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 "", ""
contact_id = contacts[0]["id"]
rc = requests.get(
f"{KOMMO_BASE}/contacts/{contact_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, text: str) -> str:
# to: номер в E.164 формате: +79991234567
r = requests.post(
f"{PLIVO_BASE}/Message/",
auth=PLIVO_AUTH,
json={
"src": PLIVO_FROM_NUMBER,
"dst": to,
"text": text,
"type": "sms",
},
)
r.raise_for_status()
return r.json().get("message_uuid", [None])[0]
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 != KP_STAGE_ID:
continue
name, phone = get_contact_phone(lead_id)
if not phone:
continue
# Нормализация номера (упрощённая)
digits_only = "".join(c for c in phone if c.isdigit())
if not phone.startswith("+"):
phone = "+" + digits_only
sms_text = (
f"Добрый день, {name.split()[0] if name else ''}! "
f"Отправляем коммерческое предложение. Ответьте на это сообщение или позвоните нам."
)
msg_uuid = send_sms(phone, sms_text)
add_note(lead_id, f"Plivo SMS отправлен на {phone}. UUID: {msg_uuid}")
return jsonify({"status": "ok"}), 200
Реализация: входящие SMS -> Kommo
def verify_plivo_signature(auth_id: str, auth_token: str, uri: str, params: dict, signature: str) -> bool:
# Plivo подписывает: auth_id + nonce (из заголовка X-Plivo-Nonce) + URI
# Детали: https://www.plivo.com/docs/sms/concepts/inbound-sms-webhooks/
# Упрощённая проверка через X-Plivo-Signature
sorted_params = "".join(f"{k}{v}" for k, v in sorted(params.items()))
msg = uri + sorted_params
digest = hmac.new(auth_token.encode(), msg.encode(), hashlib.sha1).digest()
import base64
computed = base64.b64encode(digest).decode()
return hmac.compare_digest(computed, signature)
@app.route("/webhooks/plivo/inbound-sms", methods=["POST"])
def plivo_inbound_sms():
params = request.form.to_dict()
from_number = params.get("From", "")
text = params.get("Text", "")
if not from_number or not text:
return "<?xml version="1.0" encoding="utf-8" ?><Response></Response>", 200
# Найти сделку по номеру телефона в Kommo
lead_id = find_lead_by_phone(from_number)
if lead_id:
add_note(lead_id, f"Входящий SMS от {from_number}: {text}")
# Plivo ожидает XML-ответ (PHML)
return "<?xml version="1.0" encoding="utf-8" ?><Response></Response>", 200
def find_lead_by_phone(phone: str) -> int | None:
# Поиск по номеру через Kommo Contacts API
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
contact_id = contacts[0]["id"]
# Найти открытую сделку контакта
r2 = requests.get(
f"{KOMMO_BASE}/leads",
headers=KOMMO_HDR,
params={"filter[contact_id]": contact_id, "filter[status_id]": "active", "limit": 1},
)
leads = r2.json().get("_embedded", {}).get("leads", []) or []
return leads[0]["id"] if leads else None
Реализация: исходящий звонок через Plivo
При нажатии кнопки в Kommo (через custom action или вызов webhook вручную) - Plivo поднимает звонок: сначала звонит менеджеру, потом соединяет с клиентом.
ANSWER_URL = os.environ["PLIVO_ANSWER_URL"] # URL где отдаётся PHML
@app.route("/call/initiate", methods=["POST"])
def initiate_call():
data = request.json or {}
agent_phone = data.get("agent_phone") # телефон менеджера
client_phone = data.get("client_phone") # телефон клиента
lead_id = data.get("lead_id")
r = requests.post(
f"{PLIVO_BASE}/Call/",
auth=PLIVO_AUTH,
json={
"from": PLIVO_FROM_NUMBER,
"to": agent_phone, # сначала звоним агенту
"answer_url": f"{ANSWER_URL}/phml/connect?client={client_phone}&lead={lead_id}",
"answer_method": "GET",
},
)
r.raise_for_status()
return jsonify({"call_uuid": r.json().get("request_uuid")}), 200
@app.route("/phml/connect", methods=["GET"])
def phml_connect():
client_phone = request.args.get("client", "")
lead_id = request.args.get("lead", "")
# PHML: когда агент поднял трубку - набрать клиента
phml = (
'<?xml version="1.0" encoding="utf-8" ?>
'
'<Response>
'
' <Speak>Соединяю с клиентом.</Speak>
'
f' <Dial callerId="{PLIVO_FROM_NUMBER}" record="true" recordingCallbackUrl="{ANSWER_URL}/webhooks/plivo/recording">
'
f' <Number>{client_phone}</Number>
'
' </Dial>
'
'</Response>'
)
return phml, 200, {"Content-Type": "application/xml"}
@app.route("/webhooks/plivo/recording", methods=["POST"])
def plivo_recording():
params = request.form.to_dict()
recording_url = params.get("RecordUrl", "")
lead_id = params.get("lead", "") # передаётся через callback URL параметр
if recording_url and lead_id:
add_note(int(lead_id), f"Plivo: запись звонка: {recording_url}")
return "", 200
Стоимость Plivo
По состоянию на Q2 2026:
- SMS в США: $0.0035/сообщение (исходящее)
- SMS в Великобритании: $0.04/сообщение
- Звонки в США: $0.013/мин (исходящий)
- Номер: $0.80/месяц
Для сравнения: Twilio - SMS $0.0079, звонки $0.015/мин.
Реальный кейс
Агентство недвижимости, 12 менеджеров, 80 лидов в месяц. Plivo использовался для отправки напоминаний о просмотрах объектов. До интеграции: SMS отправлялись вручную. После: при смене этапа “Назначен просмотр” автоматически уходит SMS с адресом объекта и временем. Saving: 2 часа/неделю на рутинные SMS.
Для кого актуально
Компании с высоким объёмом SMS (50+ в месяц) в странах с высокими тарифами Twilio. Особенно: агентства, сервисные компании, B2B SaaS с SMS-онбордингом. Plivo эффективен если нужен низкий CAC по коммуникациям.
Аналогичные интеграции описаны для Kommo + Twilio и Kommo + Salesmsg.
Часто задаваемые вопросы
Нужна ли регистрация 10DLC для SMS в США через Plivo?
Да. Для A2P (Application-to-Person) SMS в США требуется регистрация бренда и кампании в The Campaign Registry (TCR) - аналогично Twilio и другим провайдерам. Plivo предоставляет инструменты для регистрации через их Dashboard. Незарегистрированные кампании блокируются операторами.
Поддерживает ли Plivo WhatsApp?
Да, через Plivo WhatsApp API (в beta по состоянию на Q2 2026). Работает через официальный WhatsApp Business API. Отправка шаблонных и сессионных сообщений. Для боевого использования требует одобрения Meta.
Как обрабатывать delivery reports (статус доставки SMS)?
Plivo отправляет DLR (Delivery Status Callback) на URL, указанный в параметре url при отправке SMS. Статусы: sent, delivered, failed. Добавьте этот URL при вызове API и логируйте статус в Kommo через note.
Итог
Kommo + Plivo - программируемые коммуникации из воронки:
- Basic Auth (Auth ID + Auth Token), E.164 для номеров
- Kommo webhook -> Plivo SMS API -> логировать UUID в note
- Входящие SMS: Plivo webhook -> найти сделку по телефону -> add note
- Исходящие звонки:
POST /Call/-> PHML connect -> запись - Дешевле Twilio на 30-50%, подходит для высоких объёмов
Если нужна интеграция Kommo с Plivo или другим CPaaS - описание задачи команде Exceltic.dev.