HTTP Status Codes
| Status Code | Meaning | Description |
|---|---|---|
200 | OK | Request succeeded |
201 | Created | Resource was successfully created |
202 | Accepted | Request accepted for async processing |
204 | No Content | Request succeeded with no response body |
400 | Bad Request | Invalid request data or parameters |
401 | Unauthorized | Missing API key, empty Bearer token, or a malformed or invalid API key |
403 | Forbidden | Insufficient permissions, or the API key lacks the scope the call needs |
404 | Not Found | Resource doesn’t exist |
422 | Unprocessable Entity | The request was well-formed but could not be processed. Read error_code for the specific reason |
429 | Too Many Requests | Rate limit exceeded. Check the Retry-After header |
500 | Internal Server Error | Unexpected server error |
502 | Bad Gateway | Upstream service error |
503 | Service Unavailable | Service temporarily unavailable |
504 | Gateway Timeout | Backend did not respond in time |
Error Response Format
Every error response follows one JSON structure:{
"error": "Brief, human-readable error description",
"detail": "Detailed explanation of what went wrong",
"error_code": "MACHINE_READABLE_ERROR_CODE",
"retryable": false,
"request_id": "a1b2c3d4e5f67890abcdef1234567890"
}
| Field | Description |
|---|---|
error | Short, human-readable summary |
detail | Detailed explanation for debugging. A validation error sends an array here |
error_code | Machine-readable code for programmatic handling |
retryable | Whether the client should retry this request |
request_id | Unique request identifier for debugging and support |
Error Codes Reference
The status column is the registry default; a route may return a different status for the same code via a per-call override.| Error Code | Default HTTP Status | Retryable | Description |
|---|---|---|---|
ACCESS_DENIED | 403 | No | You do not have access to this resource. |
AGENTIC_MULTIFILE_UNSUPPORTED | 409 | No | Agentic extraction cannot process a multi-file document. |
ASSISTANT_SPEND_LIMIT_REACHED | 429 | No | The organization has reached its daily spend limit for the assistant. |
ATTACHMENT_LIMIT_EXCEEDED | 422 | No | This would put too many attached files on the conversation. |
AUTH_FAILED | 401 | No | Authentication failed. |
CONCURRENT_EDIT | 409 | Yes | Someone else changed this at the same time. Reload it and try again. |
DATASET_DOCUMENT_REJECTED | 422 | No | A file in the dataset document was rejected. |
DATASET_GROUND_TRUTH_SAVE_FAILED | 422 | No | The ground truth could not be saved. |
DATASET_INVALID_REQUEST | 422 | No | The dataset registration request is not valid. |
EMPTY_EVAL | 422 | No | The eval has no rows to run. |
ENDPOINT_RETIRED | 410 | No | This endpoint has been retired. Use the successor named in the Link header. |
EXPORT_EXPIRED | 410 | No | This export has expired. Run it again to get a fresh archive. |
EXTRACT_MODE_FORBIDDEN | 403 | No | This organization cannot use that extract mode. |
EXTRACT_NODE_UNRESOLVED | 422 | No | The request does not say which extract node to use. |
FILENAME_CONFLICT | 409 | No | A file with that name already exists. |
FILE_NOT_IN_DATASET | 409 | No | The ground truth cites a file the dataset does not hold. |
FILE_TOO_LARGE | 413 | No | The file is larger than the maximum allowed size. |
GATEWAY_TIMEOUT | 504 | Yes | The upstream service did not answer in time. |
GRAPH_READBACK_FAILED | 502 | No | The workflow was saved and its graph could not be read back. |
GT_INVALID | 422 | No | The ground truth is not valid. |
IDEMPOTENCY_KEY_REUSED | 422 | No | The idempotency key was already used with a different request. |
IMMUTABLE_WORKFLOW | 409 | No | This is a system workflow and it cannot change. |
INSUFFICIENT_CREDIT | 402 | No | The organization has no extraction credit left. |
INTEGRATION_NOT_CONNECTED | 404 | No | The organization has no active integration. |
INTERNAL_ERROR | 500 | Yes | An internal error stopped the request. |
INTERNAL_INCONSISTENCY | 400 | No | The server found an internal inconsistency. |
INVALID_API_KEY | 401 | No | The API key is not valid. |
INVALID_JSON | 400 | No | The request body is not valid JSON. |
INVALID_LOOKUP_FILE_ENCODING | 400 | No | The lookup file uses an encoding the server cannot read. |
INVALID_STATE | 400 | No | The state value is not valid or it expired. |
INVALID_VALUE | 400 | No | A value in the request is not valid. |
INVALID_WORKFLOW_CONFIG | 400 | No | The workflow holds a node configuration that is not valid. |
JOIN_REQUEST_CONFLICT | 409 | No | A join request for this organization already exists. |
KNOWLEDGE_NOT_ENABLED | 409 | No | The knowledge base is not enabled. |
KNOWLEDGE_NOT_READY | 409 | Yes | The knowledge base is not ready yet. |
KNOWLEDGE_THREAD_MISMATCH | 409 | No | The thread belongs to another workflow. |
KNOWLEDGE_UNSUPPORTED_SNAPSHOT_FORMAT | 409 | No | The knowledge base’s snapshot format is not supported. |
LOOKUP_FIELD_WITHOUT_FILE | 400 | No | A lookup field has no lookup file. |
MEMBERSHIP_CONFLICT | 409 | No | The user is already a member of this organization. |
METHOD_NOT_ALLOWED | 405 | No | The HTTP method is not allowed for this resource. |
MISSING_API_KEY | 401 | No | The request carries no API key. |
NOT_ACCEPTABLE | 406 | No | No representation matches the request’s Accept header. |
NOT_ASSIGNED_VERIFIER | 403 | No | Another user is the assigned verifier for this file. |
NOT_FOUND | 404 | No | The resource does not exist. |
NOT_IMPLEMENTED | 501 | No | This endpoint is not implemented. |
NOT_IN_DATASET | 409 | No | The document is not in the workflow’s dataset. |
NO_ACTIVE_ORGANIZATION | 400 | No | No active organization selected. |
OBJECT_STORAGE_UNAVAILABLE | 502 | Yes | Object storage did not complete the request. |
OPERATOR_DEPRECATED | 400 | No | This workflow version uses a deprecated operator and cannot run. |
OPERATOR_NOT_AVAILABLE | 400 | No | This workflow uses an operator that is no longer available. |
OPTIMIZATION_ALREADY_RUNNING | 409 | Yes | An optimization is already running for this workflow. |
PARSE_ERROR | 400 | No | The server could not parse the request. |
PARSE_MODE_FORBIDDEN | 403 | No | This organization cannot use that parse mode. |
PASSWORD_VALIDATION_FAILED | 400 | No | The password does not meet the requirements. |
PAYMENT_GATEWAY_ERROR | 502 | Yes | The payment gateway did not complete the request. |
PAYMENT_REQUIRED | 402 | No | Payment is required to continue. |
PERMANENT_REQUIRES_ANYFORMAT_OWNER | 400 | No | Permanent debug is only available to an organization an AnyFormat user owns. |
PRECONDITION_FAILED | 412 | Yes | The results are not available yet. |
RATE_LIMITED | 429 | Yes | Too many requests. |
REQUEST_TOO_LARGE | 400 | No | The request body is larger than the maximum allowed size. |
RUN_IN_PROGRESS | 409 | Yes | A run is still in progress. Wait for it to finish, then delete again. |
RUN_NOT_FINISHED | 409 | Yes | The run has not finished yet. |
SLACK_CHANNELS_FAILED | 502 | Yes | Slack did not return the channels. |
SLACK_CHANNEL_INACCESSIBLE | 502 | No | The Slack channel is not accessible. |
SLACK_EXCHANGE_FAILED | 400 | No | The Slack token exchange failed. |
SLACK_OAUTH_UNCONFIGURED | 503 | No | Slack is not configured on this deployment. |
SLACK_TEST_DELIVERY_FAILED | 502 | Yes | Slack did not accept the test message. |
SPLIT_HAS_NO_PAGES | 409 | No | The split holds no pages. |
SPLIT_SOURCE_NOT_PDF | 409 | No | The split’s source document is not a PDF. |
SPLIT_WORKFLOW_DATASET_UNSUPPORTED | 409 | No | Split workflows do not support datasets yet. |
SUBSCRIPTION_ALREADY_ACTIVE | 409 | No | The organization already has an active subscription. |
TAG_NAME_CONFLICT | 409 | No | A tag with that name already exists. |
TOPOLOGY_INVALID | 400 | No | The workflow topology is not valid. |
UNKNOWN_FILTER_PARAM | 422 | No | The request has an unknown filter parameter. |
UNSUPPORTED_FILE_TYPE | 400 | No | The file type is not supported. |
UNSUPPORTED_LOOKUP_FILE | 400 | No | The lookup file type is not supported. |
UNSUPPORTED_MEDIA_TYPE | 415 | No | The request media type is not supported. |
UPLOAD_SNIFF_BUDGET_EXCEEDED | 503 | Yes | The server could not check the uploaded files in time. |
URL_UNREACHABLE | 422 | No | The URL’s hostname could not be resolved. |
VALIDATION_ERROR | 400 | No | The request failed validation. |
WEBHOOK_SUBSCRIPTION_LIMIT | 429 | No | The organization has reached its webhook subscription limit. |
WORKFLOW_CONCURRENT_EDIT | 503 | No | Another request is editing this workflow. |
WORKFLOW_NAME_CONFLICT | 409 | No | A workflow with that name already exists. |
ZERO_DATA_RETENTION_REFUSES_LANGFUSE_DEBUG | 400 | No | This organization keeps no data, so debug capture cannot start. |
deprecated_operator_ack_required | 409 | No | This workflow uses a deprecated operator, so the save needs an acknowledgement. |
insufficient_api_key_scope | 403 | No | The API key does not carry the scope this call needs. |
stripe_customer_not_provisioned | 409 | No | The organization has no billing account yet. |
terms_acceptance_required | 403 | No | You must accept the terms of service to continue. |
too_many_in_request | 409 | No | The request names more items than one call accepts. |
voucher_already_redeemed_by_user | 400 | No | You already redeemed this voucher. |
voucher_expired | 400 | No | This voucher expired or it reached its usage limit. |
voucher_no_membership | 403 | No | You are not a member of this organization. |
voucher_not_found | 400 | No | That voucher code does not exist. |
voucher_only_for_new_orgs | 400 | No | This voucher only applies to a new organization. |
Authentication Errors
AUTH_FAILED (401). The API key is invalid.{
"error": "Authentication failed",
"detail": "Invalid or missing authentication credentials.",
"error_code": "AUTH_FAILED",
"retryable": false,
"request_id": "a1b2c3d4e5f67890abcdef1234567890"
}
Authorization header, or its Bearer token is empty.
{
"error": "The request carries no API key.",
"detail": "No API key was provided. Send it as 'Authorization: Bearer <your-api-key>'. Get a key at https://app.anyformat.ai/api-key.",
"error_code": "MISSING_API_KEY",
"retryable": false,
"request_id": "a1b2c3d4e5f67890abcdef1234567890"
}
{
"error": "You do not have access to this resource.",
"detail": "You do not have permission to perform this action.",
"error_code": "ACCESS_DENIED",
"retryable": false,
"request_id": "a1b2c3d4e5f67890abcdef1234567890"
}
Validation Errors
VALIDATION_ERROR (400). Thedetail field holds an array of validation error objects.
{
"error": "Validation failed",
"detail": [
{"type": "missing", "loc": ["body", "name"], "msg": "Field required"},
{"type": "missing", "loc": ["body", "fields"], "msg": "Field required"}
],
"error_code": "VALIDATION_ERROR",
"retryable": false,
"request_id": "a1b2c3d4e5f67890abcdef1234567890"
}
{
"error": "Invalid JSON format",
"detail": "The request body contains invalid JSON. Please check your JSON syntax.",
"error_code": "INVALID_JSON",
"retryable": false,
"request_id": "a1b2c3d4e5f67890abcdef1234567890"
}
Resource Errors
NOT_FOUND (404){
"error": "Resource not found",
"detail": "The requested resource could not be found.",
"error_code": "NOT_FOUND",
"retryable": false,
"request_id": "a1b2c3d4e5f67890abcdef1234567890"
}
Rate Limiting
RATE_LIMITED (429). The response carries aRetry-After header.
{
"error": "Rate limited",
"detail": "Rate limit exceeded. Try again in 12s.",
"error_code": "RATE_LIMITED",
"retryable": true,
"request_id": "a1b2c3d4e5f67890abcdef1234567890"
}
Retry-After gives the number of seconds to wait before retrying, and appears only on a 429 response. Every successful response carries x-ratelimit-limit, x-ratelimit-remaining and x-ratelimit-reset for the tier that applies to that endpoint, extraction or general.
Server Errors
INTERNAL_ERROR (500, or 502/503 when the gateway passes an unstructured upstream failure through with its status){
"error": "Internal server error",
"detail": "An unexpected error occurred while processing your request. Please try again or contact support.",
"error_code": "INTERNAL_ERROR",
"retryable": true,
"request_id": "a1b2c3d4e5f67890abcdef1234567890"
}
{
"error": "Gateway timeout",
"detail": "The backend did not respond in time. Please try again.",
"error_code": "GATEWAY_TIMEOUT",
"retryable": true,
"request_id": "a1b2c3d4e5f67890abcdef1234567890"
}
request_id when you contact support; it is the sole correlation handle for an error.
Error Handling Best Practices
1. Check HTTP Status Codes
import requests
response = requests.get(
"https://api.anyformat.ai/v3/workflows/",
headers={"Authorization": "Bearer your-api-key"}
)
if response.status_code == 200:
workflows = response.json()
elif response.status_code == 401:
print("Authentication failed. Check that you send a valid API key.")
elif response.status_code == 403:
print("Access denied. The API key has no access to this resource.")
elif response.status_code == 404:
print("Resource not found.")
elif response.status_code == 429:
retry_after = response.headers.get("Retry-After", "5")
print(f"Rate limited. Retry after {retry_after}s.")
elif response.status_code >= 500:
print("Server error. Retry with backoff.")
else:
error = response.json()
print(f"Error: {error.get('detail')}")
2. Use Error Codes for Logic
def handle_api_error(response):
if response.status_code >= 400:
error_data = response.json()
error_code = error_data.get('error_code')
if error_code in ('MISSING_API_KEY', 'INVALID_API_KEY', 'AUTH_FAILED'):
print("Missing or invalid API key.")
elif error_code == 'ACCESS_DENIED':
print("The API key has no access to this resource.")
elif error_code == 'VALIDATION_ERROR':
show_validation_error(error_data.get('detail'))
elif error_code == 'NOT_FOUND':
handle_missing_resource()
elif error_code == 'RATE_LIMITED':
retry_after = int(response.headers.get('Retry-After', 5))
time.sleep(retry_after)
return retry_request()
elif error_code == 'INTERNAL_ERROR':
retry_with_backoff()
elif error_code == 'GATEWAY_TIMEOUT':
retry_with_backoff()
else:
log_unexpected_error(error_data)
3. Implement Retry Logic
Use exponential backoff for the retryable errors: 5xx and 429.import time
import random
def api_request_with_retry(url, headers, max_retries=3):
for attempt in range(max_retries + 1):
try:
response = requests.get(url, headers=headers)
# Don't retry non-retryable client errors
if 400 <= response.status_code < 500 and response.status_code != 429:
return response
# Handle rate limiting
if response.status_code == 429:
delay = int(response.headers.get('Retry-After', 5))
time.sleep(delay)
continue
# Retry server errors (5xx)
if response.status_code >= 500 and attempt < max_retries:
delay = (2 ** attempt) + random.uniform(0, 1)
time.sleep(delay)
continue
return response
except requests.exceptions.RequestException as e:
if attempt < max_retries:
delay = (2 ** attempt) + random.uniform(0, 1)
time.sleep(delay)
continue
raise e
4. Validate Before Sending
def create_workflow(name, description, nodes, edges):
# Validate required fields locally first
if not name:
raise ValueError("Workflow name is required")
if not nodes:
raise ValueError("At least one node is required")
node_ids = {node.get("id") for node in nodes}
for edge in edges:
if edge.get("source") not in node_ids or edge.get("target") not in node_ids:
raise ValueError("Every edge must connect two declared nodes")
# Make API request
response = requests.post(
"https://api.anyformat.ai/v3/workflows/",
headers={"Authorization": "Bearer your-api-key", "Content-Type": "application/json"},
json={"name": name, "description": description, "nodes": nodes, "edges": edges}
)
return handle_response(response)
5. Log Errors for Debugging
import logging
def log_api_error(response, context=""):
# Log codes and ids only: `detail` and the raw body can echo document content.
try:
error_data = response.json()
error_code = error_data.get('error_code', 'UNKNOWN')
request_id = error_data.get('request_id', 'N/A')
except ValueError:
error_code, request_id = 'NON_JSON_RESPONSE', 'N/A'
logging.error(
f"API Error {response.status_code}: {error_code} "
f"[Context: {context}] [Request: {request_id}]"
)
Testing Error Scenarios
When building your integration, test these common scenarios:| Scenario | How to Test |
|---|---|
| Missing API key | Omit the Authorization header |
| Invalid API key | Use a fake or expired key |
| Missing required fields | Create a workflow without name or nodes |
| Invalid JSON | Send malformed JSON in request body |
| Nonexistent resource | Request a workflow or document packet ID that does not exist |
| Run not finished | Poll GET /v3/runs/{run_id}/ while processing runs, and expect 200 with a non-terminal status |
| Rate limit exceeded | Send rapid requests to trigger 429 with a Retry-After header |
| Server errors | Test the retry logic against mocked 500 responses |

