Kommo + Mixpanel: события воронки продаж в продуктовую аналитику
Mixpanel — платформа продуктовой аналитики с серверным Python SDK: события из любого источника отправляются через track(), профили пользователей — через people_set(). Без интеграции с Kommo Mixpanel видит только поведение в продукте, но не знает когда лид стал клиентом и сколько дней провёл в воронке. С интеграцией — события смены этапов, Won и Lost из CRM попадают в Mixpanel, и аналитик видит сквозную картину от первого касания до оплаты.
Зачем соединять Kommo и Mixpanel
Без интеграции:
— Mixpanel показывает retention, activation, feature usage — но не знает кто из пользователей стал платящим клиентом
— В Kommo есть данные о сделках, суммах, длине цикла — но нет связи с продуктовым поведением
— Product manager не может ответить: «Клиенты которые дошли до Won — они на каком этапе онбординга были?»
— Атрибуция выручки по маркетинговым каналам в Mixpanel не замыкается на реальные сделки
С интеграцией:
— Новый контакт в Kommo -> профиль в Mixpanel с CRM-свойствами (источник, менеджер, тип)
— Смена этапа -> событие Lead Stage Changed с именем этапа и ID сделки
— Won -> событие Deal Won с суммой, длиной цикла, источником
— Lost -> событие Deal Lost с причиной из Kommo
— В Mixpanel строятся воронки, когортный анализ и retention по реальным сделкам
Что синхронизируется
Kommo -> Mixpanel:
— Новый контакт -> people_set с email, компанией, источником лида, ответственным менеджером
— Смена этапа -> track('Lead Stage Changed', {stage, deal_id, deal_value})
— Won -> track('Deal Won', {revenue, cycle_days, source, manager})
— Lost -> track('Deal Lost', {reason, stage_at_loss, cycle_days})
— Создание задачи на контакте -> опционально track('CRM Task Created')
Mixpanel -> Kommo (опционально):
— Когорта «Активные пользователи» из Mixpanel -> тег на контакте в Kommo для приоритизации менеджером
— Спад активности в продукте -> Note в Kommo для превентивного звонка CSM
Архитектура
Kommo Webhook: контакт создан / обновлён
↓ Backend
1. GET /api/v4/contacts/{id}
-> email (как distinct_id), имя, компания, источник из кастомных полей
2. Mixpanel: people_set(distinct_id=email, properties)
-> $email, $name, crm_source, kommo_contact_id
Kommo Webhook: сделка изменена (этап / статус)
↓ Backend
1. GET /api/v4/leads/{id} + contacts
-> этап, сумма, контакт, дата создания
2. distinct_id = email контакта
3. Mixpanel: track(distinct_id, event_name, properties)
-> Для Won: 'Deal Won' + {revenue, cycle_days, source, manager}
-> Для Lost: 'Deal Lost' + {reason, stage_at_loss}
-> Для смены этапа: 'Lead Stage Changed' + {from_stage, to_stage, deal_id}
Mixpanel Python SDK: ключевые запросы
pip install mixpanel
Инициализация (с поддержкой EU):
from mixpanel import Mixpanel, Consumer
MP_TOKEN = 'your_project_token'
MP_SECRET = 'your_api_secret' # для import_data
# EU data residency - обязательно для EU-рынков (GDPR)
mp = Mixpanel(MP_TOKEN, consumer=Consumer(api_host='api-eu.mixpanel.com'))
# US (по умолчанию)
# mp = Mixpanel(MP_TOKEN)
Создать/обновить профиль контакта:
def sync_contact_to_mixpanel(email: str, name: str, company: str,
source: str, kommo_id: int):
mp.people_set(email, {
'$email': email,
'$name': name,
'company': company,
'crm_source': source,
'kommo_contact_id': kommo_id
})
Отправить событие смены этапа:
from datetime import datetime, date
def track_stage_change(email: str, deal_id: int, deal_name: str,
from_stage: str, to_stage: str, deal_value: float):
mp.track(email, 'Lead Stage Changed', {
'deal_id': deal_id,
'deal_name': deal_name,
'from_stage': from_stage,
'to_stage': to_stage,
'deal_value': deal_value
})
def track_deal_won(email: str, deal_id: int, revenue: float,
source: str, manager: str, created_at: datetime):
cycle_days = (date.today() - created_at.date()).days
mp.track(email, 'Deal Won', {
'deal_id': deal_id,
'revenue': revenue,
'source': source,
'manager': manager,
'cycle_days': cycle_days
})
# Обновить профиль - пометить как платящего клиента
mp.people_set(email, {
'customer_status': 'paying',
'first_purchase_date': date.today().isoformat(),
'ltv': revenue
})
def track_deal_lost(email: str, deal_id: int, reason: str,
stage_at_loss: str, created_at: datetime):
cycle_days = (date.today() - created_at.date()).days
mp.track(email, 'Deal Lost', {
'deal_id': deal_id,
'loss_reason': reason,
'stage_at_loss': stage_at_loss,
'cycle_days': cycle_days
})
Импорт исторических данных (события старше 5 дней):
def import_historical_event(email: str, event_name: str,
timestamp: int, properties: dict):
mp.import_data(
api_secret=MP_SECRET,
distinct_id=email,
event_name=event_name,
timestamp=timestamp, # Unix timestamp
properties=properties
)
import_data() нужен при первичном наполнении — когда загружается история сделок из Kommo за последние месяцы. В реальном времени используется track().
Webhook-обработчик Kommo:
from flask import Flask, request
app = Flask(__name__)
@app.route('/webhooks/kommo', methods=['POST'])
def kommo_webhook():
data = request.json
event_type = data.get('leads', {}).get('status')
if event_type:
for lead in event_type:
deal_id = lead.get('id')
# Получить полные данные сделки и контакта
deal = get_kommo_lead(deal_id)
contact = get_lead_contact(deal)
if not contact or not contact.get('email'):
continue # без email distinct_id не определить
handle_deal_event(deal, contact)
return '', 200
def handle_deal_event(deal: dict, contact: dict):
email = contact['email']
status_id = deal.get('status_id')
if status_id == 142: # Won (стандартный ID в Kommo)
track_deal_won(email, deal['id'], deal.get('price', 0),
deal.get('source', ''), deal.get('manager', ''),
deal['created_at'])
elif status_id == 143: # Lost
loss_reason = deal.get('loss_reason', {}).get('name', '')
track_deal_lost(email, deal['id'], loss_reason,
deal.get('stage_name', ''), deal['created_at'])
else:
track_stage_change(email, deal['id'], deal.get('name', ''),
deal.get('prev_stage', ''), deal.get('stage_name', ''),
deal.get('price', 0))
Distinct ID: email как универсальный идентификатор
Mixpanel использует distinct_id для связи профиля и событий. В серверной интеграции с Kommo проще всего использовать email контакта — он есть в CRM и уже известен Mixpanel если контакт ранее взаимодействовал с продуктом.
Проблема: если контакт зарегистрировался в продукте с одним email, а в Kommo записан с другим — профили разделятся. Решение: $merge через Mixpanel Identity API, или строгая политика использования единого email на всех платформах.
Если продукт использует Mixpanel JS SDK и distinct_id генерируется автоматически (anonymous ID) — в Kommo нужно хранить этот ID в кастомном поле и передавать в события вместо email.
Реальный кейс
B2B SaaS (EU, 150–200 триалов в месяц, продуктовая и sales команды работают независимо):
- До: Mixpanel показывал activation funnel, но product team не могли ответить «сколько активированных пользователей стали платящими». Kommo хранил Won-сделки, но без связи с продуктовым поведением.
- После: каждый Won из Kommo -> событие в Mixpanel с
cycle_daysиrevenue. В Mixpanel built воронка: Trial Start -> Activation -> Deal Won. Выяснилось: пользователи прошедшие онбординг за <3 дня конвертируются в 2.3x чаще — это стало приоритетом product team. - Дополнительно: когорта «чернил активность за 14 дней» из Mixpanel -> Note в Kommo -> CSM делает проактивный звонок до оттока.
Для глубокой SQL-аналитики по данным Kommo без Mixpanel — смотрите Redash. Для атрибуции рекламных каналов до закрытой сделки — Prooflytics даёт более точную картину чем Mixpanel.
Для кого актуально
- Компания использует Mixpanel для продуктовой аналитики и Kommo для продаж
- Product team хочет видеть какие паттерны поведения в продукте коррелируют с Won
- Sales team хочет получать сигналы из Mixpanel (спад активности, достижение лимитов) в Kommo
- Нужна когортная аналитика по циклу сделки: сколько дней от первого касания до оплаты
Часто задаваемые вопросы
Mixpanel track() или import_data() — когда что использовать?
track() — для событий в реальном времени: принимает события не старше 5 дней. import_data() — для исторических данных: любой timestamp, требует api_secret (не токен), использует endpoint /import. При первичном наполнении используйте import_data для истории, затем переключайтесь на track для ongoing событий.
Как настроить EU data residency в Mixpanel?
При инициализации SDK передать кастомный Consumer: Mixpanel(token, consumer=Consumer(api_host='api-eu.mixpanel.com')). Без этого данные пойдут на US-серверы — нарушение GDPR для EU-контактов. EU residency настраивается на уровне проекта в Mixpanel UI и должна совпадать с конфигурацией SDK.
Что использовать как distinct_id?
Для серверной интеграции с CRM — email контакта: он доступен в Kommo и позволяет объединить CRM-профиль с продуктовым. Если в вашем продукте distinct_id генерируется клиентом (anonymous UUID при первом визите) — нужно хранить его в кастомном поле Kommo и передавать в события. Email как distinct_id — проще, но требует единой политики email на всех платформах.
Mixpanel принимает события из бэкенда без SDK?
Да. Можно отправлять события напрямую через HTTP API: POST https://api.mixpanel.com/track (или api-eu.mixpanel.com/track) с base64-encoded JSON. Python SDK делает это автоматически — рекомендуется использовать SDK, а не raw HTTP, для корректной обработки ошибок и retry.
Нужен ли токен или secret для track()?
track() и people_set() используют project token (публичный, безопасно хранить в коде). import_data() требует API secret (приватный, хранить в переменных окружения). Оба значения доступны в Mixpanel: Settings -> Project Settings.
Итого
- Mixpanel Python SDK:
pip install mixpanel, EU consumer дляapi-eu.mixpanel.com people_set()— профиль контакта из Kommo;track()— события этапов, Won, Lostimport_data()с api_secret — для первичной загрузки истории сделокdistinct_id= email контакта — простейший вариант для CRM-интеграции- В Mixpanel строятся воронки по CRM-событиям и когортный анализ цикла сделки
- Типовой срок разработки — 1–2 недели
Если вы используете Mixpanel и Kommo и хотите видеть сквозную картину от первого касания до закрытой сделки — опишите вашу структуру: какие события важны, какие свойства сделок нужны в аналитике. Exceltic.dev настроит маппинг и первичный импорт истории.