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

Error Response Format

{
  "error": {
    "code": "ERROR_CODE",
    "message": "Human-readable description of the error"
  }
}
Some errors include additional details:
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid request body",
    "details": {
      "field": "input.document.base64",
      "reason": "Invalid base64 encoding"
    }
  }
}

HTTP Status Codes

StatusMeaningCommon Causes
400Bad RequestInvalid JSON, missing required fields
401UnauthorizedMissing or invalid API key
402Payment RequiredInsufficient credits
403ForbiddenAPI key lacks required scope
404Not FoundFlow or execution doesn’t exist
409ConflictExecution already cancelled
422Unprocessable EntityValid JSON but invalid data
429Too Many RequestsRate limit exceeded
500Internal Server ErrorServer-side error
503Service UnavailableMaintenance or overload

Error Codes Reference

Authentication Errors

CodeStatusDescription
UNAUTHORIZED401Missing or invalid authorization header
INVALID_API_KEY401API key is malformed or revoked
FORBIDDEN403API key missing required scope
Example:
{
  "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

CodeStatusDescription
INVALID_INPUT400Request body is invalid JSON
VALIDATION_ERROR422Request data fails validation
MISSING_REQUIRED_FIELD422Required field not provided
INVALID_DOCUMENT422Document data is corrupted or unsupported
Example:
{
  "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

CodeStatusDescription
FLOW_NOT_FOUND404Flow ID doesn’t exist or isn’t accessible
EXECUTION_NOT_FOUND404Execution ID doesn’t exist
VERSION_NOT_FOUND404Requested flow version doesn’t exist
Example:
{
  "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

CodeStatusDescription
INSUFFICIENT_CREDITS402Not enough credits for this operation
SUBSCRIPTION_REQUIRED402Feature requires paid subscription
Example:
{
  "error": {
    "code": "INSUFFICIENT_CREDITS",
    "message": "Insufficient credits. Required: 10, Available: 3"
  }
}
Fix: Purchase more credits or upgrade your plan in the Dashboard.

Execution Errors

CodeStatusDescription
EXECUTION_FAILED500Flow execution failed
EXECUTION_TIMEOUT504Execution exceeded time limit
ALREADY_CANCELLED409Execution was already cancelled
NOT_CANCELLABLE409Execution already completed or failed
Example:
{
  "error": {
    "code": "ALREADY_CANCELLED",
    "message": "Execution exec_abc123 was already cancelled"
  }
}

Rate Limit Errors

CodeStatusDescription
RATE_LIMITED429Too many requests
CONCURRENT_LIMIT429Too many concurrent executions
Example:
{
  "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

CodeStatusDescription
INTERNAL_ERROR500Unexpected server error
SERVICE_UNAVAILABLE503Service temporarily unavailable
PROVIDER_ERROR502Upstream provider (LLM/OCR) error
Example:
{
  "error": {
    "code": "PROVIDER_ERROR",
    "message": "OCR provider temporarily unavailable"
  }
}
Fix: Retry with exponential backoff. If persistent, check the status page.

Error Handling Best Practices

Retry Logic

Implement retries with exponential backoff for transient errors:
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:
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:
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