Campaign Monitor - платформа email-маркетинга с простым API и надёжной доставкой. Kommo - CRM с воронкой продаж. Без интеграции список рассылки живёт отдельно от воронки: маркетолог не знает, на какой стадии находится каждый подписчик, и не может сегментировать кампании по CRM-статусу. С интеграцией переход сделки в новый этап автоматически обновляет теги подписчика в Campaign Monitor - рассылки становятся точечными.
Ключевое отличие Campaign Monitor от Mailchimp в контексте B2B: CM работает с подписчиками как с customer journey, позволяет триггерные автоматизации на основе тегов и хорошо интегрируется в стеки с несколькими CRM. Для компаний с русскоязычной аудиторией вне СНГ CM часто предпочтительнее как инструмент с высокой доставляемостью в европейские почтовые системы.
Campaign Monitor API использует Basic Auth с API-ключом в качестве username и произвольной строкой в качестве password. Все запросы к https://api.createsend.com/api/v3.3/.
Направления интеграции
Двусторонняя синхронизация:
- Kommo -> Campaign Monitor: смена стадии сделки обновляет теги подписчика в CM
- Campaign Monitor -> Kommo: открытие/клик письма логируется как заметка в карточке сделки
Kommo: сделка -> стадия "КП отправлено"
-> Найти подписчика в CM по email
-> Добавить тег "stage_proposal_sent"
-> Убрать тег предыдущей стадии
Campaign Monitor: подписчик открыл письмо
-> POST /your-server/webhooks/cm {event: "Open", EmailAddress: "..."}
-> Найти сделку в Kommo по email
-> POST /api/v4/leads/{id}/notes {text: "Открыл письмо: subject"}
Реализация: Kommo -> Campaign Monitor
import requests, os
from flask import Flask, request, jsonify
import base64
app = Flask(__name__)
KOMMO_DOMAIN = os.environ["KOMMO_DOMAIN"]
KOMMO_TOKEN = os.environ["KOMMO_TOKEN"]
CM_API_KEY = os.environ["CM_API_KEY"]
CM_LIST_ID = os.environ["CM_LIST_ID"]
KOMMO_BASE = f"https://{KOMMO_DOMAIN}/api/v4"
KOMMO_HDR = {"Authorization": f"Bearer {KOMMO_TOKEN}"}
CM_BASE = "https://api.createsend.com/api/v3.3"
CM_HDR = {
"Authorization": "Basic " + base64.b64encode(f"{CM_API_KEY}:x".encode()).decode(),
"Content-Type": "application/json",
}
# Маппинг стадий Kommo -> теги Campaign Monitor
STAGE_TAGS = {
11111: "stage_new_lead",
22222: "stage_qualified",
33333: "stage_proposal_sent",
44444: "stage_negotiation",
142: "stage_won",
143: "stage_lost",
}
@app.route("/webhooks/kommo", methods=["POST"])
def kommo_event():
data = request.json or {}
for lead in data.get("leads", {}).get("status", []):
handle_stage_change(lead["id"], lead.get("status_id"))
return "ok", 200
def get_contact_email(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
cr = requests.get(f"{KOMMO_BASE}/contacts/{contacts[0]['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") == "EMAIL":
vals = f.get("values", [])
if vals:
return str(vals[0]["value"])
return None
def update_subscriber_tags(email: str, new_stage_id: int):
# Получить текущего подписчика
r = requests.get(
f"{CM_BASE}/subscribers/{CM_LIST_ID}.json",
headers=CM_HDR,
params={"email": email},
)
if r.status_code == 404:
# Подписчик не найден - создать
requests.post(
f"{CM_BASE}/subscribers/{CM_LIST_ID}.json",
headers=CM_HDR,
json={
"EmailAddress": email,
"Resubscribe": True,
"Tags": [STAGE_TAGS.get(new_stage_id, "stage_unknown")],
},
)
return
if not r.ok:
return
# Текущие теги подписчика
current_tags = r.json().get("Tags", [])
# Убрать все stage_ теги, добавить новый
stage_tag_values = list(STAGE_TAGS.values())
clean_tags = [t for t in current_tags if t not in stage_tag_values]
new_tag = STAGE_TAGS.get(new_stage_id)
if new_tag:
clean_tags.append(new_tag)
# Обновить через /updateemail или пересоздать с новыми тегами
requests.put(
f"{CM_BASE}/subscribers/{CM_LIST_ID}.json",
headers=CM_HDR,
params={"email": email},
json={
"EmailAddress": email,
"Tags": clean_tags,
"Resubscribe": False,
},
)
def handle_stage_change(lead_id: int, status_id: int | None):
if status_id is None:
return
email = get_contact_email(lead_id)
if not email:
return
update_subscriber_tags(email, status_id)
Реализация: Campaign Monitor -> Kommo
Campaign Monitor отправляет webhook-события при открытии писем, кликах и отписках. Настройте Webhook в CM Dashboard > Transactional > Webhooks:
@app.route("/webhooks/cm", methods=["POST"])
def cm_event():
# CM webhooks не подписываются HMAC - проверяем secret token в URL
# Настройте URL как /webhooks/cm?secret=YOUR_SECRET
secret = request.args.get("secret", "")
if secret != os.environ.get("CM_WEBHOOK_SECRET", ""):
return "unauthorized", 401
events = request.json or []
if not isinstance(events, list):
events = [events]
for event in events:
event_type = event.get("Type", "")
email = event.get("EmailAddress", "")
subject = event.get("Subject", "")
if event_type in ("Open", "Click") and email:
log_cm_event_to_kommo(email, event_type, subject)
return "ok", 200
def find_lead_by_email(email: str) -> int | None:
r = requests.get(
f"{KOMMO_BASE}/contacts",
headers=KOMMO_HDR,
params={"query": email, "limit": 1},
)
if not r.ok:
return None
contacts = r.json().get("_embedded", {}).get("contacts", [])
if not contacts:
return None
contact_id = contacts[0]["id"]
lr = requests.get(f"{KOMMO_BASE}/contacts/{contact_id}/links", headers=KOMMO_HDR)
if not lr.ok:
return None
links = lr.json().get("_embedded", {}).get("links", [])
lead_links = [l for l in links if l.get("to_entity_type") == "leads"]
return lead_links[0]["to_entity_id"] if lead_links else None
def log_cm_event_to_kommo(email: str, event_type: str, subject: str):
lead_id = find_lead_by_email(email)
if not lead_id:
return
label = "Открыл письмо" if event_type == "Open" else "Кликнул в письме"
requests.post(
f"{KOMMO_BASE}/leads/{lead_id}/notes",
headers=KOMMO_HDR,
json=[{"note_type": "common", "params": {"text": f"Campaign Monitor: {label} - {subject}"}}],
)
Горячие лиды из активности в письмах
Добавьте логику: если лид открыл 3+ письма за последнюю неделю - создать задачу менеджеру. Это реализуется через счётчик в Redis или PostgreSQL: при каждом Open-событии инкрементировать счётчик для email. При достижении порога - задача в Kommo.
Реальный кейс
Компания с 500 лидами в Kommo и регулярными email-рассылками в Campaign Monitor. До интеграции: маркетолог сегментировал список вручную раз в неделю. После: теги обновляются в реальном времени при смене стадии. CTR по сегменту “stage_negotiation” вырос на 34% по сравнению с несегментированными рассылками.
Для кого актуально
Компании с активным email-маркетингом и параллельной CRM-воронкой продаж. Особенно если у вас длинный цикл сделки (30+ дней) - нурчуринг по email в этот период должен соответствовать стадии переговоров.
Смежные интеграции: Kommo + ClickUp для задач при смене стадии, кастомные интеграции в Kommo CRM для обзора архитектурных подходов.
Часто задаваемые вопросы
Как Campaign Monitor обрабатывает одинаковые email-адреса в разных списках?
Каждый список в CM независим. Один subscriber может быть в нескольких списках с разными тегами. Для интеграции с Kommo рекомендуется один мастер-список “CRM Leads” - все CRM-контакты в одном месте с тегами по стадиям.
Поддерживает ли Campaign Monitor двойное согласие (double opt-in) при создании подписчика через API?
Да, но при создании через API с параметром "ConsentToTrack": "Yes" и "Resubscribe": true можно добавить без double opt-in. Убедитесь что у вас есть основание для рассылки согласно GDPR/CAN-SPAM.
Как обрабатывать отписки в Campaign Monitor - убирать из Kommo?
При событии Unsubscribe в CM webhook добавьте тег “unsubscribed” в кастомное поле контакта в Kommo. Не удаляйте контакт - сохраните историю. Менеджер по продажам может связаться по другому каналу.
Итог
Kommo + Campaign Monitor интеграция:
- Kommo webhook (статус сделки) -> обновить теги подписчика в CM
- Campaign Monitor webhook (Open/Click) -> заметка в карточке сделки
- Сегментация рассылок по стадии воронки в реальном времени
- Basic Auth: base64(api_key:x) в заголовке Authorization
Если хотите настроить email-кампании с точной сегментацией по воронке - обратитесь в Exceltic.dev. Реализуем интеграцию под ваши стадии и CМ-шаблоны.