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
CodeWhat it means here
200Read, or a successful update
201Created
204Deleted, no body returned
400The body failed validation, or a value in it points at nothing
401Token missing or invalid
403Token valid, role insufficient
404What you addressed does not exist, including a domain that is not a site
409Already taken: site domain, tag name, artifact name, SEO canonical, or catalog URL
429Too many requests -- slow down and try again
500A 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.