Kommo + Loops: современный B2B email для SaaS-команд из воронки продаж

Kommo + Loops: современный B2B email для SaaS-команд из воронки продаж

Loops — email-платформа построенная специально для SaaS-продуктов: event-driven рассылки на основе действий пользователей, транзакционные письма, onboarding-цепочки. Отличается от Mailchimp или GetResponse упором на product-led growth: вместо «списков контактов» Loops работает с событиями (user.signed_up, trial.started, plan.upgraded). Нативной интеграции с Kommo нет — разбираем архитектуру event-driven связки: действие в Kommo -> событие в Loops -> email-серия.

Loops vs Mailchimp vs Customer.io для SaaS B2B

ПараметрLoopsMailchimpCustomer.io
МодельEvent-driven (SaaS-first)Список-ориентированнаяEvent-driven (enterprise)
Транзакционные письмаДа (нативно)Через Mandrill (доп. плата)Да
Onboarding sequencesДаС ограничениямиДа
API-firstДа (REST v1)Да, но сложнееДа
Ценаот $49/месот $13/месот $100/мес
UI простотаВысокаяСредняяСложная
Нативная KommoНетНетНет

Loops выбирают SaaS-команды до 5000 контактов где нужна простая event-модель без overhead Enterprise-инструментов.

Архитектура: что синхронизируется

Kommo -> Loops:
— Новый контакт (лид) -> POST /contacts/create в Loops
— Изменение email/имени -> PUT /contacts/update
— Won -> POST /events/send с событием deal_won -> триггер Welcome Series
— Смена этапа -> POST /events/send -> соответствующая email-серия

Loops -> Kommo:
— Webhook email.bounced -> Note + Task: «Email недоставлен»
— Webhook contact.unsubscribed -> Note: «Отписался от рассылки»

Loops REST API v1: базовые запросы

Base URL: https://app.loops.so/api/v1. Аутентификация: Authorization: Bearer {api_key} (из Loops -> Settings -> API).

import requests

LOOPS_API_KEY = "your_loops_api_key"
LOOPS_BASE    = "https://app.loops.so/api/v1"
LOOPS_HEADERS = {
    "Authorization": f"Bearer {LOOPS_API_KEY}",
    "Content-Type":  "application/json",
}

def create_loops_contact(email: str, name: str = "",
                          company: str = "", user_group: str = "") -> dict:
    # Создать или обновить контакт (upsert по email)
    first_name = name.split()[0] if name else ""
    last_name  = " ".join(name.split()[1:]) if len(name.split()) > 1 else ""
    payload = {
        "email":       email,
        "firstName":   first_name,
        "lastName":    last_name,
        "companyName": company,
        "userGroup":   user_group,
        "source":      "kommo_crm",
    }
    resp = requests.post(
        f"{LOOPS_BASE}/contacts/create",
        headers=LOOPS_HEADERS,
        json=payload,
    )
    resp.raise_for_status()
    return resp.json()

def update_loops_contact(email: str, properties: dict) -> dict:
    payload = {"email": email, **properties}
    resp = requests.put(
        f"{LOOPS_BASE}/contacts/update",
        headers=LOOPS_HEADERS,
        json=payload,
    )
    resp.raise_for_status()
    return resp.json()

def send_loops_event(email: str, event_name: str,
                      properties: dict = None) -> dict:
    # Отправить событие -> триггер email-серии
    payload = {
        "email":      email,
        "eventName":  event_name,
        "eventProperties": properties or {},
    }
    resp = requests.post(
        f"{LOOPS_BASE}/events/send",
        headers=LOOPS_HEADERS,
        json=payload,
    )
    resp.raise_for_status()
    return resp.json()

def send_loops_transactional(email: str,
                              transactional_id: str,
                              data_variables: dict = None) -> dict:
    # Транзакционное письмо (contract, invoice, welcome)
    payload = {
        "email":             email,
        "transactionalId":   transactional_id,
        "dataVariables":     data_variables or {},
    }
    resp = requests.post(
        f"{LOOPS_BASE}/transactional",
        headers=LOOPS_HEADERS,
        json=payload,
    )
    resp.raise_for_status()
    return resp.json()

Kommo webhook -> Loops event

Kommo -> Settings -> Webhooks -> Add выдаёт события сделок. При смене этапа или создании лида запускаем соответствующее Loops событие:

@app.route("/webhooks/kommo", methods=["POST"])
def kommo_webhook():
    payload   = request.json
    event     = list(payload.keys())[0]  # "leads[status]", "leads[add]" и т.д.
    lead_data = payload.get(event, [{}])[0]

    lead_id      = lead_data.get("id")
    pipeline_id  = lead_data.get("pipeline_id")
    status_id    = lead_data.get("status_id")

    contact = get_kommo_contact_for_lead(lead_id)
    if not contact:
        return "", 200

    email = get_contact_email(contact)
    if not email:
        return "", 200

    name    = contact.get("name", "")
    company = get_contact_company(contact)

    if "add" in event:
        # Новый лид - создать контакт в Loops
        create_loops_contact(email, name, company, user_group="leads")
        send_loops_event(email, "lead_created", {
            "lead_id":   lead_id,
            "lead_name": lead_data.get("name", ""),
        })

    elif "status" in event:
        # Смена этапа
        if status_id == WON_STATUS_ID:
            send_loops_event(email, "deal_won", {
                "deal_value": lead_data.get("price", 0),
                "company":    company,
            })
            # Транзакционное Welcome письмо
            send_loops_transactional(
                email,
                transactional_id=WELCOME_TEMPLATE_ID,
                data_variables={"name": name, "company": company},
            )
        elif status_id in NURTURE_STAGE_IDS:
            send_loops_event(email, "entered_nurture", {
                "stage": get_stage_name(status_id),
            })

    return "", 200

Loops -> Kommo: обработка отписок и bounce

Loops поддерживает webhooks через Settings -> Webhooks. Важно обрабатывать отписки — иначе будете слать email тем кто не хочет их получать.

@app.route("/webhooks/loops", methods=["POST"])
def loops_webhook():
    payload    = request.json
    event_type = payload.get("type", "")
    contact    = payload.get("contact", {})
    email      = contact.get("email", "")

    lead_id = find_kommo_lead_by_email(email)
    if not lead_id:
        return "", 200

    if event_type == "email.bounced":
        bounce_type = payload.get("bounceType", "")  # "hard" | "soft"
        create_kommo_note(lead_id,
            f"Loops: email недоставлен ({bounce_type} bounce) - {email}")
        if bounce_type == "hard":
            create_kommo_task(lead_id,
                f"Loops: обновить email контакта - hard bounce на {email}")

    elif event_type == "contact.unsubscribed":
        create_kommo_note(lead_id,
            f"Loops: контакт {email} отписался от рассылки")
        # Добавить тег в Kommo чтобы не слать повторно через другие каналы
        add_kommo_tag(lead_id, "email_unsubscribed")

    return "", 200

SaaS onboarding: цепочка из Kommo

Loops лучше всего показывает себя в product-led модели: trial -> onboarding emails -> upgrade. Если Kommo — CRM рядом с SaaS-продуктом, цепочка строится так:

ONBOARDING_EVENTS = {
    TRIAL_STAGE_ID:    "trial_started",
    ACTIVE_STAGE_ID:   "account_activated",
    UPGRADE_STAGE_ID:  "upgrade_initiated",
    WON_STATUS_ID:     "deal_won",
}

def on_stage_change(lead_id: int, new_status_id: int):
    contact = get_kommo_contact_for_lead(lead_id)
    email   = get_contact_email(contact)
    if not email:
        return

    event_name = ONBOARDING_EVENTS.get(new_status_id)
    if event_name:
        lead    = get_kommo_lead(lead_id)
        send_loops_event(email, event_name, {
            "plan":       get_custom_field(lead, PLAN_FIELD_ID),
            "trial_days": get_custom_field(lead, TRIAL_DAYS_FIELD_ID),
        })

Реальный кейс

B2B SaaS (EU, 3 менеджера по продажам, Kommo + Loops):

  • До: после Won менеджер вручную добавлял email в Mailchimp-список, запускал onboarding вручную. 20–30% новых клиентов не получали onboarding-серию в первый день.
  • После: Won -> deal_won событие в Loops -> автоматический старт 5-письменной onboarding-серии. Bounce -> Note в Kommo + Task менеджеру. Отписка -> тег в Kommo чтобы не трогать по email.
  • Дополнительно: Trial -> trial_started -> Loops запускает 3-дневную urgency-серию. Upgrade -> upgrade_initiated -> отдельная цепочка с инструкциями.

Для кого актуально

  • SaaS-компании где Kommo CRM работает параллельно с SaaS-продуктом и нужна event-driven email-автоматизация
  • Команды до 5000 контактов которым Mailchimp неудобен из-за списочной модели
  • Product-led growth компании где trial -> onboarding -> upgrade проходит через CRM
  • Стартапы которым нужна простая email-платформа без сложности Customer.io

Часто задаваемые вопросы

Loops vs Mailchimp — в чём принципиальная разница для CRM-интеграции?

Mailchimp работает со списками: вы добавляете контакт в список -> он попадает в цепочку. Loops работает с событиями: вы отправляете событие (deal_won, trial_started) -> Loops запускает нужную серию. Для CRM-интеграции event-модель Loops значительно чище — не нужно управлять подписками на списки, достаточно слать события из Kommo при каждом изменении этапа.

Loops поддерживает транзакционные письма (счёт, договор)?

Да. POST /transactional с transactionalId (ID шаблона из Loops UI) и dataVariables (динамические поля). Транзакционные письма отправляются независимо от подписки контакта — они не являются маркетинговыми. Для интеграции с Kommo: Won -> отправить транзакционный welcome + invoice email с данными из сделки.

Как Loops обрабатывает дубли контактов?

Loops использует email как первичный ключ. POST /contacts/create с уже существующим email — это upsert (обновление свойств). Дубли по email невозможны. Если контакт в Kommo меняет email — нужно создать новый контакт в Loops и удалить старый через DELETE /contacts с параметром email.

Loops отслеживает открытия и клики — можно ли это видеть в Kommo?

Loops не предоставляет webhook на уровне отдельных открытий (только aggregate в дашборде). Для email engagement в Kommo лучше использовать Loops -> Zapier/Make (если нужны клики -> Note) или Customer.io где per-event webhooks богаче. Loops даёт webhooks: email.bounced, contact.unsubscribed, email.spam_complaint — этих достаточно для гигиены базы.

Итого

  • API: Bearer token, base URL https://app.loops.so/api/v1
  • Контакты: POST /contacts/create (upsert по email), PUT /contacts/update
  • События: POST /events/send с eventName -> триггер email-серии в Loops
  • Транзакционные: POST /transactional с transactionalId + dataVariables
  • Webhook Loops -> Kommo: email.bounced -> Note/Task, contact.unsubscribed -> Note + тег

Если у вас SaaS-продукт с Kommo и вы хотите event-driven onboarding email без Mailchimp — опишите текущую воронку и количество контактов. Exceltic.dev настроит интеграцию за 1–2 рабочих дня.

Ещё статьи

Все →