Overview
The Veridox API uses conventional HTTP status codes and structured error responses to indicate the success or failure of requests. This guide covers common error patterns, recommended retry strategies, and best practices for robust error handling.
Consistent Error Format : All error responses follow a standardised schema with error_code, error_message, and optional error_details fields.
All errors follow this consistent structure:
{
"error_code" : "string" ,
"error_message" : "string" ,
"error_details" : {
// Additional context (optional)
}
}
Error Response Fields
Field Type Description error_codestring Machine-readable error identifier for programmatic handling error_messagestring Human-readable error description error_detailsobject Optional additional context (validation errors, retry info, etc.)
HTTP Status Codes
The API uses standard HTTP status codes to indicate request outcomes:
Status Code Meaning Common Causes 200 OK Request succeeded 201 Created Resource created successfully 400 Bad Request Malformed request body or invalid parameters 401 Unauthorised Missing, invalid, or expired API key 403 Forbidden Valid credentials but insufficient permissions 404 Not Found Resource does not exist 409 Conflict Request conflicts with current resource state 429 Too Many Requests Rate limit exceeded 500 Internal Server Error Server-side error occurred 503 Service Unavailable Temporary server overload or maintenance
Common Error Types
Authentication Errors (401)
Authentication failures occur when API key validation fails.
Invalid API Key
{
"error_code" : "request.authentication.api-key.invalid" ,
"error_message" : "The provided API key is invalid."
}
Causes :
API key typo or formatting error
API key deleted or revoked
Wrong environment (staging vs production key)
Resolution :
Verify API key format and value
Regenerate API key if compromised
Ensure using correct environment key
Expired API Key
{
"error_code" : "request.authentication.api-key.expired" ,
"error_message" : "The API key has expired."
}
Causes :
API key reached expiration date
Organisation subscription expired
Resolution :
Generate new API key from dashboard
Update application configuration
Verify organisation subscription status
Invalid Membership
{
"error_code" : "request.authentication.api-key.membership.invalid" ,
"error_message" : "You do not have a valid membership associated with this API key."
}
Causes :
User removed from organisation
Organisation membership revoked
API key associated with deleted user
Resolution :
Verify organisation membership status
Contact organisation administrator
Generate new API key if membership restored
Validation Errors (400)
Validation errors provide field-level feedback when request data is malformed or invalid.
Request Body Validation
{
"error_code" : "client.bad-request" ,
"error_message" : "The request body was malformed or contained invalid data." ,
"error_details" : {
"validation_errors" : [
{
"field" : "label" ,
"message" : "Invalid input: expected string, received undefined"
},
{
"field" : "expires_at" ,
"message" : "Expiration date must be in the future"
}
]
}
}
Handling Strategy :
Parse validation_errors array for field-specific issues
Display errors next to relevant form fields
Validate input client-side before submission
Log validation patterns to improve UX
Example Handler :
function handleValidationError ( error ) {
if ( error . error_details ?. validation_errors ) {
const fieldErrors = {};
error . error_details . validation_errors . forEach (({ field , message }) => {
fieldErrors [ field ] = message ;
});
return {
type: 'validation' ,
fields: fieldErrors
};
}
return {
type: 'generic' ,
message: error . error_message
};
}
Rate Limiting Errors (429)
Rate limits protect API infrastructure and ensure fair usage.
{
"error_code" : "request.rate-limit" ,
"error_message" : "Too many requests. Please try again later." ,
"error_details" : {
"retry_after" : 60
}
}
Rate Limits by Operation :
Operation Limit Scope Window Create case 10 requests Per IP Per minute Create document request 10 requests Per IP Per minute File analysis retrieval 300 requests Per user Per hour List operations 30 requests Per IP Per minute Risk confirmations 10 requests Per user Per minute Progress polling 600 requests Per API key Per hour
Handling Strategy :
Implement exponential backoff retry logic
Respect retry_after header when provided
Use Server-Sent Events instead of polling where possible
Cache responses to reduce redundant requests
Exponential Backoff Implementation :
async function retryWithBackoff ( fn , maxRetries = 5 ) {
let retries = 0 ;
while ( retries < maxRetries ) {
try {
return await fn ();
} catch ( error ) {
if ( error . status === 429 ) {
retries ++ ;
// Extract retry_after or calculate exponential backoff
const retryAfter = error . error_details ?. retry_after ||
Math . min ( 1000 * Math . pow ( 2 , retries ), 60000 );
console . log ( `Rate limited. Retrying after ${ retryAfter } ms (attempt ${ retries } / ${ maxRetries } )` );
await new Promise ( resolve => setTimeout ( resolve , retryAfter ));
} else {
throw error ;
}
}
}
throw new Error ( 'Max retries exceeded' );
}
// Usage
const caseData = await retryWithBackoff (() =>
fetch ( 'https://api.{region}.veridox.ai/cases' , {
method: 'POST' ,
headers: { 'X-API-Key' : apiKey },
body: JSON . stringify ({ label: 'Customer #123' , file_labels: [ 'id.pdf' ] })
}). then ( res => res . json ())
);
Resource Errors (404, 409)
Resource Not Found (404)
{
"error_code" : "resource.not-found" ,
"error_message" : "The requested resource was not found."
}
Common Causes :
Case ID, file ID, or request ID doesn’t exist
Resource deleted before request
Typo in resource identifier
Handling Strategy :
Validate UUIDs before making requests
Implement graceful fallback for missing resources
Cache resource existence checks
Resource Conflict (409)
{
"error_code" : "resource.conflict.case-locked" ,
"error_message" : "Cannot add files to a locked case. The case is already processing."
}
Common Scenarios :
Attempting to upload files to locked case
Duplicate operation detected
State transition not allowed
Handling Strategy :
Check resource state before operations
Create new case if existing case is locked
Implement idempotency where possible
Server Errors (500, 503)
Internal Server Error (500)
{
"error_code" : "server.internal-error" ,
"error_message" : "An unexpected error occurred. Please try again later."
}
Causes :
Unexpected server-side exception
Database connectivity issues
Processing pipeline failures
Handling Strategy :
Retry after short delay (2-5 seconds)
Log full error context for debugging
Contact support if error persists
Implement fallback or degraded mode
Service Unavailable (503)
{
"error_code" : "server.unavailable" ,
"error_message" : "Service temporarily unavailable. Please retry after indicated time." ,
"error_details" : {
"retry_after" : 120
}
}
Causes :
Scheduled maintenance
Temporary overload
Infrastructure scaling
Handling Strategy :
Display maintenance message to users
Retry after specified delay
Queue operations for later processing
Error Handling Best Practices
Comprehensive Error Handler
class VeridoxAPIClient {
constructor ( apiKey , baseUrl = 'https://api.{region}.veridox.ai' ) {
this . apiKey = apiKey ;
this . baseUrl = baseUrl ;
}
async request ( endpoint , options = {}) {
const url = ` ${ this . baseUrl }${ endpoint } ` ;
try {
const response = await fetch ( url , {
... options ,
headers: {
'X-API-Key' : this . apiKey ,
'Content-Type' : 'application/json' ,
... options . headers
}
});
// Parse response
const data = await response . json ();
// Handle success
if ( response . ok ) {
return data ;
}
// Handle errors
return this . handleError ( response . status , data );
} catch ( error ) {
// Network errors
if ( error . name === 'TypeError' || error . name === 'NetworkError' ) {
throw {
type: 'network' ,
message: 'Network connection failed. Please check your internet connection.' ,
originalError: error
};
}
throw error ;
}
}
handleError ( status , errorData ) {
const error = {
status ,
code: errorData . error_code ,
message: errorData . error_message ,
details: errorData . error_details
};
switch ( status ) {
case 400 :
return this . handleValidationError ( error );
case 401 :
return this . handleAuthError ( error );
case 404 :
return this . handleNotFoundError ( error );
case 409 :
return this . handleConflictError ( error );
case 429 :
return this . handleRateLimitError ( error );
case 500 :
case 503 :
return this . handleServerError ( error );
default :
throw error ;
}
}
handleValidationError ( error ) {
throw {
type: 'validation' ,
message: 'Request validation failed' ,
fields: error . details ?. validation_errors || [],
originalError: error
};
}
handleAuthError ( error ) {
throw {
type: 'authentication' ,
message: 'Authentication failed. Please check your API key.' ,
code: error . code ,
originalError: error
};
}
handleNotFoundError ( error ) {
throw {
type: 'not_found' ,
message: 'Resource not found' ,
originalError: error
};
}
handleConflictError ( error ) {
throw {
type: 'conflict' ,
message: error . message ,
code: error . code ,
originalError: error
};
}
handleRateLimitError ( error ) {
const retryAfter = error . details ?. retry_after || 60 ;
throw {
type: 'rate_limit' ,
message: `Rate limit exceeded. Retry after ${ retryAfter } seconds.` ,
retryAfter ,
originalError: error
};
}
handleServerError ( error ) {
throw {
type: 'server_error' ,
message: 'Server error occurred. Please try again later.' ,
retryable: true ,
originalError: error
};
}
}
// Usage with error handling
const client = new VeridoxAPIClient ( 'vdx_your_api_key' );
try {
const caseData = await client . request ( '/cases' , {
method: 'POST' ,
body: JSON . stringify ({
label: 'Customer Verification #12345' ,
file_labels: [ 'passport.pdf' , 'utility_bill.pdf' ]
})
});
console . log ( 'Case created:' , caseData . case_id );
} catch ( error ) {
switch ( error . type ) {
case 'validation' :
console . error ( 'Validation failed:' , error . fields );
// Display field-specific errors in UI
break ;
case 'authentication' :
console . error ( 'Auth failed:' , error . message );
// Redirect to login or API key configuration
break ;
case 'rate_limit' :
console . error ( 'Rate limited:' , error . message );
// Wait and retry
await new Promise ( r => setTimeout ( r , error . retryAfter * 1000 ));
break ;
case 'server_error' :
console . error ( 'Server error:' , error . message );
// Retry with exponential backoff
break ;
case 'network' :
console . error ( 'Network error:' , error . message );
// Display offline message
break ;
default :
console . error ( 'Unexpected error:' , error );
}
}
React Error Boundary
import { Component } from 'react' ;
class APIErrorBoundary extends Component {
state = { hasError: false , error: null };
static getDerivedStateFromError ( error ) {
return { hasError: true , error };
}
render () {
if ( this . state . hasError ) {
const { error } = this . state ;
if ( error . type === 'authentication' ) {
return (
< div className = "error-auth" >
< h2 > Authentication Required </ h2 >
< p > Please check your API key configuration. </ p >
< button onClick = { () => window . location . reload () } >
Retry
</ button >
</ div >
);
}
if ( error . type === 'rate_limit' ) {
return (
< div className = "error-rate-limit" >
< h2 > Rate Limit Exceeded </ h2 >
< p > Please wait { error . retryAfter } seconds before retrying. </ p >
</ div >
);
}
return (
< div className = "error-generic" >
< h2 > Something went wrong </ h2 >
< p > { error . message || 'An unexpected error occurred' } </ p >
< button onClick = { () => this . setState ({ hasError: false , error: null }) } >
Try Again
</ button >
</ div >
);
}
return this . props . children ;
}
}
Error Handling Guidelines
Design Principles
Fail gracefully - Provide clear error messages and recovery paths
Be specific - Use error_code for programmatic handling, not just status codes
Retry intelligently - Implement exponential backoff for transient errors
Log comprehensively - Capture full error context for debugging
User Experience
User-friendly messages - Translate technical errors into actionable guidance
Show progress - Display retry countdown or progress indicators
Provide alternatives - Offer fallback options when primary action fails
Don’t expose internals - Hide technical details from end users
Monitoring and Debugging
Track error rates - Monitor error types and frequencies
Set up alerts - Get notified of unusual error patterns
Log context - Include request IDs, timestamps, and user actions
Correlate errors - Group related errors for root cause analysis
Security Considerations
Don’t leak data - Avoid exposing sensitive information in error messages
Rate limit retries - Prevent abuse through excessive retry attempts
Validate client-side - Reduce unnecessary API calls with validation
Sanitise error logs - Remove PII before logging error details
Common Troubleshooting
API Key Issues
Symptom : Consistent 401 errors
Checklist :
Verify API key format (vdx_ prefix)
Check environment (staging vs production)
Confirm organisation membership
Test with newly generated key
Rate Limiting
Symptom : Frequent 429 errors
Solutions :
Implement request queueing
Use Server-Sent Events instead of polling
Cache API responses
Batch operations where possible
Timeout Errors
Symptom : Requests timing out
Causes :
Large file uploads
Complex analysis operations
Network connectivity issues
Solutions :
Increase timeout thresholds
Use progress monitoring endpoints
Split large batches into smaller chunks
Validation Failures
Symptom : Consistent 400 errors
Solutions :
Review API documentation for field requirements
Validate input formats (email, UUID, ISO dates)
Check field length limits
Ensure required fields are provided
API Reference Complete endpoint documentation with example requests
Quickstart Guide Step-by-step tutorial with error handling examples
Rate Limiting Detailed rate limit information by endpoint
Authentication API key validation and authentication testing