> ## Documentation Index
> Fetch the complete documentation index at: https://docs.viggle.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Async jobs and result availability

> Poll or watch V1 jobs reliably and handle their lifecycle.

Characters and Motions are reusable asynchronous assets. Renders are asynchronous operations that deliver artifacts.

## Status lifecycle

```mermaid theme={null}
stateDiagram-v2
  [*] --> queued
  queued --> processing
  processing --> ready
  processing --> failed
  processing --> cancelled
```

`cancelled` applies to Render state only. It is not client-triggerable — there is no cancel operation — but a Render can still reach it on its own; treat it as terminal when you see it. Poll every 3–5 seconds while a resource is `queued` or `processing`. Stop polling for `ready`, `failed`, or `cancelled`.

## Polling example

```python theme={null}
import time
import requests

def wait_for_resource(url, api_key, interval_seconds=3):
    headers = {"Authorization": f"Bearer {api_key}"}
    while True:
        response = requests.get(url, headers=headers)
        response.raise_for_status()
        resource = response.json()

        if resource["status"] == "ready":
            return resource
        if resource["status"] in {"failed", "cancelled"}:
            error = resource.get("error") or {}
            raise RuntimeError(
                f"{resource['status']}: {error.get('code')} {error.get('message')}"
            )
        time.sleep(interval_seconds)

render = wait_for_resource(
    "https://apis.viggle.ai/v1/videos/render_123abc",
    "YOUR_API_KEY",
)
print(render["video_url"])
```

Render status polling and the Characters/Motions polling above use the same pattern — only the base resource URL changes. `GET /v1/renders/{render_id}` is retired (`405`); poll `GET /v1/videos/{video_id}` instead, which also accepts `vid_`-prefixed H3 generation IDs and `anim_`-prefixed character-animation IDs. See [Get Video](/v1/api-reference/videos/get) and [List Videos](/v1/api-reference/videos/list).

## Watching instead of polling

`GET /v1/renders/{render_id}/events` streams the same lifecycle over Server-Sent Events: a snapshot on connect, one event per state change, a heartbeat every 10–15 seconds, and stream closure on a terminal state. It removes polling latency and load; reconnect with `Last-Event-ID` if the connection drops. See [Watch Render](/v1/api-reference/renders/events). Characters and Motions have no equivalent stream — poll those.

## Render progress

Render responses can include:

* `progress`: an integer from 0 to 100, when available
* `stage`: a coarse hint — `analyzing`, `rendering`, `finishing` from the direct multipart pipeline; `queued`, `preparing`, `generating`, `finalizing` from the draft-based pipeline. Treat an unrecognized value as "in progress" rather than failing. On a video fetched from `GET /v1/videos/{video_id}`, `stage` is populated only while `status` is `processing`, and only for a Render-sourced ID — it is always `null` for an H3 (`vid_`) video.
* `video_url`: available only when the Render is ready
* `alpha_url`: available with transparent output
* `links`: `{self, events, download}` on the object returned by `POST /v1/renders` — absent on renders served by the legacy migration proxy, and absent entirely from the `GET /v1/videos/{video_id}` response

## Download promptly

Export and download URLs (`video_url`, `alpha_url`, `vsplat_url`, `thumbnail_url`, and a Motion export's `mixamo_url`/`metahuman_url`/`metahuman_body_url`) are short-lived and re-signed on every read of their endpoint. Persist output in your own storage if it is needed later, and re-request the export/download endpoint rather than caching a URL.

## Recover safely

* For `failed`, inspect `error.code`, `error.retryable`, and `error.remediation`, and follow the suggested recovery path — see [Errors and recovery](/v1/production/errors).
* The JSON draft form of `POST /v1/renders` requires `Idempotency-Key` and is safe to retry after a network timeout with the same key and body. The `multipart/form-data` form of `POST /v1/renders`, and Character/Motion creation, have no idempotency key: if a create response was received, query its known resource ID; otherwise resolve the outcome with your request logs and `X-Request-Id` before submitting again.
* Include `X-Request-Id` in support requests.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.