Developers
API error codes
Every /api/v1 error response carries both a human error string and a stable, machine-readable code. Branch on the code — it never changes when we reword a message. Each code maps to one HTTP status (the failure class).
| Code | HTTP | Meaning |
|---|---|---|
invalid_request | 400 | The request body, query, or path parameter failed validation — the catch-all bad-input code. |
duplicate_slug | 400 | A unique field (a slug, or a topic source key) collides with an existing row. |
unknown_parent | 400 | A referenced parent (product / manual / chapter) does not exist or was concurrently deleted. |
content_rejected | 400 | The submitted CONTENT failed the publish pipeline (leak/PHI scan, MDX compile, link/alt gate, or policy gate). Fix the content, not the request shape. |
unauthorized | 401 | No, malformed, or unrecognized bearer token (unauthenticated). |
forbidden | 403 | A valid token lacking the required scope / admin / operator privilege (authenticated, denied). |
not_found | 404 | The addressed resource does not exist. |
payload_too_large | 413 | The request body exceeds the configured size cap. |
rate_limited | 429 | The per-identity write rate limit was exceeded — back off and retry after the Retry-After header. |
invalid_idempotency_key | 400 | The Idempotency-Key header is present but malformed (empty or over the length cap). |
idempotency_in_progress | 409 | A request with the same Idempotency-Key is still in progress; retry after Retry-After. |
idempotency_key_conflict | 422 | The Idempotency-Key was already used for a request with a DIFFERENT body (no safe replay). |
The same catalog drives the Error.code enum in the OpenAPI spec, so the documented set can never drift from what the API actually emits.