Kommo + Kit (ConvertKit): email automation from the sales pipeline

Kommo + Kit (ConvertKit): email automation from the sales pipeline

Kit (known as ConvertKit before 2024) is an email automation platform with powerful tag-based segmentation and a visual sequence builder. Without a Kommo integration, a marketer manually adds a new customer to Kit after Won, the sales manager has no visibility into which email campaigns the customer is enrolled in, and an unsubscribe event is not reflected in the CRM. With the integration, a pipeline stage change in Kommo automatically triggers the correct Kit sequence with the right tags, and every significant email event appears in the deal card.

Kit vs Customer.io: when to choose which

Kit is aimed at the creator economy and content businesses — courses, SaaS with educational onboarding, community products. Key strengths: visual automations, tags as the primary segmentation mechanism, integrations with Teachable/Kajabi/Gumroad.

Customer.io is behavioral marketing for product-led SaaS: product events -> campaigns. API-first approach, complex conditions based on user properties.

For Kommo integration, both are implemented in a similar way. Customer.io integration is chosen when event-driven sending based on in-product actions is needed. Kit is the right choice when onboarding is built around email sequences with tag logic.

What is synchronized

Kommo -> Kit:
— Won -> create or update a subscriber in Kit (upsert by email)
— Won -> add tags customer, plan:{plan_name}, source:{channel}
— Won -> add to onboarding sequence
— Stage change -> update tags (remove trial, add active)

Kit -> Kommo:
subscriber.unsubscribed -> Note + task for the manager: “Customer unsubscribed from Kit”
subscriber.bounced -> Note + task to verify email
— Sequence completed -> Note: “Onboarding campaign completed”

Architecture

Kommo Webhook: deal moved to Won
  ↓ Backend
  1. GET /api/v4/leads/{id} + contacts
     -> email, name, plan, source from custom fields
  2. Kit API v4: POST /subscribers (upsert)
     -> email, first_name, custom fields (plan, kommo_deal_id)
  3. Kit API v4: POST /tags/{tag_id}/subscribers
     -> add tags: customer, plan:{plan}, source:{source}
  4. Kit API v4: POST /sequences/{seq_id}/subscribers
     -> add to onboarding sequence
  5. Kommo: POST /leads/{id}/notes
     -> "Kit: subscriber added, tags: customer, plan:growth"

Kit Webhook: subscriber.unsubscribed
  ↓ Backend
  1. From payload: subscriber.email
  2. Find deal by contact email in Kommo
  3. Kommo: POST /leads/{deal_id}/notes
     -> "Kit: customer unsubscribed from mailing list"
  4. Kommo: POST /tasks
     -> "Clarify communication preferences - customer unsubscribed from Kit"

Kit API v4: key requests

Base URL: https://api.kit.com/v4/.
Authentication: Bearer token — Authorization: Bearer {api_key}.
API key: Kit -> Settings -> Developer -> API keys.

Create or update a subscriber:

import requests

KIT_API_KEY = "your_kit_api_key"
KIT_BASE_URL = "https://api.kit.com/v4"

headers = {
    "Authorization": f"Bearer {KIT_API_KEY}",
    "Content-Type": "application/json"
}

def upsert_subscriber(email: str, first_name: str, custom_fields: dict) -> dict:
    # Kit v4 upsert: if a subscriber with this email exists - updates; if not - creates
    resp = requests.post(
        f"{KIT_BASE_URL}/subscribers",
        headers=headers,
        json={
            "email_address": email,
            "first_name": first_name,
            "fields": custom_fields,
            "state": "active"
        }
    )
    resp.raise_for_status()
    return resp.json()

def add_tag_to_subscriber(subscriber_id: str, tag_id: str) -> None:
    resp = requests.post(
        f"{KIT_BASE_URL}/tags/{tag_id}/subscribers",
        headers=headers,
        json={"subscriber_id": subscriber_id}
    )
    resp.raise_for_status()

def add_to_sequence(subscriber_id: str, sequence_id: str) -> None:
    resp = requests.post(
        f"{KIT_BASE_URL}/sequences/{sequence_id}/subscribers",
        headers=headers,
        json={"subscriber_id": subscriber_id}
    )
    resp.raise_for_status()

def on_deal_won(lead: dict, contact: dict):
    email = get_contact_email(contact)
    first_name = contact["name"].split()[0]
    plan = get_custom_field(lead, PLAN_FIELD_ID) or "starter"
    source = get_custom_field(lead, SOURCE_FIELD_ID) or "unknown"

    subscriber = upsert_subscriber(email, first_name, {
        "plan": plan,
        "kommo_deal_id": str(lead["id"]),
        "source": source
    })
    subscriber_id = subscriber["subscriber"]["id"]

    # Tags from a dict: tag name -> tag_id (retrieve from Kit API in advance)
    tags_to_add = [TAG_IDS["customer"], TAG_IDS.get(f"plan_{plan}")]
    for tag_id in tags_to_add:
        if tag_id:
            add_tag_to_subscriber(subscriber_id, tag_id)

    # Add to onboarding sequence
    add_to_sequence(subscriber_id, ONBOARDING_SEQUENCE_ID)

    create_kommo_note(
        lead["id"],
        f"Kit: subscriber added (tags: customer, plan:{plan}), "
        f"onboarding sequence started"
    )

Retrieve tag and sequence IDs:

def get_tags() -> dict:
    resp = requests.get(f"{KIT_BASE_URL}/tags", headers=headers)
    resp.raise_for_status()
    tags = resp.json().get("tags", [])
    return {tag["name"]: tag["id"] for tag in tags}

def get_sequences() -> dict:
    resp = requests.get(f"{KIT_BASE_URL}/sequences", headers=headers)
    resp.raise_for_status()
    seqs = resp.json().get("sequences", [])
    return {seq["name"]: seq["id"] for seq in seqs}

Handling Kit Webhooks:

from flask import Flask, request

app = Flask(__name__)

@app.route("/webhooks/kit", methods=["POST"])
def kit_webhook():
    # Kit webhook has no built-in HMAC verification in v4
    # Protection: secret path + IP whitelist
    payload = request.json
    event = payload.get("type")
    subscriber = payload.get("subscriber", {})
    email = subscriber.get("email_address")

    if not email:
        return "", 200

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

    if event == "subscriber.unsubscribed":
        create_kommo_note(deal_id, "Kit: customer unsubscribed from email list")
        create_kommo_task(deal_id,
            "Clarify communication preferences - customer unsubscribed from Kit")

    elif event == "subscriber.bounced":
        create_kommo_note(deal_id, "Kit: email not delivered (bounce)")
        create_kommo_task(deal_id, "Verify and update customer email - Kit recorded a bounce")

    return "", 200

Kit Webhook setup: Settings -> Developer -> Webhooks -> Add endpoint. In Kit v4, verification is via secret path + IP whitelist (Kit publishes the list of IP addresses).

Tag segmentation strategy: mapping

Tags in Kit are the primary segmentation mechanism. A well-thought-out mapping from Kommo:

Event in KommoTag in Kit
Won (any plan)customer
Won, Growth planplan_growth
Won, Scale planplan_scale
Source = LinkedInsource_linkedin
Churn (customer lost)churned
Upgradeupgraded, remove plan_growth, add plan_scale

Tags are atomic: added/removed via separate API calls. They merge rather than overwrite. This allows building Kit automations with logic such as “has tag X AND tag Y -> campaign Z”.

Real-world case

Creator-SaaS (US, educational platform, Kommo + Kit, 60–80 new customers per month):

  • Before: a marketer exported the Won list from Kommo once a day and imported it into Kit. The first email was delayed by 24–48 hours. Tags were applied manually — errors were common.
  • After: Won -> Kit subscriber + tags + onboarding sequence within 5 seconds. First welcome email — within an hour of closing the deal.
  • Additionally: subscriber.bounced -> task to update email -> saved 8% of contacts with incorrect emails in the first month.

Who this is relevant for

  • SaaS and course businesses with email onboarding through Kit
  • Teams where the marketer works in Kit and sales in Kommo — two separate systems without a connection
  • 30+ new customers per month — manual sync creates delays and errors
  • Products with multiple plans — tag segmentation for each plan

Frequently asked questions

Kit or Mailchimp for Kommo integration?

Mailchimp integration works through audiences and tags. Kit works exclusively through tags — more flexible segmentation for complex onboarding scenarios. Mailchimp is better for mass campaigns to base segments. Kit is better for sequences triggered by events.

How do I find tag and sequence IDs in Kit?

GET /v4/tags — list of tags with IDs. GET /v4/sequences — list of sequences. We recommend saving the name -> id mapping to config during initial setup — IDs are stable and do not change.

Kit v3 vs v4 API?

Kit v4 (current): https://api.kit.com/v4/, Bearer token. Kit v3 (legacy): https://api.convertkit.com/v3/, api_key as a query param. New integrations should only use v4 — v3 is supported but not being developed further.

How do I remove tags when a stage changes in Kommo?

DELETE /v4/tags/{tag_id}/subscribers/{subscriber_id} — removes a specific tag from a subscriber. For a plan change: delete plan_growth, add plan_scale. Implemented via a PATCH /api/v4/leads webhook triggered when the “Plan” custom field changes.

Summary

  • Kit API v4: Bearer token, https://api.kit.com/v4/
  • Upsert subscriber: POST /subscribers (by email)
  • Add tag: POST /tags/{tag_id}/subscribers
  • Add to sequence: POST /sequences/{sequence_id}/subscribers
  • Webhook: subscriber.unsubscribed, subscriber.bounced — no HMAC, protection via secret URL
  • Tag strategy: map plan/source/status -> Kit tags for correct segmentation

If you use Kit and Kommo and want to automate onboarding by pipeline stage — describe your plan structure and sequences. Exceltic.dev will configure the mapping and webhook handler.

More articles

All →