Kommo + Notion: Auto-Create a Project Page When a Deal is Won

The moment a deal is closed in Kommo is the handoff point from sales to the delivery team. This is where context most often gets lost: the account executive closes the deal, but the project manager in Notion creates the project manually, chases details over Slack, and spends the first day of onboarding gathering information that already exists in the CRM.

Notion is a popular project management tool for development teams, consulting firms, and digital agencies. If your sales pipeline runs in Kommo and your projects are tracked in Notion, the gap between closing a deal and starting a project is unavoidable without automation.

This article shows how to automatically create a project page in a Notion Database - complete with all the relevant fields (client name, deal value, contact, responsible manager, start date) - the moment a deal moves to the “Successfully Completed” stage in Kommo.

Why There Is No Native Integration

Notion does not have an official integration with Kommo. The Notion integrations gallery includes Slack, Figma, GitHub, and a handful of others - CRM systems are absent. Zapier supports both Kommo and Notion, but a Zapier chain is unreliable for structured data transfers: there is no type control, no error handling, and no idempotency.

In 2025, the Notion API gained support for webhook subscriptions to track changes in databases. This opened the door to a full two-way integration: Kommo -> Notion when a deal is won, and Notion -> Kommo when a project status is updated.

Setup: Notion Database Structure

Create a “Projects” database in Notion with the following properties:

PropertyTypePurpose
NametitleProject / client name
StatusselectStatus: Backlog, In Progress, Completed
Deal ValuenumberDeal amount (from Kommo)
Start DatedateDate the deal was closed
Managerrich_textName of the responsible manager
Kommo Lead IDrich_textDeal ID for reverse lookup
Client EmailemailPrimary contact email

The database ID is the 32-character string in the Notion page URL: notion.so/workspace/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX?v=...

Implementation

Step 1 - Handle the Kommo webhook when a deal is won:

from flask import Flask, request
import requests

app = Flask(__name__)

NOTION_TOKEN = "secret_..."   # Internal Integration Token from Notion Developers
NOTION_DB_ID = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"  # ID of your "Projects" database
NOTION_BASE  = "https://api.notion.com/v1"

KOMMO_DOMAIN = "yourdomain.kommo.com"
KOMMO_TOKEN  = "your_kommo_token"
KOMMO_BASE   = f"https://{KOMMO_DOMAIN}/api/v4"

KOMMO_WON_STATUS = 142  # "Successfully Completed" in Kommo (standard ID)

def notion_headers():
    return {
        "Authorization":  f"Bearer {NOTION_TOKEN}",
        "Content-Type":   "application/json",
        "Notion-Version": "2022-06-28"
    }

@app.route("/kommo/webhook", methods=["POST"])
def kommo_webhook():
    data = request.json
    leads = data.get("leads", {})
    for items in leads.get("status_changed", []):
        if items.get("status_id") == KOMMO_WON_STATUS:
            handle_deal_won(items.get("id"))
    return "ok", 200

Step 2 - Fetch deal details from Kommo:

def get_lead_details(lead_id: int) -> dict:
    hs = requests.Session()
    hs.headers.update({
        "Authorization": f"Bearer {KOMMO_TOKEN}",
        "Content-Type":  "application/json"
    })

    r = hs.get(f"{KOMMO_BASE}/leads/{lead_id}",
               params={"with": "contacts,responsible_user"})
    lead = r.json()

    # Responsible manager name
    responsible_id = lead.get("responsible_user_id")
    r2 = hs.get(f"{KOMMO_BASE}/users/{responsible_id}")
    manager_name = r2.json().get("name", "") if r2.status_code == 200 else ""

    # Primary contact email
    contacts = lead.get("_embedded", {}).get("contacts", [])
    client_email = ""
    client_name  = ""
    if contacts:
        c = contacts[0]
        client_name = c.get("name", "")
        for field in c.get("custom_fields_values") or []:
            if field.get("field_type") == "EMAIL":
                vals = field.get("values", [])
                if vals:
                    client_email = vals[0].get("value", "")
                    break

    import datetime
    return {
        "lead_id":     lead_id,
        "name":        lead.get("name", ""),
        "deal_value":  lead.get("price", 0),
        "manager":     manager_name,
        "client_email": client_email,
        "client_name":  client_name,
        "start_date":   datetime.date.today().isoformat(),
    }

Step 3 - Create a page in the Notion Database:

def create_notion_project(details: dict) -> str:
    """Create project page in Notion Database. Returns Notion page ID."""
    n = requests.Session()
    n.headers.update(notion_headers())

    project_name = (
        f"{details['client_name']} - {details['name']}"
        if details["client_name"] else details["name"]
    )

    payload = {
        "parent":     {"database_id": NOTION_DB_ID},
        "properties": {
            "Name": {
                "title": [{"text": {"content": project_name}}]
            },
            "Status": {
                "select": {"name": "Backlog"}
            },
            "Deal Value": {
                "number": details["deal_value"]
            },
            "Start Date": {
                "date": {"start": details["start_date"]}
            },
            "Manager": {
                "rich_text": [{"text": {"content": details["manager"]}}]
            },
            "Kommo Lead ID": {
                "rich_text": [{"text": {"content": str(details["lead_id"])}}]
            },
            "Client Email": {
                "email": details["client_email"] or None
            }
        }
    }

    r = n.post(f"{NOTION_BASE}/pages", json=payload)
    r.raise_for_status()
    return r.json()["id"]

def handle_deal_won(lead_id: int):
    details    = get_lead_details(lead_id)
    notion_id  = create_notion_project(details)

    # Save the Notion page link back to Kommo
    notion_url = f"https://notion.so/{notion_id.replace('-', '')}"

    hs = requests.Session()
    hs.headers.update({
        "Authorization": f"Bearer {KOMMO_TOKEN}",
        "Content-Type":  "application/json"
    })
    hs.post(f"{KOMMO_BASE}/leads/notes", json=[{
        "entity_id": lead_id,
        "note_type":  "common",
        "params":     {"text": f"Project created in Notion: {notion_url}"}
    }])

Reverse Sync: Notion -> Kommo

Since 2025, Notion supports webhook subscriptions via the API. Subscribe to property changes on pages in the “Projects” database:

def setup_notion_webhook(endpoint_url: str) -> str:
    """Register Notion webhook for project database changes."""
    n = requests.Session()
    n.headers.update(notion_headers())

    r = n.post(f"{NOTION_BASE}/webhooks", json={
        "url":    endpoint_url,
        "filter": {
            "type":        "database_id",
            "database_id": NOTION_DB_ID
        },
        "events": ["page.updated"]
    })
    return r.json().get("id", "")

@app.route("/notion/webhook", methods=["POST"])
def notion_webhook():
    event   = request.json
    page_id = event.get("page_id", "")
    if not page_id:
        return "ok", 200

    # Fetch the current page properties
    n = requests.Session()
    n.headers.update(notion_headers())
    r = n.get(f"{NOTION_BASE}/pages/{page_id}")
    page = r.json()
    props = page.get("properties", {})

    # Extract Kommo Lead ID
    lead_texts = props.get("Kommo Lead ID", {}).get("rich_text", [])
    if not lead_texts:
        return "ok", 200
    lead_id = lead_texts[0].get("plain_text", "")

    # Current project status
    status = props.get("Status", {}).get("select", {}).get("name", "")

    # Add a note in Kommo about the status change
    hs = requests.Session()
    hs.headers.update({
        "Authorization": f"Bearer {KOMMO_TOKEN}",
        "Content-Type":  "application/json"
    })
    hs.post(f"{KOMMO_BASE}/leads/notes", json=[{
        "entity_id": int(lead_id),
        "note_type":  "common",
        "params":     {"text": f"Notion project: status changed to '{status}'"}
    }])
    return "ok", 200

Real-World Case

A digital agency closing 15-20 deals per month with projects tracked in Notion. Before the integration, the PM spent 30-60 minutes on every new project - requesting data from the account manager, creating the page, and filling in the fields. Errors in client names and deal amounts had to be corrected manually.

After the integration:

  • The Notion page is created automatically within 2-3 seconds of the deal being closed
  • All data from Kommo (client, amount, manager) is populated correctly
  • The PM receives a Notion notification and can start planning immediately
  • The link to the Notion project is saved in Kommo for reverse lookup

Development time: 1 day. The critical edge case is handling duplicates when webhook events are delivered more than once - solved by checking for an existing Kommo Lead ID before creating a new page.

Who This Is For

Agencies, consulting firms, and development teams - anyone running sales in Kommo and projects in Notion. The key indicator: you have a clear handoff moment between sales and delivery.

A similar integration for project management is described in Kommo + Rocketlane - a dedicated platform for client onboarding - and Kommo + GanttPRO for Gantt-based planning.

Frequently Asked Questions

Which Notion plans support the API and webhooks?

The Notion API is available on all plans including Free (with rate limits on API requests). Webhooks are available on paid plans (Plus and above). For team use, the minimum plan is Plus ($8/user/month as of 2026).

How do I prevent duplicates if a webhook fires multiple times?

Add a check before creating: query the Notion Database with a filter Kommo Lead ID = {lead_id}. If the page already exists, skip creation. This makes the operation idempotent. The Notion API supports filters via POST /v1/databases/{id}/query.

Can I create sub-pages or use templates in Notion via the API?

Yes. The Notion API supports POST /v1/blocks for creating content blocks (text, headings, lists) inside a page. For template-based creation - Notion templates cannot be exported via the API directly, but you can duplicate an existing page via POST /v1/pages with the desired block structure.

If a client has multiple deals in Kommo that should all be tracked under one Notion project, add logic to search for an existing project by client email or company name before creating a new one. If found, update the existing project’s properties via PATCH /v1/pages/{id}.

Summary

The Kommo + Notion integration closes the gap between sales and delivery. The flow:

  • Kommo webhook status_changed with won status -> POST /v1/pages to Notion Database
  • Deal data (client, amount, manager, date) populates the page properties
  • A link to the Notion project is saved in Kommo as a note
  • (Optional) Notion webhook page.updated -> a note in Kommo about the status change

If your team closes deals in Kommo and manages projects in Notion, describe your setup to the Exceltic.dev team. We will tailor the integration to your database structure and pipeline.

More articles

All →