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

# Doubao Seedance Asset Library

> Seedance 2.0 private asset library: upload virtual/real-person assets and reference them in video generation via asset://.

Doubao Seedance 2.0 lets you ingest assets (image/video/audio) and reference them in generation with `asset://<asset_id>`, instead of passing a public URL every time. The gateway mirrors the official Volcengine asset API 1:1 (`/doubao/?Action=...`).

## Overview

The Seedance 2.0 asset library has two kinds:

* **Virtual avatar / generic assets**: assets you own and may use commercially. Create group -> upload -> poll -> use.
* **Real-person assets**: require real-person verification (H5 liveness check) first, then upload that person's assets. Volcengine forbids uploading reference images containing real faces directly, so real-person material must go through this flow.

End-to-end flow:

```mermaid theme={null}
sequenceDiagram
    participant Client
    participant Gateway as API Gateway
    participant Volc as Volcengine Ark
    Client->>Gateway: POST /doubao/?Action=CreateAsset
    Gateway->>Volc: forward with AK/SK V4 signature
    Volc-->>Gateway: { Result: { Id: "asset-..." } }
    Gateway-->>Client: return asset_id (records asset->channel mapping)
    loop poll until Active
        Client->>Gateway: POST /doubao/?Action=GetAsset
        Gateway-->>Client: { Result: { Status: "Processing" } }
    end
    Client->>Gateway: POST /v1/task/submit (reference_images: ["asset://asset-..."])
    Gateway-->>Client: generates video, auto-routed back to the owning channel
```

**Account binding (important)**: `asset_id` is bound to a Volcengine account. An asset can only be referenced by generation under the same account that created it. The gateway records an asset->channel mapping automatically, so generation requests referencing `asset://` are hard-pinned back to the channel that created the asset; you do not need to specify it manually.

***

## Prerequisites (admin setup)

1. In the VolcEngine (Doubao) channel's "Advanced Settings", fill in **Asset Library Access Key (AK)** and **Secret Key (SK)**.
   * Video generation still uses the channel's `ark-` API Key (Bearer); the two are independent.
   * **The AK/SK MUST belong to the same Volcengine account as that `ark-` key**, otherwise referencing `asset://` during generation returns "asset not found".
   * If the channel key itself is in `ak\|sk` format, you can leave AK/SK blank and the gateway will reuse it.
2. (Recommended) Set the **Asset Library Project (ProjectName)** in the channel's advanced settings. `ProjectName` is a Volcengine IAM project (resource scope) that the AK/SK must be **authorized** on. When set, the gateway **force-overrides** the `ProjectName` in the user's request body so the channel controls it — end users then do **not** need to (and should not) pass `ProjectName` themselves, which avoids `AccessDenied` from a wrong value and guarantees create-group / upload / query all use the same project. Leave blank to pass through the user's value (Volcengine default: `default`).
3. Add the sentinel model **`doubao-asset`** to the channel's model list (used to route asset-management requests to this channel).
4. Asset-management calls are **free** (not billed).

Common error `AccessDenied: User is not authorized to perform: ark:CreateAsset on resource: trn:iam::<account>:project/<project>` is a Volcengine IAM permission issue (not a gateway issue): the AK/SK (usually a sub-account) lacks `ark:CreateAsset` permission on that project. Fix: use the main-account AK/SK, or grant the sub-account the Ark asset permissions scoped to that project in Volcengine Access Control; and prefer setting the channel's "Asset Library Project" to control `ProjectName` centrally.

***

## Authentication

All requests use the gateway token:

```
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
```

Unified asset endpoint (mirrors the official API): `POST /doubao/?Action=<Action>&Version=2024-01-01`

***

## Simplified interface (recommended)

Besides mirroring the official `/doubao/?Action=...`, the gateway offers a **unified simplified interface** that reuses the platform's common `POST /v1/task/submit` entry and `{model, input}` style, hiding Volcengine's Action / GroupId / ProjectName / signing complexity. **Recommended for most use cases.**

* Single entry `POST /v1/task/submit`, `model` is always `doubao-asset`, and `input.action` selects the operation.
* **Synchronous**: asset operations are fast and return the result (`asset_id`, etc.) immediately, no polling.
* **Auto-managed asset group**: when `upload` omits `group_id`, the gateway auto-creates and reuses a **per-user asset group**; it lands in the **same group** as the native `/doubao` interface, so assets uploaded either way are mutually referenceable.
* Unified response: `{ "code": 0, "message": "ok", "data": {...} }` (`code != 0` means error).

### Supported actions

```bash Upload theme={null} theme={null}
curl -X POST "https://api.ephone.ai/v1/task/submit" \
  -H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \
  -d '{ "model": "doubao-asset", "input": { "action": "upload", "url": "https://example.com/a.png", "asset_type": "Image", "name": "demo" } }'
```

```bash Query theme={null} theme={null}
curl -X POST "https://api.ephone.ai/v1/task/submit" \
  -H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \
  -d '{ "model": "doubao-asset", "input": { "action": "query", "asset_id": "asset-xxx" } }'
```

```bash List own assets theme={null} theme={null}
curl -X POST "https://api.ephone.ai/v1/task/submit" \
  -H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \
  -d '{ "model": "doubao-asset", "input": { "action": "list" } }'
```

```bash Delete theme={null} theme={null}
curl -X POST "https://api.ephone.ai/v1/task/submit" \
  -H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \
  -d '{ "model": "doubao-asset", "input": { "action": "delete", "asset_id": "asset-xxx" } }'
```

`action` values: `upload` (needs `url`+`asset_type`, optional `name`/`group_id`), `query` (needs `asset_id`), `list` (lists your own assets), `delete` (needs `asset_id`), `create_validate_session` / `get_validate_result` (real-person verification, see below).

A successful upload returns:

```json theme={null}
{ "code": 0, "message": "ok", "data": { "Id": "asset-20260...-xxxx" } }
```

Once processed (`query` returns `Status: "Active"`), reference it in video generation via `asset://<asset_id>`.

**Isolation + interop**: each user's assets go into their own dedicated group, isolated from others; for a given user, assets created via the simplified interface and the native `/doubao` interface share the same group and are mutually referenceable.

### Real-person verification (simplified)

Real-person verification requires the actor to complete a liveness face scan on their phone, which happens between "actor browser ↔ Volcengine" and cannot be proxied by the gateway -- you still provide a `callback_url` to receive Volcengine's redirect after the scan. The simplified interface only unifies the entry and field names:

```bash 1. Create a validation session theme={null} theme={null}
curl -X POST "https://api.ephone.ai/v1/task/submit" \
  -H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \
  -d '{ "model": "doubao-asset", "input": { "action": "create_validate_session", "callback_url": "https://your.app/cb" } }'
```

```bash 2. Exchange byted_token for a group theme={null} theme={null}
curl -X POST "https://api.ephone.ai/v1/task/submit" \
  -H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \
  -d '{ "model": "doubao-asset", "input": { "action": "get_validate_result", "byted_token": "byted_token_..." } }'
```

With the verified `group_id`, call `upload` and pass `group_id` explicitly to upload the real-person asset into that group.

### Using it in Playground

Admins configure `playground_mode=task` and the following `task_input_schema` (JSON Schema) on the `doubao-asset` model to render a dynamic form in Playground:

```json theme={null}
{
  "type": "object",
  "properties": {
    "action": { "type": "string", "enum": ["upload", "query", "list", "delete", "create_validate_session", "get_validate_result"], "title": "Action" },
    "url": { "type": "string", "title": "Asset URL (upload)" },
    "asset_type": { "type": "string", "enum": ["Image", "Video", "Audio"], "title": "Asset type (upload)" },
    "name": { "type": "string", "title": "Name (optional)" },
    "group_id": { "type": "string", "title": "Group ID (optional / real-person)" },
    "asset_id": { "type": "string", "title": "Asset ID (query/delete)" },
    "callback_url": { "type": "string", "title": "Callback URL (real-person)" },
    "byted_token": { "type": "string", "title": "BytedToken (real-person)" }
  },
  "required": ["action"]
}
```

***

## Native mirror interface

Below are the 1:1 mirrors of Volcengine's official low-level endpoints (`/doubao/?Action=...`). If you already use the simplified interface above, you usually don't need these directly; they are for cases that require precise `GroupId` control or integrating with Volcengine's official SDK.

## Virtual avatar / generic asset flow

### Step 1: Create an asset group

```bash cURL theme={null} theme={null}
curl -X POST "https://api.ephone.ai/doubao/?Action=CreateAssetGroup&Version=2024-01-01" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "Name": "my-character-group",
    "Description": "For Seedance video generation",
    "GroupType": "AIGC"
  }'
```

Response:

```json theme={null}
{ "ResponseMetadata": { "RequestId": "..." }, "Result": { "Id": "group-20260318033332-xxxxx" } }
```

### Step 2: Upload an asset

Upload one asset per request. `AssetType` can be `Image` / `Video` / `Audio`; `URL` must be publicly reachable.

```bash cURL theme={null} theme={null}
curl -X POST "https://api.ephone.ai/doubao/?Action=CreateAsset&Version=2024-01-01" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "GroupId": "group-20260318033332-xxxxx",
    "URL": "https://example.com/character.png",
    "AssetType": "Image",
    "Name": "character front view"
  }'
```

Response:

```json theme={null}
{ "Result": { "Id": "asset-20260318071009-xxxxx" } }
```

### Step 3: Poll asset status

Upload is processed asynchronously; poll until `Status` becomes `Active` before using it (`Failed` means processing failed).

```bash cURL theme={null} theme={null}
curl -X POST "https://api.ephone.ai/doubao/?Action=GetAsset&Version=2024-01-01" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "Id": "asset-20260318071009-xxxxx" }'
```

Response:

```json theme={null}
{ "Result": { "Id": "asset-20260318071009-xxxxx", "Status": "Active", "AssetType": "Image", "URL": "https://...(valid 12h)" } }
```

### Other management endpoints

`ListAssetGroups` / `ListAssets` / `GetAssetGroup` / `UpdateAssetGroup` / `UpdateAsset` / `DeleteAsset` / `DeleteAssetGroup` all work the same way: `POST /doubao/?Action=<Action>&Version=2024-01-01` with the corresponding JSON body.

***

## Real-person asset flow

Real-person assets require real-person verification (H5 liveness check) first.

```mermaid theme={null}
flowchart LR
    A["1. CreateVisualValidateSession -> H5Link + BytedToken"] --> B["2. Actor opens H5Link and verifies<br/>Volcengine redirects to your CallbackURL"]
    B --> C["3. GetVisualValidateResult exchanges BytedToken for GroupId"]
    C --> D["4. CreateAsset uploads the asset"]
    D --> E["5. GetAsset polls until Active"]
```

### Step 1: Create a verification session

```bash cURL theme={null} theme={null}
curl -X POST "https://api.ephone.ai/doubao/?Action=CreateVisualValidateSession&Version=2024-01-01" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "CallbackURL": "https://your-callback.com/done" }'
```

The response contains `H5Link` (the actor opens it to verify) and `BytedToken` (valid 120s, single-use):

```json theme={null}
{ "Result": { "H5Link": "https://...volccdn.com/verify?token=...", "BytedToken": "byted_token_..." } }
```

After the actor completes the liveness check on mobile, Volcengine redirects back to your `CallbackURL` with `resultCode` (`10000` means success) and related params.

### Step 2: Exchange BytedToken for GroupId

```bash cURL theme={null} theme={null}
curl -X POST "https://api.ephone.ai/doubao/?Action=GetVisualValidateResult&Version=2024-01-01" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "BytedToken": "byted_token_..." }'
```

```json theme={null}
{ "Result": { "GroupId": "group-..." } }
```

> If verification is not finished yet, it may return `ValidatePending`; retry shortly after.

### Steps 3-4: Upload + poll

With the `GroupId`, call `CreateAsset` (same as virtual avatar Step 2) to upload the person's asset, then poll with `GetAsset` until `Active`. Different looks of the same person can live under the same `GroupId`.

***

## Referencing assets in video generation

Once an asset is `Status=Active`, write it as `asset://<asset_id>` to reference it in a generation request.

### Option A: Unified async task endpoint `/v1/task/submit`

Put `asset://...` into `reference_images` / `first_frame` / `reference_videos` / `reference_audio`:

```bash cURL theme={null} theme={null}
curl -X POST "https://api.ephone.ai/v1/task/submit" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "doubao-seedance-2-0-260128",
    "input": {
      "prompt": "the character turns slowly in front of a floor-to-ceiling window, slight camera push-in",
      "reference_images": ["asset://asset-20260318071009-xxxxx"]
    }
  }'
```

### Option B: Native `/doubao` endpoint

Put `asset://...` into the `content` array's `image_url` / `video_url` / `audio_url`:

```bash cURL theme={null} theme={null}
curl -X POST "https://api.ephone.ai/doubao/api/v3/contents/generations/tasks" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "doubao-seedance-2-0-260128",
    "content": [
      { "type": "text", "text": "the character turns slowly in a city night scene" },
      { "type": "image_url", "image_url": { "url": "asset://asset-20260318071009-xxxxx" }, "role": "reference_image" }
    ]
  }'
```

***

## Notes

* A successful upload does not mean it is immediately usable; wait until `Status=Active`.
* Asset URLs must stay publicly reachable, otherwise processing fails.
* Keep a single character / single person's assets in one group.
* `asset_id` is account-bound: it cannot be referenced across accounts. The gateway records an asset->channel mapping on creation and auto-pins generation back to that channel.
* **Per-user isolation**: each token (user) can only query / update / delete assets it created via the gateway; `ListAssets` / `ListAssetGroups` return only your own; calling `GetAsset` / `DeleteAsset` etc. with someone else's `asset_id` is rejected with `403 AssetNotOwned`; **referencing someone else's `asset://` during video generation is likewise rejected with 403** (prevents misusing another user's real-person/virtual assets). Users sharing the same channel (same Volcengine account) cannot see each other's assets.
* If you create assets in the Volcengine console / experience center (not via this gateway), the gateway has no mapping; those assets are **not visible / not operable for regular users** (the intended isolation behavior). In multi-channel (multi-account) setups, use a token bound to the matching channel (token fixed channel) to ensure correct routing.


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