Building dashboards as JSON
Writing a definition by hand and importing it, or keeping definitions in a repository
Instead of placing panels one by one in the editor, you can write the definition JSON yourself and upload it. That is the path for cloning a dashboard across workspaces, or for reviewing and reverting definitions the way you do with code.
There are two ways in.
| Method | When | Where |
|---|---|---|
| Import | A one-off upload — a document exported elsewhere, or one written by hand. | Dashboards → New → Import JSON |
| Repository connection | Keeping definitions in Git and applying them by commit | Dashboards → Connect repository |
The syntax is in the Definition JSON reference; this page is about where the document goes.
Before you write — check the field names
Field names in a definition must not be guessed. A name outside the dictionary is rejected on save, and a name that exists but never arrived in your workspace saves fine yet leaves the panel empty.
| What to check | Where |
|---|---|
| Which fields logs and traces carry | The filter on the Logs and Traces screens — whatever autocomplete offers is what a definition may use. |
| Which metrics have arrived | The metric list on the Metrics screen. A source.metric that is not there renders as "needs instrumentation". |
| Which labels a metric has | Pick the metric on the Metrics screen and its labels and values are listed. |
Autocomplete and the save path read the same dictionary. If you could pick it on screen, you can write it in a definition.
Import
Paste a document into the import screen and it runs through the same validator as the editor before anything is stored. If a rule fails, nothing is saved and the exact spot and reason are shown — the spot as a JSON Pointer (/panels/0/queries/0/groupBy), the reason as a stable code (REQUIRED, OUT_OF_RANGE, and so on). The full code list is in the Definition JSON reference.
Once it passes you give it a name and save. What gets stored may differ from what you sent: missing defaults are filled in and operator and aggregation names are normalized to uppercase.
Export
Export on the dashboard detail screen hands you the document below. It is the same format import accepts, so the file can go straight to another workspace or into a repository.
{
"format_version": 1,
"dashboard": {
"name": "Checkout RED",
"description": null,
"definition": { "schemaVersion": 1, "panels": [ ... ] }
}
}| Item | Detail |
|---|---|
format_version | Version of the envelope, currently 1. A different layer from the inner definition.schemaVersion, and it moves separately. |
| What is dropped | Author, revision, workspace — values that mean nothing where the document is going. |
uid | Cleared on export; keeping it would leave the receiving side with two dashboards sharing one uid. |
Starting from an exported template beats starting from an empty file: it already passes validation, so its structure can be copied directly.
Managing definitions in a repository
Connect a repository and the JSON files in the chosen directory become dashboards. Definitions then follow the same flow as code — reviewed through a change request, and recovered by reverting a commit.
A connection is three things: the repository, the base branch, and the directory holding the definition files. The suggested default directory is .collabops/observability/dashboards.
my-service/
└── .collabops/
└── observability/
└── dashboards/ ← the connected directory (root path)
├── checkout-red.json
├── payments-logs.json
└── infra.json| Rule | Detail |
|---|---|
| File format | The same JSON envelope as export. Only files ending in .json are read. |
| Scope | Only below the connected directory; changes elsewhere are unrelated to this connection. |
| One file, one dashboard | uid identifies it. A hand-written file without one gets a generated uid, and two files sharing a uid in one commit are rejected. |
| Unit of application | A commit is applied in full or not at all. |
| On failure | The last successfully applied state stays live, and the failing files and reasons remain on the connection screen. The dashboards keep working. |
| Per-commit cap | 500 files · 6 MiB. Above that the commit is rejected rather than partially applied. |
Pushing from the screen
A connected dashboard can be edited on screen and written back with "Push to repository". You choose the file path, the commit message, and how it lands.
| Mode | Behavior |
|---|---|
| Change request | The default — creates a branch and opens a change request, so it lands after review. |
| Direct commit | Commits straight to the base branch of the connection. |
Edits you do not push are overwritten the moment that file is committed again. Once a repository is connected, the file is the source.
Having an AI write the definition
Do not let it guess field names. Hand it the fields, metrics, and labels you confirmed on screen and you avoid empty panels built on plausible names that do not exist.
Giving it an exported template works better than describing the rules in prose: it is a document that already validates, so structure, field names, and visualization pairings all come as examples.
Check the result by pasting it into the import screen. A rule failure names the spot as a JSON Pointer and the reason as a code, which is exactly what you hand back for the fix.
Passing validation does not mean the panels will have values. Validation only checks syntax and rules — whether that signal is actually being sent is something you confirm on screen after saving.