Kommo + Sisense: Real-Time Sales BI Analytics from CRM Data

Kommo + Sisense: Real-Time Sales BI Analytics from CRM Data

When Kommo and Sisense are integrated, deal, contact, and activity data flows automatically into the Sisense data store on a scheduled sync. The executive sees up-to-date dashboards - stage conversion, average deal size, deal velocity - without manually exporting from Kommo.

Sisense is an enterprise BI platform with its own columnar ElastiCube storage that handles large data volumes without traditional ETL. It is popular among companies that need embedded analytics dashboards inside their own products or deep visualization customization.

For companies whose core sales pipeline lives in Kommo, Sisense addresses a key problem: Kommo’s standard reports provide aggregated numbers but do not allow complex multi-dimensional slicing - by manager + source + period + deal size simultaneously. That requires exporting to a BI tool.

In custom Kommo integration projects, we see a consistent pattern: leadership wants to see pipeline data inside an existing BI platform, but there is no native Kommo-to-Sisense connector. Data is exported manually via CSV once a week, an analyst spends hours building pivot tables, and the report is already stale by the time it is shared. The lag between a pipeline event and its reflection in a dashboard runs from several days to a full week. This article covers the ETL architecture that closes this gap: a scheduled sync from the Kommo API through an intermediate store into Sisense ElastiCube.

Why the native integration does not work

Sisense has no ready-made connector for Kommo. CRM connectors in Sisense exist for Salesforce and HubSpot - not for Kommo. This means the standard path (configure a data source in Sisense and click “Connect”) does not work.

The alternative - manually exporting a CSV from Kommo and loading it into Sisense - has obvious limitations: data goes stale immediately, the process requires recurring manual effort, and large exports from Kommo are constrained by pagination and API rate limits.

Using Zapier here is pointless: Sisense is an analytical store, not an operational tool. Zapier cannot incrementally update ElastiCube datasets.

What gets built - solution architecture

Kommo API (scheduled)
  |
  | Every N hours:
  | - export new/modified deals
  | - export activities (notes, calls, emails)
  | - export contacts and companies
  v
ETL service (Python)
  |
  | Transform:
  | - normalize statuses
  | - calculate derived metrics (days_in_stage, deal_velocity)
  | - enrich from custom fields
  v
Intermediate store (PostgreSQL / BigQuery)
  |
  v
Sisense ElastiCube (scheduled build)
  |
  v
Sisense Dashboards:
  - Pipeline overview
  - Manager performance
  - Stage conversion
  - Revenue forecast

The key architectural decision is the intermediate data store. Directly loading from the API into Sisense rebuilds the entire dataset every time. An intermediate database stores accumulated history and enables incremental updates (only new and modified records).

Technical details

Kommo API v4 uses a Long-lived access token or OAuth 2.0. For scheduled sync, a Long-lived token with the permissions leads, contacts, companies, notes, calls is recommended. The API returns data paginated (250 records per page); a full export requires recursive pagination through the page parameter.

Sisense supports several ways to load data into ElastiCube: JDBC connector (connect to PostgreSQL/BigQuery), Sisense REST API for managing builds, and a CSV connector. For production, a JDBC connection to the intermediate database is recommended.

import requests
import psycopg2
from datetime import datetime, timedelta
import json

KOMMO_BASE_URL = "https://YOUR_ACCOUNT.kommo.com/api/v4"
KOMMO_TOKEN = "your_long_lived_token"

def fetch_modified_leads(since_timestamp: int) -> list:
    """Export deals modified since the given timestamp"""
    headers = {"Authorization": f"Bearer {KOMMO_TOKEN}"}
    leads = []
    page = 1
    
    while True:
        resp = requests.get(
            f"{KOMMO_BASE_URL}/leads",
            headers=headers,
            params={
                "filter[updated_at][from]": since_timestamp,
                "page": page,
                "limit": 250,
                "with": "contacts,companies,loss_reason,pipeline"
            }
        )
        if resp.status_code == 204:  # No data
            break
        resp.raise_for_status()
        
        data = resp.json()
        batch = data.get("_embedded", {}).get("leads", [])
        if not batch:
            break
        
        leads.extend(batch)
        
        # Check pagination
        next_link = data.get("_links", {}).get("next")
        if not next_link:
            break
        page += 1
    
    return leads

def transform_lead(lead: dict) -> dict:
    """Transform raw Kommo data into a flat record for BI"""
    created_at = datetime.fromtimestamp(lead["created_at"])
    updated_at = datetime.fromtimestamp(lead["updated_at"])
    
    # Calculate derived metrics
    close_date = datetime.fromtimestamp(lead["closed_at"]) if lead.get("closed_at") else None
    days_to_close = (close_date - created_at).days if close_date else None
    
    return {
        "lead_id": lead["id"],
        "name": lead["name"],
        "price": lead.get("price", 0),
        "status_id": lead["status_id"],
        "pipeline_id": lead["pipeline_id"],
        "responsible_user_id": lead["responsible_user_id"],
        "created_at": created_at.isoformat(),
        "updated_at": updated_at.isoformat(),
        "closed_at": close_date.isoformat() if close_date else None,
        "days_to_close": days_to_close,
        "is_won": lead.get("status_id") == 142,  # WON status
        "is_lost": lead.get("status_id") == 143,  # LOST status
        "loss_reason": lead.get("loss_reason", {}).get("name") if lead.get("loss_reason") else None,
        # Custom fields
        "deal_type": get_custom_field(lead, "deal_type"),
        "lead_source": get_custom_field(lead, "lead_source"),
        "industry": get_custom_field(lead, "industry"),
        "company_size": get_custom_field(lead, "company_size")
    }

def get_custom_field(lead: dict, field_name: str) -> str:
    for field in lead.get("custom_fields_values", []) or []:
        if field.get("field_code") == field_name:
            values = field.get("values", [])
            return values[0]["value"] if values else None
    return None

def sync_to_postgres(leads: list, conn):
    """Write/update deals in the intermediate database (upsert)"""
    cursor = conn.cursor()
    for lead in leads:
        cursor.execute("""
            INSERT INTO kommo_leads (
                lead_id, name, price, status_id, pipeline_id,
                responsible_user_id, created_at, updated_at, closed_at,
                days_to_close, is_won, is_lost, loss_reason,
                deal_type, lead_source, industry, company_size
            ) VALUES (
                %(lead_id)s, %(name)s, %(price)s, %(status_id)s, %(pipeline_id)s,
                %(responsible_user_id)s, %(created_at)s, %(updated_at)s, %(closed_at)s,
                %(days_to_close)s, %(is_won)s, %(is_lost)s, %(loss_reason)s,
                %(deal_type)s, %(lead_source)s, %(industry)s, %(company_size)s
            )
            ON CONFLICT (lead_id) DO UPDATE SET
                price = EXCLUDED.price,
                status_id = EXCLUDED.status_id,
                updated_at = EXCLUDED.updated_at,
                closed_at = EXCLUDED.closed_at,
                days_to_close = EXCLUDED.days_to_close,
                is_won = EXCLUDED.is_won,
                is_lost = EXCLUDED.is_lost
        """, transform_lead(lead))
    conn.commit()

Step-by-step implementation

Step 1. Design the data schema for your BI needs

Define which metrics you need on the dashboard: stage conversion, average deal size by manager, velocity by lead source. Design intermediate database tables around those questions. Standard tables: kommo_leads, kommo_contacts, kommo_companies, kommo_activities, kommo_pipelines, kommo_users.

Step 2. Deploy the ETL service

Configure a cron job to run ETL every 4-6 hours. Each run: read last_sync_timestamp from a service table, export only modified records via filter[updated_at][from], transform and upsert into PostgreSQL, update last_sync_timestamp.

Step 3. Connect Sisense to the intermediate database

In Sisense Admin -> Data -> Add New Data Source, select PostgreSQL. Enter the connection string. Create an ElastiCube, add tables from the intermediate database. Configure relationships between tables (leads -> users, leads -> pipelines).

Step 4. Create a scheduled build in Sisense

Set up automatic ElastiCube rebuilds every 6-8 hours. Sisense picks up fresh data from PostgreSQL and updates the internal analytics tables.

Step 5. Build dashboards

Standard dashboard set for Kommo data: Pipeline overview (volume and conversion by stage), Manager performance (deals, average deal size, win rate by manager), Lead source analysis (which sources produce the best leads), Deal velocity (average deal cycle by type).

Real case with numbers

B2B SaaS company, 20 people, ARR around $1-2M. Sales team of 5 works in Kommo; the CEO and investors want to see pipeline data in real time.

Before the integration: an analyst exported a CSV from Kommo once a week, built pivot tables in Excel, and sent them by email. Data went stale the moment it was sent. Preparing the report took 3-4 hours per week.

A separate problem: when asked “why did Demo stage conversion drop?” the team had no tool for a quick slice - they had to export another CSV and build another pivot manually.

After the Kommo + Sisense integration via Exceltic.dev:

  • The CEO opens a browser dashboard and sees data that is at most 4-6 hours old
  • Time to prepare the weekly report: 0 (automated)
  • Ad-hoc analysis (“show conversion over the last 30 days for LinkedIn leads only”): 2-3 minutes in Sisense vs. 2-3 hours in Excel

More on Kommo CRM features for sales analytics in the overview article.

Who this is for

The Kommo + Sisense integration is relevant for:

  • Companies with Sisense as their enterprise BI standard who need to add CRM data to an existing platform
  • B2B companies with 5+ sales managers who need team performance analytics
  • Companies reporting to a board or investors - Sisense enables polished presentation dashboards
  • SaaS with embedded analytics - Kommo data can be embedded into your product’s client-facing UI

If you do not have a Sisense enterprise license, consider more accessible options for Kommo data analytics: Metabase (self-hosted, free), Looker Studio (free), Power BI.

Frequently asked questions

Does Sisense support PostgreSQL as a data source?

Yes. Sisense supports PostgreSQL via the JDBC connector out of the box. It is one of the most common data sources for Sisense ElastiCube. MySQL, SQL Server, BigQuery, Redshift, and other OLAP stores are also supported.

How long does it take for data to get from Kommo into Sisense?

The delay is determined by the ETL service run frequency and the ElastiCube rebuild schedule in Sisense. Typical configuration: ETL every 4 hours + ElastiCube rebuild every 4 hours with an offset = data updates every 4-8 hours. For near real-time analytics the interval can be reduced to 1 hour, but this increases load on the Kommo API.

How do you preserve deal stage history when the stage changes?

Kommo does not store stage change history in the standard API (only the current status). For SCD (Slowly Changing Dimensions) you either use the Kommo events/activities API to reconstruct history, or capture every status change on each ETL run. The second approach is simpler: on each sync, create a snapshot record with the current status_id and capture timestamp.

Can a different BI platform be used instead of Sisense?

Yes. The architecture with an intermediate PostgreSQL database is universal. Sisense can be replaced with Tableau, Power BI, Looker, or Metabase - all support PostgreSQL as a source. The ETL service itself remains unchanged; only the final connection step changes. Other analytics options for Kommo CRM are covered in our overview.

How long does Kommo + Sisense integration development take?

Basic ETL (deals + contacts + activities exported to PostgreSQL) + Sisense connection + 2-3 standard dashboards - 4-5 weeks. This includes setting up the intermediate database, an ETL service with error handling and pagination. Embedded analytics or additional data sources are estimated separately.


If you need BI analytics on Kommo data in Sisense - describe your requirements to the Exceltic.dev team. We will design the data warehouse architecture and estimate the scope.

More articles

All →