How to build Vitally customer success dashboards in Metabase
Vitally is a customer success platform popular with mid-market B2B SaaS teams — accounts and users, multiple named health scores, conversations, notes, tasks, and NPS, with a clean REST API. Metabase is where you turn that record into shared, trustworthy customer success dashboards. This guide covers two complementary paths: a lightweight API + CLI route that pulls live data via the Vitally REST API and loads a CSV into Metabase with the Metabase CLI, and a durable pipeline route that lands Vitally data in a database so you can build dashboards anyone can read.
How do you connect Vitally to Metabase?
Most teams combine both routes: quick answers through API exports and CLI uploads first, then recurring reporting on a warehouse-backed model.
Live answers in, quick analysis out
Script a scoped export against the Vitally REST API, write the result to CSV, then load it into Metabase as a ready-to-query table and model. No vendor MCP server exists for Vitally, so the export script is the fast route.
- Quick questions such as "show me health score distribution across the account base"
- Loading Vitally exports into Metabase in seconds
- Spot-checks and one-off investigations without pipeline work
- Great for exploration, not the governed numbers a QBR runs on
- Use read-only credentials or scoped API keys wherever supported
- CSV uploads are snapshots — refresh or move to the pipeline for history
Durable dashboards with history
Land Vitally data in a database or warehouse — via connector or scheduled API pulls — then point Metabase at it.
- Vitally dashboards CS and finance both trust
- Joining health data with product usage, support, and billing
- Long-run trends for health score distribution across the account base and score movers: accounts improving and declining
- You own the refresh schedule and the snapshot grain
- Re-derive health score components once, in the warehouse layer
- Current-state fields need dated snapshots or history never exists
What can you analyze from Vitally data in Metabase?
These all come from the same core objects — accounts and their health scores, plus the users, conversations and notes, tasks and playbooks your sync exposes:
- Health score distribution across the account base
- Score movers: accounts improving and declining
- NPS by segment and its trend
- CSM workload and task completion
- Health score vs. product usage reality check
Which Vitally customer success dashboards should you build in Metabase?
Health overview
The distribution behind the average.
- Accounts by risk tier (bar)
- MRR by risk tier (bar)
- Median health score by segment (bar)
- Accounts that changed tier this month (table)
Score movers
The accounts that moved, not the ones that sat still.
- Biggest score gains and drops this month (table)
- Score trend for the top 10 accounts by MRR (line)
- Accounts crossing into the at-risk tier (table)
- Days since last conversation, by account (bar)
Voice of the customer
NPS read next to health, not on its own.
- NPS by segment (bar)
- NPS trend by quarter (line)
- Survey response rate (number + trend)
- Detractors with an open task (table)
Workload
Whether coverage matches the book of business.
- Accounts and MRR per CSM (bar)
- Open tasks by owner and age (table)
- Task completion rate (number + trend)
- Conversations per account per month (line)
How do you use the Vitally REST API with the Metabase CLI?
Pair the Vitally REST API with the Metabase CLI for fast, hands-on analysis. With no vendor MCP server available, scripted API exports do the pulling here; the Metabase CLI's upload command loads CSV data into Metabase and creates a ready-to-query table and model.
Example workflow
- Ask the API for every account with its current health scores and MRR, one row per account per named score.
- Export the result as CSV, keeping stable IDs, owners, statuses, and dates — and hashing or dropping any personal contact fields.
- 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
- Scripted API exports are excellent for exploration, not scheduled reporting.
- A CSV upload is a snapshot; refresh it with
mb upload replaceor move to the pipeline for real history. - The API returns current score values, so a trend needs repeated extracts appended over time — one export is a single point in time.
mb upload csvneeds an uploads database configured under Admin → Settings → Uploads.
How do you set up Vitally exports and the Metabase CLI?
Vitally REST APIcommunity
- Transport
- No vendor server; community projects run locally over stdio
- Auth
- HTTP Basic with your Vitally API key as the username
- Best for
- Live scoped account lookup and export
Metabase CLIofficial
- Install
npm install -g @metabase/cli- Auth
mb auth login- Load data
mb upload csv --file data.csv- Requires
- An uploads database (Admin → Settings → Uploads)
# No vendor MCP server exists. Export directly from the REST API:
curl -s "https://$SUBDOMAIN.rest.vitally.io/resources/accounts?limit=100" \
-u "$VITALLY_API_KEY:" \
| jq -r '.results[] | [.id, .name, .mrr, .createdAt,
(.traits.segment // "")] | @csv' \
> vitally-accounts.csvVitally publishes no MCP server (verified July 2026), and its own API documentation doesn't mention MCP at all — so this guide leads with a scripted export. Community options exist, but read the fine print before adopting one: the most complete, fiscaltec/vitally-mcp, is published by FISCAL Technologies, a company unaffiliated with Vitally, under a proprietary license with a hosted endpoint aimed at its own customers. It is not a first-party server despite how the name reads. If Vitally ships an official server later, only route 1's export step changes.
# 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 accounts export — creates a table AND a model
mb upload csv --file vitally-accounts.csv --collection root
# Refresh that same table later from a new export
mb upload replace <table-id> --file vitally-accounts.csvCan you generate a Vitally dashboard with AI?
Yes. Use the prompt below with any assistant that can run shell commands (for the API export) and the Metabase CLI. It works end to end: if Vitally tables already exist in Metabase it analyzes those; otherwise it pulls scoped, summarized data, loads it with mb upload csv, then builds the dashboard and caveats any metric that needs missing history.
Create a polished Metabase dashboard for Vitally customer success analytics.
Work end to end: get the data into Metabase if it isn't there yet, then build.
Goal: Help customer success and revenue leaders understand health score distribution, the accounts moving most, NPS by segment, and CSM workload from Vitally data.
Step 1 — Find or load the data:
- First, check what already exists in Metabase (search for vitally tables and
models). If durable Vitally data is already present — a warehouse sync or
an earlier upload — use it and skip to Step 2.
- If nothing is there, pull a scoped, summarized export by scripting the Vitally REST API:
accounts and their health scores, plus users, conversations and notes, tasks and playbooks.
Prefer one row per account per snapshot date over raw activity
feeds. 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:
Do not assume exact table or column names. Inspect available fields, owners,
segments, statuses, and date ranges before creating trend cards.
Important:
- Build on whatever data is present; don't claim Metabase connects natively to
Vitally — it reads a database or CLI-uploaded tables.
- State which health score a card uses and when it was captured. Vendor scores
are opaque composites and get redefined without notice.
- A single CSV is a point-in-time snapshot: health scores, phases, and renewal
dates are current-state fields, so only build trend cards once several dated
snapshots exist.
- Reconcile ARR against billing data, not against the Vitally record — CS
platforms hold a projection of CRM, billing, and product data.
- Only compute retention where subscription or license values exist. Gross
revenue retention excludes expansion and can never exceed 100%; net revenue
retention includes it and can.
Dashboard title: Vitally Customer Success Overview
Sections:
1. Executive summary: Accounts by risk tier; MRR at risk; Median health
score; NPS; Accounts with no conversation in 30 days.
2. Health: Risk tier distribution; MRR by tier; median score by segment.
3. Movers: Biggest gains and drops; accounts crossing into at risk.
4. Voice of the customer: NPS by segment; NPS trend; response rate.
5. Workload: Accounts and MRR per CSM; open tasks by age; completion rate.
Filters: Date range, CSM owner, Segment, Risk tier, Lifecycle stage.
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.How do you sync Vitally data into a database or warehouse?
For dashboards that need history and reliability, land Vitally data in a database first, then connect Metabase to that database.
Connector options
- Managed ETL — use a connector when one covers the objects you need, and read its support level and sync mode before you rely on it.
- Custom pipeline — use the Vitally REST API for control over snapshot grain, fields, and refresh cadence.
- API + CSV — use this for quick exploration and one-off slices.
Both major ETL vendors cover Vitally, with caveats. Airbyte's <code>source-vitally</code> (Marketplace support level) syncs accounts, admins, conversations, notes, NPS responses, tasks, and users — but it is <strong>full-refresh only</strong>, so every sync re-reads the whole account base and deletions never propagate as deletions. Fivetran offers an Application Lite connector. Scripting the API yourself is well-supported: cursor pagination with a 100-record page limit, sorting by <code>createdAt</code> or <code>updatedAt</code> for genuine incremental pulls, and a generous 1,000 requests per minute.
Notes
- Decide the snapshot grain first (one row per account per day, or per account per week) — it drives storage and every trend card.
- Land raw tables first, then build clean Metabase models on top.
- Normalize account-id, csm-owner, segment, snapshot-date, score-name, and score-value fields.
How should you model Vitally data in Metabase?
Core tables
| Table | Grain | Key columns |
|---|---|---|
cs_accounts | one row per account | account_id, name, mrr, created_at, segment, csm_owner, churned_at, traits |
account_health_scores | one row per account per named score per snapshot date | account_id, snapshot_date, score_name, score_value, grade, category |
cs_activities | one row per conversation, note, or task | activity_id, account_id, type, subject, created_at, completed_at, owner |
Modeling advice
- Build a clean
cs_accountsmodel with common columns across CS tools, so multi-source dashboards don't fork definitions. - Re-derive the health score components in the warehouse and keep the vendor's score beside your own as a separate, labeled column. Vendor scores are opaque composites, and they get redefined without anyone telling the analytics team.
- Snapshot renewal forecasts into a
renewal_forecast_snapshotstable frozen at period start — accuracy cannot be measured against a forecast that keeps moving. - Model gross and net revenue retention separately: gross excludes expansion and can never exceed 100%, net includes it and can.
- Reconcile ARR against billing rather than the CS platform. What a CS tool holds is a projection of CRM, billing, and product data, and projections drift.
Which Vitally customer success metrics should you track in Metabase?
| Metric | Definition | Notes |
|---|---|---|
| Customer health score | The named score you have chosen as the account's headline. | Pick one named score and say which one on the card. |
| Churn rate | Accounts or MRR lost in a period over the opening base. | Logo churn and revenue churn tell different stories. |
| Net revenue retention | Retained plus expansion revenue from an existing cohort. | Unlike GRR, this one can and should exceed 100%. |
| Time to value | Days from signup to the first realized customer outcome. | Product usage data, not the CS record, defines it. |
What SQL powers Vitally customer success dashboards in Metabase?
These assume a cleaned analytical model in a warehouse (PostgreSQL dialect). Adjust table and column names to match your pipeline.
One named score, bucketed, with annualized MRR per tier.
WITH latest AS (
SELECT DISTINCT ON (account_id)
account_id,
score_value
FROM account_health_scores
WHERE score_name = 'Overall Health'
ORDER BY account_id, snapshot_date DESC
)
SELECT
CASE
WHEN l.score_value >= 80 THEN 'healthy'
WHEN l.score_value >= 60 THEN 'watch'
ELSE 'at risk'
END AS risk_tier,
COUNT(*) AS accounts,
ROUND(SUM(a.mrr) * 12, 0) AS annualized_mrr
FROM latest l
JOIN cs_accounts a ON a.account_id = l.account_id
WHERE a.churned_at IS NULL
GROUP BY 1
ORDER BY MIN(l.score_value);Month-end values compared, ranked by absolute change.
WITH month_end AS (
SELECT DISTINCT ON (account_id, date_trunc('month', snapshot_date))
account_id,
date_trunc('month', snapshot_date) AS month,
score_value
FROM account_health_scores
WHERE score_name = 'Overall Health'
ORDER BY account_id, 2, snapshot_date DESC
),
movement AS (
SELECT
account_id,
month,
score_value,
LAG(score_value) OVER (PARTITION BY account_id ORDER BY month)
AS prior_value
FROM month_end
)
SELECT
a.name,
a.csm_owner,
a.mrr,
mv.prior_value,
mv.score_value AS current_value,
ROUND((mv.score_value - mv.prior_value)::numeric, 1) AS score_change
FROM movement mv
JOIN cs_accounts a ON a.account_id = mv.account_id
WHERE mv.month = date_trunc('month', CURRENT_DATE)
AND mv.prior_value IS NOT NULL
AND a.churned_at IS NULL
ORDER BY ABS(mv.score_value - mv.prior_value) DESC
LIMIT 25;The reality check: does the score match what accounts actually do?
WITH latest AS (
SELECT DISTINCT ON (account_id)
account_id,
score_value
FROM account_health_scores
WHERE score_name = 'Overall Health'
ORDER BY account_id, snapshot_date DESC
),
usage AS (
SELECT
account_id,
COUNT(*) FILTER (WHERE active_users > 0) AS active_days_30d,
AVG(active_users) AS avg_daily_active_users
FROM product_usage_daily
WHERE usage_date >= CURRENT_DATE - INTERVAL '30 days'
GROUP BY account_id
)
SELECT
CASE
WHEN l.score_value >= 80 THEN 'healthy'
WHEN l.score_value >= 60 THEN 'watch'
ELSE 'at risk'
END AS risk_tier,
COUNT(*) AS accounts,
ROUND(AVG(COALESCE(u.active_days_30d, 0))::numeric, 1) AS avg_active_days_30d,
ROUND(AVG(COALESCE(u.avg_daily_active_users, 0))::numeric, 1) AS avg_dau
FROM latest l
JOIN cs_accounts a ON a.account_id = l.account_id
LEFT JOIN usage u ON u.account_id = l.account_id
WHERE a.churned_at IS NULL
GROUP BY 1
ORDER BY MIN(l.score_value);What are common mistakes when analyzing Vitally in Metabase?
Related
Related analytics
Related dashboards
Related integrations
FAQ
Does Metabase connect natively to Vitally?
Should Metabase replace Vitally?
Is there a Vitally MCP server?
Can I sync Vitally with Airbyte?
source-vitally sits at Marketplace support level and syncs accounts, admins, conversations, notes, NPS responses, tasks, and users — but it is full-refresh only, so every sync re-reads the whole account base and deletions never propagate as deletions. If you need genuine incremental pulls, script the REST API instead: cursor pagination with a 100-record page limit, sorting by createdAt or updatedAt, and a generous 1,000 requests per minute. Fivetran offers an Application Lite connector.