Errors

Every /v1 failure returns the same structured envelope and an unambiguous HTTP status:

{
  "error": {
    "code": "invalid-input",
    "message": "The request data could not be rendered.",
    "requestId": "req_…"
  }
}
  • code — a stable, machine-readable string. It is an open enum: new codes can be added additively, so treat an unrecognized code by its HTTP status class, not by exact match.
  • message — a safe, human-readable summary. Internal engine/template details are never leaked here.
  • requestId — the correlation id. It's also returned in the x-request-id response header on success. Quote it in support requests.

Main status codes

HTTPcodeWhen
400invalid-inputMalformed / unparseable request body (bad JSON).
401unauthorizedMissing, invalid, or revoked API key. See Authentication.
402insufficient-creditsThe account is out of credits. Top up to continue — don't retry blindly.
403forbiddenValid key but the subscription/capability can't perform the action (e.g. lapsed to read-only, or missing canEditVariables).
404template-not-foundUnknown template (or a tmpl_/wtpl_/bat_ id outside your account — no existence disclosure).
422invalid-inputWell-formed but rejected: oversized values, an unsupported target/format, or template data that can't be resolved.
429rate-limitedPer-key rate-limit window exceeded. Carries Retry-After (seconds).
5xxrender-timeout (504) · engine-unavailable (503) · render-failed (502)A transient or internal render-side failure.

Retrying safely

  • 429 — wait Retry-After seconds, then retry.
  • 503 / 504 — safe to retry with backoff. Send the same Idempotency-Key so a retry that actually succeeded server-side isn't charged twice.
  • 402 — the balance is empty; top up rather than retrying.
  • 400 / 422 — the request is wrong; fix it before resending.

On the render endpoint the 422 body is returned with the response's accepted content type; for every other endpoint the envelope is application/json.