English
API Reference
Error Handling

Error Handling

How to handle API errors.

Error Response Format

All errors return a consistent JSON structure:

{
  "error": {
    "code": "ERROR_CODE",
    "message": "Human-readable description",
    "details": {}
  }
}

HTTP Status Codes

CodeDescription
200Success
201Created
204No Content (successful delete)
400Bad Request - Invalid input
401Unauthorized - Authentication required
403Forbidden - Insufficient permissions
404Not Found - Resource doesn't exist
409Conflict - Resource already exists
422Unprocessable Entity - Validation error
429Too Many Requests - Rate limited
500Internal Server Error
503Service Unavailable

Common Error Codes

Authentication Errors

UNAUTHORIZED

{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Authentication required"
  }
}

INVALID_API_KEY

{
  "error": {
    "code": "INVALID_API_KEY",
    "message": "Invalid or expired API key"
  }
}

FORBIDDEN

{
  "error": {
    "code": "FORBIDDEN",
    "message": "Insufficient permissions",
    "details": {
      "required": "components:write"
    }
  }
}

Validation Errors

VALIDATION_ERROR

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid request body",
    "details": {
      "field": "name",
      "message": "Name is required"
    }
  }
}

INVALID_FORMAT

{
  "error": {
    "code": "INVALID_FORMAT",
    "message": "Invalid date format",
    "details": {
      "field": "scheduledStartAt",
      "expected": "ISO 8601"
    }
  }
}

Resource Errors

NOT_FOUND

{
  "error": {
    "code": "NOT_FOUND",
    "message": "Component not found",
    "details": {
      "resource": "component",
      "id": "comp_123"
    }
  }
}

CONFLICT

{
  "error": {
    "code": "CONFLICT",
    "message": "Component with this name already exists"
  }
}

Rate Limiting

RATE_LIMIT_EXCEEDED

{
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "Too many requests",
    "details": {
      "limit": 100,
      "window": "60s",
      "retryAfter": 45
    }
  }
}

Server Errors

INTERNAL_ERROR

{
  "error": {
    "code": "INTERNAL_ERROR",
    "message": "An unexpected error occurred"
  }
}

Handling Errors

Example: JavaScript

async function createIncident(data) {
  const response = await fetch('/api/v1/incidents', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer sk_live_xxx',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(data)
  });
 
  if (!response.ok) {
    const error = await response.json();
 
    switch (error.error.code) {
      case 'VALIDATION_ERROR':
        console.error('Invalid input:', error.error.details);
        break;
      case 'UNAUTHORIZED':
        // Redirect to login or refresh token
        break;
      case 'RATE_LIMIT_EXCEEDED':
        // Wait and retry
        const retryAfter = error.error.details.retryAfter;
        await new Promise(r => setTimeout(r, retryAfter * 1000));
        return createIncident(data);
      default:
        throw new Error(error.error.message);
    }
  }
 
  return response.json();
}

Example: Python

import requests
import time
 
def create_incident(data):
    response = requests.post(
        'https://api.example.com/api/v1/incidents',
        headers={'Authorization': 'Bearer sk_live_xxx'},
        json=data
    )
 
    if response.status_code == 429:
        retry_after = response.json()['error']['details']['retryAfter']
        time.sleep(retry_after)
        return create_incident(data)
 
    if response.status_code >= 400:
        error = response.json()['error']
        raise Exception(f"{error['code']}: {error['message']}")
 
    return response.json()

Retry Logic

When to Retry

StatusRetry?Strategy
429YesWait for retryAfter
500YesExponential backoff
502YesExponential backoff
503YesExponential backoff
504YesExponential backoff
400NoFix request
401NoCheck credentials
403NoCheck permissions
404NoCheck resource ID

Exponential Backoff

async function retryWithBackoff(fn, maxRetries = 3) {
  let lastError;
 
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (error) {
      lastError = error;
 
      if (i < maxRetries - 1) {
        const delay = Math.pow(2, i) * 1000; // 1s, 2s, 4s
        await new Promise(r => setTimeout(r, delay));
      }
    }
  }
 
  throw lastError;
}

Request IDs

Every response includes a request ID header:

X-Request-Id: req_abc123xyz

Include this when contacting support for faster debugging.

Best Practices

  1. Always check status codes before parsing response
  2. Handle rate limits gracefully with retry logic
  3. Log error details for debugging
  4. Use request IDs when reporting issues
  5. Validate input before sending to reduce errors