viewa/docs
Core Concepts

Errors

The problem+json error model and stable error codes.

Every error the API returns is an RFC 9457 application/problem+json document with a stable, machine-readable code:

{
  "type": "https://docs.viewa.sh/errors#example-code",
  "title": "Human-readable summary",
  "status": 422,
  "code": "example_code",
  "detail": "What specifically went wrong with this request."
}

Handle the code, not the message

title and detail are for humans and may be reworded at any time. code values are part of the API contract: stable, additive-only, and safe to branch on.

if (problem.code === 'example_code') {
  // handle it
}

Job failures are not errors

problem+json is reserved for request-level failures: a bad or missing key, an unknown id, a rejected upload. When an asynchronous job fails — a photo's preprocessing, a render — that is a state the resource is in, not a problem with the request that asked about it. Polling a failed resource answers 200, with a structured error object on the body:

{ "id": "img_…", "status": "failed", "error": { "code": "preprocessing_failed", "detail": "…" } }

Those nested error.code values are just as stable as problem+json codes — see Photos & preprocessing for the image ones and Areas for the area-set ones: occlusion_failed, occlusion_timeout, and area_calculation_failed — the size and orientation of one or more areas could not be estimated from the photo; adjust the corners and try again. Your polling loop only ever branches on status; it never needs an HTTP error handler for a job outcome.

Error catalog

Codes already live on the image endpoints: unsupported_media_type (415), file_too_large (413), not_found (404), invalid_request (400) — and on the areas endpoints: invalid_corners (400), image_immutable (400), unknown_area (400), image_not_ready (409) — plus the auth codes shared by every endpoint. Render requests add invalid_selections (400), area_set_not_ready (409), and idempotency_key_reused (409). The pipeline endpoints also return visitor_token_required (403) when no X-Viewa-Visitor-Token was sent, and visitor_token_invalid (403) when the one sent is malformed, expired, or was issued for a different instance. The first applies only where the token is mandatory (an instance configured to require it, and the history endpoint always); the second applies anywhere a token is sent — see Visitor tokens. The pipeline POSTs add two more: rate_limited (429) for a transient burst or concurrency refusal, which carries a Retry-After header, and quota_exceeded (429) when a daily allowance is used up, which does not — see Limits. One code is not about your request at all: internal_error (500) means something went wrong on our side. It is returned by DELETE /visitors/{id} when a deletion does not complete, and the request is safe to retry.

Render job failures are returned in the resource's error object rather than as problem+json. The closed list is capture_failed, capture_timeout, generation_failed, generation_timeout, dispatch_failed, and internal.

On this page