dashboarding — Claude Code skill: Grafana dashboards via API
This skill writes and edits Grafana dashboard JSON, then pushes it through the HTTP API. Useful when panels should live in code instead of being clicked together in the UI.
npx skills add grafana/skills --skill dashboarding -a claude-codeWhat dashboarding does
The skill covers the dashboard JSON model: panel types (timeseries, stat, gauge, table, heatmap, logs, traces, node-graph), 24-column gridPos layout, units and thresholds. Variables get their own section — template, datasource and chained — along with the organize, calculateField and filterByValue transformations. Panel and dashboard links use ${__field.labels.x} and ${__from}, and annotations come from Loki or Prometheus.
Everything ships through the HTTP API. The agent builds a payload, checks it with jq, sends POST /api/dashboards/db with overwrite: true, then reads the response. After every push the skill wants the returned version confirmed and a GET on the dashboard uid — that is how you catch a panel that never landed.
Before you start: a Grafana stack (OSS, Enterprise or Cloud) reachable from your machine, a token with dashboards:write passed as Authorization: Bearer <token>, and jq for inspecting responses. Without that token scope the requests fail outright. The panel, unit and annotation schema sits in references/json-schema.md inside the skill; skip it and the agent guesses at field names.
What it helps with
- write dashboard JSON for a new service and push it through the API
- add a
$jobdropdown variable to an existing dashboard - compute an "Error %" column with a
calculateFieldtransformation - overlay deploys on a graph as Loki or Prometheus annotations
- export a dashboard's JSON and verify it by
uidafter the push
Example prompts for the agent
- create a dashboard for the http_requests_total metric split by status_code
- add a $job service dropdown to the demo-svc-v1 dashboard
- show errors as a percentage column in the table panel
How to install dashboarding
1Skills CLI (Vercel)
npx skills add grafana/skills --skill dashboarding -a claude-code2Manually: put the files in place
~/.claude/skills/dashboarding/SKILL.mdCopy the whole skill folder; for one project use .claude/skills/dashboarding/
1Skills CLI (Vercel)
npx skills add grafana/skills --skill dashboarding -a codex2Manually: put the files in place
.agents/skills/dashboarding/SKILL.mdFor all projects use ~/.agents/skills/
1Skills CLI (Vercel)
npx skills add grafana/skills --skill dashboarding -a cursor2Manually: put the files in place
.cursor/skills/dashboarding/SKILL.mdCursor also reads .agents/skills/ and .claude/skills/
1GitHub CLI
gh skill install grafana/skills dashboarding2Manually: put the files in place
.github/skills/dashboarding/SKILL.md1Skills CLI (Vercel)
npx skills add grafana/skills --skill dashboarding -a gemini-cli2Platform CLI
gemini skills install https://github.com/grafana/skills --path skills/grafana-core/dashboardingManually: put the files in place
.devin/skills/dashboarding/SKILL.mdThe legacy .windsurf/skills/ path still works
Skills CLI (Vercel)
npx skills add grafana/skills --skill dashboarding -a opencodeClaude.ai
Zip the folder and upload it in Settings → Capabilities → Skills
Step by step, with commands for each platform: How to install skills in Claude Code: five ways
Questions about dashboarding
How do I install dashboarding in Claude?
Run npx skills add grafana/skills --skill dashboarding -a claude-code. The skill lands at ~/.claude/skills/dashboarding/SKILL.md.
What does the dashboarding skill do?
It writes and edits Grafana dashboard JSON — panels, variables, transformations, annotations, links — pushes the result through the HTTP API and verifies it by uid.
What do I need for the skill to work?
A Grafana stack reachable from your machine, an API token with dashboards:write, and jq to inspect API responses.
Source text
SKILL.md · Apache-2.0
---
name: dashboarding
license: Apache-2.0
description: Build, modify, and ship Grafana dashboards as JSON via the HTTP API — panel types (timeseries / stat / gauge / table / heatmap / logs / traces / node-graph), `gridPos` 24-column layout, units, thresholds, template + datasource + chained variables, transformations (`organize` / `calculateField` / `filterByValue`), panel + dashboard links with `${__field.labels.x}` / `${__from}`, and Loki/Prometheus annotations. Use when scripting dashboard creation, writing the dashboard JSON for a new service, adding a `$job` dropdown variable, computing an "Error %" column with a transformation, overlaying deploys as annotations, or pushing a dashboard via `POST /api/dashboards/db` — even when the user says "create a dashboard for this metric", "add a service dropdown", "show errors as percentage", "overlay our deploys", or "export the dashboard JSON" without naming the API or schema. After every API push, verify with the returned `version` plus a GET on the dashboard UID.
---
# Grafana Dashboard Authoring
> **Docs**: https://grafana.com/docs/grafana/latest/dashboards/
Dashboards are JSON. Author once, push via API, share by `uid`.
## Prerequisites
- Grafana stack (OSS, Enterprise, or Cloud) reachable from your machine
- API token with `dashboards:write` (`Authorization: Bearer <token>`)
- `jq` for inspecting responses
- The JSON-schema cheat sheet in [`references/json-schema.md`](references/json-schema.md)
## Common Workflows
### 1. Push a new dashboard via the API + verify
```bash
# 1. Build the payload — wrap the dashboard JSON, set folder, mark overwrite
cat > /tmp/dash.json <<'JSON'
{
"dashboard": {
"uid": "demo-svc-v1",
"title": "Demo Service",
"schemaVersion": 41,
"tags": ["demo"],
"time": { "from": "now-1h", "to": "now" },
"templating": { "list": [] },
"panels": [{
"id": 1, "type": "timeseries", "title": "Request Rate",
"gridPos": { "x": 0, "y": 0, "w": 24, "h": 8 },
"datasource": { "type": "prometheus", "uid": "prometheus" },
"targets": [{
"expr": "sum(rate(http_requests_total[5m])) by (status_code)",
"legendFormat": "{{status_code}}", "refId": "A"
}],
"fieldConfig": { "defaults": { "unit": "reqps" }, "overrides": [] }
}]
},
"folderUid": "",
"overwrite": true,
"message": "initial push"
}
JSON
# 2. Validate the JSON BEFORE you send it (catches trailing-comma typos)
jq empty /tmp/dash.json && echo "json ok"
# 3. POST
RESP=$(curl -s -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
"$GRAFANA/api/dashboards/db" -d @/tmp/dash.json)
echo "$RESP" | jq '{status, uid, url, version}'
# Expect: status="success", url="/d/demo-svc-v1/...", version=1 (incremented on each push)
# 4. Verify the round-trip — read it back and confirm one panel + the expected title
curl -s -H "Authorization: Bearer $TOKEN" \
"$GRAFANA/api/dashboards/uid/demo-svc-v1" \
| jq '{title: .dashboard.title, panels: (.dashboard.panels | length)}'
# Expect: {"title":"Demo Service","panels":1}
# 5. Open the dashboard in a browser — confirm the panel renders with data.
```
### 2. Add a `$job` template variable to an existing dashboard
```bash
# 1. Fetch existing dashboard
curl -s -H "Authorization: Bearer $TOKEN" \
"$GRAFANA/api/dashboards/uid/demo-svc-v1" > /tmp/dash.json
# 2. Edit templating.list — append:
# { "name":"job", "type":"query",
# "datasource":{"type":"prometheus","uid":"prometheus"},
# "query":{"query":"label_values(up, job)","refId":"A"},
# "refresh":2, "includeAll":true, "multi":true, "label":"Service" }
# (Use jq, an editor, or the Grafana UI — schema in references/json-schema.md.)
# 3. Update the panel expr to use the variable: rate(http_requests_total{job=~"$job"}[5m])
# 4. POST it back with overwrite: true. Verify the variable appears in the UI dropdown.
```
### 3. Compute an "Error %" column with a transformation
```json
{
"id": "calculateField",
"options": {
"alias": "Error %", "mode": "reduceRow",
"reduce": { "reducer": "last" },
"binary": { "left": "errors", "right": "total", "operator": "/" }
}
}
```
Add this to the panel's `transformations: []`. Verify in the UI panel inspector — the new field should appear and update with the variable selection.
Full schema (panels, units, all transformations, annotations, links): [`references/json-schema.md`](references/json-schema.md).
## API reference
```bash
# Get
curl -s -H "Authorization: Bearer $TOKEN" \
"$GRAFANA/api/dashboards/uid/<uid>" | jq '.dashboard'
# Search
curl -s -H "Authorization: Bearer $TOKEN" \
"$GRAFANA/api/search?query=kubernetes&type=dash-db" | jq '.[] | {uid,title,folderTitle}'
# Create folder
curl -s -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" "$GRAFANA/api/folders" \
-d '{"uid":"platform-team","title":"Platform Team"}'
```
For dashboards embedded in app plugins, use `@grafana/scenes` (skill `grafana-o11y:grafana-scenes`).
## Resources
- [Dashboard JSON model](https://grafana.com/docs/grafana/latest/dashboards/build-dashboards/view-dashboard-json-model/)
- [HTTP API — dashboards](https://grafana.com/docs/grafana/latest/developers/http_api/dashboard/)
- [Panel types](https://grafana.com/docs/grafana/latest/panels-visualizations/)
- [Variables](https://grafana.com/docs/grafana/latest/dashboards/variables/)
- [Transformations](https://grafana.com/docs/grafana/latest/panels-visualizations/query-transform-data/transform-data/)
© grafana · Apache-2.0 · grafana/skills