API reference

Errors

Every non-2xx response uses a single envelope. The HTTP status carries the coarse category; the code field carries the precise, stable identifier that SDKs switch on.

The error envelope#

The body of any error is always just the envelope below, with no extra top-level keys (no data, no success: false). The same envelope is used by REST, MCP, and webhook surfaces.

json
{
  "error": {
    "code": "JOB_NOT_FOUND",
    "message": "No job with id 'job_x9k' exists for this tenant.",
    "request_id": "req_01HBXY4Z2N3K1QHTRACE6789",
    "details": null,
    "retry_after_ms": null,
    "doc_url": "https://docs.lirovo.ai/errors/job-not-found"
  }
}
error.coderequired
string

Stable UPPER_SNAKE_CASE identifier. The only field clients should branch on.

error.messagerequired
string

Human-readable, end-user-safe text. Never contains stack traces or secrets.

error.request_idrequired
string

Same value as the x-lirovo-request-id response header.

error.details
object | array | null

Per-code structured context (for example the offending id or validation issues).

error.retry_after_ms
integer | null

When set, wait at least this many ms before retrying. Mirrors the Retry-After header on 429 / 503.

error.doc_url
string | null

Permalink to the public error reference for the code.

Error codes#

There are 36 codes as of v1.1, grouped by category. The list below summarizes the real categories and the codes in each rather than reproducing the full per-code details shapes. Each code maps to exactly one HTTP status.

Authentication and authorization (401 / 403)

  • MISSING_AUTHORIZATION (401), INVALID_API_KEY (401), EXPIRED_API_KEY (401)
  • INSUFFICIENT_SCOPE (403), TENANT_SUSPENDED (403)

Resource not found (404)

  • JOB_NOT_FOUND, SCHEMA_NOT_FOUND, DESTINATION_NOT_FOUND, EXTRACTION_NOT_FOUND, API_KEY_NOT_FOUND
  • JOB_RESULT_NOT_READY, JOB_GRAPH_NOT_FOUND

The API never leaks cross-tenant existence: an unknown id and another tenant's id return the same envelope.

Validation (400 / 422)

  • INVALID_REQUEST (400), INVALID_JSON (400)
  • UNSUPPORTED_SOURCE (422), SOURCE_BLOCKED (422, SSRF screening), SCHEMA_INVALID (422), SCHEMA_TOO_LARGE (422), VIDEO_TOO_LONG (422), CONFLICTING_SCHEMA_REF (422)
  • IDEMPOTENCY_KEY_CONFLICT (422), IDEMPOTENCY_KEY_ORPHANED (422)

Rate limiting and quotas (429)

  • RATE_LIMITED, MINUTES_QUOTA_EXCEEDED, STORAGE_QUOTA_EXCEEDED, CONCURRENT_JOBS_LIMIT, COST_QUOTA_EXCEEDED

State and semantics (409 / 422)

  • JOB_ALREADY_TERMINAL (409), JOB_NOT_TERMINAL (409), DUPLICATE_API_KEY_NAME (409), DESTINATION_UNREACHABLE (422)

Server-side (5xx)

  • INTERNAL_ERROR (500), UPSTREAM_TIMEOUT (504), MODEL_UNAVAILABLE (503), STORAGE_UNAVAILABLE (503), MAINTENANCE (503)

Retry guidance#

The SDK enforces these defaults; the same rules apply to hand-rolled clients.

  • 429 with retry_after_ms (RATE_LIMITED, CONCURRENT_JOBS_LIMIT, COST_QUOTA_EXCEEDED): wait exactly retry_after_ms, then retry. Max 5 attempts.
  • 429 without retry_after_ms (MINUTES_QUOTA_EXCEEDED, STORAGE_QUOTA_EXCEEDED): do not auto-retry. Surface to the user.
  • 500, 503, 504: exponential backoff (500ms, 1s, 2s, 4s, 8s), max 5 attempts.
  • 400, 401, 403, 404, 409, 422: never auto-retry. The request itself is wrong.
Response headers
Every error carries X-Lirovo-Request-Id matching error.request_id. On 429 and 503 with a retry hint, a Retry-After header (whole seconds) accompanies retry_after_ms.