Kommo + Iterable: cross-channel marketing from the sales pipeline without manual segmentation

Iterable is an enterprise cross-channel marketing platform: email, SMS, push notifications, in-app messages, and web push from a single workspace. Unlike ActiveCampaign or Customer.io, Iterable is aimed at companies with high volume and complex cross-channel journeys — B2C and B2B with a mobile app, multiple languages, and advanced segmentation. Without Kommo integration: deal status in CRM and segment in Iterable live independently — marketing does not know when a client closed as Won, and CRM does not know the client unsubscribed.

Iterable vs Customer.io vs ActiveCampaign

ParameterIterableCustomer.ioActiveCampaign
ChannelsEmail, SMS, Push, In-App, Web PushEmail, SMS, Push, In-AppEmail, SMS
Target segmentEnterprise B2C/B2BMid-market B2B SaaSSMB
Workflow complexityHigh (drag-drop + code)MediumMedium
Mobile pushNativeYesNo
PriceEnterprise (from $500+/mo)From $100/moFrom $29/mo
Best for100k+ users, mobile appSaaS onboarding, behavioralEmail automation SMB

Iterable is chosen by companies with a mobile app, multi-channel customer journey, and the need to manage the lifecycle from first touch to retention in a single system.

What gets synchronized

Kommo -> Iterable:
— Won -> updateUser (user profile) + startWorkflow (onboarding sequence)
— Pipeline stage change -> trackEvent (CRM stage change event for Iterable Journey)
— Lost deal -> trackEvent "deal_lost" -> stop nurture workflows

Iterable -> Kommo:
unsubscribeEvent -> Note in deal: “Client unsubscribed from mailing list”
emailBounce -> Note: “Email bounced — verify address”
— Workflow-reached milestone -> Note: “Iterable: client completed onboarding sequence”

Iterable API: updating user and triggering workflow

Base URL: https://api.iterable.com/api. Authentication: header Api-Key: {api_key} (from Iterable -> Integrations -> API Keys).

import requests

ITERABLE_API_KEY = "your_api_key"
ITERABLE_BASE    = "https://api.iterable.com/api"
ITERABLE_HEADERS = {
    "Api-Key":      ITERABLE_API_KEY,
    "Content-Type": "application/json",
}

def update_iterable_user(email: str, data_fields: dict) -> dict:
    # updateUser upserts by email - creates if not exists, updates if exists
    payload = {
        "email":      email,
        "dataFields": data_fields,
    }
    resp = requests.post(
        f"{ITERABLE_BASE}/users/update",
        headers=ITERABLE_HEADERS,
        json=payload,
    )
    resp.raise_for_status()
    return resp.json()

def track_iterable_event(email: str, event_name: str, data_fields: dict = None) -> dict:
    # Event appears in Iterable Journey for trigger conditions
    payload = {
        "email":      email,
        "eventName":  event_name,
        "dataFields": data_fields or {},
    }
    resp = requests.post(
        f"{ITERABLE_BASE}/events/track",
        headers=ITERABLE_HEADERS,
        json=payload,
    )
    resp.raise_for_status()
    return resp.json()

def trigger_iterable_workflow(workflow_id: int, email: str,
                               data_fields: dict = None) -> dict:
    # Trigger a specific Workflow for a user
    payload = {
        "workflowId": workflow_id,
        "listId":     None,
        "subscribers": [
            {
                "email":      email,
                "dataFields": data_fields or {},
            }
        ],
    }
    resp = requests.post(
        f"{ITERABLE_BASE}/workflows/triggerWorkflow",
        headers=ITERABLE_HEADERS,
        json=payload,
    )
    resp.raise_for_status()
    return resp.json()

# Iterable Workflow IDs (from Iterable -> Journeys -> your workflow -> Settings)
ONBOARDING_WORKFLOW_ID = 12345
NURTURE_WORKFLOW_ID    = 12346

Integration: Kommo Won -> Iterable

def on_kommo_deal_won(lead: dict, contact: dict):
    email   = get_contact_email(contact)
    name    = contact.get("name", "")
    company = get_custom_field(lead, COMPANY_FIELD_ID) or ""
    plan    = get_custom_field(lead, PLAN_FIELD_ID) or "starter"

    # Update user profile in Iterable
    update_iterable_user(email, {
        "firstName":    name.split()[0] if name else "",
        "lastName":     " ".join(name.split()[1:]) if len(name.split()) > 1 else "",
        "companyName":  company,
        "plan":         plan,
        "crmDealId":    str(lead["id"]),
        "dealValue":    lead.get("price", 0),
        "wonAt":        lead.get("closed_at", ""),
        "isCustomer":   True,
    })

    # Won event for trigger conditions in Journey
    track_iterable_event(email, "deal_won", {
        "deal_id":   lead["id"],
        "plan":      plan,
        "value":     lead.get("price", 0),
    })

    # Trigger onboarding workflow
    trigger_iterable_workflow(
        ONBOARDING_WORKFLOW_ID,
        email,
        data_fields={"plan": plan, "company": company},
    )

    create_kommo_note(
        lead["id"],
        f"Iterable: user updated, onboarding workflow triggered (plan: {plan})",
    )

def on_kommo_stage_change(lead: dict, contact: dict, new_stage_id: int):
    email      = get_contact_email(contact)
    stage_name = STAGE_ID_TO_NAME.get(new_stage_id, str(new_stage_id))
    track_iterable_event(email, "crm_stage_changed", {
        "stage_id":   new_stage_id,
        "stage_name": stage_name,
        "deal_id":    lead["id"],
    })

def on_kommo_deal_lost(lead: dict, contact: dict):
    email = get_contact_email(contact)
    update_iterable_user(email, {"isCustomer": False, "churnedAt": ""})
    track_iterable_event(email, "deal_lost", {
        "deal_id":      lead["id"],
        "loss_reason":  get_custom_field(lead, LOSS_REASON_FIELD_ID) or "",
    })

Iterable -> Kommo: reverse synchronization

Iterable supports webhooks (Integrations -> Webhooks). For critical events — Note in Kommo:

@app.route("/webhooks/iterable", methods=["POST"])
def iterable_webhook():
    payload    = request.json
    event_type = payload.get("eventName") or payload.get("type", "")
    email      = payload.get("email", "")

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

    if event_type in ("emailUnsubscribe", "smsUnsubscribe"):
        channel = "email" if "email" in event_type.lower() else "SMS"
        create_kommo_note(lead_id,
            f"Iterable: client unsubscribed from {channel} mailing")

    elif event_type == "emailBounce":
        bounce_type = payload.get("bounceType", "")
        create_kommo_note(lead_id,
            f"Iterable: email bounced ({bounce_type}) - verify address")

    elif event_type == "inAppClick":
        button = payload.get("buttonIdentifier", "")
        create_kommo_note(lead_id,
            f"Iterable: client clicked '{button}' in in-app message")

    return "", 200

Segmentation without manual work

Without integration, marketing regularly requests from sales “a list of Won deals for the month” -> imports CSV to Iterable -> launches onboarding. With integration this happens automatically: every Won -> updateUser with isCustomer: true -> Iterable “Customers” segment updates in real time -> campaigns on the segment run without manual import.

Iterable List API also allows creating dynamic segments by dataFields:

def subscribe_to_list(email: str, list_id: int) -> dict:
    resp = requests.post(
        f"{ITERABLE_BASE}/lists/subscribe",
        headers=ITERABLE_HEADERS,
        json={"listId": list_id, "subscribers": [{"email": email}]},
    )
    resp.raise_for_status()
    return resp.json()

Real-world case

B2B SaaS (US, 15,000 users, mobile application, Kommo + Iterable):

  • Before: Won -> sales ops manually exported CSV once a week -> imported to Iterable -> launched onboarding. Delay of 2–7 days from close to first onboarding email.
  • After: Won -> Python webhook -> updateUser + triggerWorkflow -> first email in 5 minutes. Lost deal -> deal_lost event -> Journey stops nurture sequence automatically.
  • Additionally: push notifications in the mobile app are now segmented by CRM plan (starter/growth/enterprise). In-app messages are shown only to clients with isCustomer: true status in Iterable.

Who this is relevant for

  • B2B/B2C with a mobile app and cross-channel journey requirements
  • SaaS with an onboarding email sequence that must start at Won, not a week later
  • Companies where marketing requests an up-to-date client list from sales every week
  • Enterprise with 10k+ contacts in Iterable where manual import is no longer feasible

Frequently asked questions

Iterable vs Customer.io for Kommo integration — which is simpler?

The API architecture is similar: updateUser, trackEvent, triggerWorkflow. Customer.io is slightly simpler for SaaS onboarding — less configuration. Iterable is more powerful for mobile push and cross-channel. For Kommo integration, the code differs only in URL and authorization header. The platform choice depends on channels (if push is needed — Iterable), volume, and budget.

How do you sync a lost deal — stop workflows?

trackEvent "deal_lost" -> in Iterable Journey add a Filter condition: stop workflow if user received the deal_lost event. Or use updateUser with isActive: false -> Journey filter isActive == true. The key is not to leave an active onboarding running for Lost clients — it damages the impression.

Does Iterable support GDPR — how to handle deletion requests?

Iterable -> API: DELETE /users/{email} deletes the user and all associated data. When receiving a GDPR request from a client: Kommo Note -> responsible person -> triggers Iterable API + GDPR process in Kommo. Iterable also supports Data Subject Requests via UI.

How do you pass custom fields from Kommo to Iterable?

Any Kommo custom field -> dataFields object in updateUser. Iterable creates the field automatically on first pass — no need to pre-register fields. Types: string, number, boolean, array (for tags). Used in segmentation (Iterable Dynamic Segments) and template personalization (Handlebars in Iterable editor).

Summary

  • API: Api-Key: {key} header, base URL https://api.iterable.com/api
  • updateUser — upsert by email, all CRM data passed in dataFields
  • triggerWorkflow — trigger Journey at the time of event (Won, stage change)
  • trackEvent — CRM events as Iterable triggers (stage_changed, deal_lost)
  • Reverse sync: unsubscribe/bounce webhook -> Kommo Note
  • Delay Won -> first email: 5 minutes instead of a week

If you have Kommo + Iterable and marketing is working with outdated segments from a weekly CSV — describe your Journey structure and channels. Exceltic.dev will configure real-time synchronization.

More articles

All →