API reference / Errors

Errors

When a request fails, the API returns JSON (not a PDF) with a consistent envelope and the matching HTTP status.

{
  "error": {
    "code": "invalid_data",
    "message": "Variable 'items' must be a list, but got a string."
  }
}

The code is a stable, machine-readable string from the closed set below. The message is a human-readable explanation, safe to log. Branch your handling on code, show or log message.

The complete set

CodeHTTPMeaningWhat to do
unauthorized401Missing, invalid, or revoked API key.Check the Authorization header and that the key is active.
payment_required402A paid plan's subscription is not active.Update billing or re-subscribe. The Free plan is never blocked this way.
account_suspended403The account was suspended by the platform.Not retryable. Contact support if you believe this is a mistake.
rate_limited429Too many requests in a short window.Slow down; honor the Retry-After header. See limits.
limit_exceeded429Your plan's monthly quota is used up.Wait for the next month (UTC) or upgrade.
template_not_found404No template with that templateId exists on your account.Check the tpl_… code.
template_not_published422The template exists but has no published version.Publish the template, then retry.
invalid_json400The request body is not valid JSON.Fix the JSON syntax and the Content-Type.
invalid_data422Valid JSON, but the wrong shape or data that does not satisfy the template's variables.Read message; it names every offending variable.
idempotency_conflict409The same Idempotency-Key was reused with a different request body.Use a new key for a different request. See Idempotency.
render_failed502Rendering failed, or an external resource was blocked, unreachable, or timed out.Check the template; for assets, message names the URL. See assets.
service_unavailable503The render engine is momentarily at capacity.Transient. Retry shortly with backoff.
internal_error500The document rendered but recording it failed, so the call is reported as failed.Retry. If it persists, contact support.

Reading an invalid_data message

This is the error you hit most while integrating, so its message is written to tell you exactly which field is wrong and what shape it expects. It names the offending top-level variable and, for a missing one, the shape the template uses it as:

{
  "error": {
    "code": "invalid_data",
    "message": "Missing required variable 'total' (expected a simple value)."
  }
}

A value of the wrong shape reports both what was expected and what it received:

{
  "error": {
    "code": "invalid_data",
    "message": "Variable 'items' must be a list, but got a string."
  }
}

When several fields are wrong, every problem is listed so you can fix them in one pass instead of discovering them one request at a time:

The data does not match the template:
  - Missing required variable 'total' (expected a simple value).
  - Variable 'items' must be a list, but got a string.

The variable named is always a top-level key of your data object (the template's required inputs), so the fix is to add or correct that key and retry.

Quota and errors

A failed call never consumes quota. Only a successful generation increments your monthly counter (and it does so in the same transaction that records the success, so usage can never drift from what actually rendered). This means retrying a failed request is always safe with respect to billing.

What shows up in your history

The failures you can act on are recorded in your generation history with an actionable message: payment_required, account_suspended, limit_exceeded, template_not_found, template_not_published, invalid_data, and render_failed. The rest (unauthorized, rate_limited, service_unavailable, internal_error) are not logged, because there is either no trusted account to attribute them to or nothing for you to fix in the request itself.

Handling errors well

  • Fix, do not retry, the 4xx request errors (template_not_found, template_not_published, invalid_json, invalid_data, idempotency_conflict): retrying the same request will fail the same way.
  • Back off and retry the transient ones (rate_limited, service_unavailable, and usually internal_error). For rate_limited, wait at least as long as Retry-After says. See Rate limits & quotas for a retry strategy.
  • Resolve account state for unauthorized (rotate or fix the key), payment_required (billing), and account_suspended (contact support).