Errors
The Katixo ERP API wraps every response in a standard ApiResponse<T> envelope. HTTP status codes in the 2xx range indicate success, 4xx indicate a client error, and 5xx indicate a server-side issue.
Response envelope
Every response — success or failure — follows the same ApiResponse<T> record shape:
{
"success": boolean,
"message": "Human-readable summary",
"data": T | null,
"errors": { "field": "message", ... } | null
}Success example
// 200 OK
{
"success": true,
"message": "Invoice created",
"data": {
"id": "inv_8f3a...",
"invoiceNumber": "INV-2026-0042",
"status": "DRAFT"
},
"errors": null
}Error examples
400 — Validation error
{
"success": false,
"message": "Validation failed",
"data": null,
"errors": {
"invoiceDate": "invoice date is required",
"customerName": "must not be blank"
}
}401 — Missing or expired JWT
{
"success": false,
"message": "JWT token is missing or expired",
"data": null,
"errors": null
}403 — Insufficient role
{
"success": false,
"message": "Access denied — ACCOUNTANT role required",
"data": null,
"errors": null
}Authentication
All API requests must include a valid JWT Bearer token in the Authorization header:
Authorization: Bearer <jwt-token>The token encodes the user's role. The following roles are supported (most to least privileged):
| Role | Description |
|---|---|
OWNER | Full access including billing and org settings. |
ADMIN | Manage users, roles, and all operational data. |
ACCOUNTANT | Create and manage invoices, expenses, and journal entries. |
OPERATOR | Day-to-day data entry and limited edits. |
VIEWER | Read-only access to reports and dashboards. |
HTTP status codes
| Code | Status | Meaning |
|---|---|---|
200 | OK | Request succeeded. |
201 | Created | Resource created successfully. |
400 | Bad Request | Validation failed. Field-level details are in the errors map. |
401 | Unauthorized | Missing or expired JWT Bearer token. |
403 | Forbidden | Authenticated but insufficient role for this endpoint. |
404 | Not Found | The requested resource does not exist. |
409 | Conflict | Duplicate resource (e.g., invoice number already exists). |
429 | Too Many Requests | Rate limit exceeded. Retry after the interval in the Retry-After header. |
500 | Internal Error | Something went wrong on our end. These are rare and automatically alerted. |
Rate limits
API requests are rate-limited per authenticated user:
| Plan | Requests / minute | Burst limit |
|---|---|---|
| Free | 60 | 10 concurrent |
| Pro | 300 | 30 concurrent |
| Enterprise | Custom | Custom |
Rate limit headers are included in every response:
X-RateLimit-Limit: 300
X-RateLimit-Remaining: 287
X-RateLimit-Reset: 1718100120
Retry-After: 12When rate-limited, wait for the number of seconds specified in Retry-After before retrying. Implement exponential backoff for production integrations.