Skip to content

Errors

When /v1 refuses a request it answers with an RFC 9457 problem document (Content-Type: application/problem+json) carrying a stable code your client can branch on, and a detail written for the person reading it.

{
"status": 403,
"title": "This key does not hold the required scope",
"detail": "The key authenticated, but it was not granted the scope this endpoint needs. GET /v1/keys/self lists the scopes it does hold.",
"code": "scope_denied"
}

Branch on code, never on title or detail. Those are prose: they get clearer over time. The code is a promise.

code Status What it means
missing_credential 401 No Round Robin key was presented. A dashboard session token does not count
unknown 401 No key matches the token presented. A revoked key reads this way too, because revocation destroys its secrets and nothing is left to match
disabled 401 The key exists and is switched off. Recoverable
expired_secret 401 The key works, but this secret was replaced and its grace period is over
scope_denied 403 The key authenticated but does not hold the scope this endpoint needs
resource_denied 403 The key holds the scope but was restricted to specific rotations, and this is not one. A restricted key gets this on POST /v1/rotations too, because a new rotation is not one of the rotations it names
invalid_request 400 Something in the request is malformed: an unknown sort field, a page size out of range, a missing from/to, an expand value that does not exist
precondition_required 428 A write arrived without its precondition: an If-Match on a write to something that exists, an Idempotency-Key on a create. Read the resource and send back the ETag it gave you, or send If-Match: * to write unconditionally on purpose; see Conventions
precondition_failed 412 The validator you sent is no longer current, so somebody changed the rotation between your read and your write. Nothing was changed. Read again and retry
rate_limited 429 The key is over its requests-per-minute allowance. Comes with Retry-After and the RateLimit-* headers; see Rate limits

The 401 bodies also carry a keyId when we could tell which key it was: the only handle you and our support both have, since the secret is not one.

Status When
304 Not Modified Your If-None-Match matched. Not an error; see Conventions
404 Not Found No such rotation, user, group or channel in this workspace. An id belonging to another workspace is indistinguishable from one that does not exist, deliberately
409 Conflict The Idempotency-Key on a create made a rotation more than 24 hours ago, which is longer than a retry. Nothing was created; retry with a new key if you meant a second rotation. See Creating things
500 Ours. Nothing about the internals reaches the body; if one is reproducible, tell us

403 is not 404. A rotation outside your key’s selector answers 403 resource_denied, not “no such rotation”, so the refusal points you at the key rather than at the id. GET /v1/keys/self shows you the selector.

412 is not a failure. It means the rotation moved between your read and your write, which happens on its own: the schedule advances, somebody rotates from Slack. Nothing was changed, and the fix is to read again and write again. A client that retries with the same stale validator gets 412 forever.

401 unknown is not 401 missing_credential. The first means we do not recognise the token you sent; the second means you sent no Round Robin token at all, most often because a dashboard session token was used instead.

An unknown sort field or an unrecognised ?expand= value comes back 400 rather than being ignored, so a parameter that did not take is never mistaken for one that matched nothing.