Appearance
Error Handling Guide
Table of Contents
- Overview
- Error Response Format
- HTTP Status Codes
- Error Categories
- Common Error Scenarios
- Error Resolution Strategies
- Retry Logic
- Debugging and Logging
Overview
The Engagifii CRM API uses standard HTTP status codes and provides detailed error information to help developers diagnose and resolve issues quickly. All errors return structured JSON responses with consistent formatting across all endpoints.
Key Error Handling Features
- Consistent Error Format: Standardized error response structure
- Detailed Error Information: Specific error codes, messages, and resolution guidance
- Field-Level Validation: Detailed validation errors for form data
- Request Tracking: Unique request IDs for error correlation
- Rate Limit Information: Clear rate limiting feedback
- Structured Error Codes: Machine-readable error classification
Error Response Format
All error responses follow this consistent structure:
json
{
"error": {
"code": "VALIDATION_ERROR",
"message": "The request contains invalid data",
"details": [
{
"field": "email",
"message": "Email address is required",
"code": "REQUIRED_FIELD"
},
{
"field": "phone",
"message": "Phone number format is invalid",
"code": "INVALID_FORMAT"
}
],
"timestamp": "2024-01-25T10:30:00Z",
"requestId": "req-123e4567-e89b-12d3-a456-426614174000",
"path": "/api/v1/people",
"method": "POST"
}
}Error Response Properties
- code: Machine-readable error identifier for programmatic handling
- message: Human-readable error description
- details: Array of specific error details (for validation errors)
- field: Name of the field that caused the error
- message: Specific error message for the field
- code: Field-specific error code
- timestamp: ISO 8601 timestamp when error occurred
- requestId: Unique identifier for the request (useful for support)
- path: API endpoint that generated the error
- method: HTTP method used
HTTP Status Codes
Success Codes (2xx)
| Code | Description | Usage |
|---|---|---|
200 OK | Request successful | GET requests, successful updates |
201 Created | Resource created | POST requests that create new resources |
204 No Content | Request successful, no content | DELETE requests, some PUT requests |
Client Error Codes (4xx)
| Code | Description | Common Causes | Resolution |
|---|---|---|---|
400 Bad Request | Invalid request format | Missing required fields, invalid JSON, validation errors | Check request format and required fields |
401 Unauthorized | Authentication required | Missing or invalid access token | Obtain valid access token |
403 Forbidden | Access denied | Invalid tenant code, insufficient permissions | Verify tenant code and permissions |
404 Not Found | Resource not found | Invalid ID, deleted resource | Check resource ID exists |
405 Method Not Allowed | HTTP method not supported | Using GET instead of POST | Use correct HTTP method |
409 Conflict | Resource conflict | Duplicate email, circular relationships | Resolve data conflicts |
422 Unprocessable Entity | Validation error | Business rule violations | Fix validation errors |
429 Too Many Requests | Rate limit exceeded | Too many requests | Implement rate limiting |
Server Error Codes (5xx)
| Code | Description | Action |
|---|---|---|
500 Internal Server Error | Unexpected server error | Contact support with request ID |
502 Bad Gateway | Gateway error | Retry request, contact support if persists |
503 Service Unavailable | Service temporarily unavailable | Wait and retry, check status page |
504 Gateway Timeout | Request timeout | Retry request with longer timeout |
Error Categories
1. Authentication Errors
Authentication-related errors when accessing protected resources.
Example Response:
json
{
"error": {
"code": "INVALID_TOKEN",
"message": "The access token has expired",
"timestamp": "2024-01-25T10:30:00Z",
"requestId": "req-auth-001"
}
}Common Authentication Error Codes:
MISSING_TOKEN: Authorization header not providedINVALID_TOKEN: Token format is invalid or corruptedEXPIRED_TOKEN: Access token has expiredINSUFFICIENT_SCOPE: Token lacks required permissions
Resolution:
- Verify the Authorization header is included
- Check token format includes "Bearer " prefix
- Obtain a new access token if expired
- Verify client has required scopes/permissions
2. Authorization Errors
Errors related to tenant access and permissions.
Example Response:
json
{
"error": {
"code": "INVALID_TENANT",
"message": "The specified tenant code is not valid or access is denied",
"timestamp": "2024-01-25T10:30:00Z",
"requestId": "req-tenant-001"
}
}Common Authorization Error Codes:
MISSING_TENANT_CODE: tenant-code header not providedINVALID_TENANT: Tenant code is invalid or inactiveACCESS_DENIED: User lacks permission for requested operationTENANT_SUSPENDED: Tenant account is suspended
Resolution:
- Include valid tenant-code header in all requests
- Verify tenant code with system administrator
- Check user permissions for the operation
- Contact support for tenant account issues
3. Validation Errors
Detailed validation errors for request data.
Example Response:
json
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Request contains validation errors",
"details": [
{
"field": "firstName",
"message": "First name is required",
"code": "REQUIRED_FIELD"
},
{
"field": "email",
"message": "Email format is invalid",
"code": "INVALID_FORMAT"
},
{
"field": "phone",
"message": "Phone number must be in international format",
"code": "INVALID_FORMAT"
}
],
"timestamp": "2024-01-25T10:30:00Z",
"requestId": "req-validation-001"
}
}Common Validation Error Codes:
REQUIRED_FIELD: Required field is missing or emptyINVALID_FORMAT: Field format is incorrect (email, phone, date, etc.)INVALID_LENGTH: Field length exceeds limitsINVALID_VALUE: Field value is not in acceptable rangeDUPLICATE_VALUE: Value must be unique but already existsINVALID_RELATIONSHIP: Referenced entity doesn't exist or is invalid
4. Business Logic Errors
Errors related to business rules and constraints.
Example Response:
json
{
"error": {
"code": "BUSINESS_RULE_VIOLATION",
"message": "Cannot delete organization with active members",
"details": [
{
"field": "memberCount",
"message": "Organization has 25 active members that must be reassigned first",
"code": "HAS_ACTIVE_MEMBERS"
}
],
"timestamp": "2024-01-25T10:30:00Z",
"requestId": "req-business-001"
}
}Common Business Rule Error Codes:
CIRCULAR_RELATIONSHIP: Would create circular referenceHAS_ACTIVE_MEMBERS: Cannot delete entity with active associated recordsINVALID_DATE_RANGE: End date must be after start dateDUPLICATE_MEMBERSHIP: Person already member of committee/organizationCAPACITY_EXCEEDED: Maximum capacity reached
5. Resource Errors
Errors related to resource availability and access.
Example Response:
json
{
"error": {
"code": "RESOURCE_NOT_FOUND",
"message": "The requested person was not found",
"details": [
{
"field": "id",
"message": "Person with ID '123e4567-e89b-12d3-a456-426614174000' does not exist",
"code": "INVALID_ID"
}
],
"timestamp": "2024-01-25T10:30:00Z",
"requestId": "req-resource-001"
}
}Common Resource Error Codes:
RESOURCE_NOT_FOUND: Requested resource doesn't existRESOURCE_DELETED: Resource was soft-deletedRESOURCE_INACTIVE: Resource exists but is inactiveINVALID_ID: ID format is invalid or malformed
6. Rate Limiting Errors
Errors when API rate limits are exceeded.
Example Response:
json
{
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Request rate limit exceeded",
"details": [
{
"field": "rateLimit",
"message": "Maximum 1000 requests per minute exceeded",
"code": "REQUESTS_PER_MINUTE_EXCEEDED"
}
],
"timestamp": "2024-01-25T10:30:00Z",
"requestId": "req-rate-001"
}
}Rate Limit Headers:
http
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1640995260
Retry-After: 60Common Error Scenarios
Scenario 1: Invalid Authentication
Request:
bash
curl -X GET "https://builtin-crm.azurewebsites.net/api/v1/people" \
-H "tenant-code: my-tenant"Response: 401 Unauthorized
json
{
"error": {
"code": "MISSING_TOKEN",
"message": "Authorization header is required"
}
}Solution: Add Authorization header with valid bearer token.
Scenario 2: Malformed Request Body
Request:
bash
curl -X POST "https://builtin-crm.azurewebsites.net/api/v1/people" \
-H "Authorization: Bearer token123" \
-H "tenant-code: my-tenant" \
-H "Content-Type: application/json" \
-d '{"firstName": "", "email": "invalid-email"}'Response: 400 Bad Request
json
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Request contains validation errors",
"details": [
{
"field": "firstName",
"message": "First name is required",
"code": "REQUIRED_FIELD"
},
{
"field": "lastName",
"message": "Last name is required",
"code": "REQUIRED_FIELD"
},
{
"field": "email",
"message": "Email format is invalid",
"code": "INVALID_FORMAT"
}
]
}
}Solution: Provide all required fields with valid formats.
Scenario 3: Resource Not Found
Request:
bash
curl -X GET "https://builtin-crm.azurewebsites.net/api/v1/people/nonexistent-id" \
-H "Authorization: Bearer token123" \
-H "tenant-code: my-tenant"Response: 404 Not Found
json
{
"error": {
"code": "RESOURCE_NOT_FOUND",
"message": "Person not found",
"details": [
{
"field": "id",
"message": "Person with ID 'nonexistent-id' does not exist",
"code": "INVALID_ID"
}
]
}
}Solution: Verify the resource ID exists and is accessible to your tenant.
Error Resolution Strategies
1. Validation Error Resolution
When receiving validation errors:
javascript
function handleValidationError(errorResponse) {
const validationErrors = {};
if (errorResponse.error.details) {
errorResponse.error.details.forEach(detail => {
validationErrors[detail.field] = {
message: detail.message,
code: detail.code
};
});
}
// Display field-specific errors to user
Object.keys(validationErrors).forEach(field => {
displayFieldError(field, validationErrors[field].message);
});
}2. Authentication Error Recovery
javascript
async function handleAuthError(errorResponse, originalRequest) {
if (errorResponse.error.code === 'EXPIRED_TOKEN') {
try {
// Attempt to refresh token
const newToken = await refreshAccessToken();
// Retry original request with new token
originalRequest.headers.Authorization = `Bearer ${newToken}`;
return await retryRequest(originalRequest);
} catch (refreshError) {
// Redirect to login if refresh fails
redirectToLogin();
}
}
}3. Business Rule Error Handling
javascript
function handleBusinessRuleError(errorResponse) {
switch (errorResponse.error.code) {
case 'HAS_ACTIVE_MEMBERS':
// Offer to reassign members before deletion
showMemberReassignmentDialog();
break;
case 'DUPLICATE_VALUE':
// Suggest alternatives or allow user to update existing
showDuplicateResolutionOptions();
break;
case 'CIRCULAR_RELATIONSHIP':
// Explain the issue and suggest valid options
showCircularRelationshipWarning();
break;
}
}Retry Logic
Exponential Backoff Strategy
Implement exponential backoff for transient errors:
javascript
async function apiRequestWithRetry(requestConfig, maxRetries = 3) {
let lastError;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const response = await makeApiRequest(requestConfig);
return response;
} catch (error) {
lastError = error;
// Don't retry client errors (4xx) except 429
if (error.status >= 400 && error.status < 500 && error.status !== 429) {
throw error;
}
// Don't retry on last attempt
if (attempt === maxRetries) {
throw error;
}
// Calculate delay with exponential backoff
const delay = Math.min(1000 * Math.pow(2, attempt - 1), 30000);
// Add jitter to prevent thundering herd
const jitter = Math.random() * 1000;
await sleep(delay + jitter);
}
}
throw lastError;
}Rate Limit Handling
javascript
async function handleRateLimit(error, requestConfig) {
if (error.status === 429) {
const retryAfter = error.headers['retry-after'];
const resetTime = error.headers['x-ratelimit-reset'];
let waitTime;
if (retryAfter) {
waitTime = parseInt(retryAfter) * 1000; // Convert to milliseconds
} else if (resetTime) {
waitTime = (parseInt(resetTime) * 1000) - Date.now();
} else {
waitTime = 60000; // Default 1 minute
}
console.log(`Rate limited. Waiting ${waitTime}ms before retry`);
await sleep(waitTime);
return await makeApiRequest(requestConfig);
}
throw error;
}Debugging and Logging
Request/Response Logging
Log requests and responses for debugging (without sensitive data):
javascript
function logApiRequest(config, response, error) {
const logData = {
timestamp: new Date().toISOString(),
method: config.method,
url: config.url,
requestId: response?.headers?.['x-request-id'] || error?.response?.data?.error?.requestId,
status: response?.status || error?.response?.status,
duration: response?.config?.metadata?.endTime - response?.config?.metadata?.startTime
};
if (error) {
logData.error = {
code: error.response?.data?.error?.code,
message: error.response?.data?.error?.message
};
}
console.log('API Request:', logData);
}Error Correlation
Use request IDs to correlate errors across systems:
javascript
function trackError(error, context) {
const errorInfo = {
requestId: error.response?.data?.error?.requestId,
timestamp: error.response?.data?.error?.timestamp,
userAgent: navigator.userAgent,
url: window.location.href,
userId: context.userId,
tenantCode: context.tenantCode
};
// Send to error tracking service
errorTracker.captureError(error, errorInfo);
}Client-Side Error Handling
javascript
// Global error handler for API requests
axios.interceptors.response.use(
response => response,
error => {
// Log error details
console.error('API Error:', {
status: error.response?.status,
code: error.response?.data?.error?.code,
message: error.response?.data?.error?.message,
requestId: error.response?.data?.error?.requestId
});
// Handle common errors
switch (error.response?.status) {
case 401:
handleAuthError(error);
break;
case 403:
handleAuthorizationError(error);
break;
case 429:
return handleRateLimit(error);
case 500:
case 502:
case 503:
case 504:
return handleServerError(error);
}
return Promise.reject(error);
}
);Error Monitoring
Set up monitoring for error patterns:
javascript
function monitorErrorPatterns() {
// Track error rates
const errorMetrics = {
'4xx': 0,
'5xx': 0,
'auth_errors': 0,
'validation_errors': 0,
'rate_limit_errors': 0
};
// Update metrics on each error
function updateErrorMetrics(error) {
const status = error.response?.status;
const code = error.response?.data?.error?.code;
if (status >= 400 && status < 500) {
errorMetrics['4xx']++;
} else if (status >= 500) {
errorMetrics['5xx']++;
}
if (code?.includes('TOKEN') || code?.includes('AUTH')) {
errorMetrics['auth_errors']++;
}
if (code === 'VALIDATION_ERROR') {
errorMetrics['validation_errors']++;
}
if (code === 'RATE_LIMIT_EXCEEDED') {
errorMetrics['rate_limit_errors']++;
}
}
// Send metrics to monitoring service periodically
setInterval(() => {
monitoringService.sendMetrics(errorMetrics);
}, 60000); // Every minute
}For Support: When contacting support about API errors, always include the requestId from the error response. This helps our support team quickly locate and diagnose the issue in our logs.
