Errors and conventions
One error envelope, correlation ids, optimistic locking, pagination, and the other rules every call obeys.
The error envelope
Every non-2xx response has exactly one shape:
{
"error": {
"code": "PROVIDER_NOT_FOUND",
"message": "Provider 'unknown' was not found",
"details": {},
"correlation_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}
}
Branch on code, never on message — wording changes, codes do not. The full table lives in Error Codes. details may carry structured context; validation failures put field pointers into details.errors without ever echoing the values you submitted.
404 is also an ownership answer
A credential that exists but belongs to someone else returns the same 404 CREDENTIAL_NOT_FOUND as one that never existed. Ids are not probeable; do not treat 404 as "retry later".
Correlation ids
Send X-Correlation-ID with your own request id, or one is generated for you; either way the response carries it back and the audit trail records it. Quote it when reporting problems. On resolve the id travels in the request body (correlation_id) and takes precedence over the header.
Optimistic locking
Every mutation of a stored credential (PATCH, make-default, disable, enable) demands the record's current version — in the body, or for PATCH alternatively as If-Match: "3". A stale version answers 409 CREDENTIAL_VERSION_CONFLICT: re-read, then retry. DELETE is the exception — it needs no version and repeating it on your own already-deleted record is an idempotent 204.
Requests and responses
- JSON only (
Content-Type: application/json); request bodies are limited to 64 KiB (413 REQUEST_TOO_LARGEbeyond). - Unknown body fields are rejected:
400 INVALID_REQUEST, not silently ignored. - UUIDs are canonical lowercase textual form; timestamps are RFC 3339 UTC (
2026-08-12T12:30:00Z). - Lists paginate with
limit(default 50, max 200) andoffset; responses carrytotal. - Responses with credential masks are sent with
Cache-Control: no-store— do not cache them. - Schema violations of the secret/configuration objects are
422with dedicated codes; everything else malformed is400. See the validation cookbook.
Availability
503 SERVICE_NOT_READY means a dependency (database, encryption keyring) is down — the same facts GET /health/ready reports. Retry after readiness returns ok; see the health endpoints.