Dashboard Expert
OpenAIOfficialCreated by MixpanelDistributed by OpenAI
- Skill
- Tool integration
- Data and AI
- MIT
- No issues found
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
Dashboard Expert provides a comprehensive framework for interacting with Mixpanel dashboards. It supports four primary modes: analyzing existing dashboards to extract insights, building new dashboards from scratch, modifying existing layouts or content, and adding data-driven annotations to provide context. The skill emphasizes a structured workflow, ensuring that data is investigated before building, and that modifications follow strict API ordering requirements to maintain dashboard integrity.
What it does
- Analyzes dashboard structure and executes underlying reports to synthesize findings.
- Builds new dashboards by planning layouts and querying metrics.
- Modifies existing dashboards by adding, reordering, or deleting content.
- Annotates dashboards with data-driven text cards to provide context.
- Validates data availability before creating new reports.
- Ensures correct API operation ordering for dashboard updates.
When to use it
- When you need to understand the performance metrics displayed on a dashboard.
- When creating a new dashboard to track product health or specific user behaviors.
- When updating an existing dashboard to include new metrics or improve layout.
- When you need to add explanatory text to clarify data trends for stakeholders.
Advantages
- Provides a consistent, repeatable process for dashboard management.
- Reduces errors by enforcing correct API operation sequences.
- Enables data-driven insights through automated report execution and summarization.
- Supports complex layout management including row and cell organization.
Usage examples
- “Analyze the Product Health dashboard and summarize the key trends.”
- The agent will read the dashboard structure, execute all contained reports, and provide a summary of metrics, trends, and cross-report correlations.
- “Create a new dashboard for tracking signup conversion.”
- The agent will investigate relevant events, propose a layout, and build a new dashboard with appropriate funnel and insight reports.
- “Add a text card to the top of my dashboard explaining the recent drop in DAU.”
- The agent will analyze the current dashboard, generate an insightful text card based on the data, and insert it into the layout.
Install
Download the skill as a ZIP:
https://mp-staging.autogineer.com/agent-skills/openai/dashboard-expert/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip dashboard-expert.zip -d .agents/skills
Documentation by Mixpanel
Dashboard Expert
Analyze, build, modify, and explain Mixpanel dashboards. Four modes — pick the one matching the user's intent.
Mode Selection
| User intent | Mode | Key actions |
|---|---|---|
| "analyze/understand/read/explore dashboard" | Analyze | Read structure, execute reports, summarize |
| "build/create/make a new dashboard" | Build | Investigate data → plan → create with layout |
| "modify/update/add to/fix/improve dashboard" | Modify | Read current state → plan changes → execute |
| "explain/annotate/add insights to dashboard" | Explain | Analyze → generate data-driven text cards |
Quick Start: Analyze an Existing Dashboard
import json, re
import mixpanel_headless as mp
ws = mp.Workspace()
dash = ws.get_dashboard(DASHBOARD_ID)
layout, contents = dash.layout, dash.contents
# Extract structure: rows → cells → content items
for row_id in layout["order"]:
row = layout["rows"][row_id]
for cell in row["cells"]:
cid, ctype = str(cell["content_id"]), cell["content_type"]
if ctype in ("report", "report-link"):
info = contents["report"][cid]
print(f" [{cell['width']}w] {info['name']} ({info['type']}) {'[linked]' if ctype == 'report-link' else ''}")
elif ctype == "text":
md = contents["text"][cid].get("markdown", "")
is_header = bool(re.search(r'<h2[\s>]', md, re.I))
print(f" [{cell['width']}w] TEXT {'[SECTION]' if is_header else ''}: {md[:60]}...")
# Execute each report → DataFrame
for cid, info in contents.get("report", {}).items():
btype, bid = info["type"], info["id"]
if btype == "flows":
result = ws.query_saved_flows(bid)
else:
result = ws.query_saved_report(bid, bookmark_type=btype)
df = result.df
print(f"{info['name']}: {len(df)} rows, columns={list(df.columns)}")Quick Start: Build a New Dashboard
from mixpanel_headless.types import CreateDashboardParams, DashboardRow, DashboardRowContent
import json
ws = mp.Workspace()
dau = ws.query("Login", math="dau", last=90)
def text(html):
return DashboardRowContent(content_type="text", content_params={"markdown": html})
def report(name, btype, result):
return DashboardRowContent(content_type="report", content_params={
"bookmark": {"name": name, "type": btype, "params": json.dumps(result.params)}})
dashboard = ws.create_dashboard(CreateDashboardParams(
title="Product Health", description="Core metrics.",
rows=[
DashboardRow(contents=[text("<h2>Product Health</h2><p>Core metrics.</p>")]),
DashboardRow(contents=[report("DAU (90d)", "insights", dau)]),
],
))
ws.pin_dashboard(dashboard.id) # Make visible to teamMode: Analyze
Read existing dashboards, execute their reports, and synthesize understanding.
Phase A1: Read Dashboard Structure
dash = ws.get_dashboard(dashboard_id)
layout, contents = dash.layout, dash.contentsParse the response into a structured representation:
layout["order"]— ordered list of row IDslayout["rows"][row_id]["cells"]— cells withcontent_id,content_type,widthcontents["report"][str(content_id)]— report metadata:id(bookmark_id),name,type,params,descriptioncontents["text"][str(content_id)]— text card:markdown
Classify each cell:
content_type == "report"→ owned, editablecontent_type == "report-link"→ linked from another dashboard, read-onlycontent_type == "text"→ text card; detect section headers viare.search(r'<h2[\s>]', md, re.I)
Build a mental model: Group reports by section (text cards with <h2> tags delimit sections). Note each report's chart type, width, and position.
Phase A2: Extract Report Details
For deeper understanding, fetch full bookmark params:
bookmark = ws.get_bookmark(bookmark_id)
params = bookmark.params # Full query definition dictKey fields in params (Insights format):
params["sections"]["show"]— metrics with event names and math typeparams["sections"]["group"]— breakdown propertiesparams["sections"]["filter"]— active filtersparams["sections"]["time"]— date rangeparams["displayOptions"]["chartType"]— visualization type
Note: params in contents["report"][id] may be a JSON string — parse with json.loads() if needed.
Phase A3: Execute and Summarize
Execute each report to get live data:
for cid, info in contents.get("report", {}).items():
bid, btype = info["id"], info["type"]
if btype == "flows":
result = ws.query_saved_flows(bid)
else:
result = ws.query_saved_report(bid, bookmark_type=btype)
df = result.dfSummarize by report type:
| Type | Key metrics to extract |
|---|---|
| insights | Total, average, latest value, min, max, trend direction |
| funnels | Step names, counts, per-step and overall conversion rate |
| retention | Day 1, Day 7, Day 30 rates; stabilization point |
| flows | Top paths, conversion rate, drop-off points |
Cross-correlate across reports: Look for relationships — DAU trends vs. retention, funnel drop-off vs. feature adoption.
Phase A4: Present Analysis
Structure findings as:
Dashboard overview — title, purpose, section count, report count
Section-by-section breakdown — what each section measures, key findings
Cross-metric insights — correlations, anomalies, patterns
Suggestions — missing metrics, better chart types, layout improvements
Multi-Dashboard Analysis
When analyzing multiple dashboards, build a unified picture:
dashboard_ids = [1001, 1002, 1003]
all_data = {}
for did in dashboard_ids:
dash = ws.get_dashboard(did)
for cid, info in dash.contents.get("report", {}).items():
result = ws.query_saved_report(info["id"], bookmark_type=info["type"])
all_data[f"{dash.title}/{info['name']}"] = result.df
# Cross-dashboard: join DataFrames on date index, compute correlationsMode: Build
Create new dashboards from scratch. Five phases.
Phase B1: Investigate
Before building, discover the data. Never build reports for events with zero volume.
ws = mp.Workspace()
top = ws.top_events(limit=15)
for t in top:
print(f"{t.event}: {t.count:,} ({t.percent_change:+.1%})")
# Validate candidate events
for event in candidate_events:
result = ws.query(event, from_date="2025-01-01", to_date="2025-03-31")
print(f"{event}: {result.df['count'].sum():,.0f} total")
# Explore properties for breakdowns
props = ws.properties(event="key_event")
values = ws.property_values(event="key_event", property="platform", limit=20)Phase B2: Plan Structure
Present a proposed structure before building. Choose a template from references/dashboard-templates.md.
A plan includes: title + description, sections with text card headers, reports per section with chart type, grid layout.
Text cards use HTML (not markdown). Every dashboard must have an intro text card and section headers.
Allowed HTML tags: <h1>, <h2>, <h3>, <p>, <strong>, <em>, <u>, <s>, <mark>, <code>, <blockquote>, <hr>, <br>, <ul>, <ol>, <li>, <a href="...">
Forbidden (stripped): <div>, <span>, <b> (use <strong>), <i> (use <em>), <img>, <table>
Critical: Strip \n and collapse whitespace from HTML before sending. Each element renders as its own line.
Text card patterns:
Intro: <h2>Dashboard Title</h2><p>What and why. Time period: last 90 days.</p>
Section: <h2>Acquisition</h2><p>How users discover and sign up.</p>
Explainer: <p>^ Signup conversion is <strong>23.4%</strong>, up 2.1pp.</p>Phase B3: Query and Build
Query each metric, verify data, then create with layout in one call.
def text(html):
return DashboardRowContent(content_type="text", content_params={"markdown": html})
def report(name, btype, result, description=None):
params = {"bookmark": {"name": name, "type": btype, "params": json.dumps(result.params)}}
if description:
params["bookmark"]["description"] = description
return DashboardRowContent(content_type="report", content_params=params)
dashboard = ws.create_dashboard(CreateDashboardParams(
title="Product Health Dashboard",
description="Key metrics for product health monitoring.",
rows=[
DashboardRow(contents=[text("<h2>Product Health</h2><p>Updated daily.</p>")]),
DashboardRow(contents=[
report("DAU (90d)", "insights", dau),
report("Signups (90d)", "insights", signups),
report("Revenue (90d)", "insights", revenue),
]),
DashboardRow(contents=[text("<h2>Conversion</h2><p>Key funnels.</p>")]),
DashboardRow(contents=[report("Signup Funnel", "funnels", funnel)]),
],
))On report failure, substitute a fallback text card:
try:
result = ws.query(event, math="total", last=90)
row_items.append(report(f"{event} Trend", "insights", result))
except Exception as e:
row_items.append(text(f"<p><strong>Failed:</strong> {event} — {e}</p>"))Phase B4: Enhance
Pin for team visibility:
ws.pin_dashboard(dashboard.id)— dashboards are invisible by defaultFavorite for personal use:
ws.favorite_dashboard(dashboard.id)Add explainer cards: see Mode: Explain
Adjust heights: see
references/dashboard-reference.mdSection 3.4
Phase B5: Verify
Open the dashboard and confirm all reports render with data, text cards display correctly, and layout matches the plan.
Mode: Modify
Update existing dashboards. Read first, then apply changes in the correct order.
Phase M1: Read Current State
Use Analyze Phase A1-A2 to understand the dashboard's structure. Present to user before making changes.
Phase M2: Plan Changes
Classify each change and plan execution order. Operations must follow this sequence:
Metadata (title/description) — standalone PATCH
Cell creates — add new content first
Row reorder (
rows_order) — after creates so temp IDs resolveCell updates — modify existing content
Cell deletes — remove content
Row deletes — remove entire rows last
Phase M3: Execute Changes
Adding content to a specific existing row — send content AND layout together:
import copy
dash = ws.get_dashboard(dashboard_id)
layout = copy.deepcopy(dash.layout)
target_row = layout["rows"][target_row_id]
# Redistribute widths
new_count = len(target_row["cells"]) + 1
cell_width = 12 // new_count
for cell in target_row["cells"]:
cell["width"] = cell_width
target_row["cells"].append({"temp_id": "-1", "width": cell_width})
ws.update_dashboard(dashboard_id, UpdateDashboardParams(
content={"action": "create", "content_type": "report",
"content_params": {"bookmark": {"name": "New Report", "type": "insights",
"params": json.dumps(result.params)}}},
layout={"rows_order": layout["order"], "rows": layout["rows"]},
))Adding content as a new row — content action alone (appends to bottom):
ws.update_dashboard(dashboard_id, UpdateDashboardParams(
content={"action": "create", "content_type": "text",
"content_params": {"markdown": "<p>^ Explainer card.</p>"}},
))Deleting content:
ws.update_dashboard(dashboard_id, UpdateDashboardParams(
content={"action": "delete", "content_type": "report", "content_id": content_id},
))Cross-type updates (e.g., text → report): API rejects changing content_type on update. Delete the old cell, then create the new one.
See references/dashboard-reference.md Section 8 for temp ID resolution, operation ordering details, and report-link semantics.
Mode: Explain
Combine analysis with targeted text card insertion.
Analyze — run Mode: Analyze to extract structure and execute reports
Generate insights — for each report, compute key metrics from the DataFrame:
latest = df.iloc[-1]["count"] prev = df.iloc[-8]["count"] trend = ((latest - prev) / prev) * 100 html = (f"<p>^ DAU is <strong>{latest:,.0f}</strong>, " f"{'up' if trend > 0 else 'down'} <strong>{abs(trend):.1f}%</strong> " f"vs. last week.</p>").replace("\n", "")Insert cards — add as new rows below each report section:
ws.update_dashboard(dashboard_id, UpdateDashboardParams( content={"action": "create", "content_type": "text", "content_params": {"markdown": html}}, ))
Critical Gotchas
Combined content+layout PATCH — send both
contentandlayoutin the sameUpdateDashboardParamsto add cells to specific existing rows. Withoutlayout, new content appends as a full-width row at the bottom.Width auto-redistribution — when adding to an existing row with N cells, set all cells (including new) to
12 // (N+1)width.Update operation ordering — metadata → cell creates → rows_order → cell updates → cell deletes → row deletes. Wrong order causes failures.
per_userrequiresmath_property— using per-user aggregation without a numeric property raisesBookmarkValidationError.CreateBookmarkParams(dashboard_id=X)does NOT add to layout — useadd_report_to_dashboard()or inline content action.add_report_to_dashboard()CLONES — creates "Duplicate of..." copy. UserowsinCreateDashboardParamsor inline content action instead.GET
ordervs PATCHrows_order— layout from GET usesorder; PATCH expectsrows_order.Never include
versionin layout PATCH — the API rejects it.Strip
\nand collapse whitespace — call.replace("\n", "").strip()on text card HTML. Newlines cause TipTap to mangle content.Limits — title 255 chars, description 400 chars, text cards 2,000 chars, max 4 items/row, max 30 rows.
Cross-type cell updates require delete+create — API rejects changing
content_typeon an update action.Report-link cells are read-only —
content_type: "report-link"references a report owned by another dashboard. You can view but not edit its params.Auto-pin after creation — dashboards are invisible to the team by default. Call
ws.pin_dashboard(dashboard.id).The
markdownfield accepts only HTML — despite the name. Markdown syntax renders as literal text.
See Also
references/dashboard-reference.md— Complete API reference, layout system, content actions, text card formatting, update operations, analysis patternsreferences/dashboard-templates.md— 9 purpose-built dashboard templates with section layouts and report specsreferences/bookmark-pipeline.md— End-to-end pipeline from typed query to dashboard report for all 4 enginesreferences/chart-types.md— Chart type selection guide with slugs, use cases, and width recommendations
Files
- ATTRIBUTION.mdAdded by AutoGineer937 B
- LICENSELicence file added by AutoGineer1.0 KiB
- SKILL.md15.4 KiB
- references/bookmark-pipeline.md9.0 KiB
- references/chart-types.md7.3 KiB
- references/dashboard-reference.md37.1 KiB
- references/dashboard-templates.md35.1 KiB
references/chart-types.md
Chart Type Selection Guide
Quick-lookup reference for choosing the right chart type per report type, with width recommendations for dashboard layout.
1. Decision Tree
| Question | Chart Type | chartType | plotStyle |
|---|---|---|---|
| Need a single headline number? | Metric | insights-metric | |
| Tracking something over time? | Line | line | |
| Comparing categories? | Bar | bar | |
| Showing composition over time? | Stacked Line | line | stacked |
| Showing composition across categories? | Stacked Bar | bar | stacked |
| Need detailed data? | Table | table | |
| Measuring conversion? | Funnel Steps | funnel-steps | |
| Measuring retention? | Retention Curve | retention-curve | |
| Exploring user paths? | Sankey | sankey |
2. Insights Chart Types
| Chart Type | chartType | plotStyle | Best For | Width |
|---|---|---|---|---|
| Line | line | standard | Trends over time | 6 or 12 |
| Stacked Line | line | stacked | Composition trends over time | 12 |
| Bar | bar | standard | Categorical comparisons, rankings | 6 or 12 |
| Stacked Bar | bar | stacked | Composition across categories | 12 |
| Column | column | standard | Vertical bar, fewer categories | 6 or 12 |
| Stacked Column | column | stacked | Composition across categories (vertical) | 12 |
| Pie | pie | Share/proportion (max 6 segments) | 6 | |
| Table | table | Multi-dimensional detailed data | 12 | |
| Metric | insights-metric | Single KPI headline number | 3 or 4 |
How Stacking Works
Stacked charts are NOT separate chart types. They use the base chartType (line, bar, or column) with plotStyle set to "stacked" in displayOptions:
"displayOptions": {
"chartType": "bar",
"plotStyle": "stacked"
}NEVER use bar-stacked, stacked-line, or stacked-column as chartType values — these are display-layer labels used by the Mixpanel frontend, not valid API values. The API will reject them.
Valid chartType values: line, bar, column, pie, table, insights-metric, funnel-steps, funnel-top-paths, retention-curve, frequency-curve
Valid plotStyle values: standard (default), stacked
When to Use / When NOT to Use
Line -- Use for time series with continuous data. Not for categorical comparisons or single data points.
Bar -- Use for ranking or comparing discrete categories. Not for time series (use line instead).
Stacked Bar/Line/Column -- Use to show part-to-whole composition. Set
chartTypeto the base type andplotStyleto"stacked". Not when individual values matter more than composition.Column -- Use like bar but vertical; better with fewer categories. Not for many categories (labels overlap).
Pie -- Use for simple share breakdowns with 2-6 segments. Not for more than 6 segments or precise comparisons.
Table -- Use when exact values or multiple dimensions are needed. Not for quick visual scanning.
Metric -- Use for a single KPI number (DAU, revenue, conversion rate). Not for trends or comparisons.
3. Funnel Chart Types
| Chart Type | Slug | Best For | Width |
|---|---|---|---|
| Steps | funnel-steps | Standard funnel visualization | 12 (3+ steps), 6 (2 steps) |
| Trend | funnel-trend | Conversion rate over time | 6 or 12 |
| Top Paths | funnel-top-paths | Alternative paths through funnel | 12 |
| Frequency Line | funnel-frequency-line | How often users convert | 6 |
| Frequency Bar | funnel-frequency-bar | Conversion frequency distribution | 6 |
| Time to Convert (Line) | funnel-ttc-line | Duration analysis | 6 or 12 |
| Time to Convert (Bar) | funnel-ttc-bar | Duration distribution | 6 |
| Median TTC | funnel-median-ttc | Central tendency of conversion time | 6 |
4. Retention Chart Types
| Chart Type | Slug | Best For | Width |
|---|---|---|---|
| Curve | retention-curve | Classic retention decay curve | 12 |
| Table | retention-table | Cohort-by-cohort retention grid | 12 |
| Trend | retention-trend | Retention rate over time | 6 or 12 |
| Trend Metric | retention-trend-metric | Single retention rate number | 3 or 4 |
| Line | line-retention | Retention as line chart | 6 or 12 |
5. Flows Chart Types
| Chart Type | Slug | Best For | Width |
|---|---|---|---|
| Sankey | sankey | User journey visualization | 12 |
| Paths | paths | Path frequency analysis | 12 |
6. Math Types
| Math | Slug | Requires Property | Description |
|---|---|---|---|
| Total | total | No | Count of events |
| Unique | unique | No | Count of unique users |
| DAU | dau | No | Daily active users |
| WAU | wau | No | Weekly active users |
| MAU | mau | No | Monthly active users |
| Sum | total | Yes (numeric) | Sum of property values |
| Average | average | Yes (numeric) | Average property value |
| Median | median | Yes (numeric) | Median property value |
| Min | min | Yes (numeric) | Minimum property value |
| Max | max | Yes (numeric) | Maximum property value |
| P25 | p25 | Yes (numeric) | 25th percentile |
| P75 | p75 | Yes (numeric) | 75th percentile |
| P90 | p90 | Yes (numeric) | 90th percentile |
| P99 | p99 | Yes (numeric) | 99th percentile |
Common Invalid Aliases
| Wrong | Correct | Note |
|---|---|---|
sum | total (with property) | "sum" is not a valid math type |
count | total | Use total without a property |
distinct | unique | |
avg | average | |
mean | average |
7. Width Recommendations
| Chart Type | Width | Layout Pattern |
|---|---|---|
insights-metric | 3 or 4 | Pack 3-4 KPIs per row |
line | 6 or 12 | 6 for paired comparison, 12 for detail |
line + stacked | 12 | Composition needs space |
bar | 6 or 12 | 6 for paired, 12 for many categories |
bar + stacked | 12 | Needs full width for stacked segments |
column | 6 or 12 | Same as bar |
column + stacked | 12 | Composition needs space |
pie | 6 | Pair with related chart |
table | 12 | Always full width |
funnel-steps | 12 | Complex funnels need space |
funnel-trend | 6 or 12 | 6 for paired, 12 for standalone |
funnel-top-paths | 12 | Path detail needs space |
retention-curve | 12 | Full width for readability |
retention-table | 12 | Full width for cohort grid |
retention-trend | 6 or 12 | 6 for paired, 12 for standalone |
retention-trend-metric | 3 or 4 | Same as insights-metric |
sankey | 12 | Always full width |
paths | 12 | Always full width |
8. Setting Chart Type
Chart type is set in displayOptions.chartType within the bookmark params JSON. When using typed query methods, the chart type is set automatically.
import json
# Typed queries set chartType automatically -- no override needed
result = ws.query("Login", from_date="2025-01-01", to_date="2025-03-31")
# The chart type lives at params["displayOptions"]["chartType"]
# To inspect:
params = result.params
chart_type = params.get("displayOptions", {}).get("chartType")
# When creating inline reports, the params carry the chart type through:
ws.update_dashboard(dashboard_id, UpdateDashboardParams(
content={
"action": "create",
"content_type": "report",
"content_params": {
"bookmark": {
"name": "DAU Trend",
"type": "insights",
"params": json.dumps(result.params),
}
},
}
))Requirements and compatibility
Written for: OpenAI Codex
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Declares allowed tools, Needs Python
Security
No issues found by our static scan· No issues found
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Created by
- Mixpanel
- Publisher
- OpenAI (Official)
- Distributed by
- OpenAI
- Source
- openai/plugins · plugins/mixpanel-headless/skills/dashboard-expert · commit d420d13
- Licence
- MIT License (redistribution allowed)
- LICENSE (added by AutoGineer from plugins/mixpanel-headless/LICENSE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by Mixpanel or OpenAI.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download dashboard-expert.zip