# Error Handling

Verso API uses standard HTTP status codes and returns detailed error responses following the RFC 7807 Problem Details format. This guide explains how to handle errors effectively.

## Error Response Format

All error responses follow the Problem Details specification:

```json
{
  "type": "https://httpproblems.com/http-status/400",
  "title": "Bad Request",
  "status": 400,
  "detail": "Detailed description of what went wrong",
  "instance": "/payrolls/3/cases",
  "traceId": "00-abc123-def456-00"
}
```

| Field | Description |
|-------|-------------|
| `type` | URI reference identifying the error type |
| `title` | Short human-readable summary |
| `status` | HTTP status code |
| `detail` | Detailed explanation of the error |
| `instance` | URI of the specific request |
| `traceId` | Unique identifier for debugging |

## HTTP Status Codes

### 2xx Success

| Code | Description |
|------|-------------|
| `200 OK` | Request succeeded, response contains data |
| `201 Created` | Resource created successfully |
| `204 No Content` | Request succeeded, no response body |

### 4xx Client Errors

| Code | Title | Common Causes |
|------|-------|---------------|
| `400` | Bad Request | Invalid JSON, missing required fields, validation errors |
| `401` | Unauthorized | Missing or invalid API key |
| `403` | Forbidden | Insufficient permissions, tenant mismatch |
| `404` | Not Found | Resource doesn't exist |
| `409` | Conflict | Duplicate identifier, state conflict |
| `422` | Unprocessable Entity | Business logic validation failed |
| `429` | Too Many Requests | Too many requests in short period |

### 5xx Server Errors

| Code | Title | Description |
|------|-------|-------------|
| `500` | Internal Server Error | Unexpected server error |
| `502` | Bad Gateway | Upstream service unavailable |
| `503` | Service Unavailable | Server temporarily unavailable |
| `504` | Gateway Timeout | Upstream service timeout |

## Common Error Scenarios

### Validation Errors (400)

```json
{
  "type": "https://httpproblems.com/http-status/400",
  "title": "Bad Request",
  "status": 400,
  "detail": "The 'identifier' field is required",
  "errors": {
    "identifier": ["The identifier field is required."]
  }
}
```

**How to fix:**
- Check required fields in your request body
- Validate data types (strings, numbers, dates)
- Ensure JSON is properly formatted

### Authentication Errors (401)

```json
{
  "type": "https://httpproblems.com/http-status/401",
  "title": "Unauthorized",
  "status": 401,
  "detail": "Invalid or missing API key"
}
```

**How to fix:**
- Verify the `Authorization` header is present
- Check API key is correctly formatted: `Bearer YOUR_API_KEY`
- Confirm API key is not expired

### Permission Errors (403)

```json
{
  "type": "https://httpproblems.com/http-status/403",
  "title": "Forbidden",
  "status": 403,
  "detail": "Access denied to division 'Sales'"
}
```

**How to fix:**
- Verify you have access to the requested resource
- Check your API key permissions
- Ensure you're operating within your tenant scope

### Not Found (404)

```json
{
  "type": "https://httpproblems.com/http-status/404",
  "title": "Not Found",
  "status": 404,
  "detail": "Employee with id '999' not found"
}
```

**How to fix:**
- Verify the resource ID exists
- Check for typos in the endpoint path
- Ensure the resource belongs to your tenant

### Conflict (409)

```json
{
  "type": "https://httpproblems.com/http-status/409",
  "title": "Conflict",
  "status": 409,
  "detail": "Employee with identifier 'john@company.com' already exists"
}
```

**How to fix:**
- Use a unique identifier
- Query existing resources before creating
- Update existing resource instead of creating new

## Error Handling Best Practices

### Implement Retry Logic

```javascript title="Retry with Exponential Backoff"
async function fetchWithRetry(url, options, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      const response = await fetch(url, options);

      if (response.status === 429) {
        const retryAfter = response.headers.get('Retry-After') || 60;
        await sleep(retryAfter * 1000);
        continue;
      }

      if (response.status >= 500) {
        await sleep(Math.pow(2, attempt) * 1000);
        continue;
      }

      return response;
    } catch (error) {
      if (attempt === maxRetries - 1) throw error;
      await sleep(Math.pow(2, attempt) * 1000);
    }
  }
}
```

### Log Error Details

Always log the `traceId` for debugging:

```javascript title="Error Logging"
try {
  const response = await api.createEmployee(data);
} catch (error) {
  console.error('API Error:', {
    status: error.status,
    title: error.title,
    detail: error.detail,
    traceId: error.traceId, // Important for support
    instance: error.instance
  });
}
```

### Handle Validation Errors

```javascript title="Display Validation Errors"
if (response.status === 400 && response.errors) {
  Object.entries(response.errors).forEach(([field, messages]) => {
    console.error(`${field}: ${messages.join(', ')}`);
  });
}
```

## Debugging Tips

### Include Trace ID in Support Requests

When contacting support, always include:
- The `traceId` from the error response
- Timestamp of the request
- The endpoint called
- Request payload (without sensitive data)

### Validate Before Sending

Validate your payloads client-side before sending:

```javascript title="Pre-validation"
function validateEmployee(data) {
  const errors = [];

  if (!data.identifier) {
    errors.push('identifier is required');
  }
  if (!data.firstName) {
    errors.push('firstName is required');
  }
  if (!data.lastName) {
    errors.push('lastName is required');
  }

  return errors;
}
```

## Next Steps

- [Authentication](/authentication) - API authentication guide
- [Error Codes Reference](/reference/error-codes) - Complete error code list
- [API Reference](/api) - Explore all endpoints
