Skip to main content
Dashboards are readable and writable through the REST API. Reads are always safe; writes are full-document replacements guarded by ETags, so concurrent writers can’t silently clobber each other — and so a dashboard can live in version control and be PUT on deploy.

Reading dashboards

GET /v1/dashboards

Returns dashboard summaries (without widget bodies) plus a cache-only collection ETag. Re-send it as If-None-Match to receive 304 Not Modified when nothing changed. This collection ETag cannot authorize a write.

GET /v1/dashboards/{slug}

Returns the complete dashboard as a bare object; its opaque revision ETag arrives as an HTTP header — keep it for conditional writes.
GET-by-slug does not support conditional reads — sending its ETag as If-None-Match still returns 200 with the full body. Only the collection returns 304.

Rendering a stored widget yourself

Run timeseries widgets through /v1/metrics/query_range; run stat, pie, and table through /v1/metrics/query evaluated at the selected range end (put whole-window aggregation such as sum(increase(m[1h])) inside the PromQL). markdown widgets need no query.

Writing dashboards

A server key can create, replace, and delete every dashboard in its project, and writes are not attributed — the stored dashboard records no actor, and an overwritten document is unrecoverable. GET the current body and keep it before any replace or delete. If you’re wiring up an automation or agent, don’t let it mutate dashboards unless that’s explicitly its job.

The document

The slug is the immutable identity in the URL — 1–63 lowercase letters/digits with internal hyphens — and is never part of the body. The exact body contract is DashboardInput in the OpenAPI spec; the essentials:
  • title — required, 1–200 characters. description — optional, up to 2,000.
  • default_time_window — required; one of 1m, 5m, 15m, 1h, 4h, 12h, 24h, 7d, 30d.
  • widgets — required (may be []), up to 24. IDs are unique, ^[A-Za-z0-9_-]{1,64}$.
  • Widget typetimeseries, stat, pie, table, or markdown.
  • grid{x, y, w, h} on 12 columns; x + w <= 12.
  • Queries are { "promql": "..." } — timeseries/table take 1–4, stat/pie exactly 1, markdown none. Every query must evaluate to an instant vector or scalar — a bare range selector is rejected at write time, so wrap it as rate(...) or sum(increase(...)).
  • Only timeseries supports style (line, area, bar, stacked_bar) and time_interval. Leave time_interval unset — the bucket then auto-sizes to whatever window the viewer selects. A fixed interval must satisfy window / time_interval <= 600 at every selectable window, or the widget renders as unavailable at wider windows.
  • A markdown widget provides non-empty markdown (up to 16,384 UTF-8 bytes). Any widget may override time_window.
  • Grouping and aggregation belong in the PromQL — there is no group_by, unit, or color field. Unknown JSON fields are rejected with 400, not ignored.
  • The body is capped at 1 MiB and requires Content-Type: application/json.
PUT is full replacement, not merge or patch. Optional fields omitted from an update are cleared. Always start from the current document.

The conditional-write contract

Every mutation requires exactly one conditional header: Supplying both headers is 400; supplying neither is 428. A malformed header value (unquoted ETag, W/"...", If-Match: *, a list of ETags) is 400, not 412 — a client bug to fix, not a race to retry. Replay the ETag verbatim, including quotes; never synthesize one or borrow it from another slug or the collection list. Conditional writes never return 404 — a well-formed If-Match on a vanished slug is 412. Reapplying an identical body is a 200 no-op with an unchanged ETag.

Create

Replace or delete

GET the latest document and ETag first, apply your changes to the complete document, then PUT/DELETE with If-Match:
On 412, another writer won: GET again, reapply your intent to the fresh document, and retry — never blindly resend a stale body. On a cleanup 412 whose follow-up GET is 404, the dashboard is already gone — stop; don’t re-create it.

Next Steps

Metrics

The PromQL that widget queries evaluate

Metric alerts

The same slug + ETag contract, for alerting