Twenty × Metabase

How to build Twenty CRM sales dashboards in Metabase

Twenty is an open-source CRM backed by PostgreSQL. Metabase is where you turn its records, opportunities, and activities into shared, trustworthy sales dashboards. Because Twenty is open source, self-hosters get an option most CRMs can't offer: point Metabase straight at the CRM's own database. This guide covers that direct Postgres route plus a lightweight MCP + CLI route that pulls live data with a Twenty MCP server and loads a CSV into Metabase with the Metabase CLI for quick analysis.

The Twenty advantage: most CRMs are closed SaaS, so Metabase always needs a sync step. Self-hosted Twenty runs on PostgreSQL — a database Metabase connects to natively — so you can report on a read replica with no ETL at all.

How do you connect Twenty to Metabase?

If you self-host, the direct Postgres route is the simplest and most powerful. Use the Twenty MCP server and Metabase CLI route alongside it to pull live data and stand up a quick analysis.

1 · Direct Postgres route (self-hosted)

Query the CRM database directly

Twenty is open source and backed by PostgreSQL. If you self-host, point Metabase at a read replica of Twenty's database — no ETL, no connector, live SQL on your own data.

Best for
  • Pipeline, win-rate, and conversion dashboards straight off the source
  • Teams already running self-hosted Twenty on their own Postgres
  • Joining CRM data with other databases in the same warehouse
Trade-offs
  • Query a read replica, never your live production writer
  • Twenty's schema evolves — pin to modeled views you control
  • Twenty Cloud users don't get direct DB access; use the sync route
2 · MCP + CLI route (AI-assisted)

Live data in, quick analysis out

Connect Twenty through a managed MCP server (Pipedream) to read live records and opportunities, then pair it with the Metabase CLI, whose upload command loads a CSV into Metabase as a ready-to-query table and model.

Best for
  • Quick lookups like "which opportunities have no next step?"
  • Loading a Twenty CSV export into Metabase in seconds
  • Spot-checks and one-off analyses without a warehouse
Trade-offs
  • Great for exploration, not governed reporting
  • Scope the Twenty MCP connection to read-only where possible
  • CSV uploads are snapshots — refresh or move to the database route for history

What can you analyze from Twenty data in Metabase?

  • Pipeline — open value by stage, owner, and company, plus coverage against target
  • Win rate — opportunities won vs. closed, by segment, source, and stage
  • Sales cycle length — created to won, with median and p90
  • Stage conversion — where opportunities advance and where they stall
  • Deal size — average and median value, plus mix by segment
  • Relationships — companies and people coverage, records with open opportunities
  • Activity — notes, tasks, and touches by owner and account

Which Twenty dashboards should you build in Metabase?

For: Sales leaders

Pipeline

The open book of business, right now.

  • Open opportunities by stage (funnel)
  • Pipeline value by owner and company (bar)
  • Coverage vs. target for the period (number)
  • Stale opportunities with no recent activity (table)
For: Sales ops, RevOps

Conversion

Where opportunities advance and where they stall.

  • Stage-to-stage conversion (funnel)
  • Win rate by segment and source (bar)
  • Records added vs. opportunities won by week (dual line)
  • Lost-reason breakdown (bar)
For: Revenue leadership

Velocity

How fast opportunities move and close.

  • Median sales cycle length (number + trend)
  • Time-in-stage by stage (bar)
  • Deal velocity: value / cycle time (number)
  • Aging of open opportunities (table)
For: Ops, leadership

Relationships

Coverage across companies and people.

  • Companies by segment (bar)
  • New records added by week (line)
  • Records with an open opportunity (number)
  • Notes and tasks by owner (bar)

How do you use the Twenty MCP server with the Metabase CLI?

Connect Twenty through a managed MCP server (Pipedream) and pair it with the Metabase CLI for fast, hands-on analysis. The Twenty MCP looks up current records and opportunities; the Metabase CLI's upload command loads a CSV into Metabase and creates a ready-to-query table and model. Scope the connection to read-only where possible.

Example workflow

  • Ask the Twenty MCP which opportunities have no note or task in the last 10 days, or pull a company's records and open opportunities.
  • Export the records or pipeline view you want to keep as a CSV.
  • Run mb upload csv to load it into Metabase as a table and model, then build questions and dashboards on top.

Be honest about the limits

  • The Twenty MCP is great for live lookups — not for scheduled or audited pipeline reporting.
  • A CSV upload is a point-in-time snapshot; refresh it with mb upload replace or use the direct Postgres route for real history.
  • Scope the connection to read-only where possible; for governed reporting, prefer the direct Postgres route on a read replica.
  • mb upload csv needs an uploads database configured under Admin → Settings → Uploads.

How do you set up the Twenty MCP server and the Metabase CLI?

Twenty MCPmanaged

Provider
Pipedream (managed connector)
Endpoint
https://mcp.pipedream.net/v2
Auth
Connect Twenty in Pipedream (API key)
Note
Open API also supports direct/community servers

Metabase CLIofficial

Install
npm install -g @metabase/cli
Auth
mb auth login (browser OAuth on v62+, or an API key)
Load data
mb upload csv --file data.csv
Requires
An uploads database (Admin → Settings → Uploads)
Cursor~/.cursor/mcp.json or .cursor/mcp.json
{
  "mcpServers": {
    "twenty": {
      "url": "https://mcp.pipedream.net/v2"
    }
  }
}
TerminalLoad a Twenty CSV with the Metabase CLI
# Install the Metabase CLI
npm install -g @metabase/cli

# Log in (opens your browser; requires Metabase v62+)
mb auth login --url https://your-metabase.example.com

# Load a Twenty CSV export — creates a table AND a model
mb upload csv --file twenty-opportunities.csv --collection root

# Refresh that same table later from a new export
mb upload replace <table-id> --file twenty-opportunities.csv

Connect your Twenty account in Pipedream, then use its static MCP URL. Because Twenty exposes an open GraphQL/REST API, community and self-hosted MCP servers also exist if you'd rather not use a managed broker. The Metabase CLI stores its credentials securely after mb auth login.

Verify before shipping: confirm an uploads database is enabled under Admin → Settings → Uploads (Metabase docs) and the current Twenty connector setup on Pipedream's Twenty MCP page.

Can you generate a Twenty dashboard with AI?

Yes. Use the prompt below with any assistant that can run the Twenty MCP server and the Metabase CLI. It works end to end: if Twenty tables already exist in Metabase — read directly from Postgres or uploaded — it analyzes those; otherwise it pulls the data over the Twenty MCP, loads it with mb upload csv, then builds the dashboard — fixing the win-rate denominator and skipping metrics the data can't support instead of faking them.

Prompt for creating a Twenty Sales Overview dashboard
Create a polished Metabase dashboard for Twenty CRM sales analytics.
Work end to end: get the data into Metabase if it isn't there yet, then build.

Goal: Help sales leaders understand pipeline, win rate, conversion, sales cycle,
and relationship coverage from Twenty CRM data.

Step 1 — Find or load the data:
- First, check what already exists in Metabase (search for Twenty tables and
  models, and any connected Twenty Postgres database). If durable data is already
  present — a direct read replica or uploaded earlier — use it and skip to Step 2.
- If nothing is there, pull it with the Twenty MCP server (scope the connection to
  read-only where possible): opportunities, companies, people, and activity. Write
  each result to a CSV, then load it with the Metabase CLI — run
  "mb upload csv --file <export>.csv" so each upload creates a table and a
  ready-to-query model. Use "mb upload replace <table-id> --file <export>.csv" to
  refresh an existing table instead of creating duplicates.

Step 2 — Inspect before querying:
Twenty stores each workspace in its own Postgres schema and supports custom
objects, so do not assume exact table or column names. CSV exports are usually
flat and pre-aggregated (one row per opportunity), while the direct Postgres
tables are raw. If connected directly to Twenty's Postgres, treat it as a read
replica and build on modeled views, not raw tables. Inspect the actual tables and
column names first.

Important:
- Build on whatever data is present; don't claim Metabase connects natively to
  Twenty's SaaS — it reads a database (Twenty's own Postgres) or CLI-uploaded
  tables.
- Define "won" and "closed" once (from the opportunity stage) and reuse them.
- For win rate, state the denominator explicitly (won / closed vs. won / created)
  and hold the cohort fixed.
- Report sales cycle length and deal size as medians (p50) and p90, never plain
  averages — both are right-skewed.
- Only compute sales cycle length, time-in-stage, or stage conversion when
  stage-change history exists; otherwise use a caveat.
- Twenty stores money as amountMicros (value x 1,000,000) plus a currencyCode —
  divide by 1,000,000 and convert to a single reporting currency; caveat any mix.
- Only build a card if its underlying column/metric exists in the data.
- A single CSV is a point-in-time snapshot: only build trend cards if there is a
  usable date column or multiple periods have been uploaded.

Dashboard title: Twenty Sales Overview

Sections:
1. Executive summary (KPI cards): Open pipeline; Coverage vs. target; Win rate
   (last 90 days); Median sales cycle length; Average and median deal size;
   Opportunities closing this period.
2. Pipeline: Open opportunities by stage; value by owner and company; stale
   opportunities.
3. Conversion: Stage-to-stage conversion; win rate by segment and source; lost
   reasons.
4. Velocity: Median cycle length; time-in-stage; deal velocity; aging.
5. Relationships: Companies by segment; new records by week; records with an open
   opportunity.

Filters: Stage, Owner, Segment, Source, Date range.

Reuse the models Metabase auto-created from uploaded CSVs, or (for a read replica
or warehouse) create reusable models: modeled_twenty_opportunities,
modeled_twenty_stage_history, modeled_twenty_companies, modeled_twenty_people,
and modeled_twenty_activity.

Output: Build the dashboard if you have permission; otherwise provide the exact
questions, SQL, model definitions, and layout. Include caveats for any metric
that cannot be calculated from the available data. Keep it practical, dense,
and executive-readable. Avoid vanity metrics.

How do you connect Twenty's database to Metabase?

Self-hosted Twenty runs on PostgreSQL, which Metabase reads natively. This is the durable, governed path.

Options

  • Direct read replica — create a read replica of Twenty's Postgres and add it in Metabase (Admin → Databases → Add → PostgreSQL). Never point Metabase at the live writer.
  • Warehouse copy — replicate the Twenty schema into your warehouse with dlt or Postgres logical replication, then model there.
  • Twenty API (GraphQL / REST) — for Twenty Cloud, where you don't get direct DB access, pull objects through the API into a database.

Notes

  • Twenty stores each workspace in its own schema and lets you add custom objects/fields — map your workspace's opportunity object and stage field deliberately.
  • Build clean modeled views on top of raw tables so schema changes don't break dashboards.
  • Capture stage-change history (timeline/activity or CDC) if you want cycle length, time-in-stage, and conversion.
  • Money is stored as amountMicros (value × 1,000,000) with a currencyCode — divide by 1,000,000 and normalize currency.

How should you model Twenty data in Metabase?

Core tables

ObjectGrainKey columns
opportunityone row per opportunityid, stage, amountMicros, amountCurrencyCode, closeDate, createdAt, pointOfContactId, companyId
opportunity_stage_historyone row per changeopportunity_id, stage, changed_at
companyone row per companyid, name, employees, createdAt
personone row per personid, companyId, createdAt
note / taskone row per activityid, createdAt, createdBy, target relations
workspaceMemberone row per memberid, name

Modeling advice

  • Build a modeled_twenty_opportunities view with clean stage, amount (from amountMicros, one currency), and close_date.
  • Derive modeled_twenty_stage_history from the timeline/activity log or change-data-capture for time-in-stage and cycle length.
  • Normalize the stage set to an ordered funnel so charts stay stable.
  • Resolve owner and company relationships to names once, in a model.
  • Because Twenty allows custom fields, pin dashboards to your modeled views rather than raw tables.

Which Twenty metrics should you track in Metabase?

MetricDefinitionNotes
Open pipelineSum of value for open opportunities.Segment by stage, owner, company.
Win rateWon ÷ closed opportunities in a cohort.Fix the denominator (closed vs. created) before comparing.
Sales cycle lengthCreated → won, in days.Report median and p90; it's right-skewed.
Stage conversionOpportunities reaching stage N+1 ÷ reaching stage N.Needs stage-change history.
Average deal sizeWon value ÷ won opportunities.Report the median too; outliers distort the mean.
Records with open oppCompanies/people linked to an open opportunity.A coverage signal for the base.

What SQL powers Twenty dashboards in Metabase?

These assume the modeled views above (PostgreSQL dialect — the same engine Twenty runs on). Adjust identifiers to match your workspace schema.

Win rate by monthPostgreSQL

Won as a share of closed opportunities over the last 12 months.

SELECT
  date_trunc('month', o.close_date) AS month,
  COUNT(*) FILTER (WHERE o.stage = 'WON')                    AS won,
  COUNT(*) FILTER (WHERE o.stage IN ('WON', 'LOST'))         AS closed,
  ROUND(
    100.0 * COUNT(*) FILTER (WHERE o.stage = 'WON')
      / NULLIF(COUNT(*) FILTER (WHERE o.stage IN ('WON', 'LOST')), 0),
    1
  ) AS win_rate_pct
FROM modeled_twenty_opportunities o
WHERE o.stage IN ('WON', 'LOST')
  AND o.close_date >= CURRENT_DATE - INTERVAL '12 months'
GROUP BY 1
ORDER BY 1;
Open pipeline by stagePostgreSQL

The current funnel — open opportunities and value by stage.

SELECT
  o.stage,
  COUNT(*)                          AS open_opportunities,
  ROUND(SUM(o.amount_micros / 1e6), 2) AS open_value
FROM modeled_twenty_opportunities o
WHERE o.stage NOT IN ('WON', 'LOST')
GROUP BY o.stage
ORDER BY open_value DESC;
Sales cycle length (median and p90)PostgreSQL

Days from created to won; medians beat averages here.

-- Median days from created to won, by month closed
SELECT
  date_trunc('month', o.close_date) AS month,
  percentile_cont(0.5) WITHIN GROUP (
    ORDER BY EXTRACT(EPOCH FROM (o.close_date - o.created_at)) / 86400.0
  ) AS median_cycle_days,
  percentile_cont(0.9) WITHIN GROUP (
    ORDER BY EXTRACT(EPOCH FROM (o.close_date - o.created_at)) / 86400.0
  ) AS p90_cycle_days
FROM modeled_twenty_opportunities o
WHERE o.stage = 'WON'
GROUP BY 1
ORDER BY 1;

What are common mistakes when analyzing Twenty in Metabase?

Pointing Metabase at the live production database.→ Query a read replica, not the writer that serves the app — analytics queries can contend with live traffic.
Reporting off raw tables that can change.→ Twenty allows custom objects and fields; build modeled views so schema changes don't break dashboards.
Forgetting money is stored as micros.→ Divide amountMicros by 1,000,000 (and normalize currency) before totaling or averaging.
Computing sales cycle without stage history.→ Capture stage changes (timeline/activity or CDC) — a snapshot can't tell you when an opportunity entered or left a stage.
Averaging deal size and cycle length.→ Both are right-skewed; report the median (and p90) alongside the mean.
Leaving win rate's denominator ambiguous.→ Won ÷ closed and won ÷ created are different metrics — pick one and label it.

Related analytics

Related metrics

Related integrations

FAQ

Can Metabase connect directly to Twenty?
If you self-host Twenty, yes — it runs on PostgreSQL, which Metabase connects to natively. Point Metabase at a read replica of Twenty's database. Twenty Cloud users don't get direct DB access, so pull data through the Twenty API into a database first.
Is there an official Twenty MCP server?
The practical managed path is Pipedream: connect your Twenty account, then use its static MCP URL (https://mcp.pipedream.net/v2). Because Twenty exposes an open GraphQL/REST API, community and self-hosted MCP servers also exist. Use MCP for live lookups, not governed reporting.
How do I quickly load Twenty data without a warehouse?
Export a CSV from Twenty and run `mb upload csv --file data.csv` with the Metabase CLI. It creates a table and a model you can build questions on right away. You'll need an uploads database enabled under Admin → Settings → Uploads. Refresh later with `mb upload replace`, or connect Twenty's Postgres directly when you need history.
How is money stored in Twenty?
As amountMicros (the value multiplied by 1,000,000) alongside a currencyCode. Divide by 1,000,000 and convert to a single reporting currency before you total or average deal value.