> ## Documentation Index
> Fetch the complete documentation index at: https://docs.instaview.sk/llms.txt
> Use this file to discover all available pages before exploring further.

# Error Handling

> Learn how to handle errors and exceptions in the InstaView API

## Overview

The InstaView API uses standard HTTP status codes and returns structured error responses to help you diagnose and handle issues in your integration.

## Response Structure

All API responses follow a consistent format:

```json theme={null}
{
  "success": boolean,
  "data": object | array | null,
  "error": {
    "code": string,
    "message": string,
    "details": object
  } | null,
  "timestamp": string
}
```

### Success Response

```json theme={null}
{
  "success": true,
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "title": "Senior Engineer"
  },
  "error": null,
  "timestamp": "2024-01-15T10:30:00Z"
}
```

### Error Response

```json theme={null}
{
  "success": false,
  "data": null,
  "error": {
    "code": "RESOURCE_NOT_FOUND",
    "message": "Job with ID 550e8400-e29b-41d4-a716-446655440000 not found",
    "details": {
      "resourceType": "Job",
      "resourceId": "550e8400-e29b-41d4-a716-446655440000"
    }
  },
  "timestamp": "2024-01-15T10:30:00Z"
}
```

## HTTP Status Codes

<Tabs>
  <Tab title="2xx Success">
    | Code | Meaning | Usage                                       |
    | ---- | ------- | ------------------------------------------- |
    | 200  | OK      | Successful GET, PATCH, DELETE requests      |
    | 201  | Created | Successful POST request creating a resource |
  </Tab>

  <Tab title="4xx Client Errors">
    | Code | Meaning           | Common Causes                           |
    | ---- | ----------------- | --------------------------------------- |
    | 400  | Bad Request       | Invalid request body, validation errors |
    | 401  | Unauthorized      | Missing or invalid API key              |
    | 403  | Forbidden         | Insufficient permissions, wrong company |
    | 404  | Not Found         | Resource doesn't exist                  |
    | 409  | Conflict          | Duplicate resource, state conflict      |
    | 422  | Unprocessable     | Valid JSON but semantic errors          |
    | 429  | Too Many Requests | Rate limit exceeded                     |
  </Tab>

  <Tab title="5xx Server Errors">
    | Code | Meaning               | Action                         |
    | ---- | --------------------- | ------------------------------ |
    | 500  | Internal Server Error | Retry with exponential backoff |
    | 502  | Bad Gateway           | Temporary issue, retry         |
    | 503  | Service Unavailable   | Maintenance, retry later       |
    | 504  | Gateway Timeout       | Request timeout, retry         |
  </Tab>
</Tabs>

## Error Codes

### Authentication Errors

<ResponseField name="INVALID_API_KEY" type="401">
  The provided API key is invalid, revoked, or expired

  ```json theme={null}
  {
    "error": {
      "code": "INVALID_API_KEY",
      "message": "The provided API key is invalid or has been revoked"
    }
  }
  ```

  **Solutions**:

  * Verify API key format
  * Check if key was revoked in dashboard
  * Ensure key hasn't expired
</ResponseField>

<ResponseField name="API_KEY_SUSPENDED" type="401">
  The API key has been temporarily suspended

  ```json theme={null}
  {
    "error": {
      "code": "API_KEY_SUSPENDED",
      "message": "This API key has been suspended",
      "details": {
        "suspendedAt": "2024-01-10T00:00:00Z",
        "reason": "Unusual activity detected"
      }
    }
  }
  ```

  **Solution**: Contact support or check dashboard for details
</ResponseField>

### Permission Errors

<ResponseField name="INSUFFICIENT_PERMISSIONS" type="403">
  API key lacks required scope

  ```json theme={null}
  {
    "error": {
      "code": "INSUFFICIENT_PERMISSIONS",
      "message": "This API key does not have the required scope: write:jobs",
      "details": {
        "required": "write:jobs",
        "available": ["read:jobs", "read:candidates"]
      }
    }
  }
  ```

  **Solution**: Add required scope to API key or create new key
</ResponseField>

<ResponseField name="RESOURCE_ACCESS_DENIED" type="403">
  Resource belongs to different company

  ```json theme={null}
  {
    "error": {
      "code": "RESOURCE_ACCESS_DENIED",
      "message": "Access denied to this resource"
    }
  }
  ```

  **Solutions**:

  * Verify resource belongs to your company
  * Check if using correct companyId (ATS keys)
  * Ensure resource hasn't been deleted
</ResponseField>

### Validation Errors

<ResponseField name="VALIDATION_ERROR" type="400">
  Request validation failed

  ```json theme={null}
  {
    "error": {
      "code": "VALIDATION_ERROR",
      "message": "Request validation failed",
      "details": {
        "fields": {
          "email": "Invalid email format",
          "phoneNumber": "Phone number is required",
          "jobId": "Must be a valid UUID"
        }
      }
    }
  }
  ```

  **Solution**: Fix validation errors in request body
</ResponseField>

<ResponseField name="MISSING_REQUIRED_FIELD" type="400">
  Required field not provided

  ```json theme={null}
  {
    "error": {
      "code": "MISSING_REQUIRED_FIELD",
      "message": "Missing required field: jobId",
      "details": {
        "field": "jobId",
        "type": "string (UUID)"
      }
    }
  }
  ```
</ResponseField>

### Resource Errors

<ResponseField name="RESOURCE_NOT_FOUND" type="404">
  Requested resource doesn't exist

  ```json theme={null}
  {
    "error": {
      "code": "RESOURCE_NOT_FOUND",
      "message": "Job not found",
      "details": {
        "resourceType": "Job",
        "resourceId": "550e8400-e29b-41d4-a716-446655440000"
      }
    }
  }
  ```
</ResponseField>

<ResponseField name="RESOURCE_CONFLICT" type="409">
  Resource already exists or state conflict

  ```json theme={null}
  {
    "error": {
      "code": "RESOURCE_CONFLICT",
      "message": "A candidate with this email already exists for this job",
      "details": {
        "conflictingField": "email",
        "existingResourceId": "candidate-uuid"
      }
    }
  }
  ```
</ResponseField>

### Rate Limiting

<ResponseField name="RATE_LIMIT_EXCEEDED" type="429">
  Too many requests

  ```json theme={null}
  {
    "error": {
      "code": "RATE_LIMIT_EXCEEDED",
      "message": "Rate limit exceeded. Please retry after 45 seconds",
      "details": {
        "retryAfter": 45,
        "limit": 300,
        "window": "minute"
      }
    }
  }
  ```

  **Solution**: Wait for `retryAfter` seconds, implement exponential backoff
</ResponseField>

### Business Logic Errors

<ResponseField name="BILLING_LIMIT_EXCEEDED" type="403">
  Company billing limit reached

  ```json theme={null}
  {
    "error": {
      "code": "BILLING_LIMIT_EXCEEDED",
      "message": "Monthly interview limit exceeded",
      "details": {
        "limit": 100,
        "used": 100,
        "resetDate": "2024-02-01T00:00:00Z"
      }
    }
  }
  ```

  **Solution**: Upgrade plan or wait for monthly reset
</ResponseField>

<ResponseField name="INVALID_STATE_TRANSITION" type="422">
  Invalid resource state change

  ```json theme={null}
  {
    "error": {
      "code": "INVALID_STATE_TRANSITION",
      "message": "Cannot delete job with active candidates",
      "details": {
        "currentState": "active",
        "attemptedAction": "delete",
        "reason": "Job has 5 active candidates"
      }
    }
  }
  ```
</ResponseField>

## Error Handling Patterns

### Basic Error Handling

<CodeGroup>
  ```javascript Node.js theme={null}
  async function createJob(jobData) {
    try {
      const response = await fetch('https://api.instaview.sk/jobs', {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${process.env.INSTAVIEW_API_KEY}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(jobData)
      });
      
      const result = await response.json();
      
      if (!result.success) {
        throw new InstaViewError(result.error);
      }
      
      return result.data;
    } catch (error) {
      console.error('Failed to create job:', error.message);
      throw error;
    }
  }

  class InstaViewError extends Error {
    constructor(errorData) {
      super(errorData.message);
      this.code = errorData.code;
      this.details = errorData.details;
      this.name = 'InstaViewError';
    }
  }
  ```

  ```python Python theme={null}
  import requests

  class InstaViewError(Exception):
      def __init__(self, error_data):
          self.code = error_data['code']
          self.message = error_data['message']
          self.details = error_data.get('details', {})
          super().__init__(self.message)

  def create_job(job_data):
      try:
          response = requests.post(
              'https://api.instaview.sk/jobs',
              headers={
                  'Authorization': f'Bearer {os.environ["INSTAVIEW_API_KEY"]}',
                  'Content-Type': 'application/json'
              },
              json=job_data
          )
          
          result = response.json()
          
          if not result['success']:
              raise InstaViewError(result['error'])
          
          return result['data']
          
      except InstaViewError as e:
          print(f'Failed to create job: {e.code} - {e.message}')
          raise
  ```

  ```php PHP theme={null}
  <?php

  class InstaViewError extends Exception {
      public $code;
      public $details;
      
      public function __construct($errorData) {
          $this->code = $errorData['code'];
          $this->details = $errorData['details'] ?? [];
          parent::__construct($errorData['message']);
      }
  }

  function createJob($jobData) {
      $apiKey = getenv('INSTAVIEW_API_KEY');
      
      $ch = curl_init('https://api.instaview.sk/jobs');
      curl_setopt($ch, CURLOPT_POST, true);
      curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($jobData));
      curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
      curl_setopt($ch, CURLOPT_HTTPHEADER, [
          'Authorization: Bearer ' . $apiKey,
          'Content-Type: application/json'
      ]);
      
      $response = curl_exec($ch);
      $result = json_decode($response, true);
      curl_close($ch);
      
      if (!$result['success']) {
          throw new InstaViewError($result['error']);
      }
      
      return $result['data'];
  }

  try {
      $job = createJob($jobData);
  } catch (InstaViewError $e) {
      echo "Failed: {$e->code} - {$e->getMessage()}\n";
  }
  ?>
  ```
</CodeGroup>

### Comprehensive Error Handler

```javascript theme={null}
class InstaViewClient {
  constructor(apiKey) {
    this.apiKey = apiKey;
    this.baseURL = 'https://api.instaview.sk/v1';
  }
  
  async request(method, path, data = null) {
    try {
      const response = await fetch(`${this.baseURL}${path}`, {
        method,
        headers: {
          'Authorization': `Bearer ${this.apiKey}`,
          'Content-Type': 'application/json'
        },
        body: data ? JSON.stringify(data) : null
      });
      
      const result = await response.json();
      
      if (!result.success) {
        return this.handleError(result.error, response.status);
      }
      
      return result.data;
    } catch (error) {
      console.error('Network error:', error);
      throw error;
    }
  }
  
  handleError(error, statusCode) {
    switch (error.code) {
      case 'INVALID_API_KEY':
      case 'API_KEY_SUSPENDED':
        console.error('Authentication error:', error.message);
        // Notify admin, stop operations
        throw new AuthenticationError(error);
        
      case 'INSUFFICIENT_PERMISSIONS':
        console.error('Permission denied:', error.message);
        console.error('Required:', error.details.required);
        console.error('Available:', error.details.available);
        throw new PermissionError(error);
        
      case 'VALIDATION_ERROR':
        console.error('Validation failed:', error.details.fields);
        // Show user-friendly messages
        throw new ValidationError(error);
        
      case 'RATE_LIMIT_EXCEEDED':
        console.log(`Rate limited. Retry after ${error.details.retryAfter}s`);
        // Implement retry logic
        throw new RateLimitError(error);
        
      case 'RESOURCE_NOT_FOUND':
        console.warn('Resource not found:', error.details);
        return null; // Handle gracefully
        
      case 'BILLING_LIMIT_EXCEEDED':
        console.error('Billing limit exceeded:', error.message);
        // Notify admin, show upgrade prompt
        throw new BillingError(error);
        
      default:
        console.error('Unknown error:', error);
        throw new InstaViewError(error);
    }
  }
}
```

### Retry with Exponential Backoff

```javascript theme={null}
async function retryWithBackoff(fn, maxRetries = 3, baseDelay = 1000) {
  let lastError;
  
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      lastError = error;
      
      // Don't retry client errors (except rate limits)
      if (error.statusCode >= 400 && error.statusCode < 500) {
        if (error.code === 'RATE_LIMIT_EXCEEDED') {
          const delay = error.details.retryAfter * 1000 || baseDelay;
          await sleep(delay);
          continue;
        }
        throw error; // Don't retry other client errors
      }
      
      // Retry server errors with exponential backoff
      if (error.statusCode >= 500) {
        const delay = baseDelay * Math.pow(2, attempt);
        console.log(`Attempt ${attempt + 1} failed. Retrying in ${delay}ms...`);
        await sleep(delay);
        continue;
      }
      
      throw error;
    }
  }
  
  throw lastError;
}

// Usage
const job = await retryWithBackoff(async () => {
  return await client.createJob(jobData);
});
```

## Validation Error Handling

Handle field-level validation errors:

```javascript theme={null}
async function createCandidateWithValidation(candidateData) {
  try {
    return await client.createCandidate(candidateData);
  } catch (error) {
    if (error.code === 'VALIDATION_ERROR') {
      const fieldErrors = error.details.fields;
      
      // Display user-friendly errors
      for (const [field, message] of Object.entries(fieldErrors)) {
        showFieldError(field, message);
      }
      
      // Or collect all errors
      const errors = Object.entries(fieldErrors).map(([field, msg]) => ({
        field,
        message: msg
      }));
      
      return { success: false, errors };
    }
    
    throw error;
  }
}

function showFieldError(field, message) {
  // Display error next to form field
  document.getElementById(`${field}-error`).textContent = message;
}
```

## Idempotency

Implement idempotency for safe retries:

```javascript theme={null}
async function createJobIdempotent(jobData, idempotencyKey) {
  try {
    return await fetch('https://api.instaview.sk/jobs', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${apiKey}`,
        'Content-Type': 'application/json',
        'Idempotency-Key': idempotencyKey // Use same key for retries
      },
      body: JSON.stringify(jobData)
    });
  } catch (error) {
    // Safe to retry with same idempotency key
    return await fetch(/* same request */);
  }
}

// Generate idempotency key
const idempotencyKey = `job-create-${Date.now()}-${Math.random()}`;
await createJobIdempotent(jobData, idempotencyKey);
```

## Logging and Monitoring

Log errors for debugging and monitoring:

```javascript theme={null}
class ErrorLogger {
  static log(error, context = {}) {
    const errorLog = {
      timestamp: new Date().toISOString(),
      code: error.code,
      message: error.message,
      details: error.details,
      statusCode: error.statusCode,
      context: context,
      stack: error.stack
    };
    
    // Send to logging service
    console.error(JSON.stringify(errorLog));
    
    // Send to error tracking (Sentry, etc.)
    if (process.env.NODE_ENV === 'production') {
      Sentry.captureException(error, {
        extra: errorLog
      });
    }
  }
}

// Usage
try {
  await createJob(jobData);
} catch (error) {
  ErrorLogger.log(error, {
    operation: 'createJob',
    jobData: jobData,
    userId: currentUser.id
  });
  throw error;
}
```

## Best Practices

<AccordionGroup>
  <Accordion title="Always Check success Field" icon="check">
    ```javascript theme={null}
    const response = await fetch(url);
    const result = await response.json();

    if (!result.success) {
      // Handle error
      throw new Error(result.error.message);
    }

    return result.data;
    ```
  </Accordion>

  <Accordion title="Provide Context in Errors" icon="info-circle">
    ```javascript theme={null}
    class DetailedError extends Error {
      constructor(error, context) {
        super(error.message);
        this.code = error.code;
        this.details = error.details;
        this.context = context; // Add operation context
      }
    }

    throw new DetailedError(apiError, {
      operation: 'createCandidate',
      candidateEmail: data.email,
      jobId: data.jobId
    });
    ```
  </Accordion>

  <Accordion title="Don't Expose Sensitive Data" icon="eye-slash">
    ```javascript theme={null}
    // ❌ Don't expose sensitive data in logs
    console.error('Failed:', error, apiKey);

    // ✅ Log safely
    console.error('Failed:', error.code, error.message);
    ```
  </Accordion>

  <Accordion title="Implement Circuit Breakers" icon="plug-circle-xmark">
    ```javascript theme={null}
    class CircuitBreaker {
      constructor(threshold = 5, timeout = 60000) {
        this.failureCount = 0;
        this.threshold = threshold;
        this.timeout = timeout;
        this.state = 'CLOSED';
        this.nextAttempt = Date.now();
      }
      
      async execute(fn) {
        if (this.state === 'OPEN') {
          if (Date.now() < this.nextAttempt) {
            throw new Error('Circuit breaker is OPEN');
          }
          this.state = 'HALF_OPEN';
        }
        
        try {
          const result = await fn();
          this.onSuccess();
          return result;
        } catch (error) {
          this.onFailure();
          throw error;
        }
      }
      
      onSuccess() {
        this.failureCount = 0;
        this.state = 'CLOSED';
      }
      
      onFailure() {
        this.failureCount++;
        if (this.failureCount >= this.threshold) {
          this.state = 'OPEN';
          this.nextAttempt = Date.now() + this.timeout;
        }
      }
    }
    ```
  </Accordion>
</AccordionGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="Rate Limiting" icon="gauge" href="/guides/rate-limiting">
    Handle rate limits properly
  </Card>

  <Card title="Best Practices" icon="star" href="/guides/best-practices">
    Production-ready patterns
  </Card>

  <Card title="Authentication" icon="key" href="/authentication">
    Fix authentication errors
  </Card>

  <Card title="API Reference" icon="code" href="/api-reference">
    See all error codes per endpoint
  </Card>
</CardGroup>
