PERS API uses structured error responses for consistent error handling across all endpoints. All errors follow the same format with security-filtered messages and correlation IDs for support.
- Consistency: All errors follow a uniform structure and categorization
- Security: Sensitive system details are filtered out; only safe messages are returned
- Traceability: Every error includes a correlation ID for support and debugging
- Programmatic Handling: Structured format enables reliable error processing
All PERS API errors return structured JSON responses following RFC 7807 (Problem Details for HTTP APIs):
{
"status": 404,
"title": "Resource Not Found",
"detail": "User with ID 12345 could not be found",
"message": "User with ID 12345 could not be found",
"code": "USER_NOT_FOUND",
"category": "DOMAIN_RULE",
"timestamp": "2025-11-01T10:30:00.000Z",
"correlationId": "req-abc-123",
"userMessage": "The requested user could not be found",
"retryable": false
}| Field | Type | Description |
|---|---|---|
status | number | HTTP status code |
title | string | Human-readable error summary |
detail | string | Specific error explanation |
message | string | Error message (usually same as detail) |
code | string | Error code for programmatic handling |
category | string | Error classification (VALIDATION, SECURITY, etc.) |
timestamp | string | ISO timestamp when error occurred (optional) |
correlationId | string | Request correlation ID for support (optional) |
userMessage | string | User-friendly message safe for display (optional) |
retryable | boolean | Whether operation can be retried |
For TypeScript applications, import error types from the shared library:
import type { StructuredError, ErrorCategory } from '@explorins/pers-shared';| Category | HTTP Status | Description | Retryable |
|---|---|---|---|
| VALIDATION | 400 | Invalid request data or format | No |
| SECURITY | 403 | Authentication/authorization | No |
| DOMAIN_RULE | 422 | Business logic validation errors | No |
| RATE_LIMIT | 429 | Request rate limiting | Yes |
| INFRASTRUCTURE | 503 | External service failures | Yes |
Authentication Error (401)
{
"status": 401,
"title": "Authentication Required",
"detail": "Valid authentication credentials are required",
"message": "Authentication required",
"code": "AUTHENTICATION_REQUIRED",
"category": "SECURITY",
"timestamp": "2025-11-01T10:30:00.000Z",
"correlationId": "req-abc-123",
"userMessage": "Please log in to access this resource",
"retryable": false
}Validation Error (400)
{
"status": 400,
"title": "Validation Error",
"detail": "The email field is required",
"message": "The email field is required",
"code": "REQUIRED_FIELD",
"category": "VALIDATION",
"timestamp": "2025-11-01T10:30:00.000Z",
"correlationId": "req-def-456",
"details": {
"field": "email",
"rejectedValue": null
},
"retryable": false
}// Example: Handling API errors in JavaScript/TypeScript
async function callAPI() {
try {
const response = await fetch('/api/users', {
method: 'POST',
headers: {
'Authorization': 'Bearer your-token',
'Content-Type': 'application/json'
},
body: JSON.stringify({ name: 'John' })
});
if (!response.ok) {
const error = await response.json();
// Handle different error categories
if (error.category === 'SECURITY') {
// Redirect to login
window.location.href = '/login';
} else if (error.category === 'VALIDATION') {
// Show validation errors to user
showValidationError(error.details);
} else {
// Show generic error message
showErrorMessage(error.userMessage || error.message);
}
return;
}
const data = await response.json();
// Handle success response
} catch (networkError) {
// Handle network errors
showErrorMessage('Network error. Please try again.');
}
}- System error details are never exposed to API consumers
- Every error includes a correlation ID for traceability
- All errors are logged with full context for debugging
- Consistent error categorization across all domains
For further details, see the Authentication Guide and Developer Resources.