Kommo + Mailchimp: contact synchronization and segmentation by pipeline stage

Kommo + Mailchimp: contact synchronization and segmentation by pipeline stage

Mailchimp is the world’s most widely used email marketing platform, with a REST API v3 supporting audiences, tags, merge fields, and webhooks. Without a Kommo integration, the marketer manually exports contacts from the CRM and uploads them to Mailchimp — losing freshness and context (which pipeline stage the contact is at, whether it is a Won or a lost deal). With the integration, contacts sync automatically and tags update based on pipeline events.

What the Kommo + Mailchimp connection delivers

Without integration:
— Marketer exports a CSV from Kommo once a week and uploads to Mailchimp
— No current pipeline stage data — campaigns are not segmented
— Won clients receive sales emails that are no longer relevant
— Mailchimp unsubscribes are not reflected in Kommo — a manager calls someone who opted out

With integration:
— Contact created in Kommo -> automatically added to Mailchimp audience
— Deal Won -> tag “Customer” -> removed from nurture sequences, moved to onboarding
— Deal Lost -> tag “Lost Lead” -> win-back campaign after 30 days
— Mailchimp unsubscribe -> Note in Kommo so the manager knows not to make a sales call

What is synchronized

Kommo -> Mailchimp:
— Email, name, company of contact -> member in Mailchimp audience
— Merge fields: phone, company, lead source
— Tags by event: New Lead, In Progress, Customer, Lost Lead
— Subscription status: subscribed / unsubscribed (based on consent in Kommo)

Mailchimp -> Kommo:
— Webhook unsubscribe -> Note in Kommo deal: “Contact unsubscribed from mailing list”
— Webhook campaign_open / click (optional) -> Note with activity

Architecture

Kommo Webhook: contact or deal created/updated
  ↓ Backend
  1. GET /api/v4/contacts/{id}
     -> email, name, company, custom fields
  2. Mailchimp API: PUT /lists/{list_id}/members/{subscriber_hash}
     -> upsert by email hash (MD5)
     -> merge_fields + tags

Kommo Webhook: deal status changed (Won/Lost)
  ↓ Backend
  1. Retrieve contact email
  2. Mailchimp API: POST /lists/{list_id}/members/{hash}/tags
     -> add/remove tags based on event

Mailchimp Webhook: unsubscribe
  ↓ Backend
  1. Find Kommo contact by email
  2. Kommo: POST /contacts/{id}/notes
     -> "Unsubscribed from email list {date}"

Mailchimp API v3: key requests

Base URL: https://{server}.api.mailchimp.com/3.0/ (server = dc suffix from the API key, e.g. us14). Authentication: Basic Auth, username = anystring, password = api_key.

Upsert contact in audience:

import hashlib
import requests
from requests.auth import HTTPBasicAuth

MC_API_KEY = 'your_api_key'
MC_SERVER = 'us14'  # last characters from API key
LIST_ID = 'your_audience_id'
BASE = f'https://{MC_SERVER}.api.mailchimp.com/3.0'
AUTH = HTTPBasicAuth('anystring', MC_API_KEY)

def get_subscriber_hash(email: str) -> str:
    return hashlib.md5(email.lower().encode()).hexdigest()

def upsert_contact(email: str, first_name: str, last_name: str,
                   company: str, phone: str = '', tags: list = None):
    sub_hash = get_subscriber_hash(email)
    payload = {
        'email_address': email,
        'status_if_new': 'subscribed',  # applies to new contacts only, does not overwrite unsubscribe
        'merge_fields': {
            'FNAME': first_name,
            'LNAME': last_name,
            'COMPANY': company,
            'PHONE': phone
        }
    }
    resp = requests.put(
        f'{BASE}/lists/{LIST_ID}/members/{sub_hash}',
        auth=AUTH,
        json=payload
    )
    # Add tags if provided
    if tags:
        add_tags(email, tags)
    return resp.status_code

Managing tags:

def add_tags(email: str, tags_to_add: list, tags_to_remove: list = None):
    sub_hash = get_subscriber_hash(email)
    tag_payload = []
    for tag in (tags_to_add or []):
        tag_payload.append({'name': tag, 'status': 'active'})
    for tag in (tags_to_remove or []):
        tag_payload.append({'name': tag, 'status': 'inactive'})

    requests.post(
        f'{BASE}/lists/{LIST_ID}/members/{sub_hash}/tags',
        auth=AUTH,
        json={'tags': tag_payload}
    )

# On Won event in Kommo:
add_tags(client_email,
         tags_to_add=['Customer'],
         tags_to_remove=['New Lead', 'In Progress'])

# On Lost event:
add_tags(client_email,
         tags_to_add=['Lost Lead'],
         tags_to_remove=['In Progress'])

Handle Mailchimp webhook (unsubscribe):

from flask import Flask, request

app = Flask(__name__)

@app.route('/webhooks/mailchimp', methods=['POST'])
def mailchimp_webhook():
    event_type = request.form.get('type')
    email = request.form.get('data[email]')

    if event_type == 'unsubscribe' and email:
        # Find contact in Kommo by email
        contact = find_kommo_contact_by_email(email)
        if contact:
            note_text = (
                f'Contact unsubscribed from Mailchimp mailing list. '
                f'Do not include in email campaigns.'
            )
            create_kommo_note(contact['id'], note_text)

    return '', 200

Mailchimp webhook setup: Mailchimp requires a URL that returns 200 on a GET request (verification). Webhooks are configured in the UI: Audience -> Manage Audience -> Settings -> Webhooks.

Merge Fields: custom fields in Mailchimp

By default a Mailchimp audience contains FNAME, LNAME. For Kommo data, custom fields need to be added:

# Create a merge field via API
requests.post(
    f'{BASE}/lists/{LIST_ID}/merge-fields',
    auth=AUTH,
    json={
        'tag': 'COMPANY',
        'name': 'Company Name',
        'type': 'text',
        'required': False
    }
)

After creation, the field is available as COMPANY in merge_fields during upsert.

Real-world case

B2B SaaS company (EU market, 200–300 new leads per month, mixed SEO + paid traffic):

  • Before: the marketer manually exported new leads from Kommo every two weeks and uploaded them to Mailchimp. Won clients remained in nurture sequences for 3–4 weeks after purchase.
  • After: every new contact in Kommo -> in Mailchimp within 5 minutes with the tag New Lead. Won -> tag Customer, removed from nurture. Win-back campaign automatically starts 30 days after Lost.
  • Additionally: managers see a Note in Kommo when a contact opens an email — an additional signal to make a call.

If more powerful behavioral segmentation is needed — consider Klaviyo. Mailchimp works best for simple tag-based segmentation; Klaviyo is better for multi-level event triggers.

Who this is relevant for

  • Using Mailchimp as the primary email platform
  • 50+ new contacts per month from Kommo
  • Marketing wants to segment by pipeline stage (nurture for prospects, onboarding for clients)
  • Two-way sync is needed: Mailchimp unsubscribes visible to managers in Kommo

Frequently asked questions

Mailchimp API — how do I find the server prefix for the URL?

The server prefix (e.g. us14) is the last characters of your API key after the hyphen: abc123def456-us14. It is also shown in the Mailchimp URL when you log in: https://us14.admin.mailchimp.com/.

What is a subscriber hash and why is it needed?

A subscriber hash is the MD5 hash of the email address in lowercase. Mailchimp uses it as an identifier in the URL instead of the email (to avoid passing email in the path). hashlib.md5(email.lower().encode()).hexdigest() — the standard way to obtain it in Python.

Does Mailchimp overwrite an unsubscribe on PUT?

No — the status_if_new parameter applies only to new contacts. An existing unsubscribed contact will not be re-subscribed via PUT. This protects against accidentally re-subscribing opted-out contacts.

Mailchimp vs ActiveCampaign for Kommo integration?

Mailchimp — simpler, cheaper ($0–13/month for small lists), adequate for tags and basic automation. ActiveCampaign — more complex, more expensive, but more powerful in automation and CRM features. For B2B SaaS with a long deal cycle — ActiveCampaign + Kommo offers more capabilities.

Yes. For EU contacts, explicit consent is required before adding to Mailchimp. In Kommo this is handled via a custom field “Email consent” — the integration checks it before upsert and sets status = subscribed only when consent is present.

Summary

  • Mailchimp API v3: Basic Auth with API key, PUT /lists/{id}/members/{hash} for upsert, POST /tags for segmentation
  • Tags by Kommo events: New Lead -> In Progress -> Customer / Lost Lead
  • Mailchimp webhook on unsubscribe -> Note in Kommo for the manager
  • Merge fields are created via API for custom data from CRM
  • Typical development timeline — 1–2 weeks

If you use Mailchimp and Kommo and want to automate contact synchronization — describe your audience structure and which CRM data fields are needed. Exceltic.dev will configure the mapping and handle all pipeline events.

More articles

All →