Errors
All API errors return a consistent JSON response body. There are two error formats depending on the error source.
Validation errors
Returned when the document definition has structural issues — invalid JSON, unrecognized properties, or type mismatches. These are caught before any PDF generation begins.
{
"code": "SCHEMA_ERROR",
"path": "$.document.content[0].tabel",
"message": "Invalid value or unrecognized property at '$.document.content[0].tabel'."
}
| Field | Type | Description |
|---|---|---|
code | string | Machine-readable error code |
path | string | JSON path to the problematic property |
message | string | Human-readable description |
Validation error codes
| Code | Description |
|---|---|
INVALID_JSON | Request body is not valid JSON |
MISSING_ROOT | Missing document root property and no content array to auto-wrap |
INVALID_DEFINITION | Document definition is null after deserialization |
EMPTY_CONTENT | Content array is present but empty |
SCHEMA_ERROR | Unrecognized property name or type mismatch at the given path |
Examples
Typo in property name:
// Request: { "document": { "content": [{ "tabel": {} }] } }
{
"code": "SCHEMA_ERROR",
"path": "$.document.content[0].tabel",
"message": "Invalid value or unrecognized property at '$.document.content[0].tabel'."
}
Wrong type:
// Request: { "document": { "pageSetup": { "margins": "50" }, "content": [{ "p": "Hi" }] } }
{
"code": "SCHEMA_ERROR",
"path": "$.document.pageSetup.margins",
"message": "Invalid value or unrecognized property at '$.document.pageSetup.margins'."
}
Not JSON:
{
"code": "INVALID_JSON",
"path": "$",
"message": "Content does not appear to be JSON."
}
Empty content:
{
"code": "EMPTY_CONTENT",
"path": "document.content",
"message": "Content array must contain at least one element."
}
Auto-wrapping
If you send a bare document definition without the document wrapper, the API will wrap it for you automatically:
// This works:
{ "content": [{ "p": "Hello" }] }
// The API treats it as:
{ "document": { "content": [{ "p": "Hello" }] } }
If neither document nor content is found at the root, the API returns a MISSING_ROOT error.
Server errors
Returned by the global exception handler for infrastructure, authorization, and runtime errors. Uses the RFC 7807 Problem Details format.
{
"status": 503,
"title": "Service Unavailable",
"detail": "Database service is temporarily unavailable.",
"instance": "/api/v1/tenants/{tenantId}/documents/generate-from-payload",
"traceId": "00-abc123def456-789012-00",
"errorCode": "DATABASE_UNAVAILABLE",
"isRetryable": true,
"retryAfterSeconds": 30
}
| Field | Type | Description |
|---|---|---|
status | integer | HTTP status code |
title | string | Standard HTTP reason phrase |
detail | string | Human-readable description |
instance | string | Request path that triggered the error |
traceId | string | Unique trace ID for support requests |
errorCode | string | Machine-readable error code |
isRetryable | boolean | Whether the request can be retried |
retryAfterSeconds | integer | Seconds to wait before retrying (when retryable) |
HTTP status codes
| Status | When | Retryable |
|---|---|---|
| 400 | Invalid JSON, schema errors, empty content, bad request parameters | No |
| 401 | Missing or invalid API key | No |
| 403 | Insufficient permissions or tenant mismatch | No |
| 404 | Template or document not found | No |
| 429 | Rate limit exceeded | Yes — check X-RateLimit-Reset header |
| 500 | PDF generation failure or unexpected server error | Maybe — retry once |
| 503 | Database, storage, or external service temporarily unavailable | Yes — check retryAfterSeconds |
Server error codes
| Code | Status | Description |
|---|---|---|
VALIDATION_ERROR | 400 | Invalid request parameters |
INVALID_OPERATION | 400 | Operation not valid in current state |
ACCESS_DENIED | 401 | Authentication failed |
TENANT_CLAIM_MISSING | 403 | Tenant information missing from token |
TENANT_MISMATCH | 403 | Accessing a resource from a different tenant |
FILE_NOT_FOUND | 404 | Requested file does not exist |
REQUEST_CANCELLED | 400 | Client cancelled the request |
DATABASE_UNAVAILABLE | 503 | Database temporarily unavailable (retryable) |
AZURE_SERVICE_ERROR | 503 | Storage service temporarily unavailable (retryable) |
EXTERNAL_SERVICE_ERROR | 503 | External dependency temporarily unavailable (retryable) |
NETWORK_ERROR | 503 | Network connectivity issue (retryable) |
SERVICE_TIMEOUT | 503 | Request timed out |
INTERNAL_ERROR | 500 | Unexpected server error |
Rate limiting
Rate limit headers are included in every response:
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 57
X-RateLimit-Reset: 1711843200
| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum requests per minute for your plan |
X-RateLimit-Remaining | Requests remaining in the current window |
X-RateLimit-Reset | Unix timestamp when the rate limit resets |
Best practices
- Check the HTTP status code first before parsing the response body.
- Use
codefor programmatic handling — match on error codes, not message strings. - Use
pathto locate the issue in validation errors — it points to the exact JSON property. - Retry on 429 and 503 with backoff. Use
retryAfterSecondsorX-RateLimit-Resetto determine wait time. - Do not retry on 400, 401, 403, or 404 — fix the request first.
- Log
traceIdfor server errors — include it when contacting support. - Validate locally with the JSON Schema to catch errors before sending to the API.