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

# Error Handling

> Handle API errors gracefully and implement robust error recovery

## Error Response Format

All Xerobi APIs return errors in a consistent JSON format to make error handling predictable.

## Standard Error Structure

```json theme={null}
{
  "success": false,
  "error": {
    "code": "ERROR_CODE",
    "message": "Human-readable error message",
    "details": {
      // Additional error-specific information
    }
  }
}
```

## Common Error Codes

### Authentication Errors

| Error Code | HTTP Status | Description | Solution |
| - | - | - | - |
| `INVALID_API_KEY` | 401 | API key is invalid or malformed | Check your API key in dashboard |
| `MISSING_AUTH_HEADER` | 401 | Authorization header missing | Include `Authorization: Bearer YOUR_KEY` |
| `INSUFFICIENT_PERMISSIONS` | 403 | API key lacks required permissions | Create key with proper permissions |
| `EXPIRED_API_KEY` | 401 | API key has expired | Generate new key in dashboard |
| `INVALID_VERSION` | 400 | API version not supported | Use `Xerobi-Version: 2026-01-01` |

### Request Errors

| Error Code | HTTP Status | Description | Solution |
| - | - | - | - |
| `INVALID_REQUEST` | 400 | Request malformed or missing required fields | Check request format and required fields |
| `VALIDATION_ERROR` | 400 | Field validation failed | Review field types and constraints |
| `RATE_LIMIT_EXCEEDED` | 429 | Too many requests | Implement exponential backoff |
| `RESOURCE_NOT_FOUND` | 404 | Requested resource doesn't exist | Verify resource ID and endpoint |
| `METHOD_NOT_ALLOWED` | 405 | HTTP method not supported | Use correct HTTP method |

### Payment-Specific Errors

| Error Code | HTTP Status | Description | Solution |
| - | - | - | - |
| `CARD_DECLINED` | 402 | Payment method was declined | Try different payment method |
| `INSUFFICIENT_FUNDS` | 402 | Account has insufficient funds | Add funds or use different method |
| `PAYMENT_INTENT_EXPIRED` | 400 | Payment intent has expired | Create new payment intent |
| `AMOUNT_TOO_LARGE` | 400 | Amount exceeds maximum allowed | Reduce amount or contact support |

### SMS-Specific Errors

| Error Code | HTTP Status | Description | Solution |
| - | - | - | - |
| `INSUFFICIENT_BALANCE` | 402 | SMS balance too low | Top up account balance |
| `INVALID_RECIPIENT` | 400 | Phone number format invalid | Check phone number format |
| `SENDER_ID_UNAVAILABLE` | 400 | Sender ID not approved | Request sender ID approval |
| `MESSAGE_TOO_LONG` | 400 | SMS exceeds character limit | Shorten message or use multiple parts |

### Certification Errors

| Error Code | HTTP Status | Description | Solution |
| - | - | - | - |
| `CREDENTIAL_ALREADY_EXISTS` | 409 | Credential ID already in use | Use unique credential identifier |
| `INVALID_CREDENTIAL_DATA` | 400 | Credential data malformed | Validate credential format |
| `VERIFICATION_FAILED` | 400 | Credential verification failed | Check credential authenticity |

## Implementing Error Handling

### Basic Error Handling

```javascript JavaScript theme={null}
async function makeApiCall(endpoint, data) {
  try {
    const response = await fetch(`https://api.hixbe.com${endpoint}`, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${process.env.HIXBE_API_KEY}`,
        'Xerobi-Version': '2026-01-01',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(data)
    });

    const result = await response.json();

    if (!result.success) {
      throw new HixbeError(result.error);
    }

    return result.data;
  } catch (error) {
    if (error instanceof HixbeError) {
      handleHixbeError(error);
    } else {
      handleNetworkError(error);
    }
  }
}

class HixbeError extends Error {
  constructor(error) {
    super(error.message);
    this.code = error.code;
    this.details = error.details;
  }
}
```

```python Python theme={null}
import requests
from requests.exceptions import RequestException

class HixbeClient:
    def __init__(self, api_key):
        self.api_key = api_key
        self.base_url = "https://api.hixbe.com"

    def _request(self, method, endpoint, data=None):
        headers = {
            'Authorization': f'Bearer {self.api_key}',
            'Xerobi-Version': '2026-01-01',
            'Content-Type': 'application/json'
        }

        try:
            response = requests.request(
                method,
                f"{self.base_url}{endpoint}",
                json=data,
                headers=headers
            )

            result = response.json()

            if not result.get('success', True):
                raise HixbeError(result['error'])

            return result.get('data')

        except RequestException as e:
            raise NetworkError(f"Network error: {e}")
        except ValueError as e:
            raise ParseError(f"Failed to parse response: {e}")

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

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

class HixbeClient {
    private $apiKey;
    private $baseUrl = 'https://api.hixbe.com';

    public function __construct($apiKey) {
        $this->apiKey = $apiKey;
    }

    public function request($method, $endpoint, $data = null) {
        $ch = curl_init();

        curl_setopt($ch, CURLOPT_URL, $this->baseUrl . $endpoint);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'Authorization: Bearer ' . $this->apiKey,
            'Xerobi-Version: 2026-01-01',
            'Content-Type: application/json'
        ]);

        if ($method === 'POST' && $data) {
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
        }

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);

        if (curl_error($ch)) {
            throw new NetworkException('Network error: ' . curl_error($ch));
        }

        curl_close($ch);

        $result = json_decode($response, true);

        if (json_last_error() !== JSON_ERROR_NONE) {
            throw new ParseException('Failed to parse JSON response');
        }

        if (!$result['success']) {
            throw new HixbeException($result['error']);
        }

        return $result['data'];
    }
}

class HixbeException extends Exception {
    public $errorCode;
    public $errorDetails;

    public function __construct($error) {
        parent::__construct($error['message']);
        $this->errorCode = $error['code'];
        $this->errorDetails = $error['details'] ?? [];
    }
}
?>
```

### Error-Specific Handling

```javascript theme={null}
function handleHixbeError(error) {
  switch (error.code) {
    case 'RATE_LIMIT_EXCEEDED':
      // Implement exponential backoff
      return retryWithBackoff();

    case 'INVALID_API_KEY':
      // Log security issue and alert admin
      console.error('Invalid API key used');
      throw new SecurityException('API key compromised');

    case 'CARD_DECLINED':
      // Handle payment failure
      notifyUser('Payment was declined. Please try a different card.');
      break;

    case 'INSUFFICIENT_BALANCE':
      // Handle SMS balance issue
      alertAdmin('SMS balance running low');
      break;

    default:
      // Log unknown errors for investigation
      console.error('Unknown error:', error);
      throw error;
  }
}
```

## Retry Logic and Backoff

### Exponential Backoff Implementation

```javascript theme={null}
class RetryHandler {
  constructor(maxRetries = 3, baseDelay = 1000) {
    this.maxRetries = maxRetries;
    this.baseDelay = baseDelay;
  }

  async executeWithRetry(operation) {
    let lastError;

    for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
      try {
        return await operation();
      } catch (error) {
        lastError = error;

        if (!this.shouldRetry(error) || attempt === this.maxRetries) {
          throw error;
        }

        const delay = this.calculateDelay(attempt);
        await this.sleep(delay);
      }
    }

    throw lastError;
  }

  shouldRetry(error) {
    // Retry on network errors and server errors
    const retryableCodes = ['RATE_LIMIT_EXCEEDED', 'INTERNAL_SERVER_ERROR'];

    return error.code && retryableCodes.includes(error.code);
  }

  calculateDelay(attempt) {
    // Exponential backoff with jitter
    const exponentialDelay = this.baseDelay * Math.pow(2, attempt);
    const jitter = Math.random() * 0.1 * exponentialDelay;

    return exponentialDelay + jitter;
  }

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

// Usage
const retryHandler = new RetryHandler();

const result = await retryHandler.executeWithRetry(async () => {
  return await makeApiCall('/payment/payment_intents', paymentData);
});
```

## Idempotency for Critical Operations

### Implementing Idempotency

```javascript theme={null}
async function createPaymentIntent(data, idempotencyKey) {
  const response = await fetch('https://api.hixbe.com/payment/payment_intents', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.HIXBE_API_KEY}`,
      'Xerobi-Version': '2026-01-01',
      'Content-Type': 'application/json',
      'Idempotency-Key': idempotencyKey  // Include idempotency key
    },
    body: JSON.stringify(data)
  });

  return response.json();
}

// Generate unique idempotency keys
function generateIdempotencyKey() {
  return `payment_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}

// Safe retry with idempotency
async function createPaymentSafely(paymentData) {
  const idempotencyKey = generateIdempotencyKey();

  try {
    return await createPaymentIntent(paymentData, idempotencyKey);
  } catch (error) {
    if (error.code === 'RATE_LIMIT_EXCEEDED') {
      // Safe to retry with same key
      await sleep(2000);
      return await createPaymentIntent(paymentData, idempotencyKey);
    }
    throw error;
  }
}
```

## Rate Limiting

### Understanding Rate Limits

Xerobi implements rate limiting to ensure fair usage:

* **Payment API**: 100 requests per minute per API key
* **SMS API**: 50 requests per minute per API key
* **Certification API**: 200 requests per minute per API key

### Rate Limit Headers

```http theme={null}
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1640995260
X-RateLimit-Retry-After: 60
```

### Handling Rate Limits

```javascript theme={null}
async function rateLimitedRequest(endpoint, options = {}) {
  const response = await fetch(`https://api.hixbe.com${endpoint}`, options);

  if (response.status === 429) {
    const retryAfter = response.headers.get('X-RateLimit-Retry-After');

    if (retryAfter) {
      await sleep(parseInt(retryAfter) * 1000);
      return rateLimitedRequest(endpoint, options); // Retry once
    }
  }

  return response;
}
```

## Monitoring and Alerting

### Error Tracking

```javascript theme={null}
class ErrorTracker {
  constructor() {
    this.errors = new Map();
  }

  trackError(error) {
    const key = `${error.code}_${Date.now()}`;
    this.errors.set(key, {
      error,
      timestamp: new Date(),
      stack: error.stack
    });

    // Alert on critical errors
    if (this.isCriticalError(error)) {
      this.alertAdmin(error);
    }

    // Clean old errors (keep last 24 hours)
    this.cleanup();
  }

  isCriticalError(error) {
    const criticalCodes = ['INVALID_API_KEY', 'INTERNAL_SERVER_ERROR'];
    return criticalCodes.includes(error.code);
  }

  alertAdmin(error) {
    // Send alert to admin (email, Slack, etc.)
    console.error('CRITICAL ERROR:', error);
  }

  cleanup() {
    const oneDayAgo = Date.now() - (24 * 60 * 60 * 1000);

    for (const [key, data] of this.errors) {
      if (data.timestamp.getTime() < oneDayAgo) {
        this.errors.delete(key);
      }
    }
  }
}
```

## Best Practices

* Always check the `success` field in responses
* Implement proper error logging and monitoring
* Use exponential backoff for retries
* Implement idempotency for critical operations
* Monitor rate limit headers
* Handle both expected and unexpected errors
* Test error scenarios in sandbox
* Keep error messages user-friendly

## Need Help?

> [card] [Troubleshooting](https://docs.xerobi.com/faq-guides/troubleshooting)
>
> Common error scenarios and solutions

> [card] [Best Practices](https://docs.xerobi.com/faq-guides/best-practices.md)
>
> Learn more integration best practices

> [card] [Webhooks](https://docs.xerobi.com/faq-guides/webhooks.md)
>
> Monitor errors with webhook notifications

> [card] [Support](mailto:support@xerobi.com)
>
> Get help with error handling


## Related topics

- [Xerobi Certification & Badge API Introduction](https://docs.xerobi.com/certify/introduction.md)
- [Xerobi Payment API Introduction](https://docs.xerobi.com/payment-api/introduction.md)
- [Quickstart Guide](https://docs.xerobi.com/faq-guides/quickstart.md)
- [Webhooks](https://docs.xerobi.com/faq-guides/webhooks.md)
- [Best Practices](https://docs.xerobi.com/faq-guides/best-practices.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.