Interactive Charts
Embed live charts and data tables in markdown documents using SQL queries or static data. Charts render inline in chat, feed, and file viewer.
Interactive Charts in Documents
TeamDay documents support interactive chart and table blocks that render inline in markdown. Charts can display static data (baked in by the AI agent) or query a live database connection on every view.
TeamDay also renders full Mermaid diagrams from normal mermaid fenced blocks. Use Mermaid for process maps, architecture diagrams, timelines, ER diagrams, sequence diagrams, and other visual explanations that are not better represented as numeric charts.
```mermaid
flowchart LR
Query[Query data] --> Report[Write Markdown]
Report --> Chart[Render chart]
Report --> Diagram[Render Mermaid]
```Chart Block Syntax
Chart blocks use fenced code blocks with a special language annotation:
```data
[{"category":"Hiking","revenue":45000},{"category":"Running","revenue":62000}]
```The annotation format is: source:blockType(param=value, param="value with spaces")
Sources
| Source | Description |
|---|---|
data | Static data embedded in the block. The AI bakes numbers in when writing the report. |
sql | Live read-only PostgreSQL query against an org data connection. Runs server-side when someone views the document. |
Block Types
| Type | Description |
|---|---|
chart | Renders a Chart.js visualization (bar, line, pie, etc.) |
table | Renders a formatted data table with headers and rows |
Parameters
| Parameter | Required | Description |
|---|---|---|
type | chart only | Chart type: bar, line, pie, doughnut, area |
title | no | Display title above the chart/table |
connection | sql only | Name of the org data connection. production-db resolves to an org secret such as TEAMDAY_DATA_CONNECTION_PRODUCTION_DB_URL. |
cache | no | Cache duration: 30s, 5m, 1h, none. Default: 5m |
Static Charts (data:chart)
Static charts contain their data directly in the block body as JSON. The AI agent computes or fetches the data during the conversation, then writes it into the markdown file.
Array of Objects
The simplest format — an array of objects where the first key becomes the x-axis label and remaining keys become data series:
```data
[
{"region": "Europe", "revenue": 145000, "orders": 2340},
{"region": "Americas", "revenue": 98000, "orders": 1560},
{"region": "Asia", "revenue": 67000, "orders": 890}
]
```This renders a bar chart with "Europe", "Americas", "Asia" as labels and two series: "revenue" and "orders".
Labels + Datasets Format
For more control, use the structured format with explicit labels and named datasets:
```data
{
"labels": ["Jan", "Feb", "Mar", "Apr", "May", "Jun"],
"datasets": [
{"label": "Revenue", "values": [45000, 48000, 52000, 51000, 58000, 63000]},
{"label": "Costs", "values": [32000, 33000, 34000, 33500, 35000, 36000]}
]
}
```Live SQL Charts (sql:chart)
SQL charts query a database connection every time the document is viewed. The query runs server-side with caching.
```sql
SELECT DATE_TRUNC('month', created_at) as month,
COUNT(*) as orders
FROM orders
WHERE created_at > NOW() - INTERVAL '12 months'
GROUP BY 1
ORDER BY 1
```Column Mapping
The SQL result columns map to chart axes automatically:
- First column = labels (x-axis)
- Remaining columns = data series (y-axis)
- Column names become series labels in the legend
SELECT month, revenue, cost, profit
-- ^ labels ^ series1 ^ series2 ^ series3
FROM monthly_summary
ORDER BY monthConnection Name
The connection parameter maps to an organization secret that stores a PostgreSQL URL. For example, connection=production-db looks for:
TEAMDAY_DATA_CONNECTION_PRODUCTION_DB_URLPRODUCTION_DB_DATABASE_URLPRODUCTION_DB_URL
The database user in that URL should be read-only. TeamDay also enforces a server-side read-only transaction, accepts only SELECT / WITH queries, applies a 5 second statement timeout, and caps results at 1,000 rows.
Plain sql fenced blocks render as SQL code examples. Executable SQL-backed report blocks must use sql:chart(...) or sql:table(...).
Caching
Live queries are cached server-side to avoid hitting the database on every page view:
| Value | Duration |
|---|---|
30s | 30 seconds |
5m | 5 minutes (default) |
1h | 1 hour |
none | No caching, always fresh |
Charts display a "cached" indicator when serving cached data.
Data Tables (sql:table / data:table)
Tables work the same way as charts but render as formatted HTML tables instead of visualizations:
```sql
SELECT product_name,
SUM(quantity) as units_sold,
SUM(revenue) as revenue
FROM order_items
GROUP BY product_name
ORDER BY revenue DESC
LIMIT 10
```Static data tables:
```data
[
{"team": "Engineering", "headcount": 12, "budget": "$1.2M"},
{"team": "Marketing", "headcount": 5, "budget": "$400K"},
{"team": "Sales", "headcount": 8, "budget": "$600K"}
]
```Tables automatically:
- Format numbers with commas (e.g., 45,000)
- Show null values as "—"
- Highlight rows on hover
- Indicate when results are truncated (>1,000 rows)
Chart Types
Bar Chart
Best for comparing categories. Use when you have discrete categories on the x-axis.
type=barLine Chart
Best for trends over time. Use when the x-axis represents a time series.
type=lineArea Chart
Like a line chart with the area filled. Good for showing volume or cumulative data.
type=areaPie / Doughnut Chart
Best for showing composition or parts of a whole. Use with a single data series.
type=pie
type=doughnutWhere Charts Render
Chart blocks render in these surfaces:
| Surface | Static (data:) | Live (sql:) |
|---|---|---|
| Chat messages | Yes | Yes |
| File viewer / editor preview | Yes | Yes |
| Feed preview | Yes | Yes |
The feed shows truncated text previews. Click through to the full document to see rendered charts.
Teaching Your Agent
To have your AI agent write reports with charts, include the chart syntax in the agent's system prompt or as a skill. Example instruction:
When writing reports with data, embed charts using this syntax:
```data
[{"label": "...", "value": ...}, ...]For live dashboards, use SQL chart blocks:
SELECT ...Available chart types: bar, line, pie, doughnut, area Available connections: production-db, analytics-db
## Limitations
- **Read-only queries** — INSERT, UPDATE, DELETE, DROP, and other mutations are blocked
- **5-second timeout** — queries that take longer are cancelled
- **1,000 row limit** — results exceeding this are truncated
- **PostgreSQL only** for live queries — MySQL, BigQuery, and others coming soon
- **No cross-database joins** — each chart block queries a single connection
## API Reference
The chart blocks use the [Connections Query API](/api/connections) under the hood. You can call it directly for programmatic access.