Getting startedErrors and status codes

Errors and status codes

The RFC 7807 problem-detail shape every /api/v1 error returns, the title tokens to branch on, and the status codes each endpoint can produce.

Every non-success response from /api/v1 is an RFC 7807 problem detail, served with Content-Type: application/problem+json:

{
  "type": "https://observer.example/problems/forbidden",
  "title": "forbidden",
  "status": 403,
  "detail": "missing scope: write:incidents"
}
FieldNotes
typeURI ending in the same token as title.
titleStable machine-readable token. Branch on this.
statusRepeats the HTTP status code.
detailHuman-readable sentence. Present on most errors. Wording is not part of the contract and may change between releases.

A 429 carries one extra field, retry_after, in seconds.

Match on title, not detail

detail exists to be read by a person looking at a failed request. It interpolates runtime values and is reworded freely. Client logic that switches on detail will break; logic that switches on title will not.

Status codes

Statustitle valuesWhen
400invalid_json, invalid_body, invalid_title, invalid_severity, invalid_postmortem_url, invalid_type, invalid_description, invalid_note, invalid_status, invalid_window, missing_window, missing_param, invalid_range, invalid_resolution, range_too_large, invalid_requestThe request body or query string is malformed, or a field failed validation.
401unauthenticatedNo bearer token, or the key is invalid or revoked.
403forbiddenThe key is valid but lacks the scope the endpoint requires. detail names the missing scope.
404not_found, metric_not_foundThe object does not exist, or it belongs to another organisation.
409already_published, already_resolved, already_started, already_completed, already_canceled, canceled, not_started, not_manual_metricThe object is in a state that forbids the requested transition.
413payload_too_largePOST /config/apply only: the document exceeds 1 MB.
422config_invalid, invalid_change_eventThe body parsed and was structurally valid, but its contents failed domain validation.
429rate_limitedA rate limit was exceeded. See Rate limiting.
500internal_errorUnhandled server error. Safe to retry.

401, 403, 429 and 500 can come back from every endpoint. The rest are per-endpoint; each operation page lists the ones it can return.

On 404

A missing object and an object owned by another organisation return the identical response. This is deliberate: a distinguishable 403 would let a caller enumerate which IDs exist in other tenants. Treat 404 as "not available to this key" rather than "does not exist".

Only endpoints addressing a specific object return 404. Collection endpoints such as GET /services return 200 with an empty array when there is nothing to list.

Success

Every successful call returns 200, including creates. The API does not use 201, 202, or 204. DELETE /incidents/{id} returns 200 with a body.

Rate limiting

Every response, success or error, carries:

X-RateLimit-Limit:     <ceiling for the burst window>
X-RateLimit-Remaining: <calls left in the current burst window>
X-RateLimit-Reset:     <burst window length, in seconds>

Limits apply per key across two windows: a short burst window and a rolling daily cap. Whichever trips first produces the 429, and detail says which one it was.

Ceilings vary by plan. Read them from the response headers rather than hard-coding a number, and back off using retry_after from the body rather than a fixed interval.