Skip to main content

Error Handling Guide

This guide covers error handling, rate limiting, and best practices for building resilient integrations with Assist Insurances APIs.

HTTP Status Codes

All APIs use standard HTTP status codes to indicate success or failure:

CodeStatusDescription
200OKRequest succeeded
400Bad RequestInvalid request parameters or format
401UnauthorizedMissing or invalid authentication token
403ForbiddenInsufficient permissions
404Not FoundResource not found
429Too Many RequestsRate limit exceeded
500Internal Server ErrorServer error (retry recommended)
503Service UnavailableTemporary service outage (retry recommended)

Error Response Format

All error responses follow a consistent JSON format:

{
"statusCode": 401,
"message": "Unauthorized. Access token is missing or invalid.",
"error": "Unauthorized"
}

Fields:

  • statusCode - HTTP status code (number)
  • message - Human-readable error description
  • error - Error type/category (optional)

Common Errors

400 Bad Request

Indicates invalid request parameters, malformed JSON/XML, or missing required fields.

Example:

{
"statusCode": 400,
"message": "Invalid request: Missing required field 'in_customer.dateOfBirth'"
}

Common Causes:

  • Missing required fields
  • Invalid date formats
  • Malformed JSON or XML
  • Invalid enum values
  • Data validation failures

How to Fix:

  1. Validate request data before sending
  2. Check required fields against documentation
  3. Ensure correct date/time formats (ISO 8601)
  4. Validate XML against schema

Example Fix:

function validateQuoteData(data) {
const required = [
'in_policy.dateInception',
'in_customer.dateOfBirth',
'in_dts.raterCover'
];

const missing = required.filter(field => !data[field]);

if (missing.length > 0) {
throw new Error(`Missing required fields: ${missing.join(', ')}`);
}

// Validate date format
if (!/^\d{4}-\d{2}-\d{2}$/.test(data['in_customer.dateOfBirth'])) {
throw new Error('Invalid date format. Use YYYY-MM-DD');
}

// Validate enum
const validCovers = ['Comprehensive', 'ThirdParty', 'ThirdPartyFireAndTheft'];
if (!validCovers.includes(data['in_dts.raterCover'])) {
throw new Error(`Invalid cover type. Must be one of: ${validCovers.join(', ')}`);
}

return true;
}

401 Unauthorized

Indicates missing, expired, or invalid authentication token.

Example:

{
"statusCode": 401,
"message": "Unauthorized. Access token is missing or invalid."
}

Common Causes:

  • Token not included in request
  • Token has expired (> 1 hour old)
  • Invalid or malformed token
  • Incorrect client credentials

How to Fix:

  1. Ensure Authorization: Bearer {token} header is present
  2. Check token expiry and refresh if needed
  3. Verify client credentials are correct
  4. Request new token if current one is invalid

Example Implementation:

class APIClient {
async makeRequest(endpoint, options = {}) {
let response = await this.sendRequest(endpoint, options);

// Handle token expiry
if (response.status === 401) {
console.log('Token expired, refreshing...');
this.token = null; // Clear cached token
const newToken = await this.getAccessToken();

// Retry with new token
response = await this.sendRequest(endpoint, {
...options,
headers: {
...options.headers,
'Authorization': `Bearer ${newToken}`,
},
});
}

return response;
}

async sendRequest(endpoint, options) {
const token = await this.getAccessToken();

return fetch(`${this.baseUrl}${endpoint}`, {
...options,
headers: {
...options.headers,
'Authorization': `Bearer ${token}`,
},
});
}
}

403 Forbidden

Indicates insufficient permissions for the requested operation.

Example:

{
"statusCode": 403,
"message": "Insufficient permissions for override operation"
}

Common Causes:

  • Client doesn't have required role/permission
  • Operation not allowed for your account type
  • Resource access restricted

How to Fix:

  • Contact support to request additional permissions
  • Verify your client has the correct roles assigned
  • Use a different operation within your permissions

404 Not Found

Indicates the requested resource doesn't exist.

Example:

{
"statusCode": 404,
"message": "Quote QT-2025-001234 not found"
}

Common Causes:

  • Invalid resource ID
  • Resource has been deleted
  • Typo in URL or ID
  • Looking up non-existent vehicle/eircode

How to Fix:

  1. Verify the resource ID is correct
  2. Check for typos in the request URL
  3. Ensure the resource hasn't been deleted
  4. For lookups, handle gracefully (not all VRMs/Eircodes exist)

Example:

async function lookupVehicleSafe(vrm) {
try {
return await client.lookupVehicle(vrm);
} catch (error) {
if (error.status === 404) {
console.log(`Vehicle ${vrm} not found in database`);
return null; // Return null instead of throwing
}
throw error; // Re-throw other errors
}
}

// Usage
const vehicle = await lookupVehicleSafe('12-D-12345');
if (vehicle) {
console.log('Vehicle found:', vehicle);
} else {
console.log('Vehicle not in database, manual entry required');
}

429 Rate Limit Exceeded

Indicates you've exceeded the rate limit of 100 requests per minute.

Example:

{
"statusCode": 429,
"message": "Rate limit exceeded"
}

Rate Limit Headers: All responses include rate limit information:

HTTP/1.1 200 OK
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 45
X-RateLimit-Reset: 1634567890

How to Fix:

  1. Implement exponential backoff
  2. Cache tokens (don't request new token for every API call)
  3. Batch requests where possible
  4. Monitor rate limit headers

Exponential Backoff Implementation:

async function makeRequestWithRetry(requestFn, maxRetries = 5) {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
const response = await requestFn();

if (response.status === 429) {
if (attempt === maxRetries) {
throw new Error('Rate limit exceeded - max retries reached');
}

// Exponential backoff: 1s, 2s, 4s, 8s, 16s
const delay = Math.pow(2, attempt) * 1000;
console.log(`Rate limited. Retrying in ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}

if (!response.ok) {
throw new Error(`Request failed: ${response.statusText}`);
}

return response;

} catch (error) {
if (attempt === maxRetries) {
throw error;
}
console.log(`Attempt ${attempt + 1} failed. Retrying...`);
}
}
}

// Usage
const response = await makeRequestWithRetry(async () => {
return fetch('https://api.assistinsurances.ie/priceQuote', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(quoteData),
});
});

500 Internal Server Error

Indicates an unexpected server error. These are usually transient.

Example:

{
"statusCode": 500,
"message": "Internal server error"
}

How to Fix:

  1. Retry the request (implement retry logic)
  2. If persists, contact support
  3. Log the error details for troubleshooting

503 Service Unavailable

Indicates temporary service outage, usually during maintenance.

Example:

{
"statusCode": 503,
"message": "Service temporarily unavailable"
}

How to Fix:

  1. Implement retry logic with exponential backoff
  2. Check service status page
  3. Queue requests if possible

Complete Error Handling Pattern

Here's a complete, production-ready error handling implementation:

class RobustAPIClient {
constructor(clientId, clientSecret, baseUrl) {
this.clientId = clientId;
this.clientSecret = clientSecret;
this.baseUrl = baseUrl;
this.token = null;
this.tokenExpiresAt = null;
}

async makeRequest(endpoint, options = {}, maxRetries = 3) {
let lastError;

for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
const token = await this.getAccessToken();
const response = await fetch(`${this.baseUrl}${endpoint}`, {
...options,
headers: {
...options.headers,
'Authorization': `Bearer ${token}`,
},
});

// Handle rate limiting
if (response.status === 429) {
if (attempt < maxRetries) {
const delay = Math.pow(2, attempt) * 1000;
console.warn(`Rate limited. Retrying in ${delay}ms...`);
await this.sleep(delay);
continue;
}
throw new Error('Rate limit exceeded - max retries reached');
}

// Handle token expiry
if (response.status === 401) {
if (attempt === 0) {
console.log('Token expired, refreshing...');
this.token = null;
continue; // Retry with new token
}
throw new Error('Authentication failed');
}

// Handle server errors with retry
if (response.status >= 500) {
if (attempt < maxRetries) {
const delay = Math.pow(2, attempt) * 1000;
console.warn(`Server error. Retrying in ${delay}ms...`);
await this.sleep(delay);
continue;
}
throw new Error(`Server error: ${response.statusText}`);
}

// Handle client errors (don't retry)
if (response.status >= 400) {
const error = await response.json();
throw new Error(
`API error (${error.statusCode}): ${error.message}`
);
}

// Success
return response.json();

} catch (error) {
lastError = error;

// Don't retry client errors (4xx except 401, 429)
if (error.message.includes('API error')) {
const statusCode = parseInt(error.message.match(/\((\d+)\)/)?.[1]);
if (statusCode >= 400 && statusCode < 500 &&
statusCode !== 401 && statusCode !== 429) {
throw error;
}
}

if (attempt === maxRetries) {
throw lastError;
}

console.log(`Attempt ${attempt + 1} failed. Retrying...`);
}
}

throw lastError;
}

sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}

async getAccessToken() {
// Token caching implementation (see Authentication guide)
// ...
}
}

Logging and Monitoring

What to Log

Always Log:

  • Request timestamp
  • Endpoint called
  • HTTP status code
  • Response time
  • Error messages

Never Log:

  • Access tokens
  • Client secrets
  • Full request/response bodies (may contain PII)

Example Logging:

function logAPICall(endpoint, method, statusCode, duration, error = null) {
const logEntry = {
timestamp: new Date().toISOString(),
endpoint,
method,
statusCode,
duration: `${duration}ms`,
success: statusCode >= 200 && statusCode < 300,
};

if (error) {
logEntry.error = error.message;
}

console.log(JSON.stringify(logEntry));
}

// Usage
const startTime = Date.now();
try {
const response = await makeRequest('/priceQuote', { method: 'POST' });
logAPICall('/priceQuote', 'POST', 200, Date.now() - startTime);
} catch (error) {
logAPICall('/priceQuote', 'POST', error.status, Date.now() - startTime, error);
}

Best Practices Summary

  1. Always implement retry logic for transient failures (429, 500, 503)
  2. Use exponential backoff to avoid overwhelming the server
  3. Cache tokens - don't request new token for every API call
  4. Handle 401 gracefully - automatic token refresh
  5. Don't retry 4xx errors (except 401, 429) - they won't succeed
  6. Log errors but never log sensitive data
  7. Set timeouts on all HTTP requests (recommended: 30 seconds)
  8. Monitor rate limits using response headers
  9. Validate input before sending to API
  10. Handle 404 gracefully for lookup endpoints

Monitoring Checklist

  • Error rate monitoring (alert if > 5%)
  • Response time monitoring (alert if > 5s)
  • Rate limit monitoring (alert if frequently hitting limit)
  • Token refresh monitoring (alert on auth failures)
  • 5xx error monitoring (alert on server errors)
  • Logging of all API interactions
  • Alerting for sustained failures

Support

If you encounter persistent errors:

  1. Check this documentation for solutions
  2. Review your logs for patterns
  3. Verify your credentials and permissions
  4. Contact technical support: neil.reilly@assistinsurances.ie

When contacting support, provide:

  • Timestamp of the error
  • Endpoint called
  • HTTP status code
  • Error message
  • Your client ID (never send client secret)
  • Request ID if available

Next Steps