Documentation

Dashboards as code

cpum.ai dashboards are plain JSON. Export the dashboards you build in the UI, check them into git, and re-import them through the API on every commit. The contract is jsonnet-friendly: emit JSON, we validate and upsert by slug.

The schema (v1)

Every dashboard document is an object with schemaVersion: 1, a unique slug, a title, a description, an optional variables map, and an array of panels. Each panel has an id, a type, a 12-column layout (x, y, w, h), optional render options, and either a typed query reference or (for text panels) a markdown text body.

{
  "schemaVersion": 1,
  "slug": "checkout-overview",
  "title": "Checkout overview",
  "description": "Latency, error rate, and recent incidents for the checkout service.",
  "variables": {},
  "panels": [
    {
      "id": "p-mttr",
      "type": "single_stat",
      "title": "MTTR (last 30d)",
      "layout": { "x": 0, "y": 0, "w": 4, "h": 3 },
      "options": { "unit": "s" },
      "query": {
        "kind": "analytics_summary",
        "field": "mttrSeconds",
        "days": 30
      }
    },
    {
      "id": "p-cpu",
      "type": "timeseries",
      "title": "CPU %",
      "layout": { "x": 4, "y": 0, "w": 8, "h": 3 },
      "options": {},
      "query": {
        "kind": "metrics_history",
        "metric": "cpu_percent",
        "windowMin": 60
      }
    },
    {
      "id": "p-incidents",
      "type": "table",
      "title": "Recent incidents",
      "layout": { "x": 0, "y": 3, "w": 12, "h": 5 },
      "options": {},
      "query": {
        "kind": "incidents",
        "days": 30,
        "limit": 50
      }
    }
  ]
}

Panel types

typedescriptionsupported queries
timeseriesRecharts line chart. One or more series over time.metrics_history, analytics_trend
single_statSingle big number with optional unit/precision in `options`.summary, analytics_summary, logs_count, trace_latency
tableTabular data; columns are determined by the bound query.slos, analytics_by_service, incidents, logs_count, trace_latency
textMarkdown / plain-text note. No query — content lives in the `text` field.—

Query kinds

Each panel binds to one typed query. The set is additive across schema versions, so dashboards exported from older cpum.ai builds keep importing cleanly. Unknown kinds are rejected at import time so schema drift never silently disappears.

metrics_history

Host CPU/memory/disk/process count over the last 5m or 60m.

{ "kind": "metrics_history", "metric": "cpu_percent", "windowMin": 60 }
summary

Live single-host KPI (cpuPercent, memoryPercent, diskUtilPercent, …).

{ "kind": "summary", "field": "cpuPercent" }
analytics_summary / analytics_trend / analytics_by_service

Incident analytics KPIs, daily trend, and per-service breakdown over the last N days.

{ "kind": "analytics_summary", "field": "mttrSeconds", "days": 30 }
slos

All SLOs for the account with current SLI and status.

{ "kind": "slos" }
incidents

Recent incidents (any status) within the configured window.

{ "kind": "incidents", "days": 30, "limit": 50 }
logs_count

OTel logs grouped by severity_text over the last N hours. Optional `service` filter. Use as a single_stat (total, or pick one bucket via `options.severity`) or as a table.

{ "kind": "logs_count", "service": "checkout-api", "hours": 1 }
trace_latency

OTel root-span p50/p95/p99 latency over the last N hours. Optional `service` filter. As a single_stat, pick a percentile via `options.percentile` ("p50" | "p95" | "p99", default p95); as a table, all three percentiles render side by side.

{ "kind": "trace_latency", "service": "checkout-api", "hours": 1 }
saved_query

Reference to another panel's query, looked up by `dashboardSlug` + `panelId`. Lets you build a library dashboard of canonical queries and reuse them everywhere. The host panel keeps its own `type` and `options`, so the same saved query can render as a stat in one dashboard and a table in another.

{ "kind": "saved_query", "dashboardSlug": "library", "panelId": "p-error-rate" }

Round-trip (export ↔ import)

Export and import are guaranteed lossless: GET /api/dashboards/:id/export returns the canonical JSON for the dashboard (keys ordered, defaults filled in), and POST /api/dashboards/import upserts by slug under your account. Re-exporting an imported dashboard yields byte-identical bytes.

jsonnet → JSON → API

We don't bundle a jsonnet runtime. Compile your jsonnet locally (or in CI) and POST the resulting JSON. That keeps the contract stable across language ecosystems and works with any preprocessor that emits JSON.

# Compile jsonnet → JSON, then ship it through cpumctl
jsonnet checkout.jsonnet > checkout.json

# Validate locally first (no network) — perfect for a PR check
cpumctl dashboards apply ./checkout.json --dry-run

# Then upsert it for real (uses CPUM_API_KEY from the environment)
cpumctl dashboards apply ./dashboards/*.json

Validation

The API validates every imported document against the v1 Zod schema. On failure you get a 400 with a fieldErrors map keyed by JSON path (e.g. panels.2.layout.w) so it's obvious where the document broke. Unknown top-level fields are rejected — surface schema drift early instead of silently dropping it.

GitHub Action

The cpum-ai/cpum-ai dashboard-diff action diffs every changed dashboard JSON file in a pull request against its base branch, posts a panel-level summary as a PR comment, and fails the job when any file fails schema validation. Use cpum-ai/cpum-ai/actions/dashboard-diff@v1 to ride the latest patch automatically.

The action is self-contained — no cpumctl install required. It uses git show <base-sha>:<path> to read the previous version of each file, so the only prerequisite is a checkout with enough history ( fetch-depth: 0).

Copy-paste workflow — save as .github/workflows/dashboard-diff.yml

name: Dashboard diff

on:
  pull_request:
    paths:
      - "dashboards/**/*.json"

jobs:
  diff:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: write   # needed to post the diff comment
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0     # full history so git show <base-sha>:<file> works

      - name: cpum.ai dashboard diff
        uses: cpum-ai/cpum-ai/actions/dashboard-diff@v1
        with:
          files: "dashboards/**/*.json"   # glob relative to repo root
          strict: "true"                  # fail the job on schema errors
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
inputdefaultdescription
filesdashboards/**/*.jsonGlob of dashboard JSON files to diff (relative to repo root).
base-refPR base SHA / HEAD~1Git ref to diff against. Auto-detected from the pull_request event payload.
stricttrueFail the job when any dashboard fails Zod schema validation.
comment-on-prtruePost the panel-level diff as a PR comment. Requires pull-requests: write permission.

Starter templates

Three ready-to-use dashboard JSON files live in the dashboards/ directory of the cpum-ai repo. Copy one into your project, edit the slug and title, then apply it:

# Copy a starter template and apply it
curl -fsSL https://raw.githubusercontent.com/cpum-ai/cpum-ai/main/dashboards/starter-server-health.json \
     -o dashboards/my-server-health.json
# Edit the slug, then push to cpum.ai:
cpumctl dashboards apply dashboards/my-server-health.json

Get started

Open the cpum.ai dashboard, click Dashboards, then either build a new dashboard visually and click Export JSON, or paste an existing JSON document into Import JSON. The same endpoints power both.

Open dashboard
Dashboards as code — cpum.ai | cpum.ai