Errors and Status Codes
One error shape everywhere -- switch on code, not on the message.
Every error uses the same shape -- RFC 9457 Problem Details -- and the content type application/problem+json.
Response
{
"type": "https://api.eninesites.com/errors/permission_denied",
"title": "Forbidden",
"status": 403,
"code": "permission_denied",
"detail": "Editor or admin role required on 'mysite.e9sites.com'.",
"instance": "/api/v1/site/mysite.e9sites.com/artifacts/"
}
Switch on code. It is a stable machine-readable string. detail is written for people and may be reworded at any time.
A validation failure adds an errors object, one entry per offending field:
Response
{
"type": "https://api.eninesites.com/errors/validation_error",
"title": "Bad Request",
"status": 400,
"code": "validation_error",
"detail": "Request body failed validation.",
"errors": {"username": ["User not found."]}
}
Status codes
| Code | What it means here |
|---|---|
| 200 | Read, or a successful update |
| 201 | Created |
| 204 | Deleted, no body returned |
| 400 | The body failed validation, or a value in it points at nothing |
| 401 | Token missing or invalid |
| 403 | Token valid, role insufficient |
| 404 | What you addressed does not exist, including a domain that is not a site |
| 409 | Already taken: site domain, tag name, artifact name, SEO canonical, or catalog URL |
| 429 | Too many requests -- slow down and try again |
| 500 | A server-side precondition failed -- surface it rather than retrying |
The split between 400 and 404 is deliberate. Addressing something that does not exist gives 404. A value inside your payload pointing at nothing gives 400, with the field named in errors.