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

# Export 3D Character (for 3D/game engines)

> Get a signed download URL for a Character's vsplat extraction.

Only meaningful for a Character created with `type` `vsplat` or `all`; a `render`-only Character has nothing to export. This replaces the old, now-removed `GET /v1/avatars/{avatar_id}`.

`vsplat_url` and `thumbnail_url` are populated on different conditions, not both gated by `download_type`:

* `vsplat_url` is populated only when the request carries `download_type=vsplat`, and is re-signed on every call.
* `thumbnail_url` is unconditional — it's a default output of the extraction, populated whenever one was rendered (that is, the Character was created with `render_thumbnail=true`; see [Create Character](/v1/api-reference/characters/create)), independent of `download_type` and even if it's omitted entirely. It's not something you opt into via a request parameter.

There is no `glb` output for a Character export. `download_type` only ever accepts `vsplat` — `glb` is a Motion-only extraction; see [Export 3D Motion (for 3D/game engines)](/v1/api-reference/motions/export) for that.

## Request parameters

| Parameter       | Type   | Required | Description                                                                                                                                                                                                                                   |
| --------------- | ------ | :------: | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `character_id`  | string |    Yes   | Complete public Character ID returned by Create or List Characters. The Character must have been created with `type=vsplat` or `all` to expose an export.                                                                                     |
| `download_type` | string |    No    | Selects the vsplat download URL: only `vsplat` is accepted. Any other value — including the old `thumbnail`, or `glb` (a Motion-only extraction) — is rejected with `400`. Omit to retrieve status (and `thumbnail_url`, if one exists) only. |

## Response parameters

Returns `200 OK`.

| Field           | Type           | Always present | Description                                                                                                                                                                                                              |
| --------------- | -------------- | :------------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `id`            | string         |       Yes      | Requested public Character ID.                                                                                                                                                                                           |
| `status`        | string         |       Yes      | `queued`, `processing`, `ready`, or `failed`.                                                                                                                                                                            |
| `download_type` | string or null |       Yes      | Echoes the requested `download_type` (always `vsplat` or `null`; never `thumbnail`).                                                                                                                                     |
| `vsplat_url`    | string or null |       Yes      | Populated only when `download_type=vsplat` and the extraction is ready.                                                                                                                                                  |
| `thumbnail_url` | string or null |       Yes      | Populated whenever the extraction rendered a thumbnail and it's ready — regardless of `download_type`. `null` if the Character was created with `render_thumbnail=false` (the default) or the thumbnail isn't ready yet. |
| `created_at`    | string or null |       Yes      | ISO 8601 timestamp with a UTC offset. May be an empty string on a task that has not recorded one.                                                                                                                        |
| `updated_at`    | string or null |       Yes      | ISO 8601 timestamp of the most recent update.                                                                                                                                                                            |
| `error`         | object or null |       Yes      | Structured failure details when `status` is `failed`.                                                                                                                                                                    |

Requested with `download_type=vsplat` on a Character that was also created with `render_thumbnail=true` — both URLs come back together:

```json theme={null}
{
  "id": "char_550e8400-e29b-41d4-a716-446655440000",
  "status": "ready",
  "download_type": "vsplat",
  "vsplat_url": "https://assets.viggle.ai/results/character.vsplat",
  "thumbnail_url": "https://assets.viggle.ai/results/character_thumb.png",
  "created_at": "2026-07-31T09:15:22+00:00",
  "updated_at": "2026-07-31T09:16:40+00:00",
  "error": null
}
```

The same Character requested with no `download_type` at all — `vsplat_url` withholds, `thumbnail_url` still doesn't:

```json theme={null}
{
  "id": "char_550e8400-e29b-41d4-a716-446655440000",
  "status": "ready",
  "download_type": null,
  "vsplat_url": null,
  "thumbnail_url": "https://assets.viggle.ai/results/character_thumb.png",
  "created_at": "2026-07-31T09:15:22+00:00",
  "updated_at": "2026-07-31T09:16:40+00:00",
  "error": null
}
```

## Examples

<CodeGroup>
  ```go Go theme={null}
  req,_:=http.NewRequest("GET","https://apis.viggle.ai/v1/characters/"+characterID+"/export?download_type=vsplat",nil); req.Header.Set("Authorization","Bearer "+os.Getenv("VIGGLE_API_KEY")); resp,err:=http.DefaultClient.Do(req); if err!=nil {panic(err)}; defer resp.Body.Close()
  ```

  ```javascript JavaScript theme={null}
  const response=await fetch(`https://apis.viggle.ai/v1/characters/${characterId}/export?download_type=vsplat`,{headers:{Authorization:`Bearer ${process.env.VIGGLE_API_KEY}`}}); const exported=await response.json();
  ```

  ```python Python theme={null}
  import os, requests
  response=requests.get(f"https://apis.viggle.ai/v1/characters/{character_id}/export",params={"download_type":"vsplat"},headers={"Authorization":f"Bearer {os.environ['VIGGLE_API_KEY']}"}); response.raise_for_status(); exported=response.json()
  ```

  ```bash cURL theme={null}
  curl "https://apis.viggle.ai/v1/characters/$CHARACTER_ID/export?download_type=vsplat" -H "Authorization: Bearer $VIGGLE_API_KEY"
  ```
</CodeGroup>

<Tip>
  Download URLs are short-lived and re-signed on every read — request `download_type` again rather than caching the URL.
</Tip>

## Next step

The 2D render side of this same Character (`capabilities: ["video_render"]`) is independent of the vsplat export above — use [Render Video (from Character and/or Motion)](/v1/api-reference/renders/create) with `character_id` once it's ready.


## OpenAPI

````yaml openapi.yaml GET /v1/characters/{character_id}/export
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/characters/{character_id}/export:
    parameters:
      - $ref: '#/components/parameters/CharacterId'
      - $ref: '#/components/parameters/RequestId'
      - $ref: '#/components/parameters/SourceChannel'
    get:
      tags:
        - Characters
      summary: Get a signed download URL for a character's vsplat extraction
      description: |
        Only meaningful for a character created with `type` `vsplat` or
        `all`; a `render`-only character has nothing to export.
        `vsplat_url` is populated only when the request carried
        `download_type=vsplat`, and is re-signed on every call.
        `thumbnail_url` is unconditional: it is populated whenever the
        extraction has a rendered thumbnail, independent of
        `download_type`, since it is a default output of the extraction
        rather than an asset a caller opts into fetching. There is no
        `glb` output for a character export — `download_type` only ever
        accepts `vsplat`; `glb` extraction applies only to Motions (see
        `GET /v1/motions/{motion_id}/export`).
        Replaces the old `GET /v1/avatars/{avatar_id}`.
      operationId: exportCharacter
      parameters:
        - name: download_type
          in: query
          required: false
          description: >-
            Selects the signed output URL to populate. Use `vsplat` after
            extraction is ready; omit it when only checking status or retrieving
            the thumbnail.
          schema:
            type: string
            enum:
              - vsplat
      responses:
        '200':
          description: Current vsplat export state.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/RequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CharacterExport'
        '400':
          $ref: '#/components/responses/ResourceBadRequest'
        '401':
          $ref: '#/components/responses/ResourceUnauthorized'
        '404':
          $ref: '#/components/responses/ResourceNotFound'
        '409':
          $ref: '#/components/responses/ResourceConflict'
        '500':
          $ref: '#/components/responses/ResourceInternalServerError'
        '502':
          $ref: '#/components/responses/ResourceBadGateway'
        '503':
          $ref: '#/components/responses/ResourceServiceUnavailable'
components:
  parameters:
    CharacterId:
      name: character_id
      in: path
      required: true
      description: >-
        Public Character ID returned by character creation or listing. It
        normally begins with `char_` and must identify a Character owned by the
        caller.
      schema:
        type: string
        minLength: 1
      example: char_2f1c4b8e9d7a4f2ab6c3d5e7f9a1b3c5
    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
  headers:
    RequestId:
      description: Stable request identifier for support and tracing.
      schema:
        type: string
  schemas:
    CharacterExport:
      description: |
        A character's vsplat extraction, for download. `vsplat_url` is
        populated only when the request carried `download_type=vsplat`.
        `thumbnail_url` is unconditional — a default output of the
        extraction, populated whenever one was rendered, independent of
        `download_type`. Download URLs are short-lived and re-signed on
        every read. Formerly `AvatarResource`, returned by the now-removed
        `GET /v1/avatars/{avatar_id}`.
      type: object
      additionalProperties: false
      required:
        - id
        - status
        - download_type
        - vsplat_url
        - thumbnail_url
        - created_at
        - updated_at
        - error
      properties:
        id:
          type: string
          description: ID of the Character whose vsplat export is represented.
          minLength: 1
        status:
          description: >-
            Current lifecycle of the vsplat extraction. Download only when this
            is `ready` and `vsplat_url` is populated.
          allOf:
            - $ref: '#/components/schemas/ResourceStatus'
        download_type:
          description: >-
            Echoes the requested `download_type`; `vsplat` selects the signed
            asset URL, while null means no downloadable artifact was explicitly
            requested.
          type: string
          nullable: true
          enum:
            - vsplat
            - null
        vsplat_url:
          type: string
          description: >-
            Short-lived signed URL for the `.vsplat` asset when
            `download_type=vsplat` and extraction is ready; otherwise null.
          nullable: true
        thumbnail_url:
          type: string
          description: >-
            Short-lived signed URL for the standard extraction thumbnail when
            one was generated; independent of `download_type`, otherwise null.
          nullable: true
        created_at:
          description: >-
            ISO 8601 timestamp with a UTC offset. May be an empty string on a
            task that has not recorded one.
          type: string
          nullable: true
        updated_at:
          type: string
          description: >-
            ISO 8601 timestamp with a UTC offset for the latest extraction-state
            update; null if unavailable.
          nullable: true
        error:
          type: object
          description: >-
            Structured extraction failure when `status` is `failed`; otherwise
            null.
          nullable: true
          allOf:
            - $ref: '#/components/schemas/ResourceError'
    ResourceStatus:
      description: |
        The public lifecycle shared by every asynchronous resource. It carries
        the same values as `RenderStatus`.
      type: string
      enum:
        - queued
        - processing
        - ready
        - failed
        - cancelled
    ResourceError:
      description: |
        The failure attached to an asynchronous resource. `code` is a stable,
        lower_snake_case string and deliberately not an enum: the extraction
        and render workers own this vocabulary and add to it over time, and a
        failure with no published mapping surfaces as `processing_failed`.

        Published codes: `worker_stopped`, `motion_model_mismatch`,
        `image_required`, `motion_video_required`, `unsupported_video_format`,
        `video_too_long`, `video_too_large`, `video_resolution_too_high`,
        `video_unreadable`, `invalid_background_mode`,
        `video_dimensions_must_be_even`, `result_expired`,
        `video_inaccessible`, `no_humans_detected`, `all_workers_busy`,
        `task_failed`, `unexpected_error`, `processing_failed`.
      type: object
      additionalProperties: false
      required:
        - code
        - message
        - request_id
      properties:
        code:
          type: string
          description: >-
            Stable, lower_snake_case asynchronous failure code. The vocabulary
            is open, so clients must tolerate new values.
          minLength: 1
        message:
          type: string
          description: Human-readable explanation of the asynchronous processing failure.
        request_id:
          type: string
          description: >-
            Correlation identifier from the worker or originating request; null
            when unavailable.
          nullable: true
    ResourceErrorResponse:
      type: object
      additionalProperties: false
      required:
        - error
      properties:
        error:
          description: Top-level synchronous resource-operation error payload.
          allOf:
            - $ref: '#/components/schemas/ResourceHttpError'
    ResourceHttpError:
      description: |
        The failure envelope of a synchronous resource request. Every code is
        produced by one mapping in the API service, so the set is closed; a
        failure with no published mapping surfaces as `processing_failed`.

        The asynchronous `error` object on a resource carries the wider,
        deliberately open vocabulary of `ResourceError` instead.
      type: object
      additionalProperties: false
      required:
        - code
        - message
        - request_id
      properties:
        code:
          type: string
          description: >-
            Closed, lower_snake_case error code for a synchronous resource
            request; use it for programmatic handling.
          enum:
            - authentication_required
            - invalid_api_key
            - insufficient_credits
            - invalid_request
            - motion_not_ready
            - character_not_found
            - motion_not_found
            - not_found
            - id_already_exists
            - rate_limited
            - service_unavailable
            - task_failed
            - internal_error
            - processing_failed
        message:
          type: string
          description: Human-readable explanation of the synchronous request failure.
        request_id:
          description: |
            Always null on a synchronous failure. Correlate with the
            `X-Request-Id` response header instead.
          type: string
          nullable: true
  responses:
    ResourceBadRequest:
      description: |
        Invalid request. The resource operations answer every request-shape
        rejection with `400`; they never answer `422`.
      headers:
        X-Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ResourceErrorResponse'
    ResourceUnauthorized:
      description: Missing, invalid, or expired API key.
      headers:
        X-Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ResourceErrorResponse'
    ResourceNotFound:
      description: |
        The resource does not exist, or it is not owned by the calling
        principal. The two cases are deliberately indistinguishable.
      headers:
        X-Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ResourceErrorResponse'
    ResourceConflict:
      description: The client-supplied task ID is already in use.
      headers:
        X-Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ResourceErrorResponse'
    ResourceInternalServerError:
      description: Unexpected server error.
      headers:
        X-Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ResourceErrorResponse'
    ResourceBadGateway:
      description: The extraction backend is unavailable or the extraction failed.
      headers:
        X-Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ResourceErrorResponse'
    ResourceServiceUnavailable:
      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/ResourceErrorResponse'
  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.

````