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
| Parameter | Iterable | Customer.io | ActiveCampaign |
|---|---|---|---|
| Channels | Email, SMS, Push, In-App, Web Push | Email, SMS, Push, In-App | Email, SMS |
| Target segment | Enterprise B2C/B2B | Mid-market B2B SaaS | SMB |
| Workflow complexity | High (drag-drop + code) | Medium | Medium |
| Mobile push | Native | Yes | No |
| Price | Enterprise (from $500+/mo) | From $100/mo | From $29/mo |
| Best for | 100k+ users, mobile app | SaaS onboarding, behavioral | Email 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_lostevent -> 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: truestatus 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 URLhttps://api.iterable.com/api updateUser— upsert by email, all CRM data passed indataFieldstriggerWorkflow— 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.