Dashboards
Compose your own dashboards from a 13-widget library and pin one as the org default. Dashboards are owned by the organization — anyone with the right role sees the same set.
System Presets
Four presets ship out of the box. Presets are read-only — you can clone any preset to create your own dashboard, but the originals never change. Presets are served from memory and are never persisted to the database.
| Preset | Audience | Default widgets |
|---|---|---|
executive | Leadership | Stat cards, cost trend, cost reports, budget health, top costly resources, recommendations |
engineering | Engineering teams | Stat cards, cost trend, cost reports, recommendations, schedule activity, schedule success rate |
finops | Finance partners | Stat cards, cost trend, cost reports, budget health, top costly resources, recommendations, schedule activity, schedule success rate |
all_widgets | Everyone | Stat cards, cost trend, cost reports, top costly resources, budget health, recommendations, schedule activity, schedule success rate. The fallback when no org default is set. |
Widget library
Every dashboard is a layout over the same 13 widgets. Each widget declares its required policy and is rendered through an error boundary — a widget you can't see, or a widget that throws, becomes a placeholder instead of unmounting the page.
| Widget type | What it shows | Required policy |
|---|---|---|
stat_cards | Resources, spend, savings, and savings rate at a glance | report:view |
cost_trend | Daily and accumulated cost over time | report:view |
cloud_spend | Spend split by cloud provider | report:view |
cost_reports | Providers, teams, and tags in one tabbed card | report:view |
top_costly | Highest-cost resources this period | report:view |
recommendations | Top open recommendations by potential monthly savings | recommendation:view |
quick_insights | Top open recommendations as scannable cards | recommendation:view |
budget_health | Budget utilisation by team or group | budget:view |
showback | Cost attribution by team or tag | report:view |
schedule_execution | Recent schedule runs and failures | state-history:view |
upcoming_actions | Scheduled starts and stops in the next window | schedule:view |
schedule_activity | Past schedule runs and upcoming actions in one tabbed card | state-history:view |
schedule_success_rate | Success rate for scheduled start/stop runs | report:view |
Bounds
| Bound | Limit | On exceed |
|---|---|---|
| Dashboards per org | 50 | 409 on create |
| Widgets per dashboard | 50 | 400 on save |
| Widget config size | 4 KiB | 400 on save |
| Layout payload size | 64 KiB | 400 on save |
| Name uniqueness | Per-org (active rows) | 409 on create or rename |
RBAC
Four policies gate dashboard operations:
dashboard:view— read dashboards and presetsdashboard:create— create new dashboards (counts against the org's 50-dashboard quota)dashboard:update— rename, edit layout, change widgets, set defaultdashboard:delete— delete a user-created dashboard (presets are immutable)
API
List dashboards
/dashboardsList dashboards visible to the caller, plus the org's default-dashboard pointer.
Query: ?limit=20&offset=0&sort_by=name&sort_order=asc. limit defaults to 50 (max 100). sort_by is created_at (default), updated_at, or name. sort_order defaults to desc. The four presets always come first, with isSystem: true.
Layouts are trimmed to one widget and only two of the four presets are shown below.
{
"data": {
"data": [
{
"id": "executive",
"orgId": "org_001",
"name": "Executive",
"description": "Headline KPIs, trends, savings, and budget health — the leadership view.",
"layout": {
"version": 1,
"widgets": [
{ "id": "p-exec-1", "type": "stat_cards", "size": "full", "order": 0 }
]
},
"isSystem": true,
"category": "leadership",
"createdAt": "0001-01-01T00:00:00Z",
"updatedAt": "0001-01-01T00:00:00Z"
},
{
"id": "3f2b8c1e-6d4a-4b7e-9a51-2c8e7f0d1a93",
"orgId": "org_001",
"name": "Platform Team",
"layout": {
"version": 1,
"widgets": [
{ "id": "w1", "type": "stat_cards", "size": "full", "order": 0 }
]
},
"isSystem": false,
"createdBy": "jane@example.com",
"createdAt": "2026-04-26T10:30:00Z",
"updatedAt": "2026-04-27T08:00:00Z"
}
],
"defaultDashboard": "executive"
}
}Get dashboard
/dashboards/{id}Get a dashboard by id. Preset keys are served from memory; UUIDs hit the dashboards table.
Create dashboard
/dashboardsCreate a new user-owned dashboard. Cloning a preset is a client-side operation: GET the preset, give its widgets new ids, POST.
{
"name": "Platform Team",
"description": "Spend and schedules for the platform team",
"layout": {
"version": 1,
"widgets": [
{ "id": "w1", "type": "stat_cards", "size": "full", "order": 0 },
{ "id": "w2", "type": "cost_trend", "size": "half", "order": 1, "config": {} }
]
}
}layout.version must be 1. Each widget needs a unique id, a known type, and a size of full, half, or third. order values must run 0, 1, 2, … with no gaps. titleOverride (max 128 characters) and config are optional.
Update dashboard
/dashboards/{id}Partial update — name, description, or layout. A sent layout replaces the old one whole. An empty body returns 400. PATCH on a preset id returns 405.
Set default dashboard
/dashboards/defaultSet the org's default dashboard. Pointer lives in org_settings under key default_dashboard.
{ "id": "executive" }Send { "id": "" } to clear the pointer — frontend falls back to all_widgets.
Delete dashboard
/dashboards/{id}Delete a user-owned dashboard. Preset keys return 405. If the deleted dashboard is the org default, the pointer is cleared right after the delete.
Tenant cascade
See Roles & Permissions for how dashboard policies compose with the rest of the policy table.