Kommo + Segment: Sales Pipeline Events in CDP and All Downstream Tools

Segment is a Customer Data Platform (CDP): it receives events from all sources (product, CRM, website) and routes them to all connected tools through a single API. Without Kommo integration, a Won event exists only in the CRM — Amplitude does not know the user became a client, Intercom shows them the trial onboarding, and advertising audiences do not exclude converted customers. With the integration, a single deal_won event from Kommo -> Segment -> is automatically distributed to Amplitude, Intercom, Klaviyo, Facebook Custom Audiences, HubSpot Marketing, and any other tool connected to Segment.

Why Segment, and Not a Direct Integration with Each Tool

Direct integrations Kommo -> Amplitude, Kommo -> Intercom, Kommo -> Klaviyo — each is separate, each needs to be maintained. Adding a new tool means a new integration.

Segment changes the equation: one source (Kommo) -> one destination (Segment) -> N tools via Segment Destinations. When adding a new tool — enable a Destination in Segment, and the code does not change.

For comparison: Kommo + Amplitude is a direct integration with a single analytics tool. Kommo + Segment — when Amplitude is one of 5+ tools in the stack.

What Gets Synchronised

Kommo -> Segment: — Any stage change -> track event (stage_changed, deal_won, deal_lost) — Won -> identify contact with attributes (plan, amount, source) — Won -> group call with company data (for B2B account-level analytics) — Owner change -> track event owner_changed

Via Segment Destinations (automatically): — Amplitude: pipeline events in cohort analysis — Intercom: update user attributes, switch between onboarding flows — Klaviyo / Customer.io: tagging on conversion — Facebook / Google Ads: exclude converted customers from advertising audiences — Snowflake / BigQuery: events in data warehouse

Architecture

Kommo Webhook: any deal event
  ↓ Backend
  1. GET /api/v4/leads/{id} + contacts
     -> email, name, company, plan, source
  2. Segment Track API: POST /track
     -> identify (if Won or new contact)
     -> track (pipeline event)
     -> group (company, if B2B)
  3. Segment routes data to all Destinations
  4. Kommo: POST /leads/{id}/notes (optional)
     -> "Segment: event {event} sent"

Segment Track API: Key Requests

Base URL: https://api.segment.io/v1. Authentication: Basic Auth — write_key as username, empty password. Write key: Segment -> Sources -> Your Source -> Settings -> API Keys.

import requests
from requests.auth import HTTPBasicAuth
import uuid
from datetime import datetime, timezone

SEGMENT_WRITE_KEY = "your_write_key"
SEGMENT_BASE_URL = "https://api.segment.io/v1"
SEGMENT_AUTH = HTTPBasicAuth(SEGMENT_WRITE_KEY, "")

def segment_identify(user_id: str, traits: dict, anonymous_id: str = None) -> None:
    payload = {
        "userId": user_id,
        "traits": traits,
        "timestamp": datetime.now(timezone.utc).isoformat(),
        "messageId": str(uuid.uuid4()),
    }
    if anonymous_id:
        payload["anonymousId"] = anonymous_id
    resp = requests.post(
        f"{SEGMENT_BASE_URL}/identify",
        auth=SEGMENT_AUTH,
        json=payload
    )
    resp.raise_for_status()

def segment_track(user_id: str, event: str, properties: dict) -> None:
    resp = requests.post(
        f"{SEGMENT_BASE_URL}/track",
        auth=SEGMENT_AUTH,
        json={
            "userId": user_id,
            "event": event,
            "properties": properties,
            "timestamp": datetime.now(timezone.utc).isoformat(),
            "messageId": str(uuid.uuid4()),
        }
    )
    resp.raise_for_status()

def segment_group(user_id: str, group_id: str, traits: dict) -> None:
    resp = requests.post(
        f"{SEGMENT_BASE_URL}/group",
        auth=SEGMENT_AUTH,
        json={
            "userId": user_id,
            "groupId": group_id,
            "traits": traits,
            "timestamp": datetime.now(timezone.utc).isoformat(),
            "messageId": str(uuid.uuid4()),
        }
    )
    resp.raise_for_status()

def on_deal_won(lead: dict, contact: dict):
    email = get_contact_email(contact)
    name = contact["name"]
    plan = get_custom_field(lead, PLAN_FIELD_ID) or "starter"
    amount = lead.get("price", 0)
    company = get_custom_field(lead, COMPANY_FIELD_ID) or ""
    source = lead.get("loss_reason", {})

    # identify - update user profile
    segment_identify(email, {
        "email": email,
        "name": name,
        "plan": plan,
        "company": company,
        "kommo_deal_id": lead["id"],
        "customer": True,
        "mrr": amount,
    })

    # track - conversion event
    segment_track(email, "Deal Won", {
        "deal_id": lead["id"],
        "plan": plan,
        "amount": amount,
        "currency": "USD",
        "pipeline": lead.get("pipeline_id"),
        "owner": get_manager_email(lead),
    })

    # group - for B2B: company data
    if company:
        segment_group(email, company, {
            "name": company,
            "plan": plan,
            "mrr": amount,
        })

def on_stage_change(lead_id: int, old_stage: str, new_stage: str):
    lead = get_kommo_lead(lead_id)
    contact = get_kommo_contact(lead_id)
    email = get_contact_email(contact)

    segment_track(email, "Stage Changed", {
        "deal_id": lead_id,
        "from_stage": old_stage,
        "to_stage": new_stage,
    })

def on_deal_lost(lead: dict, contact: dict):
    email = get_contact_email(contact)
    loss_reason = get_custom_field(lead, LOSS_REASON_FIELD_ID) or "unknown"

    segment_identify(email, {
        "plan": None,
        "customer": False,
        "churned": True,
    })

    segment_track(email, "Deal Lost", {
        "deal_id": lead["id"],
        "loss_reason": loss_reason,
        "amount": lead.get("price", 0),
    })

Segment Destinations: What to Enable for Common Tasks

TaskSegment Destination
Cohort-based funnel analyticsAmplitude, Mixpanel
Email onboarding on conversionCustomer.io, Klaviyo, Brevo
Exclude converted customers from adsFacebook Pixel, Google Ads Conversions
Update plan in ticketing systemIntercom, Zendesk
Data warehouse for BISnowflake, BigQuery, Redshift
Reverse ETL back to KommoCensus, Hightouch

Each Destination is configured in the Segment UI — the code changes once (or not at all).

Idempotency and Deduplication

messageId (UUID) in each call — Segment uses it for deduplication. A repeated call with the same messageId is ignored. Generate UUID4 for each event.

For Kommo webhooks with retry logic: store messageId in cache (Redis) for 24 hours. On a repeated webhook — check by messageId before sending to Segment.

Real-World Case

B2B SaaS (EU, Kommo + Segment + 6 downstream tools: Amplitude, Intercom, Klaviyo, HubSpot Marketing, Facebook Ads, BigQuery):

  • Before: 6 direct integrations from Kommo to each tool, each requiring separate maintenance. Adding a seventh tool (Brevo) would mean a new integration.
  • After: Kommo -> Segment -> all 6 tools. Adding Brevo — 5 minutes in the Segment UI.
  • Result: converted clients were automatically excluded from retargeting audiences -> advertising budget stopped being spent on existing clients ($800/month saved).

Who This Is Relevant For

  • Stacks of 4+ marketing tools — Segment pays for itself over direct integrations with each
  • Product-led growth: product events + CRM events must be in a single CDP
  • Teams with a data warehouse — Segment -> BigQuery/Snowflake is cheaper and simpler than direct exports
  • International companies: Segment EU Data Residency — data stays in the EU only

Frequently Asked Questions

Is Segment expensive — are there open-source alternatives?

Rudderstack is an open-source CDP with a similar API (identify, track, group). Self-hosted on Railway or Render. The Kommo integration code is identical — only the Base URL changes. For small volumes (up to 25k events/month), Segment’s Free tier is sufficient.

How do I pass userId when Kommo only has email?

Use email as userId. This is standard practice for CRM events. When merging with web analytics via an alias call (anonymousId -> userId), a complete user journey from first visit to Won is created.

Does Segment work with EU GDPR?

Segment Business Plan supports EU Data Residency — data is stored and processed only in the EU (EU West region). For GDPR-sensitive data — ensure EU Data Residency is enabled, not just an EU endpoint.

How do I verify that events were received?

The Segment Debugger (Source -> Debugger) shows all incoming events with payloads in real time. For each Destination — a separate Delivery Overview with successful/failed events. Errors are sent to Slack/PagerDuty via Segment Alerts.

Summary

  • Segment API: Basic Auth (write_key:empty password), https://api.segment.io/v1
  • identify: update user profile on Won and attribute changes
  • track: each pipeline event (Deal Won, Stage Changed, Deal Lost)
  • group: B2B account-level company data
  • messageId = UUID4 — idempotency and deduplication
  • One source in Segment -> N Destinations without code changes

If you want to route events from Kommo to multiple tools through a single CDP — describe your stack. Exceltic.dev will configure the Segment source and pipeline event mapping.

More articles

All →