Kommo + Hive: Automatic Onboarding Project Creation When a Deal Is Won

Hive is a project management tool with a REST API for creating projects and tasks programmatically. For companies that sell through Kommo and deliver through Hive, the integration closes a common gap: a deal is won in the CRM, but the project manager manually creates an onboarding project from scratch. With 10+ new clients per month, this becomes a systematic waste of time.

The Hive API uses api_key as a query parameter (not a Bearer token). The main endpoints are: POST /workspaces/{workspaceId}/projects to create a project, and POST /workspaces/{workspaceId}/actions to create a task (in Hive, a task is called an “action”). The Workspace ID and User ID for the assignee are taken from Hive settings.

Hive is a project management platform with analytics, time tracking, and a portfolio mode. It is popular with agencies and teams of 10+ people running multiple client projects in parallel. Unlike Asana and ClickUp, it has built-in portfolio analytics and resource management out of the box.

The Problem Without Integration

When a deal closes in Kommo, the project manager receives a notification in Slack or email. They open Hive, create a project, add 5-8 standard onboarding tasks, assign owners, and set deadlines. This takes 20-30 minutes. Multiply by 15 new clients per month and that is 5-8 hours of routine work.

Standard project templates in Hive are not connected to the CRM: the project name is typed by hand, client data is copied manually, and deadlines are recalculated from scratch every time.

Architecture

Kommo: deal -> Closed Won
  -> Kommo webhook leads.status.changed
  -> Your server

Your server:
  -> GET Kommo: deal name, contact, email, amount
  -> POST /workspaces/{id}/projects -> projectId
  -> POST /workspaces/{id}/actions (x5) -> create onboarding tasks
  -> Kommo: note "Hive project created: #{projectId}"

Implementation

import requests, os
from datetime import datetime, timedelta
from flask import Flask, request, jsonify

app = Flask(__name__)

HIVE_API_KEY     = os.environ["HIVE_API_KEY"]
HIVE_WORKSPACE   = os.environ["HIVE_WORKSPACE_ID"]
HIVE_BASE        = "https://app.hive.com/api/v1"
HIVE_PM_USER_ID  = os.environ["HIVE_PM_USER_ID"]  # ID PM в Hive

KOMMO_SUBDOMAIN  = os.environ["KOMMO_SUBDOMAIN"]
KOMMO_TOKEN      = os.environ["KOMMO_ACCESS_TOKEN"]
CLOSED_WON_ID    = int(os.environ["KOMMO_CLOSED_WON_ID"])

KOMMO_BASE = f"https://{KOMMO_SUBDOMAIN}.kommo.com/api/v4"
KOMMO_HDR  = {"Authorization": f"Bearer {KOMMO_TOKEN}",
              "Content-Type": "application/json"}

def hive_params():
    return {"api_key": HIVE_API_KEY}

def create_project(name: str, description: str) -> str:
    r = requests.post(
        f"{HIVE_BASE}/workspaces/{HIVE_WORKSPACE}/projects",
        params=hive_params(),
        json={
            "name":        name,
            "description": description,
        },
    )
    r.raise_for_status()
    return r.json()["id"]

def create_action(title: str, project_id: str,
                  due_days: int = 7) -> str:
    due_date = (datetime.now() + timedelta(days=due_days)).strftime("%Y-%m-%d")
    r = requests.post(
        f"{HIVE_BASE}/workspaces/{HIVE_WORKSPACE}/actions",
        params=hive_params(),
        json={
            "title":      title,
            "projectId":  project_id,
            "assigneeId": HIVE_PM_USER_ID,
            "dueDate":    due_date,
            "status":     "NOT_STARTED",
        },
    )
    r.raise_for_status()
    return r.json()["id"]

def get_lead_contact(lead_id: int) -> tuple:
    r = requests.get(
        f"{KOMMO_BASE}/leads/{lead_id}",
        headers=KOMMO_HDR,
        params={"with": "contacts"},
    )
    lead = r.json()
    contacts = lead.get("_embedded", {}).get("contacts", [])
    contact = {}
    if contacts:
        rc = requests.get(
            f"{KOMMO_BASE}/contacts/{contacts[0]['id']}",
            headers=KOMMO_HDR,
            params={"with": "custom_fields_values"},
        )
        contact = rc.json()
    return lead, contact

def get_email(contact: dict) -> str:
    for cf in contact.get("custom_fields_values", []) or []:
        if cf.get("field_code") == "EMAIL":
            vals = cf.get("values", [])
            return vals[0].get("value", "") if vals else ""
    return ""

def add_note(lead_id: int, text: str):
    requests.post(
        f"{KOMMO_BASE}/notes",
        headers=KOMMO_HDR,
        json=[{"entity_id": lead_id, "entity_type": "leads",
               "note_type": "common", "params": {"text": text}}],
    )

ONBOARDING_TASKS = [
    ("Send welcome email to client", 1),
    ("Conduct kickoff call", 3),
    ("Set up product access / provide credentials", 5),
    ("Onboarding session: overview of key features", 7),
    ("Review client's first results", 14),
    ("Request feedback after 30 days", 30),
]

@app.route("/webhooks/kommo", methods=["POST"])
def kommo_webhook():
    data = request.json or {}
    for lead_data in data.get("leads", {}).get("status", []):
        lead_id    = lead_data.get("id")
        new_status = lead_data.get("status_id")
        if new_status != CLOSED_WON_ID:
            continue

        lead, contact = get_lead_contact(lead_id)
        deal_name   = lead.get("name", f"Deal #{lead_id}")
        contact_name = contact.get("name", "")
        email        = get_email(contact)
        amount       = lead.get("price", 0)

        project_name = f"Onboarding: {contact_name or deal_name}"
        description  = (
            f"Deal: {deal_name}\n"
            f"Contact: {contact_name} ({email})\n"
            f"Amount: {amount}\n"
            f"Kommo Deal ID: {lead_id}"
        )

        project_id = create_project(project_name, description)

        action_ids = []
        for task_title, due_days in ONBOARDING_TASKS:
            action_id = create_action(task_title, project_id, due_days)
            action_ids.append(action_id)

        add_note(
            lead_id,
            f"Hive project created: {project_name}\n"
            f"Project ID: {project_id}\n"
            f"Created {len(action_ids)} onboarding tasks."
        )

    return jsonify({"status": "ok"}), 200

Getting Workspace ID and User ID

# Workspace ID:
r = requests.get(
    f"{HIVE_BASE}/workspaces",
    params={"api_key": HIVE_API_KEY},
)
workspaces = r.json()
# [{"id": "ws_xxx", "name": "My Company", ...}, ...]

# User ID (PM for task assignment):
r = requests.get(
    f"{HIVE_BASE}/workspaces/{HIVE_WORKSPACE}/users",
    params={"api_key": HIVE_API_KEY},
)
users = r.json()
# [{"id": "usr_xxx", "email": "pm@company.com", ...}, ...]

Run this once locally and save the required IDs to environment variables.

Customizing the Task Template

The ONBOARDING_TASKS list is a template. You can use different templates for different deal types:

TEMPLATES = {
    "saas":       [...],  # SaaS onboarding
    "consulting": [...],  # Consulting project
    "agency":     [...],  # Agency deliverables
}

def get_template(lead: dict) -> list:
    # Determine type from deal's custom field or pipeline name
    pipeline_id = lead.get("pipeline_id", 0)
    if pipeline_id == int(os.environ.get("SAAS_PIPELINE_ID", "0")):
        return TEMPLATES["saas"]
    return TEMPLATES["consulting"]

Who This Is For

Agencies, consulting firms, and B2B SaaS companies where winning a deal kicks off a structured delivery process. Especially relevant for teams of 10+ where PM and sales are separate roles and handing off information about a new client takes dedicated time. Hive is popular with companies that need portfolio mode: seeing all active onboarding projects in one place with a timeline.

Other task integrations: Kommo + Shortcut (engineering tasks), Kommo + Fibery (linked projects), Kommo + Linear (dev tasks).

Frequently Asked Questions

Does the Hive API support nested tasks (subtasks)?

Yes. After creating an action, create a subaction: POST /workspaces/{id}/actions/{actionId}/subactions with similar fields. Nesting depth is 1 level (action -> subaction).

How do I assign different owners to different tasks?

Pass a different assigneeId for each create_action call. To list workspace users: GET /workspaces/{id}/users. You can store a role-to-userId mapping in your configuration and assign automatically.

Does Hive have webhooks to notify Kommo when a project is completed?

Hive supports basic integrations via Zapier/Make, but there is no native webhook API for custom events. To monitor project status, use periodic polling: GET /workspaces/{id}/projects/{projectId} and check the status field.

Can projects be created from Hive templates via the API?

As of Q2 2026, the Hive API does not support creating projects from templates directly. The workaround is to store the template task list in your own code and create the tasks via the API, as shown above.

Summary

Kommo + Hive - automatic onboarding project on Closed Won:

  • api_key query parameter (not Bearer token)
  • POST /workspaces/{id}/projects -> projectId
  • POST /workspaces/{id}/actions with dueDate, assigneeId, status
  • Task template in code: different tasks for different deal types
  • Workspace ID and User ID - retrieve once via API, store in env

If your team wants to automate data transfer from Kommo to Hive, describe your requirements to the Exceltic.dev team.

More articles

All →