Kommo + Omnisend: email и SMS автоматизация для e-commerce из воронки

Kommo + Omnisend: email и SMS автоматизация для e-commerce из воронки

Omnisend — омниканальная e-commerce платформа: email, SMS, push-уведомления, попапы в одном инструменте с нативной интеграцией с Shopify, WooCommerce, BigCommerce. В отличие от Drip или Klaviyo, Omnisend сильнее в SMS + email в одном workflow и automation для e-commerce событий. Без интеграции с Kommo маркетолог не знает кто из подписчиков Omnisend уже стал платным клиентом и с каким тарифом. С интеграцией Won -> профиль в Omnisend мгновенно обновляется -> запускается post-purchase sequence для нового клиента.

Omnisend vs Klaviyo vs Drip для e-commerce

ПараметрOmnisendKlaviyoDrip
SMSДа, нативноДа (SMS add-on)Да (US только)
Push-уведомленияДаНетНет
E-commerce automationShopify/WooCommerce нативноShopify нативноAPI-based
ЦенаОт $16/месОт $20/месОт $39/мес
APIREST с API KeyREST с API KeyREST Basic Auth

Omnisend выбирают команды с Shopify + собственной продажей через менеджеров: нативная интеграция с магазином + CRM-контекст через Kommo.

Что синхронизируется

Kommo -> Omnisend:
— Won -> создать/обновить Contact с тегами customer, plan_{tier}, channel_{source}
— Won -> установить кастомные поля: plan, mrr, deal_id, won_date
— Won -> добавить в сегмент «Paying customers» (через тег)
— Смена тарифа -> обновить тег и кастомное поле plan
— Потеря клиента -> добавить тег churned, убрать active

Omnisend -> Kommo:
contact.unsubscribed -> Note + задача менеджеру
contact.bounced -> Note: проверить email контакта
automation.email.clicked -> Note (опционально для key emails)

Omnisend API: ключевые запросы

Base URL: https://api.omnisend.com/v3.
Аутентификация: X-API-KEY: {api_key} в заголовке.
API Key: Omnisend -> Store Settings -> Integrations -> API Keys.

import requests
from datetime import datetime, timezone

OMNISEND_API_KEY = "your_api_key"
OMNISEND_BASE_URL = "https://api.omnisend.com/v3"
HEADERS = {
    "X-API-KEY": OMNISEND_API_KEY,
    "Content-Type": "application/json",
}

def upsert_contact(email: str, tags: list, custom_props: dict,
                   first_name: str = "", phone: str = "") -> dict:
    # Создать или обновить контакт. Omnisend upsert по email
    payload = {
        "email": email,
        "status": "subscribed",
        "statusDate": datetime.now(timezone.utc).isoformat(),
        "tags": tags,
    }
    if first_name:
        payload["firstName"] = first_name
    if phone:
        payload["phone"] = phone  # E.164 формат: +31612345678
    if custom_props:
        payload["customProperties"] = custom_props

    resp = requests.post(
        f"{OMNISEND_BASE_URL}/contacts",
        headers=HEADERS,
        json=payload
    )
    # 200 = обновлён, 201 = создан, оба OK
    if resp.status_code not in (200, 201):
        resp.raise_for_status()
    return resp.json()

def add_tags(contact_id: str, tags: list) -> None:
    resp = requests.post(
        f"{OMNISEND_BASE_URL}/contacts/{contact_id}/tags",
        headers=HEADERS,
        json={"tags": tags}
    )
    resp.raise_for_status()

def remove_tag(contact_id: str, tag: str) -> None:
    resp = requests.delete(
        f"{OMNISEND_BASE_URL}/contacts/{contact_id}/tags/{tag}",
        headers=HEADERS
    )
    if resp.status_code != 404:
        resp.raise_for_status()

def get_contact_by_email(email: str) -> dict | None:
    resp = requests.get(
        f"{OMNISEND_BASE_URL}/contacts",
        headers=HEADERS,
        params={"email": email}
    )
    resp.raise_for_status()
    contacts = resp.json().get("contacts", [])
    return contacts[0] if contacts else None

def on_deal_won(lead: dict, contact: dict):
    email = get_contact_email(contact)
    name = contact["name"]
    first_name = name.split()[0] if name else ""
    plan = get_custom_field(lead, PLAN_FIELD_ID) or "starter"
    source = get_custom_field(lead, SOURCE_FIELD_ID) or "direct"
    amount = lead.get("price", 0)
    phone = get_contact_phone(contact) or ""

    tags = ["customer", f"plan_{plan}", f"source_{source}", "active"]

    custom_props = {
        "plan": plan,
        "mrr": str(amount),
        "kommo_deal_id": str(lead["id"]),
        "won_date": datetime.now(timezone.utc).strftime("%Y-%m-%d"),
    }

    omnisend_contact = upsert_contact(
        email=email,
        tags=tags,
        custom_props=custom_props,
        first_name=first_name,
        phone=phone,
    )

    create_kommo_note(lead["id"],
        f"Omnisend: контакт обновлён (теги: customer, plan_{plan})")

def on_plan_upgrade(lead: dict, contact: dict, old_plan: str, new_plan: str):
    email = get_contact_email(contact)
    c = get_contact_by_email(email)
    if not c:
        return
    contact_id = c["contactID"]
    remove_tag(contact_id, f"plan_{old_plan}")
    add_tags(contact_id, [f"plan_{new_plan}", "upgraded"])

def on_deal_lost(lead: dict, contact: dict):
    email = get_contact_email(contact)
    c = get_contact_by_email(email)
    if not c:
        return
    contact_id = c["contactID"]
    remove_tag(contact_id, "active")
    add_tags(contact_id, ["churned"])

Обработка Omnisend Webhook:

from flask import Flask, request

app = Flask(__name__)

@app.route("/webhooks/omnisend", methods=["POST"])
def omnisend_webhook():
    payload = request.json
    event = payload.get("event")
    contact = payload.get("contact", {})
    email = contact.get("email", "")

    if not email:
        return "", 200

    deal_id = find_kommo_deal_by_contact_email(email)
    if not deal_id:
        return "", 200

    if event == "contact.unsubscribed":
        create_kommo_note(deal_id,
            "Omnisend: клиент отписался от рассылок")
        create_kommo_task(deal_id,
            "Уточнить предпочтения по коммуникации - клиент отписался от Omnisend")

    elif event == "contact.bounced":
        create_kommo_note(deal_id,
            "Omnisend: письмо не доставлено (bounce)")
        create_kommo_task(deal_id,
            "Проверить email - Omnisend зафиксировал hard bounce")

    return "", 200

Настройка Webhook в Omnisend: Store Settings -> Integrations -> Webhooks -> Add Webhook. Выбрать события и указать URL.

SMS-автоматизация: уникальное преимущество Omnisend

Для EU e-commerce команд SMS-канал в Omnisend особенно ценен: EU-тарифы ниже US, поддержка GDPR opt-in/opt-out встроена. При Won с номером телефона — Omnisend автоматически включает контакт в SMS-last-step если email не открыт.

# При Won с телефоном - устанавливаем SMS-статус
if phone:
    payload["phone"] = phone
    payload["phoneStatus"] = "subscribed"  # GDPR: только если есть согласие
    payload["phoneStatusDate"] = datetime.now(timezone.utc).isoformat()

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

DTC e-commerce SaaS (EU, Shopify + Kommo + Omnisend, 40–60 новых клиентов в месяц):

  • До: Omnisend сегментировал всех подписчиков без разделения «купил / не купил». Post-purchase sequence уходила всем — в том числе тем, кто ещё не стал клиентом. Onboarding-письма без CRM-контекста.
  • После: Won -> тег customer + кастомные поля plan/mrr -> в Omnisend запускается правильный сегмент. Trial-пользователи и paying-клиенты получают разные sequences. Email с «upgrade» не идут уже максимальным тарифом.
  • Дополнительно: при churned-теге -> Omnisend запускает win-back automation. Возврат 9% ушедших клиентов за 6 месяцев.

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

  • E-commerce с собственным отделом продаж: Shopify + менеджеры
  • Команды с SMS-каналом в EU — Omnisend нативно поддерживает EU-операторов
  • SaaS с подписочной моделью: разные email-sequence для каждого тарифа
  • Компании с 30+ активными клиентами, где сегментация уже важна

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

Omnisend vs Klaviyo — когда что выбрать для e-commerce?

Klaviyo: если у вас Shopify и нужны максимально богатые Shopify-триггеры (abandoned cart с конкретными товарами, Browse Abandonment, Price Drop). Omnisend: если нужен SMS + email в одном workflow, более доступная цена, чуть более простой UI. Для Kommo + Klaviyo — аналогичная архитектура, разница в возможностях платформы.

Omnisend customProperties — как создать кастомное поле?

Через API: при первом POST /contacts с customProperties: {"field_name": "value"} Omnisend автоматически создаёт кастомное свойство. Затем оно доступно в сегментации и персонализации. Имя поля — произвольная строка, best practice: snake_case.

Omnisend webhook — как защитить endpoint?

Omnisend не подписывает webhook HMAC. Защита: секретный URL (/webhooks/omnisend/{random_secret}) + опционально IP whitelist. Whitelist IP-адресов Omnisend публикует в документации.

Omnisend поддерживает GDPR double opt-in?

Да. При создании контакта через API можно указать status: "unconfirmed" — контакт получит confirmation email. После подтверждения статус меняется на subscribed. Для EU-команд это обязательно если нет явного согласия при регистрации.

Итого

  • Omnisend API: X-API-KEY в заголовке, https://api.omnisend.com/v3
  • Upsert контакт: POST /contacts — 200/201 оба OK
  • Теги: POST /contacts/{id}/tags, удалить: DELETE /contacts/{id}/tags/{tag}
  • customProperties: создаются автоматически при первом использовании в API
  • SMS: передавать phone (E.164) и phoneStatus: "subscribed" только при наличии согласия
  • Webhook: нет HMAC — защита через секретный path + IP whitelist

Если вы используете Omnisend и Kommo и хотите синхронизировать CRM-статусы с email/SMS-сегментами — опишите структуру тарифов и текущие automation-workflow. Exceltic.dev настроит маппинг и обработку событий.

Ещё статьи

Все →