Last updated: October 2, 2026
Use the SLO API
SLOs can be created and managed through the Dash0 API using an OpenSLO v1 SLO definition (apiVersion: openslo/v1, kind: SLO). Dash0 implements a subset of the specification. The same SLOs can also be created and edited in the UI; see Create SLOs.
Authentication
Calls require an Authorization: Bearer <token> header using a Dash0 auth token (format auth_...). Create and manage tokens in Organization Settings › Auth Tokens (Admins only).
- Creating, updating, or deleting an SLO requires a token with the All permissions (
*) option. - Reading SLOs (
GET) requires at least the Reading (*:read) option. A Reading token is rejected with403 Forbiddenon create, update, or delete. - A token with only the Ingesting option gets
403 Forbiddenon every SLO call.
Endpoints
The SLO API lives on the Dash0 API host.
| Method and path | Purpose |
|---|---|
POST /api/slos?dataset={dataset} | Create an SLO |
GET /api/slos?dataset={dataset}[&originPrefix={prefix}] | List SLOs, optionally only those whose dash0.com/origin starts with prefix |
GET /api/slos/{originOrId}?dataset={dataset}[&format=yaml] | Read the latest version of an SLO; format=yaml returns it as an OpenSLO YAML document |
PUT /api/slos/{originOrId}?dataset={dataset} | Update an SLO, or create it when {originOrId} is an unknown dash0.com/origin (see below) |
DELETE /api/slos/{originOrId}?dataset={dataset} | Soft-delete an SLO (returns 204; a later PUT to the same origin restores it) |
{originOrId} is either the server-issued id (the dash0.com/id label, slo_...) or the dash0.com/origin label you set yourself.
The dataset query parameter is optional. When omitted, POST and PUT fall back to the dash0.com/dataset label in metadata.labels and then to default; GET and DELETE fall back to your token's only dataset or to default, and answer 403 if the token cannot access that dataset. The Dash0-Dataset request header is an alternative to the query parameter. On POST and PUT requests (which have a body), the dataset query parameter overrides the dash0.com/dataset label if both are provided.
Upsert and restore. For auth-token callers, PUT /api/slos/{originOrId} creates the SLO when {originOrId} matches nothing and is not a slo_... id; the path value becomes the SLO's dash0.com/origin. A slo_... id that does not exist returns 404. A PUT to a deleted SLO restores it and writes a new version.
Optimistic locking. Every read returns a dash0.com/version label. You may send it back on PUT: if present it must match the current version, otherwise the request is rejected with 400 (superseded). Omit it to overwrite unconditionally.
The base host is region-specific (for example https://api.eu-west-1.aws.dash0.com). See the Dash0 API reference for the current host for your region and the full request and response schema.
The SLO object
123456789101112131415161718192021222324{"apiVersion": "openslo/v1","kind": "SLO","metadata": {"name": "doc-validation-availability","labels": { "team": "platform" }},"spec": {"budgetingMethod": "Occurrences","description": "99.7% of control-plane-api requests complete without an ERROR span status over a rolling 28-day window.","service": "control-plane-api","indicator": {"spec": {"ratioMetric": {"counter": true,"good": { "metricSource": { "type": "Prometheus", "spec": { "query": "..." } } },"total": { "metricSource": { "type": "Prometheus", "spec": { "query": "..." } } }}}},"timeWindow": [ { "duration": "28d", "isRolling": true } ],"objectives": [ { "displayName": "control-plane-api: 99.7% server availability", "target": 0.997 } ]}}
Notes:
targetis a fraction strictly between0and1(0.997= 99.7%). The equivalenttargetPercentfield (strictly between0and100) is also accepted. Set exactly one of the two.apiVersionisopenslo/v1oropenslo.com/v1(both accepted on write). Responses, JSON and YAML, always useopenslo.com/v1, the form that is also installable as a Kubernetes CRD, so a document you read back can bePUTas is.kindmust beSLO.metadata.nameis required.- The server sets the
dash0.com/idanddash0.com/versionlabels inmetadata.labelsand thedash0.com/created-at/dash0.com/updated-atannotations. Do not senddash0.com/idonPOST(400). Use thedash0.com/idvalue, or your owndash0.com/originlabel if you set one, as{originOrId}in the single-SLO endpoints. - The SLI is a
ratioMetricwith inline PromQL over your telemetry, in exactly one of three shapes:good+total,bad+total(Dash0 derives good astotal - bad), orraw+rawType(successorfailure) for a query that already returns a ratio between0and1. Agood,bad, ortotalquery must be a bare vector selector (label matchers only, no functions or aggregations, else a400); it may match many series, which Dash0 sums into one count. Arawquery may be any PromQL expression that returns an instant vector and must resolve to one series. Make thegoodquery a subset of thetotalquery. counterdefaults totrue: Dash0 appliesincrease()over 5-minute windows to thegood,bad, andtotalselectors. Setcounter: falsewhen the series are gauges or other values that go up and down; Dash0 then only sums the selector. Ignored forraw.service(optional) links the SLO to a Dash0 service. Usenameornamespace/name; prefix the value with/if the service name itself contains a slash. The service is shown in the catalog and on the detail page, scopes the deployment markers on the charts, and is stamped asservice.name/service.namespaceresource attributes on the SLO metrics. If nothing matches, the SLO is simply not linked. It is also a list filter (?service=).descriptionis capped at 1050 characters.timeWindowis optional and defaults to a rolling 28-day window.
Dash0 metadata
metadata.name is the stable identifier. You can attach up to 50 custom labels and 50 custom annotations. Keys starting with dash0.com/ other than the ones below are reserved and dropped silently.
| Key | Purpose |
|---|---|
metadata.labels["dash0.com/origin"] | Your own stable identifier, for example a Terraform resource id. Must be unique in the organization (400 otherwise) and must not start with slo_ or check_rule_. It resolves as {originOrId} and enables the PUT upsert. If omitted on a create made with an auth token, the server assigns an api-... origin. SLOs with an origin are read-only in the UI. |
metadata.annotations["dash0.com/display-name"] | Human-readable name shown instead of metadata.name in the UI. |
metadata.annotations["dash0.com/enabled"] | "true" (default) or "false". A disabled SLO keeps its definition but stops evaluating, produces no metrics, and does not count toward your SLO limit. Re-enabling re-checks the limit. |
metadata.annotations["dash0.com/sharing"] | Comma-separated read-access grants, team:<team_id> and user:<email>. Honored only for auth tokens and only on SLOs that carry a dash0.com/origin; 400 otherwise. |
Supported OpenSLO capabilities
| Capability | Behavior |
|---|---|
budgetingMethod: Occurrences | Supported |
| Single objective | Supported |
ratioMetric SLI with inline PromQL (good / total) | Supported |
ratioMetric SLI with bad + total | Supported |
ratioMetric SLI with raw + rawType (success or failure) | Supported; the raw query may be any PromQL expression that returns an instant vector and must resolve to one series |
Rolling 28-day window (duration: "28d" or "4w", isRolling: true) | Supported; timeWindow is optional and defaults to this, but when you do supply it, set isRolling: true explicitly (omitting it is a 400) |
Timeslices / RatioTimeslices budgeting | Rejected with 400 |
| Calendar windows | Rejected with 400 |
Window durations other than 4w/28d | Rejected with 400 |
| More than one objective | Rejected with 400 |
thresholdMetric SLIs | Rejected with 400 |
indicatorRef (top-level or per-objective) | Rejected with 400 |
Composite objectives (compositeWeight, per-objective indicator) | Rejected with 400 |
Per-objective threshold fields (op / value) | Rejected with 400 |
timeSliceTarget / timeSliceWindow | Rejected with 400 |
metricSource.type other than Prometheus | Rejected with 400 (omit type or set it to Prometheus; spec.query must be a non-empty PromQL string) |
metricSource.metricSourceRef | Rejected with 400 — inline the query instead |
metadata.name missing | Rejected with 400 |
alertPolicies | Accepted for OpenSLO compatibility but not stored: dropped on write and absent when you read the SLO back (configure alerting with check rules) |
Limits. An organization can have up to 25 enabled SLOs by default. Creating another, re-enabling a disabled one, or restoring a deleted one returns 400 The maximum number of SLOs (25) for this organization has been reached. Disabled and deleted SLOs do not count. Disable (dash0.com/enabled: "false") or delete an SLO, or contact Dash0 to raise the limit.
Errors
400 Bad Request— the definition uses a capability that is not supported (the message names the capability); thedash0.com/versionlabel does not match the current version (superseded); thedash0.com/originis already in use or uses a reserved prefix; more than 50 labels or annotations; an invaliddash0.com/sharingstring; or the SLO limit has been reached.403 Forbidden—This feature is not enabled for this organizationmeans SLOs are not enabled for your organization; contact support if you believe they should be. Otherwise the token cannot access the requested or defaulted dataset, or lacks the permission for the call (writes need All permissions, reads need Reading).404 Not Found—{originOrId}does not exist or is not readable with this token (also onPUTfor an unknownslo_...id).