Errors
How the API reports failures — status codes and the error body shape.
Errors come back as JSON with a snake_case code and a matching HTTP status:
Some errors carry extra detail alongside the code (for example a list of offending field keys). Always branch on the HTTP status first, then on the error code.
Status codes
The 404-as-leak-guard convention matters: a 404 on a space-scoped resource can mean “doesn’t exist” or “you can’t see it.” The API deliberately doesn’t distinguish the two, so you can’t probe for the existence of resources you lack access to.
Common error codes
Errors on capability endpoints
The capability endpoints (/v1/…) use a richer envelope. The legacy { "error": … } shape above is unchanged on every other route.
invalid_request
400. The body or query is malformed: a missing field, a bad type, or a mutually exclusive pair sent together (file and files, schema and action, spaceId and space on keep). It also covers a parameter that doesn’t apply to the input — style on a PDF, mode on audio — and a saved sign action passed as another verb’s action. message names the offending field. Fix the call; retrying identically will fail identically.
A slow run is never an error. When the synchronous cap is reached the call returns 202 with the run handle, and a run waiting on a human returns 202 with needs_input — both are successes with a body to poll.
invalid_schema
400. The schema you sent to extract breaks the extraction schema dialect. docUrl points at the exact rule and message at the exact node.
not_found
404. No such run, file, template, action, webhook endpoint, or produced output. Also returned when a sub-route addresses a step the run doesn’t have — GET /v1/runs/plr_…/envelope on a pipeline with no sign step. Right after a synchronous run returns, GET /v1/runs/:id/output/:name can 404 for a moment while the produced file is registered; re-fetch the run and use output.file.url.
Other capability codes
New codes can be added over time; branch on the ones you handle and treat anything unknown as a plain failure of its HTTP status class.
Retrying
503 auth_unavailableand503downstream errors are transient — retry with exponential backoff.410 expiredis terminal for that resource; don’t retry.429 rate_limitedis transient too, but retry on the terms the response gives you: sleep for theRetry-Afterseconds, then back off exponentially. The budget and the tactics are on Rate limits.4xxother than429means a bad request — fix the call rather than retrying.