Kommo + Domo: enterprise BI-дашборд продаж с данными из CRM

Kommo + Domo: enterprise BI-дашборд продаж с данными из CRM

Domo — enterprise cloud BI-платформа: 1000+ готовых коннекторов (Salesforce, Google Ads, Shopify), DataSet API для кастомных источников, живые дашборды, Domo Apps для интерактивных инструментов. Используется в enterprise-компаниях где несколько источников данных нужно объединить в единый дашборд для руководства. В отличие от Grafana или Metabase, Domo позиционируется как no-SQL BI для бизнес-пользователей с меньшим техническим погружением. Нативного коннектора Kommo в Domo нет — разбираем интеграцию через DataSet API.

Domo vs Tableau vs Grafana для sales analytics

ПараметрDomoTableauGrafana
Целевая аудиторияBusiness users, enterpriseData analystsDevOps, engineers
SQL требованияМинимальныеСредниеВысокие
Готовых коннекторов1000+100+50+
DataSet API (push)ДаЧерез REST APIДа (InfluxDB/Postgres)
Живые дашбордыДаДаДа
ЦенаEnterprise (от ~$83/user/мес)от $70/user/месOpen-source (self-host)
Нативная KommoНетНетНет

Domo выбирают enterprise-компании где BI-дашборды потребляет менеджмент без технического фона, а данные приходят из 5+ разных источников.

Архитектура: Kommo -> Domo DataSet

Domo DataSet API позволяет создать DataSet и пушить в него строки через HTTP. Для Kommo: cron каждый час экспортирует сделки и контакты -> заменяет данные в Domo DataSet -> Domo автоматически обновляет дашборды.

Kommo API -> Python cron -> Domo DataSet API (replace/append)

                         Domo Dashboard (auto-refresh)

Domo OAuth 2.0 и DataSet API

Domo использует OAuth 2.0 client credentials flow. Клиент создаётся в Domo -> Admin -> OAuth Apps -> New Client.

import requests
from datetime import datetime, timezone

DOMO_CLIENT_ID     = "your_client_id"
DOMO_CLIENT_SECRET = "your_client_secret"
DOMO_BASE          = "https://api.domo.com"

def get_domo_token() -> str:
    resp = requests.post(
        f"{DOMO_BASE}/oauth/token",
        params={
            "grant_type": "client_credentials",
            "scope":      "data",
        },
        auth=(DOMO_CLIENT_ID, DOMO_CLIENT_SECRET),
    )
    resp.raise_for_status()
    return resp.json().get("access_token", "")

def get_domo_headers() -> dict:
    token = get_domo_token()
    return {
        "Authorization": f"Bearer {token}",
        "Content-Type":  "application/json",
    }

def create_domo_dataset(name: str, columns: list) -> str:
    # columns: [{"name": "deal_id", "type": "LONG"}, ...]
    # types: STRING, LONG, DOUBLE, DECIMAL, DATE, DATETIME
    payload = {
        "name":   name,
        "schema": {"columns": columns},
    }
    resp = requests.post(
        f"{DOMO_BASE}/v1/datasets",
        headers=get_domo_headers(),
        json=payload,
    )
    resp.raise_for_status()
    return resp.json().get("id", "")

def replace_domo_dataset(dataset_id: str, csv_data: str):
    # Полная замена данных (не append) - для ежечасного sync
    headers = get_domo_headers()
    headers["Content-Type"] = "text/csv"
    resp = requests.put(
        f"{DOMO_BASE}/v1/datasets/{dataset_id}/data",
        headers=headers,
        params={"updateMethod": "REPLACE"},
        data=csv_data.encode("utf-8"),
    )
    resp.raise_for_status()

def append_domo_dataset(dataset_id: str, csv_data: str):
    # Добавление строк без замены - для event-based обновлений
    headers = get_domo_headers()
    headers["Content-Type"] = "text/csv"
    resp = requests.put(
        f"{DOMO_BASE}/v1/datasets/{dataset_id}/data",
        headers=headers,
        params={"updateMethod": "APPEND"},
        data=csv_data.encode("utf-8"),
    )
    resp.raise_for_status()

Экспорт Kommo -> Domo: основной sync

import csv, io

DEALS_DATASET_ID   = "your_deals_dataset_id"
CONTACTS_DATASET_ID = "your_contacts_dataset_id"

DEAL_COLUMNS = [
    {"name": "deal_id",       "type": "LONG"},
    {"name": "deal_name",     "type": "STRING"},
    {"name": "stage",         "type": "STRING"},
    {"name": "pipeline",      "type": "STRING"},
    {"name": "price",         "type": "DOUBLE"},
    {"name": "responsible",   "type": "STRING"},
    {"name": "created_at",    "type": "DATETIME"},
    {"name": "closed_at",     "type": "DATETIME"},
    {"name": "is_won",        "type": "LONG"},
    {"name": "is_lost",       "type": "LONG"},
    {"name": "contact_email", "type": "STRING"},
    {"name": "contact_name",  "type": "STRING"},
    {"name": "source",        "type": "STRING"},
]

def export_kommo_to_domo():
    leads = get_all_kommo_leads()  # постраничный обход API

    buf = io.StringIO()
    writer = csv.DictWriter(buf, fieldnames=[c["name"] for c in DEAL_COLUMNS])
    writer.writeheader()

    for lead in leads:
        contact = get_kommo_contact_for_lead(lead["id"])
        writer.writerow({
            "deal_id":       lead["id"],
            "deal_name":     lead.get("name", ""),
            "stage":         get_stage_name(lead.get("status_id")),
            "pipeline":      get_pipeline_name(lead.get("pipeline_id")),
            "price":         lead.get("price") or 0,
            "responsible":   get_user_name(lead.get("responsible_user_id")),
            "created_at":    format_dt(lead.get("created_at")),
            "closed_at":     format_dt(lead.get("closed_at")),
            "is_won":        1 if lead.get("status_id") == WON_STATUS_ID else 0,
            "is_lost":       1 if lead.get("closed_at") and lead.get("status_id") != WON_STATUS_ID else 0,
            "contact_email": get_contact_email(contact) if contact else "",
            "contact_name":  contact.get("name", "") if contact else "",
            "source":        get_custom_field(lead, SOURCE_FIELD_ID) or "",
        })

    replace_domo_dataset(DEALS_DATASET_ID, buf.getvalue())

# Запускать по cron каждый час
# 0 * * * * python sync_kommo_domo.py

Domo DataFusion: объединение с другими источниками

Преимущество Domo — объединение Kommo-данных с данными из Google Ads, Stripe, HubSpot через DataFusion (JOIN без SQL):

  • Kommo deals DataSet + Google Ads campaigns DataSet -> стоимость привлечения по источнику
  • Kommo deals DataSet + Stripe charges DataSet -> когорты по плану оплаты
  • Kommo contacts DataSet + HubSpot contacts DataSet -> дубли между системами

DataFusion настраивается в Domo UI через drag-and-drop JOIN без написания кода.

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

Enterprise B2B (US, 50 сотрудников, Kommo + Domo + Salesforce):

  • До: revenue-данные в Kommo, ad-данные в Google Ads, финансы в QuickBooks. Еженедельный Excel-отчёт для CFO собирался вручную 4–5 часов.
  • После: Kommo -> Domo DataSet (hourly), Google Ads -> Domo (нативный коннектор), QuickBooks -> Domo (нативный). DataFusion JOIN в Domo -> единый дашборд. CFO видит live CAC, pipeline velocity, revenue breakdown без Excel.
  • Дополнительно: Domo Alert — если conversion rate упал ниже 15% -> push-уведомление CFO и Head of Sales.

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

  • Enterprise-компании с несколькими источниками данных (CRM + ad platforms + ERP + billing)
  • CFO и Head of Sales которым нужны live дашборды без технического вмешательства
  • Компании где Domo уже является корпоративным BI-стандартом
  • Команды где данные из Kommo нужно объединять с данными вне CRM через DataFusion

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

Domo Connector vs DataSet API — что использовать для Kommo?

Domo имеет готовые коннекторы для Salesforce, HubSpot, Pipedrive. Для Kommo готового коннектора нет. Единственный путь — DataSet API (push через HTTP). Создаём DataSet с нужной схемой -> пушим CSV каждый час через cron. Это стандартная практика для систем без нативного коннектора.

Domo DataSet: REPLACE vs APPEND — когда что использовать?

REPLACE полностью заменяет данные DataSet при каждом sync. APPEND добавляет строки. Для Kommo deals рекомендуется REPLACE с полным дампом всех сделок — это гарантирует актуальность при изменении данных (изменение стадии, бюджета, ответственного). APPEND имеет смысл только для immutable событий (activity log, call log).

Как обеспечить актуальность токена OAuth 2.0?

Domo access_token живёт 1 час. Паттерн: get_domo_token() вызывать перед каждым batch-запросом, не кешировать дольше 55 минут. Для долгих операций (большой DataSet) — обновлять токен внутри процесса через requests.Session с автоматическим retry.

Domo Alerts — можно ли настроить уведомления по данным из Kommo?

Да. В Domo -> Alerts можно задать условие на любой метрике DataSet: «если среднее время в стадии «Переговоры» > 14 дней -> уведомить менеджера». Уведомления через email, Slack, Domo Buzz. Настройка в UI без кода.

Итого

  • Auth: OAuth 2.0 client credentials, POST /oauth/token с scope data
  • DataSet создаётся один раз: POST /v1/datasets со схемой колонок
  • Push данных: PUT /v1/datasets/{id}/data с CSV, updateMethod=REPLACE (hourly cron)
  • DataFusion: JOIN Kommo DataSet с Google Ads, Stripe, QuickBooks в Domo UI
  • Domo Alerts: уведомления по метрикам без кода

Если у вас Kommo и Domo и вы хотите видеть данные CRM в enterprise BI-дашборде — опишите какие источники уже подключены к Domo. Exceltic.dev настроит DataSet sync за 1–2 рабочих дня.

Ещё статьи

Все →