Handle errors by code, not message
Every API error uses one envelope and every response includes an X-Request-ID.
{
"error": {
"code": "validation_failed",
"message": "The request could not be accepted.",
"request_id": "req_...",
"details": {}
}
}
message is safe for people and can improve over time. Branch on the stable HTTP status and error.code. Log the request ID with your operation record.
Retry matrix
| Status | Meaning | Default behavior |
|---|---|---|
400 |
Malformed request or required header | Fix the client; do not retry unchanged |
401 |
Missing, invalid, expired, or revoked credential | Replace/fix authentication |
403 |
Authenticated but not authorized | Fix scopes or role; do not retry unchanged |
404 |
Missing or deliberately tenant-hidden resource | Verify tenant and identifier |
409 |
State, concurrency, baseline, or idempotency conflict | Read the code; replay only when documented |
411 |
Binary upload omitted Content-Length |
Send exact byte length |
413 |
Body or source exceeds a limit | Reduce the asset; do not retry unchanged |
415 |
Declared media type is not accepted or mismatches | Fix media type/source |
422 |
Structurally valid request failed domain validation | Correct fields or profile/source compatibility |
429 |
Rate, quota, credit, or concurrency capacity | Honor Retry-After; avoid hot loops |
503 |
Required service is temporarily unavailable | Back off with jitter and a bounded deadline |
Idempotency keys
Create operations require Idempotency-Key. The key is scoped to the authenticated principal, method, and route and retained for at least 24 hours.
- Same key and same body returns the original result.
- Same key with a different body returns a conflict.
- A concurrent request with the same key can return a conflict until the first commits.
Generate a high-entropy key per logical operation. Persist it beside the client operation until the outcome is known.
Idempotency-Key: 0e8052b8-37e6-4e95-9eab-9ab52d390970
Do not generate a fresh key merely because your HTTP client timed out. A fresh key can create duplicate uploads, scans, webhook endpoints, or other resources.
Request IDs
Capture X-Request-ID from success and failure responses. The JSON error repeats it as error.request_id, but the response header is the universal location.
Do not send source content, API keys, or full sensitive URLs in a support message. The request ID and UTC timestamp are the right starting point.