Platform

Platform overviewArchitectureWorkflow orchestrationGitOps configurationGovernance and AAAAI and MCPKnowledge and contextRuntime and executionEvidence and monitoring

Use cases

All use casesProduction incidentRelease preparationHotfix to productionSecurity scan triage
Why NopsAIIntegrationsSecurity

Resources

All resourcesAI agent governanceMCP governanceMCP securitySelf-hosted platforms
PricingGitHub

Company

How a run worksAboutContactBook a demo

Dashboards API

Dashboards, sections, source bindings, publication history, refreshes, and refresh schedules.

ReferenceOperatorAutomation authorAdministrator

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_refresh decides 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_key is what a pipeline dashboard.section must 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

NameInTypeRequiredDescription
team_pathquerystringOptionalRestrict to one team path.

Call it

List dashboardsapi-dashboards request
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/dashboards" | jq '.[] | {id, team_path}'
Result

Dashboards the caller may see, filtered per caller by AAA.

Responses

200application/json

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

StatusCauseWhat to do
503Authorization is unavailable.Check AAA.
500The query failed.Platform fault.

Side effects

  • None.

Proven by

  • services/nopsai/dashboard_auth_test.go
  • services/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

Create a dashboard with one sectionapi-dashboards request
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 .id
Result

The dashboard id, which a pipeline then publishes into through dashboard.ref.

Responses

201application/json

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

StatusCauseWhat to do
400A missing name or an unusable team path.The message names the field.
409A dashboard with that name already exists in the team.Pick another name.
500The 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.go
  • services/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

NameInTypeRequiredDescription
dashboardIDpathstringRequiredDashboard identifier.

Call it

Read a dashboard definitionapi-dashboards request
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/dashboards/$DASHBOARD_ID" | jq
Result

Sections, sources, and settings — the definition rather than the rendered data.

Responses

200application/json

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.go
  • services/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

NameInTypeRequiredDescription
dashboardIDpathstringRequiredDashboard identifier.

Call it

Read the rendered dashboardapi-dashboards request
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/dashboards/$DASHBOARD_ID/view" | jq
Result

Sections with their published entries, which is what the UI renders.

Responses

200application/json

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

StatusCauseWhat to do
500The view could not be composed.Platform fault.

Side effects

  • None.

Proven by

  • services/nopsai/dashboard_publication_test.go
  • services/nopsai/dashboards.go
PUT/v1/dashboards/{dashboardID}Authorized

Replaces a dashboard definition.

Parameters

NameInTypeRequiredDescription
dashboardIDpathstringRequiredDashboard identifier.

Call it

Replace a dashboardapi-dashboards request
curl -sX PUT "$NOPSAI_URL/v1/dashboards/$DASHBOARD_ID" \
  -H "Authorization: Bearer $NOPSAI_TOKEN" \
  -H "Content-Type: application/json" \
  --data @dashboard.json | jq
Result

The stored definition. Sections omitted from the body are removed with their entries.

Replace before running
  • A PUT replaces the whole definition; use PATCH to change one field.

Responses

200application/json

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

StatusCauseWhat to do
400An invalid section or layout.The message names the field.
409The new name collides with another dashboard in the team.Pick another name.
500The 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.go
  • services/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

NameInTypeRequiredDescription
dashboardIDpathstringRequiredDashboard identifier.

Call it

Retitle a dashboardapi-dashboards request
curl -sX PATCH "$NOPSAI_URL/v1/dashboards/$DASHBOARD_ID" \
  -H "Authorization: Bearer $NOPSAI_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"title":"Payments service health"}' | jq
Result

Only the supplied fields change; sections and entries are untouched.

Responses

200application/json

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

StatusCauseWhat to do
400A supplied field is invalid.The message names the field.
500The update failed.Retry.

Side effects

  • Writes an audit record.

Proven by

  • services/nopsai/dashboard_publication_test.go
  • services/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

NameInTypeRequiredDescription
dashboardIDpathstringRequiredDashboard identifier.

Call it

Delete a dashboardapi-dashboards request
curl -sX DELETE -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/dashboards/$DASHBOARD_ID" -w "%{http_code}\n"
Result

204. Pipelines publishing into it start failing that output item.

Responses

204

Dashboard deleted.

When it fails

StatusCauseWhat to do
500The delete failed.Retry.

Side effects

  • Every published entry goes with it.
  • Pipelines with a dashboard.ref to it fail that output item on their next run.
  • Writes an audit record.

Proven by

  • services/nopsai/dashboard_publication_test.go
  • services/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

NameInTypeRequiredDescription
dashboardIDpathstringRequiredDashboard identifier.

Call it

List sectionsapi-dashboards request
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/dashboards/$DASHBOARD_ID/sections" | jq
Result

Sections with their keys and display order. A pipeline publishes into a section by key.

Responses

200application/json

Sections.

[
  { "section_key": "releases", "title": "Releases", "display_order": 1 }
]

When it fails

StatusCauseWhat to do
500The query failed.Platform fault.

Side effects

  • None.

Proven by

  • services/nopsai/dashboard_publication_test.go
  • services/nopsai/dashboards.go
POST/v1/dashboards/{dashboardID}/sectionsAuthorized

Adds a section.

Parameters

NameInTypeRequiredDescription
dashboardIDpathstringRequiredDashboard identifier.

Call it

Add a sectionapi-dashboards request
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}' | jq
Result

The section, ready to be published into by key.

Responses

201application/json

Section created.

{ "section_key": "incidents", "title": "Incidents", "display_order": 2 }

When it fails

StatusCauseWhat to do
400A missing key or title.Both are required.
409That section key already exists on the dashboard.Keys are unique per dashboard.
500The section could not be created.Retry.

Side effects

  • Writes an audit record.

Proven by

  • services/nopsai/dashboard_publication_test.go
  • services/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

NameInTypeRequiredDescription
dashboardIDpathstringRequiredDashboard identifier.
sectionIDpathstringRequiredSection identifier or key.

Call it

Replace a sectionapi-dashboards request
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}' | jq
Result

The stored section. Its published entries are unaffected.

Responses

200application/json

Section replaced.

{ "section_key": "releases", "title": "Releases", "display_order": 1 }

When it fails

StatusCauseWhat to do
400An invalid layout or order.The message names the field.
404No section with that key.List the sections.
500The update failed.Retry.

Side effects

  • Writes an audit record.

Proven by

  • services/nopsai/dashboard_publication_test.go
  • services/nopsai/dashboards.go
PATCH/v1/dashboards/{dashboardID}/sections/{sectionID}Authorized

Partially updates a section.

Parameters

NameInTypeRequiredDescription
dashboardIDpathstringRequiredDashboard identifier.
sectionIDpathstringRequiredSection identifier or key.

Call it

Reorder a sectionapi-dashboards request
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}' | jq
Result

Only the supplied fields change.

Responses

200application/json

Section updated.

{ "section_key": "releases", "title": "Releases", "display_order": 3 }

When it fails

StatusCauseWhat to do
400A supplied field is invalid.The message names the field.
404No section with that key.List the sections.
500The update failed.Retry.

Side effects

  • Writes an audit record.

Proven by

  • services/nopsai/dashboard_publication_test.go
  • services/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

NameInTypeRequiredDescription
dashboardIDpathstringRequiredDashboard identifier.
sectionIDpathstringRequiredSection to delete.

Call it

Delete a sectionapi-dashboards request
curl -sX DELETE -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/dashboards/$DASHBOARD_ID/sections/incidents" -w "%{http_code}\n"
Result

204, with every entry published into it.

Responses

204

Section deleted.

When it fails

StatusCauseWhat to do
404No section with that key.It may already be deleted.
500The 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.go
  • services/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

NameInTypeRequiredDescription
dashboardIDpathstringRequiredDashboard identifier.

Call it

List source bindingsapi-dashboards request
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/dashboards/$DASHBOARD_ID/sources" | jq
Result

Which pipeline and output feeds each section, and whether it is required for a strict refresh.

Responses

200application/json

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

StatusCauseWhat to do
500The query failed.Platform fault.

Side effects

  • None.

Proven by

  • services/nopsai/dashboard_refresh_test.go
  • services/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

NameInTypeRequiredDescription
dashboardIDpathstringRequiredDashboard identifier.

Call it

Bind a pipeline output to a sectionapi-dashboards request
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}' | jq
Result

The binding. A refresh now runs this pipeline to repopulate the section.

Responses

201application/json

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

StatusCauseWhat to do
400An unknown pipeline, output name, or section key.The output name must match the pipeline final output item.
500The 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.go
  • services/nopsai/dashboards.go
PUT/v1/dashboards/{dashboardID}/sources/{sourceID}Authorized

Replaces a source binding.

Parameters

NameInTypeRequiredDescription
dashboardIDpathstringRequiredDashboard identifier.
sourceIDpathuuidRequiredSource binding identifier.

Call it

Replace a source bindingapi-dashboards request
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 | jq
Result

The stored binding.

Responses

200application/json

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

StatusCauseWhat to do
400An unknown pipeline or output name.The message names the field.
404No binding with that id.List the sources.
500The update failed.Retry.

Side effects

  • Changes what the next refresh runs.
  • Writes an audit record.

Proven by

  • services/nopsai/dashboard_refresh_test.go
  • services/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

NameInTypeRequiredDescription
dashboardIDpathstringRequiredDashboard identifier.
sourceIDpathuuidRequiredSource binding identifier.

Call it

Make a source optional for refreshapi-dashboards request
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}'
Result

A strict refresh now continues when this source fails instead of failing the whole run.

Responses

200application/json

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

StatusCauseWhat to do
400A supplied field is invalid.The message names the field.
404No binding with that id.List the sources.
500The 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.go
  • services/nopsai/dashboards.go
DELETE/v1/dashboards/{dashboardID}/sources/{sourceID}Authorized

Removes a source binding.

Parameters

NameInTypeRequiredDescription
dashboardIDpathstringRequiredDashboard identifier.
sourceIDpathuuidRequiredBinding to remove.

Call it

Remove a source bindingapi-dashboards request
curl -sX DELETE -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/dashboards/$DASHBOARD_ID/sources/$SOURCE_ID" -w "%{http_code}\n"
Result

204. Entries already published stay; refreshes stop running that pipeline.

Responses

204

Binding removed.

When it fails

StatusCauseWhat to do
404No binding with that id.It may already be removed.
500The 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.go
  • services/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

NameInTypeRequiredDescription
dashboardIDpathstringRequiredDashboard identifier.
limitqueryintegerOptionalHow many publications to return.

Call it

Read publication historyapi-dashboards request
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/dashboards/$DASHBOARD_ID/history" | jq
Result

What was published, when, and by which run.

Responses

200application/json

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

StatusCauseWhat to do
500The query failed.Platform fault.

Side effects

  • None.

Proven by

  • services/nopsai/dashboard_publication_test.go
  • services/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

NameInTypeRequiredDescription
dashboardIDpathstringRequiredDashboard identifier.
publicationIDpathstringRequiredPublication identifier from the history.

Call it

Remove a published entryapi-dashboards request
curl -sX DELETE -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/dashboards/$DASHBOARD_ID/publications/$PUBLICATION_ID" -w "%{http_code}\n"
Result

204. The card disappears from the view; the run that produced it is untouched.

Responses

204

Publication removed.

When it fails

StatusCauseWhat to do
404No publication with that id.Take the id from the history.
500The 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.go
  • services/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

NameInTypeRequiredDescription
dashboardIDpathstringRequiredDashboard identifier.
strictquerybooleanOptionalFail the refresh when a source marked required_for_refresh fails.Default: false

Call it

Start a strict refreshapi-dashboards request
curl -sX POST -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/dashboards/$DASHBOARD_ID/refresh?strict=true" | jq -r .id
Result

202 with a refresh id. This starts real pipeline runs, so it consumes runner capacity.

Responses

202application/json

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

StatusCauseWhat to do
400The dashboard has no source bindings to refresh.Bind at least one pipeline output first.
401The caller identity could not be resolved.A refresh runs pipelines as the caller and refuses to run as nobody.
409A 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.go
  • services/nopsai/dashboards.go
  • doc/dashboards.md
GET/v1/dashboards/{dashboardID}/refreshesAuthorized

Lists refresh runs.

Parameters

NameInTypeRequiredDescription
dashboardIDpathstringRequiredDashboard identifier.
limitqueryintegerOptionalHow many refreshes to return.

Call it

List refreshesapi-dashboards request
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/dashboards/$DASHBOARD_ID/refreshes" | jq '.[] | {id, state, sources_failed}'
Result

Recent refreshes with how many sources completed and failed.

Responses

200application/json

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

StatusCauseWhat to do
500The query failed.Platform fault.

Side effects

  • None.

Proven by

  • services/nopsai/dashboard_refresh_test.go
  • services/nopsai/dashboards.go
GET/v1/dashboards/{dashboardID}/refreshes/{refreshID}Authorized

Reads one refresh and its per-source progress.

Parameters

NameInTypeRequiredDescription
dashboardIDpathstringRequiredDashboard identifier.
refreshIDpathstringRequiredRefresh identifier.

Call it

Track a refreshapi-dashboards request
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/dashboards/$DASHBOARD_ID/refreshes/$REFRESH_ID" | jq
Result

State plus per-source progress, so a stuck refresh points at the source holding it up.

Responses

200application/json

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

StatusCauseWhat to do
404No refresh with that id.List the refreshes.
500The refresh could not be loaded.Platform fault.

Side effects

  • None.

Proven by

  • services/nopsai/dashboard_refresh_test.go
  • services/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

NameInTypeRequiredDescription
dashboardIDpathstringRequiredDashboard identifier.
refreshIDpathstringRequiredRefresh to cancel.

Call it

Cancel a refreshapi-dashboards request
curl -sX POST -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/dashboards/$DASHBOARD_ID/refreshes/$REFRESH_ID/cancel" | jq
Result

Remaining source runs are cancelled; sources that already published keep their entries.

Responses

200application/json

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.go
  • services/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

NameInTypeRequiredDescription
dashboardIDpathstringRequiredDashboard identifier.
refreshIDpathstringRequiredRefresh to retry.

Call it

Retry the failed sourcesapi-dashboards request
curl -sX POST -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/dashboards/$DASHBOARD_ID/refreshes/$REFRESH_ID/retry-failed" | jq
Result

202. Only the failed sources run again, which is much cheaper than a full refresh.

Responses

202application/json

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

StatusCauseWhat to do
401The 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.go
  • services/nopsai/dashboards.go
GET/v1/dashboards/{dashboardID}/refresh-schedulesAuthorized

Lists scheduled refreshes.

Parameters

NameInTypeRequiredDescription
dashboardIDpathstringRequiredDashboard identifier.

Call it

List refresh schedulesapi-dashboards request
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/dashboards/$DASHBOARD_ID/refresh-schedules" | jq
Result

Each schedule with its cron expression, timezone, and whether it is strict.

Responses

200application/json

Refresh schedules.

[{
  "id": "e17b8c05-2a49-4d31-b6f0-9c1e4a7d3b52",
  "cron_expression": "0 6 * * *",
  "timezone": "Europe/Berlin",
  "enabled": true,
  "strict": true
}]

When it fails

StatusCauseWhat to do
500The query failed.Platform fault.

Side effects

  • None.

Proven by

  • services/nopsai/dashboard_refresh_test.go
  • services/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

NameInTypeRequiredDescription
dashboardIDpathstringRequiredDashboard identifier.

Call it

Refresh every morningapi-dashboards request
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 .id
Result

The schedule id. Each firing starts the same work a manual refresh does.

Replace before running
  • timezone is an IANA name; without one a cron expression drifts twice a year.

Responses

201application/json

Schedule created.

{
  "id": "e17b8c05-2a49-4d31-b6f0-9c1e4a7d3b52",
  "cron_expression": "0 6 * * *",
  "timezone": "Europe/Berlin",
  "enabled": true,
  "strict": true
}

When it fails

StatusCauseWhat to do
400An invalid cron expression or timezone.The message names the field.
409An equivalent schedule already exists.Update the existing one.
500The 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.go
  • services/nopsai/dashboards.go
PUT/v1/dashboards/{dashboardID}/refresh-schedules/{scheduleID}Authorized

Replaces a refresh schedule.

Parameters

NameInTypeRequiredDescription
dashboardIDpathstringRequiredDashboard identifier.
scheduleIDpathstringRequiredSchedule identifier.

Call it

Change the refresh timeapi-dashboards request
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}' | jq
Result

The stored schedule with a recalculated next firing.

Responses

200application/json

Schedule replaced.

{
  "id": "e17b8c05-2a49-4d31-b6f0-9c1e4a7d3b52",
  "cron_expression": "0 6 * * *",
  "timezone": "Europe/Berlin",
  "enabled": true,
  "strict": true
}

When it fails

StatusCauseWhat to do
400An invalid cron expression or timezone.The message names the field.
404No schedule with that id.List the schedules.
500The update failed.Retry.

Side effects

  • Recalculates the next firing.
  • Writes an audit record.

Proven by

  • services/nopsai/dashboard_refresh_test.go
  • services/nopsai/dashboards.go
PATCH/v1/dashboards/{dashboardID}/refresh-schedules/{scheduleID}Authorized

Partially updates a refresh schedule.

Parameters

NameInTypeRequiredDescription
dashboardIDpathstringRequiredDashboard identifier.
scheduleIDpathstringRequiredSchedule identifier.

Call it

Make a scheduled refresh non-strictapi-dashboards request
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}'
Result

Failing sources no longer fail the whole scheduled refresh.

Responses

200application/json

Schedule updated.

{
  "id": "e17b8c05-2a49-4d31-b6f0-9c1e4a7d3b52",
  "cron_expression": "0 6 * * *",
  "timezone": "Europe/Berlin",
  "enabled": true,
  "strict": true
}

When it fails

StatusCauseWhat to do
400A supplied field is invalid.The message names the field.
404No schedule with that id.List the schedules.
500The update failed.Retry.

Side effects

  • Writes an audit record.

Proven by

  • services/nopsai/dashboard_refresh_test.go
  • services/nopsai/dashboards.go
POST/v1/dashboards/{dashboardID}/refresh-schedules/{scheduleID}/enableAuthorized

Enables a refresh schedule.

Parameters

NameInTypeRequiredDescription
dashboardIDpathstringRequiredDashboard identifier.
scheduleIDpathstringRequiredSchedule to enable.

Call it

Enable a refresh scheduleapi-dashboards request
curl -sX POST -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/dashboards/$DASHBOARD_ID/refresh-schedules/$SCHEDULE_ID/enable" | jq '{id, enabled}'
Result

Firing resumes from now; missed occurrences are not replayed.

Responses

200application/json

Schedule enabled.

{
  "id": "e17b8c05-2a49-4d31-b6f0-9c1e4a7d3b52",
  "cron_expression": "0 6 * * *",
  "timezone": "Europe/Berlin",
  "enabled": true,
  "strict": true
}

When it fails

StatusCauseWhat to do
404No schedule with that id.List the schedules.
500The change failed.Retry.

Side effects

  • Schedules the next firing from now.
  • Writes an audit record.

Proven by

  • services/nopsai/dashboard_refresh_test.go
  • services/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

NameInTypeRequiredDescription
dashboardIDpathstringRequiredDashboard identifier.
scheduleIDpathstringRequiredSchedule to disable.

Call it

Pause a refresh scheduleapi-dashboards request
curl -sX POST -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/dashboards/$DASHBOARD_ID/refresh-schedules/$SCHEDULE_ID/disable" | jq '{id, enabled}'
Result

Firing stops. A refresh already running is not cancelled.

Responses

200application/json

Schedule disabled.

{
  "id": "e17b8c05-2a49-4d31-b6f0-9c1e4a7d3b52",
  "cron_expression": "0 6 * * *",
  "timezone": "Europe/Berlin",
  "enabled": true,
  "strict": true
}

When it fails

StatusCauseWhat to do
404No schedule with that id.List the schedules.
500The change failed.Retry.

Side effects

  • Stops future firings. An in-flight refresh continues.
  • Writes an audit record.

Proven by

  • services/nopsai/dashboard_refresh_test.go
  • services/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

NameInTypeRequiredDescription
dashboardIDpathstringRequiredDashboard identifier.
scheduleIDpathstringRequiredSchedule to run.

Call it

Fire a scheduled refresh immediatelyapi-dashboards request
curl -sX POST -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/dashboards/$DASHBOARD_ID/refresh-schedules/$SCHEDULE_ID/run" | jq
Result

202, using the schedule own strictness. The next scheduled firing is unchanged.

Responses

202application/json

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

StatusCauseWhat to do
404No schedule with that id.List the schedules.
500The 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.go
  • services/nopsai/dashboards.go
DELETE/v1/dashboards/{dashboardID}/refresh-schedules/{scheduleID}Authorized

Deletes a refresh schedule.

Parameters

NameInTypeRequiredDescription
dashboardIDpathstringRequiredDashboard identifier.
scheduleIDpathstringRequiredSchedule to delete.

Call it

Delete a refresh scheduleapi-dashboards request
curl -sX DELETE -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/dashboards/$DASHBOARD_ID/refresh-schedules/$SCHEDULE_ID" -w "%{http_code}\n"
Result

204. Past refreshes and their published entries stay.

Responses

204

Schedule deleted.

When it fails

StatusCauseWhat to do
404No schedule with that id.It may already be deleted.
500The delete failed.Retry.

Side effects

  • Stops future firings. Refresh history remains.
  • Writes an audit record.

Proven by

  • services/nopsai/dashboard_refresh_test.go
  • services/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.go

    Dashboard, section, source, publication, and refresh handlers.

  • doc/dashboards.md

    Dashboard model, publication, refresh orchestration, and GitOps ownership.