Dashboards
Bridge Town dashboards are self-contained HTML pages generated from a successfully completed model run. Each dashboard is backed by a single key in the run’s outputs dict — the same dict your model writes to /outputs/ during execution. The HTML is stored in S3 and can be viewed inside the web app or shared via a time-limited pre-signed URL.
Prerequisites
Section titled “Prerequisites”- A Bridge Town account with at least one project
- A model that writes chart-compatible data to
/outputs/ - At least Editor access to the project (to create dashboards)
- A Pro subscription (to generate shareable links)
How dashboards work
Section titled “How dashboards work”Model run succeeds │ ▼Outputs dict contains chart data under a named key │ ▼create_dashboard(project, dashboard_name, chart_type, model_run_id, output_key) │ ▼HTML generated (Plotly or AG Grid) → uploaded to S3 → Dashboard record saved │ ▼share_dashboard(project, dashboard_name, expires_hours) → pre-signed URL + iframe snippetStep 1 — Write chart-compatible output in your model
Section titled “Step 1 — Write chart-compatible output in your model”During a model run, anything your script writes to /outputs/<filename> is collected into the run’s outputs dict. Dashboard creation reads one key from that dict, so you must produce data that matches the schema for your chosen chart type.
The output_key parameter in create_dashboard is the full filename (including the .json extension) that you wrote to /outputs/.
Step 2 — Discover supported chart types
Section titled “Step 2 — Discover supported chart types”Call list_chart_types to see every supported chart and its required schema:
{ "name": "list_chart_types", "arguments": {}}Response:
{ "chart_types": [ { "name": "bar", "description": "Vertical bar chart — compare values across categories.", "required_keys": ["categories", "values"], "example_schema": "{\"categories\": [...], \"values\": [...], \"series_name\"?: \"Value\"}" }, { "name": "line", "description": "Line chart over time — single series or multi-series trend.", "required_keys": ["x"], "example_schema": "{\"x\": [...], \"y\": [...]} or multi-series {\"x\": [...], \"series\": [{\"name\": \"...\", \"values\": [...]}]}" }, { "name": "table", "description": "Sortable Plotly table — columnar data with row values.", "required_keys": ["columns", "rows"], "example_schema": "{\"columns\": [\"Col1\", ...], \"rows\": [[v1, v2, ...], ...]}" }, { "name": "waterfall", "description": "Bridge/waterfall chart — shows cumulative positive/negative changes; last element is always rendered as the running total.", "required_keys": ["labels", "values"], "example_schema": "{\"labels\": [...], \"values\": [...]} (last element rendered as total)" }, { "name": "revenue_waterfall", "description": "FP&A waterfall chart with Bridge Town colour palette — revenue bridge from start to end; last element rendered as total.", "required_keys": ["labels", "values"], "example_schema": "{\"labels\": [...], \"values\": [...]} (last element rendered as total)" }, { "name": "expense_breakdown", "description": "Horizontal bar chart of expense categories — sorted largest to smallest.", "required_keys": ["categories", "values"], "example_schema": "{\"categories\": [...], \"values\": [...], \"series_name\"?: \"Expense\"}" }, { "name": "headcount_timeline", "description": "Headcount trend line — single series or stacked multi-series breakdown.", "required_keys": ["x"], "example_schema": "{\"x\": [...], \"y\": [...]} or {\"x\": [...], \"series\": [{\"name\": \"...\", \"values\": [...]}], \"stacked\"?: true}" }, { "name": "cash_flow_projection", "description": "Actual vs projected cash flow — months on the x-axis; either 'actual' or 'projected' series may be omitted for months where unavailable.", "required_keys": ["months"], "example_schema": "{\"months\": [...], \"actual\"?: [...], \"projected\"?: [...]}" }, { "name": "data_grid", "description": "Interactive AG Grid table — supports sorting, filtering, and CSV export. Accepts object rows or simplified array rows.", "required_keys": ["columns", "rows"], "example_schema": "{\"columns\": [{\"field\": \"Name\", \"type\"?: \"text|number|date\"}, ...], \"rows\": [{...}]} or simplified {\"columns\": [\"Name\", ...], \"rows\": [[\"Alice\", ...], ...]}" } ], "count": 9}Step 3 — Create a dashboard
Section titled “Step 3 — Create a dashboard”Once you have a successful run with compatible output, call create_dashboard.
Bar chart example
Section titled “Bar chart example”Model output (/outputs/quarterly_revenue.json):
{ "categories": ["Q1", "Q2", "Q3", "Q4"], "values": [1200000, 1350000, 1500000, 1800000], "series_name": "Revenue"}Tool call:
{ "name": "create_dashboard", "arguments": { "project_name": "forecasts", "dashboard_name": "Q4 Revenue", "chart_type": "bar", "model_run_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "output_key": "quarterly_revenue.json", "description": "Quarterly revenue breakdown", "subtitle": "FY2026", "currency": "USD" }}Response:
{ "dashboard_id": "d1e2f3a4-b5c6-7890-def1-234567890abc", "dashboard_name": "Q4 Revenue", "project": "forecasts", "chart_type": "bar", "model_run_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "has_html": true, "created": true, "created_at": "2026-04-28T10:00:00+00:00", "updated_at": "2026-04-28T10:00:00+00:00"}Table example
Section titled “Table example”Model output (/outputs/plan_vs_actual.json):
{ "columns": ["Metric", "Budget", "Actual", "Variance"], "rows": [ ["Revenue", 5000000, 5200000, 200000], ["COGS", 2000000, 2100000, -100000], ["Gross Profit", 3000000, 3100000, 100000], ["OpEx", 1500000, 1450000, 50000], ["Net Income", 1500000, 1650000, 150000] ]}Tool call:
{ "name": "create_dashboard", "arguments": { "project_name": "forecasts", "dashboard_name": "P&L Summary", "chart_type": "table", "model_run_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "output_key": "plan_vs_actual.json", "currency": "USD" }}Line chart example (multi-series)
Section titled “Line chart example (multi-series)”Model output (/outputs/headcount.json):
{ "x": ["Jan", "Feb", "Mar", "Apr", "May", "Jun"], "series": [ {"name": "Engineering", "values": [30, 32, 35, 38, 40, 42]}, {"name": "Sales", "values": [10, 12, 12, 14, 16, 18]}, {"name": "G&A", "values": [5, 5, 6, 6, 6, 7]} ]}Tool call:
{ "name": "create_dashboard", "arguments": { "project_name": "hr-planning", "dashboard_name": "Headcount Trend", "chart_type": "line", "model_run_id": "b2c3d4e5-f6a7-8901-bcde-f2345678901b", "output_key": "headcount.json", "subtitle": "H1 2026" }}Waterfall / cash-flow example
Section titled “Waterfall / cash-flow example”Model output (/outputs/revenue_bridge.json):
{ "labels": ["Opening ARR", "New ARR", "Expansion", "Churn", "Closing ARR"], "values": [5000000, 1500000, 500000, -300000, 6700000]}Tool call:
{ "name": "create_dashboard", "arguments": { "project_name": "forecasts", "dashboard_name": "ARR Bridge", "chart_type": "revenue_waterfall", "model_run_id": "c3d4e5f6-a7b8-9012-cdef-3456789012cd", "output_key": "revenue_bridge.json", "currency": "USD" }}Cash flow projection (/outputs/cash_flow.json):
{ "months": ["Jan", "Feb", "Mar", "Apr"], "actual": [100000, 120000, null, null], "projected": [null, null, 130000, 140000]}Tool call:
{ "name": "create_dashboard", "arguments": { "project_name": "forecasts", "dashboard_name": "Cash Flow", "chart_type": "cash_flow_projection", "model_run_id": "d4e5f6a7-b8c9-0123-defa-4567890123de", "output_key": "cash_flow.json", "currency": "USD" }}Data grid example
Section titled “Data grid example”Model output (/outputs/customers.json):
{ "columns": [ {"field": "Name", "type": "text"}, {"field": "ARR", "type": "number"}, {"field": "Renewal Date", "type": "date"} ], "rows": [ {"Name": "Acme Corp", "ARR": 240000, "Renewal Date": "2026-06-15"}, {"Name": "Globex", "ARR": 120000, "Renewal Date": "2026-09-01"} ]}Tool call:
{ "name": "create_dashboard", "arguments": { "project_name": "sales", "dashboard_name": "Customer List", "chart_type": "data_grid", "model_run_id": "e5f6a7b8-c9d0-1234-efab-5678901234ef", "output_key": "customers.json" }}Step 4 — List existing dashboards
Section titled “Step 4 — List existing dashboards”Use list_dashboards to see what has already been created for a project:
{ "name": "list_dashboards", "arguments": { "project_name": "forecasts" }}Response:
{ "project": "forecasts", "dashboards": [ { "name": "Q4 Revenue", "description": "Quarterly revenue breakdown", "is_shared": false, "has_html": true, "created_at": "2026-04-28T10:00:00+00:00", "updated_at": "2026-04-28T10:00:00+00:00" }, { "name": "ARR Bridge", "description": null, "is_shared": true, "has_html": true, "created_at": "2026-04-27T14:30:00+00:00", "updated_at": "2026-04-27T16:00:00+00:00" } ], "count": 2}Step 5 — Share a dashboard
Section titled “Step 5 — Share a dashboard”Call share_dashboard to generate a time-limited, anonymous pre-signed URL:
{ "name": "share_dashboard", "arguments": { "project_name": "forecasts", "dashboard_name": "ARR Bridge", "expires_hours": 48 }}Response:
{ "dashboard_name": "ARR Bridge", "project": "forecasts", "share_url": "https://s3.example.com/dashboards/...?X-Amz-Expires=172800", "expires_hours": 48, "embed_snippet": "<iframe\n src=\"https://s3.example.com/dashboards/...\"\n title=\"ARR Bridge\"\n width=\"100%\"\n height=\"600px\"\n frameborder=\"0\"\n allowfullscreen\n></iframe>"}The embed_snippet is a ready-to-paste <iframe> that works in Notion, Confluence, or any HTML page. The URL expires after the requested window (default 24 hours, maximum 168 hours / 7 days).
Web app behavior
Section titled “Web app behavior”The Bridge Town web app provides a first-class dashboard experience alongside the MCP tools.
Dashboard gallery (/dashboards)
Section titled “Dashboard gallery (/dashboards)”After logging in, navigate to Dashboards in the sidebar. The gallery page (/dashboards) lists every dashboard saved by your tenant, ordered by most-recently updated first. Each card shows:
- Dashboard name and description
- Whether the dashboard has rendered HTML (
has_html) - Whether it has been shared (
is_shared) - Creation and update timestamps
Click any card to open the full-screen viewer. A delete action is available on each card.
Dashboard viewer (/dashboards/{id})
Section titled “Dashboard viewer (/dashboards/{id})”The viewer page fetches a fresh pre-signed S3 URL for the dashboard HTML and renders it in a sandboxed iframe:
allow-scriptsis required for Plotly interactivityallow-popupslets linked drill-downs open in new tabs- Navigation and form submission are blocked
If the dashboard HTML has not been generated yet, or if S3 is not configured, the page shows an error state with a link back to the gallery.
Share URLs
Section titled “Share URLs”Share URLs generated by share_dashboard are anonymous — anyone with the link can view the dashboard without signing in. There is no sign-in gate on the share URL. Because the URL is pre-signed S3, it works even if the Bridge Town web app is temporarily unavailable.
To revoke access before the expiry, call share_dashboard again with a short expires_hours (e.g. 1) or delete the dashboard entirely. There is no explicit “revoke share” tool — shortening the TTL is the recommended method.
Plan and permission boundaries
Section titled “Plan and permission boundaries”| Action | Required access | Plan restriction |
|---|---|---|
list_chart_types | None (static metadata) | None |
create_dashboard | Editor or Owner on project | None |
list_dashboards | Viewer or higher on project | None |
share_dashboard | Viewer or higher on project | Pro required |
View in web app (/dashboards) | Authenticated tenant member | None |
| Delete in web app | Authenticated tenant member | None |
Common errors
Section titled “Common errors”| Error | Cause | Fix |
|---|---|---|
output-not-found | The output_key does not exist in the run’s outputs dict | Check the filename your model wrote to /outputs/ and pass the full filename including .json |
output-shape-invalid | The output dict exists but is missing required keys or has the wrong type | Compare your data against the schema returned by list_chart_types |
model_run_id … not a valid UUID | Malformed run ID | Copy the exact run_id from run or list_runs |
status failed | The model run did not complete successfully | Re-run the model and wait for success status |
Dashboard … not found | dashboard_name does not exist in the project | Check spelling or call list_dashboards to see valid names |
expires_hours must be between 1 and 168 | TTL out of range | Pass a value between 1 and 168 hours |
Pro subscription required | Free-tier tenant calling share_dashboard | Upgrade to Pro in Settings → Billing |
Related tools
Section titled “Related tools”| Tool | Description |
|---|---|
list_chart_types | Discover supported charts and their schemas |
create_dashboard | Generate and save dashboard HTML from a model run |
list_dashboards | List saved dashboards for a project |
share_dashboard | Generate a pre-signed share URL and iframe snippet |
run | Execute a model and produce the outputs dict |
list_runs | Find completed runs and their run_id values |