Documentation Index
Fetch the complete documentation index at: https://docs.lilury.com/llms.txt
Use this file to discover all available pages before exploring further.
Errors
The Lilury API uses standard HTTP status codes and returns a consistent error body on every failure. Understanding the error format lets you handle failures gracefully without guessing.
HTTP status codes
| Code | Meaning |
|---|
200 | Request succeeded |
400 | Something is wrong with the request — see the error body for details |
401 | Not authenticated — missing or invalid token |
403 | Authenticated but not allowed to perform this action |
409 | A concurrent request with the same Idempotency-Key is already in progress, or a version conflict was detected |
429 | Too many requests — you have exceeded the rate limit |
500 | Unexpected server error |
Note that domain-level “not found” errors (e.g. looking up a journal that does not exist) return 400, not 404. The error body’s code field tells you exactly what went wrong.
Error response schema
All errors return a JSON body with this shape:
{
"type": "https://www.rfc-editor.org/rfc/rfc7231",
"title": "One or more errors occurred.",
"status": 400,
"instance": "/api/v1/Companies/01924abc-/Journals",
"traceId": "00-3f2a1b...",
"errors": [
{
"name": "generalErrors",
"reason": "The journal was not found.",
"code": "NotFound"
}
]
}
| Field | Description |
|---|
status | The HTTP status code |
instance | The request path that produced the error |
traceId | A unique ID for this request — include it when reporting issues to support |
errors | Array of one or more error objects (see below) |
Error object
Each item in the errors array has:
| Field | Description |
|---|
name | Which field caused the error, or "generalErrors" for non-field errors |
reason | A human-readable, localized message describing the problem |
code | A machine-readable error type (see Error codes below) |
Error codes
The code field tells you the category of the error so you can handle it programmatically, independently of the reason message (which may be translated).
| Code | Meaning |
|---|
NotFound | A referenced resource does not exist |
Invalid | A value is malformed or violates a business rule |
NotAllowed | The operation is not permitted in the current state |
Duplicated | A unique constraint was violated |
Missing | A required value or dependency is absent |
Warning | The operation was rejected due to a business-level warning |
Unauthorized | Authentication is required or the provided credentials are invalid |
Forbidden | The authenticated user does not have permission for this action |
Conflict | A concurrent request with the same idempotency key is already in progress |
Validation errors vs. business errors
There are two distinct kinds of 400 errors.
Validation errors occur when the request body itself is malformed — a required field is missing, a string is too long, a date is invalid, etc. They are caught before any business logic runs. The name field identifies the specific field that failed:
{
"status": 400,
"errors": [
{
"name": "name",
"reason": "'Name' must not be empty.",
"code": "NotEmptyValidator",
"severity": "Error"
},
{
"name": "expiresAt",
"reason": "Expiry date must be in the future.",
"code": "PredicateValidator",
"severity": "Error"
}
]
}
Business errors occur when the request is structurally valid but violates a domain rule — for example, trying to post a journal that is already posted. The name field is always "generalErrors":
{
"status": 400,
"errors": [
{
"name": "generalErrors",
"reason": "This journal has already been posted.",
"code": "NotAllowed"
}
]
}
Authentication errors
A 401 response means the request was not authenticated. This happens when:
- The
Authorization header is missing
- The access token has expired
- The API key has been revoked or expired
{
"status": 401,
"errors": [
{
"name": "generalErrors",
"reason": "Authentication is required.",
"code": "Unauthorized"
}
]
}
When you receive a 401 with a user token, use your refresh token to get a new access token and retry the request. See Authentication for details.
Permission errors
A 403 response means you are authenticated but the token or API key does not have the required permission for this endpoint.
{
"status": 403,
"errors": [
{
"name": "generalErrors",
"reason": "You do not have permission to perform this action.",
"code": "Forbidden"
}
]
}
Check that the permission required by the endpoint is included in your token’s company permissions or your API key’s permission list.
Conflict errors
A 409 response has two distinct causes.
Idempotency conflict — a request with the same Idempotency-Key is currently being processed. Wait briefly and retry; once the first request completes, a retry with the same key will return the original response instead of re-executing the operation.
{
"status": 409,
"errors": [
{
"name": "generalErrors",
"reason": "a request with this idempotency key is already in progress",
"code": "Conflict"
}
]
}
If the first request completed successfully and you retry with the same Idempotency-Key, you will receive the original 200 response without the operation being executed again. See Idempotency for details.
Version conflict — the resource was modified by another request between your read and your write. Re-fetch the resource to get the current version and retry.
{
"status": 409,
"errors": [
{
"name": "generalErrors",
"reason": "the resource was modified by another request; re-fetch and retry",
"code": "Conflict"
}
]
}
See Concurrency for details on how to use the version field.
Rate limit errors
A 429 response means your client IP has exceeded 25 requests per second. Slow down and retry after a brief pause.
The response body is empty — no error object is returned.
Server errors
A 500 response means something went wrong on our end. The body will be empty. Record the request’s traceId from a preceding error response if available, and contact support.