> ## 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.

# Watch Render

> Watch render state through Server-Sent Events instead of polling.

The server sends a snapshot first, emits a heartbeat every 10 to 15 seconds, and closes the stream after `ready`, `failed`, or `cancelled`. Reconnect with `Last-Event-ID` to resume after a snapshot. If the event history has expired, call [Get Video](/v1/api-reference/videos/get) (`GET /v1/videos/{render_id}`) and reconnect from the current state.

## Request parameters

| Parameter       | Type   | Required | Description                                                                                                                                                                      |
| --------------- | ------ | :------: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `render_id`     | string |    Yes   | Full public Render ID returned by Render Video (from Character and/or Motion) or List Videos, normally beginning with `render_`. The stream reports events only for this Render. |
| `Last-Event-ID` | string |    No    | Request header containing the last SSE event ID processed completely. Send it when reconnecting so retained history resumes after that event; omit it for a fresh stream.        |

## Response

Returns `200 OK` with a `text/event-stream` body. Business events carry a JSON `data` payload; heartbeats may omit `data`.

| Event        | `data` payload    | Description                                                  |
| ------------ | ----------------- | ------------------------------------------------------------ |
| `snapshot`   | Render event data | Sent immediately on connect.                                 |
| `queued`     | Render event data | Render accepted, not yet processing.                         |
| `processing` | Render event data | Processing has started.                                      |
| `progress`   | Render event data | `progress` and/or `stage` changed.                           |
| `ready`      | Render event data | Terminal — `video_url`/`alpha_url` populated. Stream closes. |
| `failed`     | Render event data | Terminal — `error` populated. Stream closes.                 |
| `cancelled`  | Render event data | Terminal. Stream closes.                                     |
| `heartbeat`  | none              | Keep-alive; no business data.                                |

Each Render event's `data` field:

| Field                     | Type            | Always present | Description                                                |
| ------------------------- | --------------- | :------------: | ---------------------------------------------------------- |
| `event_id`                | string          |       Yes      | Pass as `Last-Event-ID` to resume after this event.        |
| `sequence`                | integer         |       Yes      | Monotonically increasing per render.                       |
| `id`                      | string          |       Yes      | Render ID.                                                 |
| `status`                  | string          |       Yes      | `queued`, `processing`, `ready`, `failed`, or `cancelled`. |
| `stage`                   | string or null  |       Yes      | Coarse progress hint when available.                       |
| `progress`                | integer or null |       Yes      | Progress from 0 to 100 when available.                     |
| `video_url` / `alpha_url` | string or null  |       Yes      | Populated on `ready`.                                      |
| `occurred_at`             | string          |       Yes      | ISO 8601 timestamp of the event.                           |
| `completed_at`            | string or null  |       Yes      | ISO 8601 completion timestamp.                             |
| `error`                   | object or null  |       Yes      | Populated on `failed`.                                     |

## Examples

<CodeGroup>
  ```bash cURL theme={null}
  curl -N "https://apis.viggle.ai/v1/renders/render_789ghi/events" \
    -H "Authorization: Bearer $VIGGLE_API_KEY" \
    -H "Accept: text/event-stream"
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch("https://apis.viggle.ai/v1/renders/render_789ghi/events", {
    headers: { Authorization: `Bearer ${process.env.VIGGLE_API_KEY}`, Accept: "text/event-stream" },
  });
  const reader = response.body.getReader();
  const decoder = new TextDecoder();
  let buffer = "";
  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    buffer += decoder.decode(value, { stream: true });
    // Split on blank lines to get whole SSE frames, then parse `event:`/`data:`.
  }
  ```

  ```python Python theme={null}
  import os, requests

  with requests.get(
      "https://apis.viggle.ai/v1/renders/render_789ghi/events",
      headers={"Authorization": f"Bearer {os.environ['VIGGLE_API_KEY']}", "Accept": "text/event-stream"},
      stream=True,
  ) as response:
      response.raise_for_status()
      for line in response.iter_lines(decode_unicode=True):
          if line:
              print(line)
  ```
</CodeGroup>

<Tip>
  Prefer this endpoint over polling [Get Video](/v1/api-reference/videos/get) when you can hold a long-lived connection — it removes polling latency and load without changing how you interpret render state.
</Tip>

## Next step

Once a `ready` event arrives, use [Download Render](/v1/api-reference/renders/download) or the event's own `video_url`/`alpha_url` to fetch the result.


## OpenAPI

````yaml openapi.yaml GET /v1/renders/{render_id}/events
openapi: 3.0.3
info:
  title: Viggle API
  description: Generate AI-powered character animation videos
  version: 2.0.0
  contact:
    name: Viggle Support
    url: https://viggle.ai
servers:
  - url: https://apis.viggle.ai
    description: Production server
security:
  - bearerAuth: []
tags:
  - name: Renders
    description: Prepare inputs, create renders, observe progress, and retrieve results.
  - name: Credits
    description: Read the credit balance available to the calling principal.
  - name: Characters
    description: |
      Create, list, inspect, and delete reusable character assets. `type`
      selects whether creation also extracts a 3D vsplat; export its
      download URL separately once ready.
  - name: Motions
    description: |
      Create, list, inspect, and delete reusable motion assets, and import one
      from an official motion template. `type` selects whether creation also
      extracts or generates a 3D animation; export its download URL
      separately once ready.
  - name: Videos
    description: |
      Generate a MiniMax H3 text-to-video (`vid_` prefix), and read the
      unified view over every video the caller owns — H3 generations and
      character+motion Renders (`render_` prefix) — merged into one
      resource.
paths:
  /v1/renders/{render_id}/events:
    parameters:
      - $ref: '#/components/parameters/RenderId'
      - $ref: '#/components/parameters/RequestId'
      - $ref: '#/components/parameters/SourceChannel'
    get:
      tags:
        - Renders
      summary: Watch render state through Server-Sent Events
      description: |
        The server sends a snapshot first, emits a heartbeat every 10 to 15
        seconds, and closes after `ready`, `failed`, or `cancelled`. Reconnect
        with `Last-Event-ID`. If the event history has expired, call
        `GET /v1/videos/{render_id}` and reconnect from the current state.
      operationId: watchRender
      parameters:
        - $ref: '#/components/parameters/LastEventId'
      responses:
        '200':
          description: |
            Server-Sent Events stream. Business event `data` fields contain a
            JSON `RenderEventData` value. Heartbeats may omit `data`.
          headers:
            Cache-Control:
              schema:
                type: string
              example: no-cache
            X-Accel-Buffering:
              schema:
                type: string
              example: 'no'
            X-Request-Id:
              $ref: '#/components/headers/RequestId'
          content:
            text/event-stream:
              schema:
                type: string
                description: UTF-8 Server-Sent Events stream.
          x-sse-event-name:
            $ref: '#/components/schemas/RenderEventName'
          x-sse-events:
            snapshot:
              $ref: '#/components/schemas/RenderEventData'
            queued:
              $ref: '#/components/schemas/RenderEventData'
            processing:
              $ref: '#/components/schemas/RenderEventData'
            progress:
              $ref: '#/components/schemas/RenderEventData'
            ready:
              $ref: '#/components/schemas/RenderEventData'
            failed:
              $ref: '#/components/schemas/RenderEventData'
            cancelled:
              $ref: '#/components/schemas/RenderEventData'
            heartbeat:
              description: Keep-alive event with no required data.
        '401':
          $ref: '#/components/responses/V1Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalServerError'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
components:
  parameters:
    RenderId:
      name: render_id
      in: path
      required: true
      description: >-
        Public Render ID returned by render creation or listing. It normally
        begins with `render_` and must identify a render owned by the caller.
      schema:
        type: string
        minLength: 1
      example: render_123
    RequestId:
      name: X-Request-Id
      in: header
      required: false
      description: >-
        Optional caller-supplied correlation ID, up to 128 characters. The
        service returns the effective value in the response header for tracing
        and support.
      schema:
        type: string
        minLength: 1
        maxLength: 128
    SourceChannel:
      name: X-Viggle-Source
      in: header
      required: false
      description: >-
        Optional source-channel label, up to 128 characters, used to attribute
        traffic to an SDK, integration, product surface, or internal workflow.
      schema:
        type: string
        minLength: 1
        maxLength: 128
    LastEventId:
      name: Last-Event-ID
      in: header
      required: false
      description: >-
        ID of the last SSE event the client processed completely. Send it on
        reconnection so the server can resume after that event when retained
        history is available.
      schema:
        type: string
        minLength: 1
  headers:
    RequestId:
      description: Stable request identifier for support and tracing.
      schema:
        type: string
  responses:
    V1Unauthorized:
      description: Missing, invalid, or expired credential.
      headers:
        WWW-Authenticate:
          schema:
            type: string
        X-Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    Forbidden:
      description: Credential does not grant access to this resource.
      headers:
        X-Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    NotFound:
      description: Draft or render not found.
      headers:
        X-Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    RateLimited:
      description: Rate limit exceeded.
      headers:
        Retry-After:
          schema:
            type: integer
            minimum: 0
        X-Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    InternalServerError:
      description: Unexpected server error.
      headers:
        X-Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    ServiceUnavailable:
      description: Service temporarily unavailable.
      headers:
        Retry-After:
          schema:
            type: integer
            minimum: 0
        X-Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
  schemas:
    ErrorResponse:
      type: object
      additionalProperties: false
      required:
        - error
      properties:
        error:
          description: >-
            Top-level draft-flow error payload containing the stable code,
            message, retry guidance, correlation ID, and error-specific details.
          allOf:
            - $ref: '#/components/schemas/ErrorBody'
    ErrorBody:
      type: object
      additionalProperties: false
      required:
        - code
        - message
        - retryable
        - request_id
        - details
        - remediation
      properties:
        code:
          description: Stable error category from the draft-based render error vocabulary.
          allOf:
            - $ref: '#/components/schemas/ErrorCode'
        message:
          type: string
          description: >-
            Human-readable explanation suitable for logs or presentation to a
            developer.
        retryable:
          type: boolean
          description: >-
            Whether retrying the operation without changing its semantic input
            may succeed.
        request_id:
          type: string
          description: >-
            Correlation identifier for tracing and support. Include it when
            reporting the failure.
        details:
          type: object
          description: Error-specific structured context; available keys depend on `code`.
          additionalProperties: true
        remediation:
          description: Suggested recovery action and optional retry delay.
          allOf:
            - $ref: '#/components/schemas/ErrorRemediation'
    ErrorCode:
      description: |
        The failure vocabulary of the draft-based render flow. The resource
        operations project the same vocabulary onto the lower_snake_case codes
        described by `ResourceError`.
      type: string
      enum:
        - UNAUTHENTICATED
        - INVALID_CREDENTIAL
        - FORBIDDEN
        - INVALID_REQUEST
        - INVALID_CHARACTER
        - INVALID_MOTION
        - UNSUPPORTED_MEDIA
        - CONTENT_POLICY_VIOLATION
        - UPLOAD_REQUIRED
        - UPLOAD_MISMATCH
        - UPLOAD_EXPIRED
        - DRAFT_NOT_FOUND
        - DRAFT_ALREADY_CONSUMED
        - RENDER_NOT_FOUND
        - RENDER_NOT_CANCELLABLE
        - CHARACTER_NOT_FOUND
        - MOTION_NOT_FOUND
        - MOTION_NOT_READY
        - MOTION_TEMPLATE_NOT_FOUND
        - AVATAR_NOT_FOUND
        - ANIMATION_NOT_FOUND
        - EXTRACTION_UNAVAILABLE
        - EXTRACTION_FAILED
        - IDEMPOTENCY_KEY_REUSED
        - INSUFFICIENT_CREDITS
        - RATE_LIMITED
        - SERVICE_BUSY
        - INTERNAL_ERROR
    ErrorRemediation:
      type: object
      additionalProperties: false
      required:
        - action
        - retry_after_ms
      properties:
        action:
          type: string
          description: >-
            Suggested machine-readable or human-readable next action, such as
            retrying later, uploading media again, or correcting input.
        retry_after_ms:
          type: integer
          description: >-
            Recommended delay in milliseconds before retrying; null when no
            timed retry is advised.
          nullable: true
          format: int64
          minimum: 0
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: API key or OAuth access token
      description: |
        Server-side SDK clients use a project API key. Remote MCP clients use
        an OAuth access token. Never expose a project API key in browser code.

````