How to build Klaviyo dashboards in Metabase
Klaviyo is the email and SMS marketing platform built for ecommerce, holding the record of every campaign, automation flow, and customer engagement event. Metabase is where you turn that activity into shared, trustworthy dashboards. This guide covers two complementary paths: a lightweight MCP + CLI route that pulls live data with the Klaviyo MCP server and loads a CSV into Metabase with the Metabase CLI for quick analysis, and a durable pipeline route that syncs Klaviyo into a database so you can build dashboards anyone can read.
How do you connect Klaviyo to Metabase?
Most teams combine both routes: use the Klaviyo MCP server and Metabase CLI route to pull live data and stand up a quick analysis, and the pipeline route for the dashboards people depend on.
Live data in, quick analysis out
Pair Klaviyo's official remote MCP server (to read live campaign, flow, and metric data) with the Metabase CLI, whose upload command loads a CSV into Metabase as a ready-to-query table and model.
- Quick lookups like "which flow drove the most revenue this week?"
- Loading a Klaviyo CSV export into Metabase in seconds
- Spot-checks and one-off analyses without a warehouse
- Great for exploration, not governed reporting
- Use read-only mode on the Klaviyo MCP to avoid accidental writes
- CSV uploads are snapshots — refresh or move to the pipeline for history
Durable dashboards with history
Sync Klaviyo into a database or warehouse with a managed connector, dlt, or the API, then point Metabase at it.
- Campaign and flow revenue dashboards leaders depend on
- Joining email/SMS engagement with store orders
- Long-run list growth, deliverability, and LTV analysis
- You own the refresh schedule and the data model
- Event volume is high — plan incremental syncs
- Attribution windows must be defined consistently
What can you analyze from Klaviyo data in Metabase?
- Attributed revenue — from campaigns and automation flows
- Engagement — open, click, and conversion rates over time
- Flow performance — welcome, abandoned cart, post-purchase
- List growth — net subscriber growth by source
- Deliverability — bounce, unsubscribe, and spam rates
- Cross-channel — join engagement with store orders for true ROI
Which Klaviyo dashboards should you build in Metabase?
Channel overview
The pulse of email and SMS performance.
- Attributed revenue by channel (bar)
- Campaigns sent vs. revenue (combo)
- Open, click, and conversion rate trend (line)
- Revenue per recipient (number + trend)
Flow performance
Automations that earn while you sleep.
- Revenue by flow (welcome, abandoned cart, post-purchase) (bar)
- Flow conversion rate (table)
- Revenue per recipient by flow (bar)
- Flow vs. campaign revenue split (pie)
List growth & health
Are you building a durable audience?
- Net subscriber growth by week (line)
- New subscribers by source (bar)
- Unsubscribe and spam rate (line)
- Engaged vs. dormant profiles (bar)
Deliverability & engagement
Keep landing in the inbox.
- Bounce rate by campaign (line)
- Open and click rate by segment (bar)
- Spam complaints over time (line)
- Sending volume vs. engagement (combo)
How do you use the Klaviyo MCP server with the Metabase CLI?
Pair the Klaviyo MCP server with the Metabase CLI for fast, hands-on analysis. Klaviyo hosts a first-party remote MCP server with OAuth that reads live campaign, flow, and metric data; the Metabase CLI's upload command loads a CSV into Metabase and creates a ready-to-query table and model. For analysis, connect Klaviyo in read-only mode.
Example workflow
- Ask the Klaviyo MCP which flow or campaign drove the most revenue this week.
- Export the campaign or flow performance 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 Klaviyo MCP is great for live lookups — not for scheduled or audited reporting.
- A CSV upload is a point-in-time snapshot; refresh it with
mb upload replaceor move to the pipeline for real history. - Use Klaviyo's read-only option so analysis can't trigger writes.
mb upload csvneeds an uploads database configured under Admin → Settings → Uploads.
How do you set up the Klaviyo MCP server and the Metabase CLI?
Klaviyo MCPofficial
- Endpoint
https://mcp.klaviyo.com/mcp- Transport
- Streamable HTTP
- Auth
- OAuth (log in to your Klaviyo account)
- Options
?read-only=trueand?core-tools-only=true
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": {
"klaviyo": {
"url": "https://mcp.klaviyo.com/mcp?read-only=true"
}
}
}# 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 Klaviyo CSV export — creates a table AND a model
mb upload csv --file klaviyo-campaigns.csv --collection root
# Refresh that same table later from a new export
mb upload replace <table-id> --file klaviyo-campaigns.csvOn first connection the Klaviyo server opens a browser window to authorize;read-only=true keeps analysis safe from accidental writes. The Metabase CLI stores its credentials securely after mb auth login.
Can you generate a Klaviyo dashboard with AI?
Yes. Use the prompt below with any assistant that can run the Klaviyo MCP server and the Metabase CLI. It works end to end: if Klaviyo tables already exist in Metabase it analyzes those; otherwise it pulls the data over the Klaviyo MCP, loads it with mb upload csv, then builds the dashboard — defining the attribution window and skipping cards it has no data for.
Create a polished Metabase dashboard for Klaviyo email/SMS marketing analytics.
Work end to end: get the data into Metabase if it isn't there yet, then build.
Goal: Help marketing leaders understand attributed revenue, campaign and flow
performance, list growth, and deliverability from Klaviyo data.
Step 1 — Find or load the data:
- First, check what already exists in Metabase (search for Klaviyo tables and
models). If durable Klaviyo data is already present — synced from a warehouse or
uploaded earlier — use it and skip to Step 2.
- If nothing is there, pull it with the Klaviyo MCP server in read-only mode:
campaign performance, flow performance, list growth, and profile/segment
summaries. 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:
Klaviyo CSV exports are usually flat and pre-aggregated (one row per campaign or
flow, with columns like recipients, open rate, click rate, placed-order value,
and revenue per recipient). Warehouse tables are raw and event-grained (filter an
events table by metric: Received/Opened/Clicked Email, Placed Order). Inspect the
actual tables and column names first; do not assume exact names or that an
event-level table exists.
Important:
- Build on whatever data is present; don't claim Metabase connects natively to
Klaviyo — it reads a database or CLI-uploaded tables.
- If the data already provides rates, chart them directly; only recompute over the
delivered (not sent) base when raw counts are available.
- Define the attribution window explicitly (e.g. Klaviyo's default) and state it.
- Separate campaign revenue from flow revenue.
- 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: Klaviyo Marketing Overview
Sections:
1. Executive summary (KPI cards): Attributed revenue last 30 days; Revenue per
recipient; Open rate; Click rate; Net subscriber growth.
2. Campaigns: Revenue by campaign; Open/click/conversion trend; Revenue per
recipient.
3. Flows: Revenue by flow; Flow conversion rate; Flow vs campaign split.
4. List growth: Net growth by week; New subscribers by source; Unsubscribe rate.
5. Deliverability: Bounce rate; Spam complaints; Engagement by segment.
Filters: Date range, Channel (email/SMS), Campaign, Flow, Segment.
Reuse the models Metabase auto-created from uploaded CSVs, or (for a warehouse)
create reusable models: modeled_klaviyo_campaigns, modeled_klaviyo_flows,
modeled_klaviyo_events, and modeled_klaviyo_profiles.
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 sync Klaviyo data into a database or warehouse?
For dashboards that need history and reliability, land Klaviyo data in a database first, then connect Metabase to that database.
Connector options
- Managed ETL — Klaviyo is a common source for connectors like Fivetran and Airbyte, which land campaigns, flows, events, and profiles into your warehouse.
- dlt (code) — write a Python pipeline against the Klaviyo API for full control over tables and refresh.
- Klaviyo REST API (raw) — the source of truth; the Events endpoint carries high volume, so sync incrementally.
- Join with store data — sync Shopify or another store alongside Klaviyo to reconcile attributed revenue against actual orders.
Notes
- Land raw tables first, then build clean models on top.
- Event volume is large — plan incremental, timestamp-based syncs.
- Pin the attribution window so campaign and flow revenue stay comparable.
How should you model Klaviyo data in Metabase?
Core tables
| Table | Grain | Key columns |
|---|---|---|
campaigns | one row per campaign | id, name, channel, send_time |
flows | one row per flow | id, name, status, trigger_type |
events | one row per event | metric, profile_id, campaign_id, flow_id, value, timestamp |
profiles | one row per profile | id, email, created, source |
lists | one row per list/segment | id, name, type |
Modeling advice
- The
eventstable is the engine — filter bymetric(Received/Opened/Clicked Email, Placed Order) for engagement and revenue. - Compute rates over the delivered base (Received Email), not the number sent.
- Separate campaign revenue from flow revenue; label each explicitly.
- Define the attribution window once and reuse it everywhere.
Which Klaviyo metrics should you track in Metabase?
| Metric | Definition | Notes |
|---|---|---|
| Attributed revenue | Order value tied to a send. | State the attribution window. |
| Revenue per recipient | Attributed revenue ÷ delivered. | Comparable across campaign sizes. |
| Open / click rate | Opens or clicks ÷ delivered. | Use delivered, not sent, as the base. |
| Net list growth | New subscribers − unsubscribes. | Segment by acquisition source. |
| Unsubscribe / spam rate | Opt-outs or complaints ÷ delivered. | Watch after volume spikes. |
What SQL powers Klaviyo dashboards in Metabase?
These assume synced tables in a warehouse (PostgreSQL dialect). Adjust metric names to match your Klaviyo account and connector.
Placed Order events tied to each campaign over 30 days.
SELECT
c.name AS campaign,
COUNT(*) FILTER (WHERE e.metric = 'Placed Order') AS orders,
SUM(e.value) FILTER (WHERE e.metric = 'Placed Order') AS attributed_revenue
FROM events e
JOIN campaigns c ON c.id = e.campaign_id
WHERE e.timestamp >= CURRENT_DATE - INTERVAL '30 days'
GROUP BY c.name
ORDER BY attributed_revenue DESC NULLS LAST
LIMIT 20;Engagement computed over the delivered base.
SELECT
date_trunc('week', e.timestamp) AS week,
COUNT(*) FILTER (WHERE e.metric = 'Received Email') AS delivered,
ROUND(100.0 * COUNT(*) FILTER (WHERE e.metric = 'Opened Email')
/ NULLIF(COUNT(*) FILTER (WHERE e.metric = 'Received Email'), 0), 2) AS open_rate,
ROUND(100.0 * COUNT(*) FILTER (WHERE e.metric = 'Clicked Email')
/ NULLIF(COUNT(*) FILTER (WHERE e.metric = 'Received Email'), 0), 2) AS click_rate
FROM events e
GROUP BY 1
ORDER BY 1;Profile creation trend over the last 180 days.
SELECT
date_trunc('week', p.created) AS week,
COUNT(*) AS new_profiles
FROM profiles p
WHERE p.created >= CURRENT_DATE - INTERVAL '180 days'
GROUP BY 1
ORDER BY 1;