Key points
- A refresh is not a query. It re-runs the bound source pipelines, so it is asynchronous, consumes runner capacity, and answers 409 while one is already in flight.
required_for_refreshdecides whether a strict refresh fails on a source failure or carries on.- Retrying only the failed sources is much cheaper than a fresh refresh, and successful sources are not re-run.
section_keyis what a pipelinedashboard.sectionmust match — a typo publishes nowhere without erroring.- Deleting a section deletes the entries published into it, and pipelines publishing there then silently stop appearing.
- Removing a publication is a display fix, not a data fix: the next publication brings it back.
- A PUT on a dashboard removes any section it omits; PATCH is the safe edit.
Operations
GET/v1/dashboardsAuthorized
Lists dashboards the caller can see.
Notes
A dashboard is owned by a team path, so two callers legitimately see different lists.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
team_path | query | string | Optional | Restrict to one team path. |
Call it
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/dashboards" | jq '.[] | {id, team_path}'Responses
Visible dashboards.
[{
"id": "platform/service-health",
"name": "service-health",
"team_path": "platform",
"title": "Service health",
"sections": [{ "section_key": "releases", "title": "Releases", "display_order": 1 }]
}]When it fails
| Status | Cause | What to do |
|---|---|---|
| 503 | Authorization is unavailable. | Check AAA. |
| 500 | The query failed. | Platform fault. |
Side effects
- None.
Proven by
services/nopsai/dashboard_auth_test.goservices/nopsai/dashboards.go
POST/v1/dashboardsAuthorized
Creates a dashboard.
Notes
The dashboard is the container; the data arrives from pipeline final outputs of type: dashboard.
Call it
curl -sX POST "$NOPSAI_URL/v1/dashboards" \
-H "Authorization: Bearer $NOPSAI_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"service-health","team_path":"platform","sections":[{"section_key":"releases","title":"Releases"}]}' | jq -r .idResponses
Dashboard created.
{
"id": "platform/service-health",
"name": "service-health",
"team_path": "platform",
"title": "Service health",
"sections": [{ "section_key": "releases", "title": "Releases", "display_order": 1 }]
}When it fails
| Status | Cause | What to do |
|---|---|---|
| 400 | A missing name or an unusable team path. | The message names the field. |
| 409 | A dashboard with that name already exists in the team. | Pick another name. |
| 500 | The dashboard could not be created. | Retry. |
Side effects
- Creates a publication target. Nothing appears until a run publishes into it.
- Writes an audit record.
Proven by
services/nopsai/dashboard_publication_test.goservices/nopsai/dashboards.go
GET/v1/dashboards/{dashboardID}Authorized
Reads a dashboard definition.
Notes
Definition and rendered view are separate routes: this one never composes published data.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
dashboardID | path | string | Required | Dashboard identifier. |
Call it
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/dashboards/$DASHBOARD_ID" | jqResponses
The dashboard definition.
{
"id": "platform/service-health",
"name": "service-health",
"team_path": "platform",
"title": "Service health",
"sections": [{ "section_key": "releases", "title": "Releases", "display_order": 1 }]
}Side effects
- None.
Proven by
services/nopsai/dashboard_auth_test.goservices/nopsai/dashboards.go
GET/v1/dashboards/{dashboardID}/viewAuthorized
Reads the composed dashboard with its current entries.
Notes
An empty section means nothing has published yet, not that the dashboard is broken. The publication history tells the two apart.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
dashboardID | path | string | Required | Dashboard identifier. |
Call it
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/dashboards/$DASHBOARD_ID/view" | jqResponses
The composed view.
{
"id": "platform/service-health",
"sections": [
{
"section_key": "releases",
"entries": [{ "entry_key": "payments", "published_at": "2026-08-19T12:44:02Z" }]
}
]
}When it fails
| Status | Cause | What to do |
|---|---|---|
| 500 | The view could not be composed. | Platform fault. |
Side effects
- None.
Proven by
services/nopsai/dashboard_publication_test.goservices/nopsai/dashboards.go
PUT/v1/dashboards/{dashboardID}Authorized
Replaces a dashboard definition.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
dashboardID | path | string | Required | Dashboard identifier. |
Call it
curl -sX PUT "$NOPSAI_URL/v1/dashboards/$DASHBOARD_ID" \
-H "Authorization: Bearer $NOPSAI_TOKEN" \
-H "Content-Type: application/json" \
--data @dashboard.json | jqResponses
Dashboard replaced.
{
"id": "platform/service-health",
"name": "service-health",
"team_path": "platform",
"title": "Service health",
"sections": [{ "section_key": "releases", "title": "Releases", "display_order": 1 }]
}When it fails
| Status | Cause | What to do |
|---|---|---|
| 400 | An invalid section or layout. | The message names the field. |
| 409 | The new name collides with another dashboard in the team. | Pick another name. |
| 500 | The update failed. | Retry. |
Side effects
- Removing a section removes the entries published into it.
- Writes an audit record.
Proven by
services/nopsai/dashboard_publication_test.goservices/nopsai/dashboards.go
PATCH/v1/dashboards/{dashboardID}Authorized
Partially updates a dashboard.
Notes
Prefer PATCH for edits: a PUT that forgets a section deletes it and everything published into it.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
dashboardID | path | string | Required | Dashboard identifier. |
Call it
curl -sX PATCH "$NOPSAI_URL/v1/dashboards/$DASHBOARD_ID" \
-H "Authorization: Bearer $NOPSAI_TOKEN" \
-H "Content-Type: application/json" \
-d '{"title":"Payments service health"}' | jqResponses
Dashboard updated.
{
"id": "platform/service-health",
"name": "service-health",
"team_path": "platform",
"title": "Service health",
"sections": [{ "section_key": "releases", "title": "Releases", "display_order": 1 }]
}When it fails
| Status | Cause | What to do |
|---|---|---|
| 400 | A supplied field is invalid. | The message names the field. |
| 500 | The update failed. | Retry. |
Side effects
- Writes an audit record.
Proven by
services/nopsai/dashboard_publication_test.goservices/nopsai/dashboards.go
DELETE/v1/dashboards/{dashboardID}Authorized
Deletes a dashboard.
Notes
Check which pipelines publish into it first: the failure surfaces at their next run, not here.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
dashboardID | path | string | Required | Dashboard identifier. |
Call it
curl -sX DELETE -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/dashboards/$DASHBOARD_ID" -w "%{http_code}\n"Responses
Dashboard deleted.
When it fails
| Status | Cause | What to do |
|---|---|---|
| 500 | The delete failed. | Retry. |
Side effects
- Every published entry goes with it.
- Pipelines with a
dashboard.refto it fail that output item on their next run. - Writes an audit record.
Proven by
services/nopsai/dashboard_publication_test.goservices/nopsai/dashboards.go
GET/v1/dashboards/{dashboardID}/sectionsAuthorized
Lists a dashboard’s sections.
Notes
section_key is what a pipeline’s dashboard.section must match. A typo there publishes nowhere without erroring.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
dashboardID | path | string | Required | Dashboard identifier. |
Call it
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/dashboards/$DASHBOARD_ID/sections" | jqResponses
Sections.
[
{ "section_key": "releases", "title": "Releases", "display_order": 1 }
]When it fails
| Status | Cause | What to do |
|---|---|---|
| 500 | The query failed. | Platform fault. |
Side effects
- None.
Proven by
services/nopsai/dashboard_publication_test.goservices/nopsai/dashboards.go
POST/v1/dashboards/{dashboardID}/sectionsAuthorized
Adds a section.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
dashboardID | path | string | Required | Dashboard identifier. |
Call it
curl -sX POST "$NOPSAI_URL/v1/dashboards/$DASHBOARD_ID/sections" \
-H "Authorization: Bearer $NOPSAI_TOKEN" \
-H "Content-Type: application/json" \
-d '{"section_key":"incidents","title":"Incidents","display_order":2}' | jqResponses
Section created.
{ "section_key": "incidents", "title": "Incidents", "display_order": 2 }When it fails
| Status | Cause | What to do |
|---|---|---|
| 400 | A missing key or title. | Both are required. |
| 409 | That section key already exists on the dashboard. | Keys are unique per dashboard. |
| 500 | The section could not be created. | Retry. |
Side effects
- Writes an audit record.
Proven by
services/nopsai/dashboard_publication_test.goservices/nopsai/dashboards.go
PUT/v1/dashboards/{dashboardID}/sections/{sectionID}Authorized
Replaces a section.
Notes
Renaming the title is safe; changing the key is not, because pipelines publish by key.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
dashboardID | path | string | Required | Dashboard identifier. |
sectionID | path | string | Required | Section identifier or key. |
Call it
curl -sX PUT "$NOPSAI_URL/v1/dashboards/$DASHBOARD_ID/sections/releases" \
-H "Authorization: Bearer $NOPSAI_TOKEN" \
-H "Content-Type: application/json" \
-d '{"title":"Releases","display_order":1}' | jqResponses
Section replaced.
{ "section_key": "releases", "title": "Releases", "display_order": 1 }When it fails
| Status | Cause | What to do |
|---|---|---|
| 400 | An invalid layout or order. | The message names the field. |
| 404 | No section with that key. | List the sections. |
| 500 | The update failed. | Retry. |
Side effects
- Writes an audit record.
Proven by
services/nopsai/dashboard_publication_test.goservices/nopsai/dashboards.go
PATCH/v1/dashboards/{dashboardID}/sections/{sectionID}Authorized
Partially updates a section.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
dashboardID | path | string | Required | Dashboard identifier. |
sectionID | path | string | Required | Section identifier or key. |
Call it
curl -sX PATCH "$NOPSAI_URL/v1/dashboards/$DASHBOARD_ID/sections/releases" \
-H "Authorization: Bearer $NOPSAI_TOKEN" \
-H "Content-Type: application/json" \
-d '{"display_order":3}' | jqResponses
Section updated.
{ "section_key": "releases", "title": "Releases", "display_order": 3 }When it fails
| Status | Cause | What to do |
|---|---|---|
| 400 | A supplied field is invalid. | The message names the field. |
| 404 | No section with that key. | List the sections. |
| 500 | The update failed. | Retry. |
Side effects
- Writes an audit record.
Proven by
services/nopsai/dashboard_publication_test.goservices/nopsai/dashboards.go
DELETE/v1/dashboards/{dashboardID}/sections/{sectionID}Authorized
Deletes a section and its entries.
Notes
A pipeline publishing into a missing section does not error — it publishes nowhere. That is the failure mode to watch for after deleting one.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
dashboardID | path | string | Required | Dashboard identifier. |
sectionID | path | string | Required | Section to delete. |
Call it
curl -sX DELETE -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/dashboards/$DASHBOARD_ID/sections/incidents" -w "%{http_code}\n"Responses
Section deleted.
When it fails
| Status | Cause | What to do |
|---|---|---|
| 404 | No section with that key. | It may already be deleted. |
| 500 | The delete failed. | Retry. |
Side effects
- Removes the entries published into the section.
- Pipelines publishing into it silently stop appearing.
- Writes an audit record.
Proven by
services/nopsai/dashboard_publication_test.goservices/nopsai/dashboards.go
GET/v1/dashboards/{dashboardID}/sourcesAuthorized
Lists the source bindings a refresh will run.
Notes
A source binding is what makes a refresh possible: without one, the dashboard only updates when a pipeline happens to run.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
dashboardID | path | string | Required | Dashboard identifier. |
Call it
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/dashboards/$DASHBOARD_ID/sources" | jqResponses
Source bindings.
[{
"id": "7f10c3a2-55d8-4a19-8f61-2c4b9e0d7a34",
"section_key": "releases",
"pipeline_id": "platform/release-service",
"output_name": "Release health",
"entry_key": "payments",
"enabled": true,
"required_for_refresh": true
}]When it fails
| Status | Cause | What to do |
|---|---|---|
| 500 | The query failed. | Platform fault. |
Side effects
- None.
Proven by
services/nopsai/dashboard_refresh_test.goservices/nopsai/dashboards.go
POST/v1/dashboards/{dashboardID}/sourcesAuthorized
Binds a pipeline output to a section.
Notes
required_for_refresh decides whether a strict refresh fails when this source fails, or carries on with the rest.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
dashboardID | path | string | Required | Dashboard identifier. |
Call it
curl -sX POST "$NOPSAI_URL/v1/dashboards/$DASHBOARD_ID/sources" \
-H "Authorization: Bearer $NOPSAI_TOKEN" \
-H "Content-Type: application/json" \
-d '{"section_key":"releases","pipeline_id":"platform/release-service","output_name":"Release health","entry_key":"payments","required_for_refresh":true}' | jqResponses
Source bound.
{
"id": "7f10c3a2-55d8-4a19-8f61-2c4b9e0d7a34",
"section_key": "releases",
"pipeline_id": "platform/release-service",
"output_name": "Release health",
"entry_key": "payments",
"enabled": true,
"required_for_refresh": true
}When it fails
| Status | Cause | What to do |
|---|---|---|
| 400 | An unknown pipeline, output name, or section key. | The output name must match the pipeline final output item. |
| 500 | The binding could not be stored. | Retry. |
Side effects
- A refresh will run this pipeline, which consumes runner capacity.
- Writes an audit record.
Proven by
services/nopsai/dashboard_refresh_test.goservices/nopsai/dashboards.go
PUT/v1/dashboards/{dashboardID}/sources/{sourceID}Authorized
Replaces a source binding.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
dashboardID | path | string | Required | Dashboard identifier. |
sourceID | path | uuid | Required | Source binding identifier. |
Call it
curl -sX PUT "$NOPSAI_URL/v1/dashboards/$DASHBOARD_ID/sources/$SOURCE_ID" \
-H "Authorization: Bearer $NOPSAI_TOKEN" \
-H "Content-Type: application/json" \
--data @source.json | jqResponses
Binding replaced.
{
"id": "7f10c3a2-55d8-4a19-8f61-2c4b9e0d7a34",
"section_key": "releases",
"pipeline_id": "platform/release-service",
"output_name": "Release health",
"entry_key": "payments",
"enabled": true,
"required_for_refresh": true
}When it fails
| Status | Cause | What to do |
|---|---|---|
| 400 | An unknown pipeline or output name. | The message names the field. |
| 404 | No binding with that id. | List the sources. |
| 500 | The update failed. | Retry. |
Side effects
- Changes what the next refresh runs.
- Writes an audit record.
Proven by
services/nopsai/dashboard_refresh_test.goservices/nopsai/dashboards.go
PATCH/v1/dashboards/{dashboardID}/sources/{sourceID}Authorized
Partially updates a source binding.
Notes
This is the setting to reach for when one flaky source keeps failing an otherwise healthy refresh.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
dashboardID | path | string | Required | Dashboard identifier. |
sourceID | path | uuid | Required | Source binding identifier. |
Call it
curl -sX PATCH "$NOPSAI_URL/v1/dashboards/$DASHBOARD_ID/sources/$SOURCE_ID" \
-H "Authorization: Bearer $NOPSAI_TOKEN" \
-H "Content-Type: application/json" \
-d '{"required_for_refresh":false}' | jq '{id, required_for_refresh}'Responses
Binding updated.
{
"id": "7f10c3a2-55d8-4a19-8f61-2c4b9e0d7a34",
"section_key": "releases",
"pipeline_id": "platform/release-service",
"output_name": "Release health",
"entry_key": "payments",
"enabled": true,
"required_for_refresh": true
}When it fails
| Status | Cause | What to do |
|---|---|---|
| 400 | A supplied field is invalid. | The message names the field. |
| 404 | No binding with that id. | List the sources. |
| 500 | The update failed. | Retry. |
Side effects
- Changes how a strict refresh treats a failure from this source.
- Writes an audit record.
Proven by
services/nopsai/dashboard_refresh_test.goservices/nopsai/dashboards.go
DELETE/v1/dashboards/{dashboardID}/sources/{sourceID}Authorized
Removes a source binding.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
dashboardID | path | string | Required | Dashboard identifier. |
sourceID | path | uuid | Required | Binding to remove. |
Call it
curl -sX DELETE -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/dashboards/$DASHBOARD_ID/sources/$SOURCE_ID" -w "%{http_code}\n"Responses
Binding removed.
When it fails
| Status | Cause | What to do |
|---|---|---|
| 404 | No binding with that id. | It may already be removed. |
| 500 | The delete failed. | Retry. |
Side effects
- Refreshes stop running that pipeline. Published entries remain until overwritten or expired.
- Writes an audit record.
Proven by
services/nopsai/dashboard_refresh_test.goservices/nopsai/dashboards.go
GET/v1/dashboards/{dashboardID}/historyAuthorized
Lists publication history.
Notes
This is where a missing tile is diagnosed: it separates "never published" from "published and later removed".
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
dashboardID | path | string | Required | Dashboard identifier. |
limit | query | integer | Optional | How many publications to return. |
Call it
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/dashboards/$DASHBOARD_ID/history" | jqResponses
Publication history.
[
{
"publication_id": "a30f5c81-2b47-4d90-9e12-6f3a7c0b5d24",
"section_key": "releases",
"entry_key": "payments",
"run_id": "9c1f7a5e-2b44-4d2f-8f2a-2c9f0b6d4e11",
"published_at": "2026-08-19T12:44:02Z"
}
]When it fails
| Status | Cause | What to do |
|---|---|---|
| 500 | The query failed. | Platform fault. |
Side effects
- None.
Proven by
services/nopsai/dashboard_publication_test.goservices/nopsai/dashboards.go
DELETE/v1/dashboards/{dashboardID}/publications/{publicationID}Authorized
Removes one published entry.
Notes
Removing a bad entry is a display fix, not a data fix. The next publication from that source brings it back.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
dashboardID | path | string | Required | Dashboard identifier. |
publicationID | path | string | Required | Publication identifier from the history. |
Call it
curl -sX DELETE -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/dashboards/$DASHBOARD_ID/publications/$PUBLICATION_ID" -w "%{http_code}\n"Responses
Publication removed.
When it fails
| Status | Cause | What to do |
|---|---|---|
| 404 | No publication with that id. | Take the id from the history. |
| 500 | The delete failed. | Retry. |
Side effects
- Removes one entry from the view. The producing run and its outputs are unaffected.
- Writes an audit record.
Proven by
services/nopsai/dashboard_publication_test.goservices/nopsai/dashboards.go
POST/v1/dashboards/{dashboardID}/refreshAuthorized
Starts a refresh, re-running the bound source pipelines.
Notes
A refresh is not a query. It re-runs the source pipelines, which is why it is asynchronous, capacity-bound, and refused when one is already in flight.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
dashboardID | path | string | Required | Dashboard identifier. |
strict | query | boolean | Optional | Fail the refresh when a source marked required_for_refresh fails.Default: false |
Call it
curl -sX POST -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/dashboards/$DASHBOARD_ID/refresh?strict=true" | jq -r .idResponses
Refresh started.
{
"id": "c92a1f70-4d6b-4a02-9f18-7b3c5e9d2a41",
"state": "running",
"started_at": "2026-08-19T13:02:11Z",
"sources_total": 3,
"sources_completed": 1,
"sources_failed": 0
}When it fails
| Status | Cause | What to do |
|---|---|---|
| 400 | The dashboard has no source bindings to refresh. | Bind at least one pipeline output first. |
| 401 | The caller identity could not be resolved. | A refresh runs pipelines as the caller and refuses to run as nobody. |
| 409 | A refresh is already running for this dashboard. | Poll the refresh, or cancel it before starting another. |
Side effects
- Starts a pipeline run per bound source, consuming runner capacity.
- Each run publishes into the dashboard on completion.
- Writes an audit record.
Proven by
services/nopsai/dashboard_refresh_test.goservices/nopsai/dashboards.godoc/dashboards.md
GET/v1/dashboards/{dashboardID}/refreshesAuthorized
Lists refresh runs.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
dashboardID | path | string | Required | Dashboard identifier. |
limit | query | integer | Optional | How many refreshes to return. |
Call it
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/dashboards/$DASHBOARD_ID/refreshes" | jq '.[] | {id, state, sources_failed}'Responses
Refresh runs.
[{
"id": "c92a1f70-4d6b-4a02-9f18-7b3c5e9d2a41",
"state": "running",
"started_at": "2026-08-19T13:02:11Z",
"sources_total": 3,
"sources_completed": 1,
"sources_failed": 0
}]When it fails
| Status | Cause | What to do |
|---|---|---|
| 500 | The query failed. | Platform fault. |
Side effects
- None.
Proven by
services/nopsai/dashboard_refresh_test.goservices/nopsai/dashboards.go
GET/v1/dashboards/{dashboardID}/refreshes/{refreshID}Authorized
Reads one refresh and its per-source progress.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
dashboardID | path | string | Required | Dashboard identifier. |
refreshID | path | string | Required | Refresh identifier. |
Call it
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/dashboards/$DASHBOARD_ID/refreshes/$REFRESH_ID" | jqResponses
The refresh.
{
"id": "c92a1f70-4d6b-4a02-9f18-7b3c5e9d2a41",
"state": "running",
"started_at": "2026-08-19T13:02:11Z",
"sources_total": 3,
"sources_completed": 1,
"sources_failed": 0
}When it fails
| Status | Cause | What to do |
|---|---|---|
| 404 | No refresh with that id. | List the refreshes. |
| 500 | The refresh could not be loaded. | Platform fault. |
Side effects
- None.
Proven by
services/nopsai/dashboard_refresh_test.goservices/nopsai/dashboards.go
POST/v1/dashboards/{dashboardID}/refreshes/{refreshID}/cancelAuthorized
Cancels a running refresh.
Notes
Cancelling leaves a partially refreshed dashboard: sources that finished keep their new entries.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
dashboardID | path | string | Required | Dashboard identifier. |
refreshID | path | string | Required | Refresh to cancel. |
Call it
curl -sX POST -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/dashboards/$DASHBOARD_ID/refreshes/$REFRESH_ID/cancel" | jqResponses
Cancellation accepted.
{ "id": "c92a1f70-4d6b-4a02-9f18-7b3c5e9d2a41", "state": "cancelled" }Side effects
- Cancels the outstanding source runs.
- Writes an audit record.
Proven by
services/nopsai/dashboard_refresh_test.goservices/nopsai/dashboards.go
POST/v1/dashboards/{dashboardID}/refreshes/{refreshID}/retry-failedAuthorized
Retries only the sources that failed in a refresh.
Notes
Reach for this rather than a fresh refresh after a transient failure: successful sources are not re-run.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
dashboardID | path | string | Required | Dashboard identifier. |
refreshID | path | string | Required | Refresh to retry. |
Call it
curl -sX POST -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/dashboards/$DASHBOARD_ID/refreshes/$REFRESH_ID/retry-failed" | jqResponses
Retry started for the failed sources.
{
"id": "c92a1f70-4d6b-4a02-9f18-7b3c5e9d2a41",
"state": "running",
"started_at": "2026-08-19T13:02:11Z",
"sources_total": 3,
"sources_completed": 1,
"sources_failed": 0
}When it fails
| Status | Cause | What to do |
|---|---|---|
| 401 | The caller identity could not be resolved. | A retry runs pipelines as the caller. |
Side effects
- Starts a run for each previously failed source.
- Writes an audit record.
Proven by
services/nopsai/dashboard_refresh_test.goservices/nopsai/dashboards.go
GET/v1/dashboards/{dashboardID}/refresh-schedulesAuthorized
Lists scheduled refreshes.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
dashboardID | path | string | Required | Dashboard identifier. |
Call it
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/dashboards/$DASHBOARD_ID/refresh-schedules" | jqResponses
Refresh schedules.
[{
"id": "e17b8c05-2a49-4d31-b6f0-9c1e4a7d3b52",
"cron_expression": "0 6 * * *",
"timezone": "Europe/Berlin",
"enabled": true,
"strict": true
}]When it fails
| Status | Cause | What to do |
|---|---|---|
| 500 | The query failed. | Platform fault. |
Side effects
- None.
Proven by
services/nopsai/dashboard_refresh_test.goservices/nopsai/dashboards.go
POST/v1/dashboards/{dashboardID}/refresh-schedulesAuthorized
Creates a scheduled refresh.
Notes
A daily refresh of a five-source dashboard is five pipeline runs a day. Size the cadence against capacity, not against how fresh the data could theoretically be.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
dashboardID | path | string | Required | Dashboard identifier. |
Call it
curl -sX POST "$NOPSAI_URL/v1/dashboards/$DASHBOARD_ID/refresh-schedules" \
-H "Authorization: Bearer $NOPSAI_TOKEN" \
-H "Content-Type: application/json" \
-d '{"cron_expression":"0 6 * * *","timezone":"Europe/Berlin","strict":true}' | jq -r .idResponses
Schedule created.
{
"id": "e17b8c05-2a49-4d31-b6f0-9c1e4a7d3b52",
"cron_expression": "0 6 * * *",
"timezone": "Europe/Berlin",
"enabled": true,
"strict": true
}When it fails
| Status | Cause | What to do |
|---|---|---|
| 400 | An invalid cron expression or timezone. | The message names the field. |
| 409 | An equivalent schedule already exists. | Update the existing one. |
| 500 | The schedule could not be created. | Retry. |
Side effects
- Every firing starts source pipeline runs and consumes runner capacity.
- Writes an audit record.
Proven by
services/nopsai/dashboard_refresh_test.goservices/nopsai/dashboards.go
PUT/v1/dashboards/{dashboardID}/refresh-schedules/{scheduleID}Authorized
Replaces a refresh schedule.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
dashboardID | path | string | Required | Dashboard identifier. |
scheduleID | path | string | Required | Schedule identifier. |
Call it
curl -sX PUT "$NOPSAI_URL/v1/dashboards/$DASHBOARD_ID/refresh-schedules/$SCHEDULE_ID" \
-H "Authorization: Bearer $NOPSAI_TOKEN" \
-H "Content-Type: application/json" \
-d '{"cron_expression":"0 7 * * *","timezone":"Europe/Berlin","strict":true}' | jqResponses
Schedule replaced.
{
"id": "e17b8c05-2a49-4d31-b6f0-9c1e4a7d3b52",
"cron_expression": "0 6 * * *",
"timezone": "Europe/Berlin",
"enabled": true,
"strict": true
}When it fails
| Status | Cause | What to do |
|---|---|---|
| 400 | An invalid cron expression or timezone. | The message names the field. |
| 404 | No schedule with that id. | List the schedules. |
| 500 | The update failed. | Retry. |
Side effects
- Recalculates the next firing.
- Writes an audit record.
Proven by
services/nopsai/dashboard_refresh_test.goservices/nopsai/dashboards.go
PATCH/v1/dashboards/{dashboardID}/refresh-schedules/{scheduleID}Authorized
Partially updates a refresh schedule.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
dashboardID | path | string | Required | Dashboard identifier. |
scheduleID | path | string | Required | Schedule identifier. |
Call it
curl -sX PATCH "$NOPSAI_URL/v1/dashboards/$DASHBOARD_ID/refresh-schedules/$SCHEDULE_ID" \
-H "Authorization: Bearer $NOPSAI_TOKEN" \
-H "Content-Type: application/json" \
-d '{"strict":false}' | jq '{id, strict}'Responses
Schedule updated.
{
"id": "e17b8c05-2a49-4d31-b6f0-9c1e4a7d3b52",
"cron_expression": "0 6 * * *",
"timezone": "Europe/Berlin",
"enabled": true,
"strict": true
}When it fails
| Status | Cause | What to do |
|---|---|---|
| 400 | A supplied field is invalid. | The message names the field. |
| 404 | No schedule with that id. | List the schedules. |
| 500 | The update failed. | Retry. |
Side effects
- Writes an audit record.
Proven by
services/nopsai/dashboard_refresh_test.goservices/nopsai/dashboards.go
POST/v1/dashboards/{dashboardID}/refresh-schedules/{scheduleID}/enableAuthorized
Enables a refresh schedule.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
dashboardID | path | string | Required | Dashboard identifier. |
scheduleID | path | string | Required | Schedule to enable. |
Call it
curl -sX POST -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/dashboards/$DASHBOARD_ID/refresh-schedules/$SCHEDULE_ID/enable" | jq '{id, enabled}'Responses
Schedule enabled.
{
"id": "e17b8c05-2a49-4d31-b6f0-9c1e4a7d3b52",
"cron_expression": "0 6 * * *",
"timezone": "Europe/Berlin",
"enabled": true,
"strict": true
}When it fails
| Status | Cause | What to do |
|---|---|---|
| 404 | No schedule with that id. | List the schedules. |
| 500 | The change failed. | Retry. |
Side effects
- Schedules the next firing from now.
- Writes an audit record.
Proven by
services/nopsai/dashboard_refresh_test.goservices/nopsai/dashboards.go
POST/v1/dashboards/{dashboardID}/refresh-schedules/{scheduleID}/disableAuthorized
Disables a refresh schedule.
Notes
The fastest way to stop a dashboard consuming capacity while its sources are being fixed.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
dashboardID | path | string | Required | Dashboard identifier. |
scheduleID | path | string | Required | Schedule to disable. |
Call it
curl -sX POST -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/dashboards/$DASHBOARD_ID/refresh-schedules/$SCHEDULE_ID/disable" | jq '{id, enabled}'Responses
Schedule disabled.
{
"id": "e17b8c05-2a49-4d31-b6f0-9c1e4a7d3b52",
"cron_expression": "0 6 * * *",
"timezone": "Europe/Berlin",
"enabled": true,
"strict": true
}When it fails
| Status | Cause | What to do |
|---|---|---|
| 404 | No schedule with that id. | List the schedules. |
| 500 | The change failed. | Retry. |
Side effects
- Stops future firings. An in-flight refresh continues.
- Writes an audit record.
Proven by
services/nopsai/dashboard_refresh_test.goservices/nopsai/dashboards.go
POST/v1/dashboards/{dashboardID}/refresh-schedules/{scheduleID}/runAuthorized
Runs a scheduled refresh now.
Notes
The cheapest way to test a schedule strictness setting without waiting for 6am.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
dashboardID | path | string | Required | Dashboard identifier. |
scheduleID | path | string | Required | Schedule to run. |
Call it
curl -sX POST -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/dashboards/$DASHBOARD_ID/refresh-schedules/$SCHEDULE_ID/run" | jqResponses
Refresh started from the schedule.
{
"id": "c92a1f70-4d6b-4a02-9f18-7b3c5e9d2a41",
"state": "running",
"started_at": "2026-08-19T13:02:11Z",
"sources_total": 3,
"sources_completed": 1,
"sources_failed": 0
}When it fails
| Status | Cause | What to do |
|---|---|---|
| 404 | No schedule with that id. | List the schedules. |
| 500 | The refresh could not be started. | Retry. |
Side effects
- Starts the source runs. Does not move the next scheduled firing.
- Writes an audit record.
Proven by
services/nopsai/dashboard_refresh_test.goservices/nopsai/dashboards.go
DELETE/v1/dashboards/{dashboardID}/refresh-schedules/{scheduleID}Authorized
Deletes a refresh schedule.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
dashboardID | path | string | Required | Dashboard identifier. |
scheduleID | path | string | Required | Schedule to delete. |
Call it
curl -sX DELETE -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/dashboards/$DASHBOARD_ID/refresh-schedules/$SCHEDULE_ID" -w "%{http_code}\n"Responses
Schedule deleted.
When it fails
| Status | Cause | What to do |
|---|---|---|
| 404 | No schedule with that id. | It may already be deleted. |
| 500 | The delete failed. | Retry. |
Side effects
- Stops future firings. Refresh history remains.
- Writes an audit record.
Proven by
services/nopsai/dashboard_refresh_test.goservices/nopsai/dashboards.go
How it works
The mental model is publish-then-read, not query. A pipeline final output of type: dashboard publishes an entry; the dashboard stores it; the view composes what has been stored. Nothing here reaches back into run data on demand, which is why an empty section means "nothing published yet" rather than "the query failed".
That is also why refresh cadence is a capacity decision. A daily refresh of a five-source dashboard is five pipeline runs a day, and a strict schedule turns one flaky source into a daily failure. Both settings exist to be tuned rather than defaulted.
When a tile is missing, read the publication history first: it distinguishes never published, published into the wrong section key, and published and later removed. Those have three different fixes.
Implementation evidence
services/nopsai/dashboards.goDashboard, section, source, publication, and refresh handlers.
doc/dashboards.mdDashboard model, publication, refresh orchestration, and GitOps ownership.

