API Response Formats
Content negotiation
The response format is determined by shouldReturnJSON():
| Condition | Format |
|---|---|
Route starts with /api/ |
JSON |
Accept: application/json header |
JSON |
| Otherwise | HTML |
JSON response formats
Success response
{
"success": true,
"data": {
"key": "value",
"nested": {
"more": "data"
}
}
}
Error response
{
"success": false,
"error": {
"code": "ERROR_CODE",
"message": "Human-readable error message"
}
}
Standard error codes
| HTTP Status | Error Code | Usage |
|---|---|---|
| 400 | INVALID_INPUT |
Validation errors, malformed request |
| 400 | INVALID_ID |
Invalid ID format (non-numeric, etc.) |
| 401 | UNAUTHORIZED |
Authentication required |
| 401 | INVALID_CREDENTIALS |
Wrong email/password |
| 403 | UNAUTHORIZED |
Authorization failed (insufficient permissions) |
| 404 | NOT_FOUND |
Resource not found |
| 405 | METHOD_NOT_ALLOWED |
Wrong HTTP method |
| 500 | SERVER_ERROR |
Internal server error |
Security note: For authorization-protected resources, return 403 (Forbidden) even for non-existent IDs to prevent information leakage about resource existence.
Intranet sync API format
The intranet sync endpoints (/api/intranet/...) use a different response format. See Intranet Sync API for details.
Sync success
{
"success": true,
"received": 3,
"inserted": 2,
"updated": 1,
"errors": 0,
"results": [
{"identifier": "P001", "status": "inserted"},
{"identifier": "P002", "status": "updated"},
{"identifier": "P003", "status": "inserted"}
]
}
Sync validation error
{
"success": false,
"error": {
"code": "INVALID_PAYLOAD",
"message": "Key: 'IntranetPersonBatch.Records[0].ExternalID' Error:..."
}
}
Authentication error codes (API token)
| HTTP Status | Code | Cause |
|---|---|---|
| 401 | MISSING_AUTH_HEADER |
No Authorization header |
| 401 | INVALID_AUTH_FORMAT |
Not Bearer scheme |
| 401 | EMPTY_TOKEN |
Empty token value |
| 401 | INVALID_TOKEN |
Token not found or inactive |
| 403 | IP_NOT_ALLOWED |
Client IP not in allowlist |