Kommo + Smartsheet: Automatic Project Creation from Won Deals

Smartsheet is an enterprise work management platform that combines the familiar spreadsheet interface with project management capabilities: Gantt charts, resource management, automation, reporting, and integrations. It is popular in enterprise environments where teams are accustomed to Excel but need collaboration and progress tracking. Unlike Jira or Asana, Smartsheet is closer to a spreadsheet-project — indispensable where project data needs to be viewed in tabular form and exported to Excel. Without Kommo integration, Won deal data is transferred to Smartsheet manually — 15–20 minutes with a high risk of errors.

Smartsheet vs Jira vs Asana for Implementation Teams

ParameterSmartsheetJiraAsana
Target audienceEnterprise non-techTech teamsMixed teams
Tabular interfaceYes (primary)NoNo
GanttBuilt-inPluginBuilt-in
Resource planningYesLimitedYes (Business+)
Best forOperations, PMO, constructionDevelopmentMarketing, product

Smartsheet is chosen by companies where the PM team works in spreadsheets rather than kanban boards — professional services, construction, enterprise IT.

Architecture: What Gets Synchronised

Kommo -> Smartsheet: — Won -> create a new row in the project Sheet (name, client, amount, PM, dates) — Won -> create child rows (task checklist) from a standard template

Smartsheet -> Kommo: — Row status change (In Progress -> Complete) -> Note in deal — Webhook on “Due Date” cell -> Task in Kommo 7 days before deadline

Smartsheet REST API: Creating a Row in a Sheet

Base URL: https://api.smartsheet.com/2.0. Authentication: Authorization: Bearer {token} (Personal Access Token from 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"  # from sheet URL: smartsheet.com/sheets/{ID}

def get_sheet_columns(sheet_id: str) -> dict:
    # Get column title -> column id mapping
    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()

# Standard onboarding task checklist after Won
ONBOARDING_TASKS = [
    "Kickoff call with client",
    "Requirements gathering",
    "Environment setup",
    "First delivery",
    "UAT (user acceptance testing)",
    "Go-live",
    "Final documentation",
]

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: project created, {len(ONBOARDING_TASKS)} tasks -> {sheet_url}",
    )

Webhook: Smartsheet -> Kommo

Smartsheet supports webhooks on cell changes. Setup: POST /webhooks with callbackUrl and 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 sends a challenge on initial setup
    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: project completed")

        elif col_name == "Status" and new_val == "In Progress":
            create_kommo_note(lead_id, "Smartsheet: project in progress")

        elif col_name == "Due Date" and new_val:
            schedule_kommo_task(lead_id,
                "Smartsheet: project deadline in 7 days - check status",
                due_days=7)

    return "", 200

Project Template: Automation via Smartsheet

Smartsheet supports automations (Automations in UI): when a row status changes -> send notification -> assign a new owner. For the Kommo integration, the key point is: Smartsheet automations work independently, the webhook only informs Kommo.

To create a project from a template (template Sheet) instead of manually creating rows:

def copy_sheet_as_template(template_sheet_id: str, new_name: str) -> dict:
    # Create a copy of the template sheet as a new project
    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()

Real-World Case

Professional services company (US, 60 employees, Kommo + Smartsheet):

  • Before: Won -> project coordinator receives a Slack assignment -> manually creates a row in Smartsheet -> fills in 12 fields from deal data -> creates 8 standard tasks. 30–45 minutes. Errors in contract amounts (20% of rows).
  • After: Won -> Python webhook -> row + 7 child tasks in 3 seconds. Data from Kommo (client, amount, PM) is transferred without human involvement.
  • Additionally: “Complete” in Smartsheet -> Note in Kommo -> manager initiates a renewal conversation. The delay between project completion and the renewal conversation shortened from 3 weeks to 2 days.

Who This Is Relevant For

  • Professional services and consulting where every Won means a new project with a checklist
  • Enterprise companies where the PM team works in Smartsheet and sales in Kommo
  • Construction, IT implementations, agencies — any project-based business with a fixed set of stages
  • Companies that need to view project data in tabular format and produce summary reports

Frequently Asked Questions

Smartsheet Sheet vs Grid vs Project — which to use for integration?

For Kommo API integration, the sheet type does not matter — all work goes through the universal endpoint POST /sheets/{id}/rows. The difference is only in sheet settings (Grid — plain table, Project — with Gantt and dependencies). For project management with Gantt, choose the “Project” type when creating a Sheet in the UI; the API does not change.

How do I get the Sheet ID for the Smartsheet API?

Open the required Sheet in the browser -> the URL looks like https://app.smartsheet.com/sheets/XXXXXXXXXXXXXXXX -> the last segment is the Sheet ID (16 digits). Or via API: GET /sheets returns a list of all sheets with their IDs.

Smartsheet Column ID — how do I map fields?

GET /sheets/{id} returns the full Sheet including a columns array with id, title, and type. The get_sheet_columns() function in the example above creates a {title: id} dictionary. Column ID is a number that does not change when a column is renamed. It is better to build the mapping by ID rather than name — in case the team renames a column in Smartsheet.

Can a project be created from a Smartsheet template via API?

Yes, via POST /sheets/{templateId}/copy. A template is a regular Sheet with example rows. When copied via API, a new Sheet is created with the same columns and (optionally) data. After copying — update the required rows via PUT /sheets/{newSheetId}/rows.

Summary

  • API: Bearer token (Personal Access Token), base URL https://api.smartsheet.com/2.0
  • Won -> POST /sheets/{id}/rows creates a project row + child task rows
  • get_sheet_columns() — always build mapping via API, do not hardcode column IDs
  • Webhook: POST /webhooks -> challenge handshake -> cell change events -> Kommo Notes
  • Template: POST /sheets/{templateId}/copy creates a new Sheet from the template for each project

If you have Kommo and Smartsheet and Won requires project creation with a checklist — describe your sheet structure and standard task set. Exceltic.dev will configure the integration with two-way synchronisation.

More articles

All →