On this page
Errors
Every API error follows a single contract: a JSON object with at least an error key.
{ "error": "API key invalid or revoked." }
Exception: Laravel validation rejections (HTTP 422) answer {"message": …, "errors": {…}}, without an error key — see the Template required fields section.
HTTP codes
| Code | Meaning |
|---|---|
400 |
Declared JSON body but undecodable (non-UTF-8 encoding, invalid JSON) |
401 |
Missing key (Authorization: Bearer dfk_…) or invalid/revoked |
403 |
Feature not included in the plan (see not_in_plan below) |
404 |
Unknown template or folder — or outside the key's workspace (we never reveal the existence of a foreign resource) |
409 |
Conflict: a request with the same Idempotency-Key is still running (idempotency_in_progress) or PDF unavailable on GET /documents/{id}/pdf (pdf_draft, pdf_failed, pdf_missing) |
422 |
Invalid request (Laravel validation) or rejected business parameter |
429 |
Plan API quota reached (api_calls_per_month), active-batch cap or document quota pre-flight |
500 |
The PDF could not be generated (rare — retry or contact support) |
503 |
Synchronous render pool saturated (pdf_busy) — retry after Retry-After or switch to async |
Quotas: not_in_plan and quota_exceeded
Quota errors carry an extended payload:
{
"error": "not_in_plan",
"metric": "api_calls_per_month",
"limit": null,
"plan": "free",
"message": "The feature \"API calls\" is not included in the FREE plan."
}
not_in_plan(HTTP 403): the feature does not exist in the plan — upgrade.quota_exceeded(HTTP 429): the monthly limit is reached — it resets on the next period.
The message key is localised (fr/en) and can be shown as-is to the end user.
Template required fields (422)
Variables and tracking dimensions marked required in the template's schema are enforced at document creation — on single POSTs as well as batches. A missing field answers 422 in the Laravel validation format, without an error key:
{
"message": "Le champ « Nom » est obligatoire.",
"errors": {
"nom": ["Le champ « Nom » est obligatoire."]
}
}
- The
errorskeys are the schema's variable keys (GET /templates/{public_id}/schema); for a required tracking dimension, the key is prefixed withtracking.(tracking.status). A required reference-type variable needs a record that resolves — message « La fiche « … » est obligatoire. ». Messages are worded in French. - A value counts as soon as it is resolved non-empty: a folder/workspace cascade, a schema default, a formula or the document's dedicated date satisfy the requirement — providing
datais only necessary for what is not pre-filled. - With multiple errors,
errorslists them all andmessagecarries the first one. - The check runs before quota and any write: a
422reserves no number, creates no document and consumes nothing.
Saturated synchronous render: pdf_busy (503)
Document creations without async render the PDF during the request, and every synchronous render holds a slot from a bounded pool. When the pool is saturated, the call is rejected before anything is created:
{
"error": "pdf_busy",
"message": "Le serveur est à pleine capacité de rendu synchrone. Réessayez dans quelques secondes ou utilisez async=true : la réponse part immédiatement et le PDF est rendu par la file (webhook document.pdf_ready si un endpoint est déclaré)."
}
The response carries the Retry-After: 5 header. Retry after 5 seconds, or switch to async=true: the async mode takes no render slot and answers 201 immediately (the PDF follows through the queue — document.pdf_ready webhook or GET /documents/{id} polling).
Best practices
- Treat
404and403the same way in your integration: do not distinguish "missing resource" from "another workspace's resource". - On
429, back off: retrying immediately makes the counter worse. - On
400, log the sent body: it is almost always an encoding problem on the client side.
Batch generation: specific errors
The batch endpoints add two payloads to the quota_exceeded contract (HTTP 429):
{
"error": "batch_limit_reached",
"limit": 2,
"message": "You already have 2 batches running. Wait for one to finish or fetch one before creating a new one."
}
{
"error": "quota_exceeded",
"metric": "documents_per_month",
"limit": 2000,
"remaining": 1,
"required": 3,
"message": "Insufficient document quota: 1 remaining this month for 3 requested document(s)."
}
batch_limit_reached: too many active batches (pending/processing) in the workspace — wait for a batch to finish (GET /batches/{id}) before creating a new one.quota_exceeded+remaining/required: the quota pre-flight rejected the whole batch rather than emitting part of it — shrink the batch or wait for the next period.
Finally, GET /documents/{public_id}/pdf returns 202 pdf_pending (render in progress, honours Retry-After), 409 (pdf_draft, pdf_failed, pdf_missing) and 410 (pdf_expired) — see the reference.