Kommo + Smartsheet: автоматическое создание проектов из выигранных сделок

Kommo + Smartsheet: автоматическое создание проектов из выигранных сделок

Smartsheet — enterprise work management платформа которая сочетает привычный интерфейс электронных таблиц с возможностями project management: Gantt-диаграммы, управление ресурсами, автоматизации, отчётность и интеграции. Популярна в enterprise где команды привыкли к Excel, но нуждаются в коллаборации и трекинге прогресса. В отличие от Jira или Asana, Smartsheet ближе к таблице-проекту — незаменима там где данные проекта нужно видеть в табличном виде и экспортировать в Excel. Без интеграции с Kommo Won данные сделки переносятся в Smartsheet вручную — 15–20 минут с высоким риском ошибок.

Smartsheet vs Jira vs Asana для команд внедрения

ПараметрSmartsheetJiraAsana
Целевая аудиторияEnterprise non-techTech командыMixed teams
Табличный интерфейсДа (основной)НетНет
GanttВстроенныйPluginВстроенный
Ресурсное планированиеДаОграниченноДа (Business+)
Подходит дляOperations, PMO, constructionРазработкаМаркетинг, продукт

Smartsheet выбирают компании где PM-команда работает в таблицах, а не в канбан-досках — professional services, строительство, enterprise IT.

Архитектура: что синхронизируется

Kommo -> Smartsheet:
— Won -> создать новую строку в Sheet проекта (название, клиент, сумма, PM, даты)
— Won -> создать дочерние строки (checklist задач) из стандартного шаблона

Smartsheet -> Kommo:
— Изменение статуса строки (In Progress -> Complete) -> Note в сделку
— Webhook на ячейку «Дата сдачи» -> Task в Kommo за 7 дней до дедлайна

Smartsheet REST API: создание строки в Sheet

Base URL: https://api.smartsheet.com/2.0. Аутентификация: Authorization: Bearer {token} (Personal Access Token из Smartsheet -> Account -> Apps & Integrations -> API Access -> Generate new access token).

import requests

SS_TOKEN   = "your_personal_access_token"
SS_BASE    = "https://api.smartsheet.com/2.0"
SS_HEADERS = {
    "Authorization": f"Bearer {SS_TOKEN}",
    "Content-Type":  "application/json",
}
SS_SHEET_ID = "your_sheet_id"  # из URL sheet: smartsheet.com/sheets/{ID}

def get_sheet_columns(sheet_id: str) -> dict:
    # Получить маппинг column title -> column id
    resp = requests.get(
        f"{SS_BASE}/sheets/{sheet_id}",
        headers=SS_HEADERS,
    )
    resp.raise_for_status()
    cols = resp.json().get("columns", [])
    return {col["title"]: col["id"] for col in cols}

def add_row_to_sheet(sheet_id: str, col_map: dict,
                     row_data: dict, parent_row_id: int = None) -> dict:
    cells = [
        {"columnId": col_map[key], "value": value}
        for key, value in row_data.items()
        if key in col_map
    ]
    row = {"cells": cells, "toBottom": True}
    if parent_row_id:
        row["parentId"] = parent_row_id

    resp = requests.post(
        f"{SS_BASE}/sheets/{sheet_id}/rows",
        headers=SS_HEADERS,
        json={"rows": [row]},
    )
    resp.raise_for_status()
    return resp.json()

# Стандартный checklist задач после Won
ONBOARDING_TASKS = [
    "Kickoff call с клиентом",
    "Сбор требований",
    "Настройка окружения",
    "Первая поставка",
    "UAT (приёмочное тестирование)",
    "Go-live",
    "Финальная документация",
]

def on_kommo_deal_won(lead: dict, contact: dict):
    company  = get_custom_field(lead, COMPANY_FIELD_ID) or contact.get("name", "")
    manager  = get_custom_field(lead, PM_FIELD_ID) or ""
    value    = lead.get("price", 0)
    col_map  = get_sheet_columns(SS_SHEET_ID)

    row_data = {
        "Project Name":   f"{company} - {lead.get('name', '')}",
        "Client":         company,
        "Deal Value":     value,
        "PM":             manager,
        "Status":         "Not Started",
        "Start Date":     "",
        "CRM Deal ID":    str(lead["id"]),
    }
    result  = add_row_to_sheet(SS_SHEET_ID, col_map, row_data)
    parent_id = result["result"][0]["id"]

    for task in ONBOARDING_TASKS:
        add_row_to_sheet(SS_SHEET_ID, col_map,
                         {"Project Name": task, "Status": "Not Started"},
                         parent_row_id=parent_id)

    sheet_url = f"https://app.smartsheet.com/sheets/{SS_SHEET_ID}"
    save_to_kommo_deal(lead["id"], {"smartsheet_row_id": parent_id})
    create_kommo_note(
        lead["id"],
        f"Smartsheet: проект создан, {len(ONBOARDING_TASKS)} задач -> {sheet_url}",
    )

Webhook: Smartsheet -> Kommo

Smartsheet поддерживает webhooks на изменение ячеек. Настройка: POST /webhooks с callbackUrl и scopeObjectId (Sheet ID).

def create_smartsheet_webhook(sheet_id: str, callback_url: str) -> dict:
    payload = {
        "name":          "Kommo sync",
        "callbackUrl":   callback_url,
        "scope":         "sheet",
        "scopeObjectId": int(sheet_id),
        "events":        ["*.*"],
        "version":       1,
    }
    resp = requests.post(
        f"{SS_BASE}/webhooks",
        headers=SS_HEADERS,
        json=payload,
    )
    resp.raise_for_status()
    return resp.json()

@app.route("/webhooks/smartsheet", methods=["POST"])
def smartsheet_webhook():
    # Smartsheet шлёт challenge при первой настройке
    challenge = request.headers.get("Smartsheet-Hook-Challenge")
    if challenge:
        return jsonify({"smartsheetHookResponse": challenge})

    payload = request.json
    events  = payload.get("events", [])

    for event in events:
        if event.get("objectType") != "cell":
            continue
        row_id   = event.get("rowId")
        col_id   = event.get("columnId")
        new_val  = event.get("newValue", "")

        lead_id = find_kommo_deal_by_smartsheet_row(row_id)
        if not lead_id:
            continue

        col_name = get_column_name(SS_SHEET_ID, col_id)
        if col_name == "Status" and new_val == "Complete":
            create_kommo_note(lead_id, "Smartsheet: проект завершён")

        elif col_name == "Status" and new_val == "In Progress":
            create_kommo_note(lead_id, "Smartsheet: проект в работе")

        elif col_name == "Due Date" and new_val:
            schedule_kommo_task(lead_id,
                "Smartsheet: дедлайн проекта через 7 дней - проверить статус",
                due_days=7)

    return "", 200

Шаблон проекта: автоматизация через Smartsheet

Smartsheet поддерживает автоматизации (Automations в UI): при изменении статуса строки -> отправить уведомление -> назначить нового ответственного. Для Kommo-интеграции важно: автоматизации Smartsheet работают независимо, webhook только информирует Kommo.

Для создания проекта из шаблона (template Sheet) вместо ручного создания строк:

def copy_sheet_as_template(template_sheet_id: str, new_name: str) -> dict:
    # Создать копию template-листа как новый проект
    resp = requests.post(
        f"{SS_BASE}/sheets/{template_sheet_id}/copy",
        headers=SS_HEADERS,
        json={
            "destinationType": "home",
            "newName":         new_name,
            "include":         ["data", "attachments", "automations"],
        },
    )
    resp.raise_for_status()
    return resp.json()

Реальный кейс

Professional services компания (US, 60 человек, Kommo + Smartsheet):

  • До: Won -> project coordinator получает задание в Slack -> вручную создаёт строку в Smartsheet -> заполняет 12 полей из данных сделки -> создаёт 8 стандартных задач. 30–45 минут. Ошибки в сумме контракта (20% строк).
  • После: Won -> Python webhook -> строка + 7 дочерних задач за 3 секунды. Данные из Kommo (клиент, сумма, PM) переносятся без участия человека.
  • Дополнительно: «Complete» в Smartsheet -> Note в Kommo -> менеджер инициирует разговор о renewal. Задержка между завершением проекта и renewal conversation сократилась с 3 недель до 2 дней.

Для кого актуально

  • Professional services и consulting где каждый Won — новый проект с чеклистом
  • Enterprise компании где PM-команда работает в Smartsheet, продажи — в Kommo
  • Строительство, IT-внедрения, agency — любой проектный бизнес с фиксированным набором этапов
  • Компании где данные проекта нужно видеть в табличном формате и делать сводные отчёты

Часто задаваемые вопросы

Smartsheet Sheet vs Grid vs Project — что использовать для интеграции?

Для интеграции с Kommo через API тип листа не важен — работа идёт через универсальный endpoint POST /sheets/{id}/rows. Отличие только в настройках листа (Grid — чистая таблица, Project — с Gantt и зависимостями). Для project management с Gantt выбирать тип «Project» при создании Sheet в UI, API от этого не меняется.

Как получить Sheet ID для Smartsheet API?

Открыть нужный Sheet в браузере -> URL выглядит как https://app.smartsheet.com/sheets/XXXXXXXXXXXXXXXX -> последний сегмент — это Sheet ID (16 цифр). Или через API: GET /sheets возвращает список всех листов с их ID.

Smartsheet Column ID — как маппить поля?

GET /sheets/{id} возвращает полный Sheet включая массив columns с id, title, type. Функция get_sheet_columns() в примере выше создаёт словарь {title: id}. Column ID — число, оно не меняется при переименовании колонки. Лучше строить маппинг по ID, не по названию — на случай если команда переименует колонку в Smartsheet.

Можно ли создать проект из шаблона (template) Smartsheet через API?

Да, через POST /sheets/{templateId}/copy. Шаблон — обычный Sheet с примерными строками. При копировании через API создаётся новый Sheet с теми же колонками и (опционально) данными. После копирования — обновить нужные строки через PUT /sheets/{newSheetId}/rows.

Итого

  • API: Bearer token (Personal Access Token), base URL https://api.smartsheet.com/2.0
  • Won -> POST /sheets/{id}/rows создаёт строку проекта + дочерние строки задач
  • get_sheet_columns() — всегда строить маппинг по API, не хардкодить column ID
  • Webhook: POST /webhooks -> challenge handshake -> события изменения ячеек -> Kommo Notes
  • Шаблон: POST /sheets/{templateId}/copy создаёт новый Sheet из шаблона для каждого проекта

Если у вас Kommo и Smartsheet и Won требует создания проекта с чеклистом — опишите структуру листа и стандартный набор задач. Exceltic.dev настроит интеграцию с двусторонней синхронизацией.

Ещё статьи

Все →