API errors and status codes
Raytha reports API failures with a status code and a JSON body. The body takes one of three shapes depending on where the failure happened. This page lists the status codes, shows each shape, and gives a small client function that turns any of them into a readable message.
Status codes
| Status | Meaning | Retry? |
|---|---|---|
200, 201, 202 | Success. 201 is a create. 202 is a batch that runs in the background. | - |
400 | The request is invalid: a field failed validation, the filter is wrong, the body could not be read, or a rule was broken. | No. Fix the request. |
401 | The API key is missing or wrong, or its admin lacks the permission. | No. |
404 | The item, content type, template or route does not exist. | No. |
422 | An id is not in the 22-character id format. | No. |
500 | An unexpected failure on the server. | Yes, with backoff. Then check the server log. |
A proxy or host in front of Raytha can add its own 502, 503 and 504. Treat those as retryable too.
The three error shapes
1. Problem details
Most failures, including every 401, 404, 422, 500 and bad filter, use the application/problem+json shape. detail is the message to show. The extra error field repeats it for older clients.
{
"type": "https://httpstatuses.io/404",
"title": "Not found",
"status": 404,
"detail": "Entity \"Content item\" (h5anxM6u4UW80v09zzQjXg) was not found.",
"instance": "/raytha/api/v1/ContentItems/posts/h5anxM6u4UW80v09zzQjXg",
"success": false,
"error": "Entity \"Content item\" (h5anxM6u4UW80v09zzQjXg) was not found."
}
Some lookups return the generic The resource you requested was not found. instead of naming the entity.
| Title | Status | Detail you will see |
|---|---|---|
| Unauthorized | 401 | Invalid API key. (missing, unknown or revoked key) or Unauthorized access. (valid key, missing permission) |
| Not found | 404 | The missing entity, or the generic message |
| Invalid identifier | 422 | Invalid format of identifier. |
| Invalid filter | 400 | What is wrong with the filter. See Filtering. |
| Request failed | 400 | The message of the rule that was broken |
| Bad request | 400 | The request body could not be read. |
| Server error | 500 | An unknown error has occurred. |
2. Validation problem details
When Raytha validates a request body and finds field-level problems, or cannot bind the JSON to the request type, it adds an errors map. Keys are field names, and values are lists of messages. A property that has the wrong type appears under a $. path:
{
"title": "One or more validation errors occurred.",
"status": 400,
"detail": "saveAsDraft: The JSON value could not be converted to System.Boolean.",
"errors": {
"$.saveAsDraft": ["saveAsDraft: The JSON value could not be converted to System.Boolean."]
}
}
3. Command failure
Content item commands such as create and edit report validation failures as a plain envelope, with 400 and all messages joined by a semicolon:
{
"result": null,
"error": "'Title' field is required.;category is not a recognized field for content type: Post",
"success": false
}
Do not parse that string by splitting on ; if you can avoid it. Show it as one message.
Handle them in a client
This function reads all three shapes, and separates errors you should fix from errors you can retry.
function describeError(status, body) {
if (!body) return `HTTP ${status}`;
if (body.errors) {
// Field-level problems: { "errors": { "title": ["'Title' field is required."] } }
const fields = Object.entries(body.errors).map(
([field, messages]) => `${field}: ${[].concat(messages).join(" ")}`
);
if (fields.length > 0) return fields.join("; ");
}
// Problem details have `detail`. Command failures have `error`, with messages joined by ";".
return body.detail || body.error || body.title || `HTTP ${status}`;
}
async function call(url, options) {
const response = await fetch(url, options);
const body = await response.json().catch(() => null);
if (response.ok) return body;
const message = describeError(response.status, body);
if (response.status >= 500) {
throw new Error(`Raytha failed (${response.status}): ${message}. Retry later.`);
}
throw new Error(`Request rejected (${response.status}): ${message}. Fix the request.`);
}
With curl, print the status code and fail on errors:
status=$(curl -s -o response.json -w '%{http_code}' \
"$RAYTHA_URL/raytha/api/v1/ContentItems/posts/not-a-real-id" \
-H "X-API-KEY: $RAYTHA_API_KEY")
if [ "$status" -ge 400 ]; then
echo "HTTP $status: $(jq -r '.detail // .error // .title' response.json)" >&2
exit 1
fi
Common causes
| You see | Check |
|---|---|
401 Invalid API key. | The X-API-KEY header is present, the key was copied in full, and its admin is active. |
401 Unauthorized access. | The admin's role includes the permission from Authentication for this content type or resource. |
404 on a content type URL | You used the content type's developer name, not its label. |
422 Invalid format of identifier. | The id is a 22-character id from Raytha, not a number, a GUID or a route path. |
400 … is not a recognized field | The key in content is not a field developer name of that content type. |
400 … 'X' field is required. | Publishing needs every required field. Save as a draft, or send the field. |
400 Invalid filter | Quote string values and use eq. See Filtering. |
500 | Look at the Raytha server log for the time of the request. The response never includes a stack trace outside Development. |
Gotchas
- A missing permission is a
401, not a403. Readdetailto tell a bad key from a missing permission. - The API never redirects to an HTML error page. Every path under
/raytha/apianswers in JSON. If you get HTML, you are not reaching Raytha, or your URL is missing/raytha/api. - Validation messages come in different shapes. Check for
errors, thendetail, thenerror. - A batch reports failures per item. The
202response means the job was queued, not that every item was created. ReadstatusInfoon the task. - A failed write may still be partly applied in a batch. Items are created independently. Re-send only the failed ones.
Next steps
- Authentication: fix 401 responses.
- Content items: batch results and validation rules.
- Recipes: a full client with paging and retries.