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.
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.
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.
- 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
- 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
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.
- 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
- 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?
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)
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)
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)
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 csvto 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 replaceor 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 csvneeds 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)
{
"mcpServers": {
"twenty": {
"url": "https://mcp.pipedream.net/v2"
}
}
}# 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.csvConnect 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.
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.
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 acurrencyCode— divide by 1,000,000 and normalize currency.
How should you model Twenty data in Metabase?
Core tables
| Object | Grain | Key columns |
|---|---|---|
opportunity | one row per opportunity | id, stage, amountMicros, amountCurrencyCode, closeDate, createdAt, pointOfContactId, companyId |
opportunity_stage_history | one row per change | opportunity_id, stage, changed_at |
company | one row per company | id, name, employees, createdAt |
person | one row per person | id, companyId, createdAt |
note / task | one row per activity | id, createdAt, createdBy, target relations |
workspaceMember | one row per member | id, name |
Modeling advice
- Build a
modeled_twenty_opportunitiesview with cleanstage,amount(from amountMicros, one currency), andclose_date. - Derive
modeled_twenty_stage_historyfrom 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?
| Metric | Definition | Notes |
|---|---|---|
| Open pipeline | Sum of value for open opportunities. | Segment by stage, owner, company. |
| Win rate | Won ÷ closed opportunities in a cohort. | Fix the denominator (closed vs. created) before comparing. |
| Sales cycle length | Created → won, in days. | Report median and p90; it's right-skewed. |
| Stage conversion | Opportunities reaching stage N+1 ÷ reaching stage N. | Needs stage-change history. |
| Average deal size | Won value ÷ won opportunities. | Report the median too; outliers distort the mean. |
| Records with open opp | Companies/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.
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;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;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;