~/enine-sites-help/developers/api-basics-roles-requests-and-errors
API basics: roles, requests, and errors
Reading needs site membership, writing needs the editor role, requests and responses are JSON, and errors use one shape.
Roles
| Role | Read | Write | Manage the team |
|---|---|---|---|
| reader | Yes | No | No |
| editor | Yes | Yes | No |
| admin | Yes | Yes | Yes |
Every POST, PATCH, PUT, and DELETE needs the editor role, so a reader token can explore the whole API without any risk of changing something. Only an admin can add a team member or change someone's role. A 403 means the endpoint worked and your role did not.
Requests
- Requests and responses are JSON. Media uploads are multipart/form-data, and the public contact endpoint is form-encoded.
- The API never picks a format from your Accept header. To get something other than JSON, use a query parameter such as ?dl=yaml.
| Method | Meaning |
|---|---|
| GET | Read. Site membership is enough |
| POST | Create, or update an existing resource. Editor role required |
| PATCH | Partial update. Send only the fields you are changing |
| PUT | Replace wholesale. Used by artifact roles |
| DELETE | Remove. Returns 204 with no body |
Send the whole object on POST when you mean to replace a resource, and use PATCH to change a few fields.
Pagination
List endpoints return 50 items per page. Use ?page= and ?page_size=, up to a maximum of 200. Responses look like {"count": 137, "next": "...", "previous": "...", "results": []}.
Not every list is paginated. The theme list, an artifact's tags, and the site AEO surface always return a bare array, and ?page_size=0 switches pagination off. Write your client to accept both a bare array and a count-and-results envelope.
Repeating a call and request volume
Assigning a tag or adding a role twice is safe: those endpoints return the existing row instead of failing. There is no Idempotency-Key header. Batch your calls where you can and avoid tight polling loops. To move a lot of content at once, use the export and import endpoints.
Errors
Every error uses the RFC 9457 Problem Details shape, with the content type application/problem+json. Switch on code, a stable machine-readable string. detail is written for people and may be reworded at any time.
{
"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/"
}
A validation failure adds an errors object with one entry per offending field.
| Status | 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 400 and 404 split is deliberate. Addressing something that does not exist gives 404. A value inside your payload that points at nothing gives 400, with the field named in errors.