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

# Errors

> API error codes and response format

When an API request fails, the response includes an error object with a code and message.

## Error Response Format

```json theme={null}
{
  "error": {
    "code": "ERROR_CODE",
    "message": "Human-readable description of the error"
  }
}
```

Some errors include additional details:

```json theme={null}
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid request body",
    "details": {
      "field": "input.document.base64",
      "reason": "Invalid base64 encoding"
    }
  }
}
```

## HTTP Status Codes

| Status | Meaning               | Common Causes                         |
| ------ | --------------------- | ------------------------------------- |
| 400    | Bad Request           | Invalid JSON, missing required fields |
| 401    | Unauthorized          | Missing or invalid API key            |
| 402    | Payment Required      | Insufficient credits                  |
| 403    | Forbidden             | API key lacks required scope          |
| 404    | Not Found             | Flow or execution doesn't exist       |
| 409    | Conflict              | Execution already cancelled           |
| 422    | Unprocessable Entity  | Valid JSON but invalid data           |
| 429    | Too Many Requests     | Rate limit exceeded                   |
| 500    | Internal Server Error | Server-side error                     |
| 503    | Service Unavailable   | Maintenance or overload               |

## Error Codes Reference

### Authentication Errors

| Code              | Status | Description                             |
| ----------------- | ------ | --------------------------------------- |
| `UNAUTHORIZED`    | 401    | Missing or invalid authorization header |
| `INVALID_API_KEY` | 401    | API key is malformed or revoked         |
| `FORBIDDEN`       | 403    | API key missing required scope          |

**Example:**

```json theme={null}
{
  "error": {
    "code": "FORBIDDEN",
    "message": "API key missing required scope: flows:execute"
  }
}
```

**Fix:** Generate a new API key with the required scopes in the Dashboard.

### Validation Errors

| Code                     | Status | Description                               |
| ------------------------ | ------ | ----------------------------------------- |
| `INVALID_INPUT`          | 400    | Request body is invalid JSON              |
| `VALIDATION_ERROR`       | 422    | Request data fails validation             |
| `MISSING_REQUIRED_FIELD` | 422    | Required field not provided               |
| `INVALID_DOCUMENT`       | 422    | Document data is corrupted or unsupported |

**Example:**

```json theme={null}
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid document format",
    "details": {
      "field": "input.document.mimeType",
      "reason": "Unsupported format: image/svg+xml",
      "supported": ["application/pdf", "image/png", "image/jpeg", "image/webp"]
    }
  }
}
```

**Fix:** Check the request body matches the expected schema. Ensure documents are in supported formats.

### Resource Errors

| Code                  | Status | Description                               |
| --------------------- | ------ | ----------------------------------------- |
| `FLOW_NOT_FOUND`      | 404    | Flow ID doesn't exist or isn't accessible |
| `EXECUTION_NOT_FOUND` | 404    | Execution ID doesn't exist                |
| `VERSION_NOT_FOUND`   | 404    | Requested flow version doesn't exist      |

**Example:**

```json theme={null}
{
  "error": {
    "code": "FLOW_NOT_FOUND",
    "message": "Flow 'invoice-extractor' not found"
  }
}
```

**Fix:** Verify the flow ID is correct. Use `GET /flows` to list available flows.

### Billing Errors

| Code                    | Status | Description                           |
| ----------------------- | ------ | ------------------------------------- |
| `INSUFFICIENT_CREDITS`  | 402    | Not enough credits for this operation |
| `SUBSCRIPTION_REQUIRED` | 402    | Feature requires paid subscription    |

**Example:**

```json theme={null}
{
  "error": {
    "code": "INSUFFICIENT_CREDITS",
    "message": "Insufficient credits. Required: 10, Available: 3"
  }
}
```

**Fix:** Purchase more credits or upgrade your plan in the Dashboard.

### Execution Errors

| Code                | Status | Description                           |
| ------------------- | ------ | ------------------------------------- |
| `EXECUTION_FAILED`  | 500    | Flow execution failed                 |
| `EXECUTION_TIMEOUT` | 504    | Execution exceeded time limit         |
| `ALREADY_CANCELLED` | 409    | Execution was already cancelled       |
| `NOT_CANCELLABLE`   | 409    | Execution already completed or failed |

**Example:**

```json theme={null}
{
  "error": {
    "code": "ALREADY_CANCELLED",
    "message": "Execution exec_abc123 was already cancelled"
  }
}
```

### Rate Limit Errors

| Code               | Status | Description                    |
| ------------------ | ------ | ------------------------------ |
| `RATE_LIMITED`     | 429    | Too many requests              |
| `CONCURRENT_LIMIT` | 429    | Too many concurrent executions |

**Example:**

```json theme={null}
{
  "error": {
    "code": "RATE_LIMITED",
    "message": "Rate limit exceeded. Retry after 30 seconds"
  }
}
```

**Fix:** Implement exponential backoff. Check `X-RateLimit-Reset` header for when to retry.

### Server Errors

| Code                  | Status | Description                       |
| --------------------- | ------ | --------------------------------- |
| `INTERNAL_ERROR`      | 500    | Unexpected server error           |
| `SERVICE_UNAVAILABLE` | 503    | Service temporarily unavailable   |
| `PROVIDER_ERROR`      | 502    | Upstream provider (LLM/OCR) error |

**Example:**

```json theme={null}
{
  "error": {
    "code": "PROVIDER_ERROR",
    "message": "OCR provider temporarily unavailable"
  }
}
```

**Fix:** Retry with exponential backoff. If persistent, check the [status page](https://status.doclo.ai).

## Error Handling Best Practices

### Retry Logic

Implement retries with exponential backoff for transient errors:

```typescript theme={null}
async function callAPI(request: Request, maxRetries = 3): Promise<Response> {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const response = await fetch(request);

    if (response.ok) {
      return response;
    }

    const { error } = await response.json();

    // Don't retry client errors (except rate limits)
    if (response.status >= 400 && response.status < 500 && response.status !== 429) {
      throw new Error(error.message);
    }

    // Exponential backoff
    const delay = Math.pow(2, attempt) * 1000;
    await new Promise(resolve => setTimeout(resolve, delay));
  }

  throw new Error('Max retries exceeded');
}
```

### Idempotency

Use idempotency keys to safely retry failed requests:

```typescript theme={null}
const response = await fetch('https://app.doclo.ai/api/v1/flows/invoice/run', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${apiKey}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    idempotencyKey: `invoice-${documentId}-${Date.now()}`,
    input: { document: { base64: documentData } }
  })
});
```

### Error Logging

Log errors with context for debugging:

```typescript theme={null}
try {
  const result = await runFlow(flowId, input);
} catch (error) {
  console.error('Flow execution failed', {
    flowId,
    errorCode: error.code,
    errorMessage: error.message,
    requestId: error.requestId,
    timestamp: new Date().toISOString()
  });
}
```

## Next Steps

<CardGroup cols={2}>
  <Card title="Run Flow" icon="play" href="/api-reference/flows/run">
    Execute flows with proper error handling
  </Card>

  <Card title="SDK Client" icon="code" href="/cloud/client">
    Use the SDK for automatic retries
  </Card>
</CardGroup>
