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

# Asset library authentication and conventions

> Endpoint, HMAC-SHA256 signing, response format, pagination and error codes of the Seedance asset library management API.

The asset library stores reference images, audio and videos for video generation. Once an asset is imported and its status is `Active`, reference it as `asset://<asset ID>` when you [create a video generation task](https://docs-vip.apigo.ai/en/api-reference/endpoints/seedance/create-task.md).

## Actions

All management actions share one endpoint and are selected by `Action`:

```text theme={null}
POST {BaseUrl}/?Action={Action}&Version=2024-01-01
Content-Type: application/json
```

| Action | Description |
| - | - |
| [CreateAssetGroup](https://docs-vip.apigo.ai/en/api-reference/endpoints/seedance/create-asset-group.md) | Create an asset group |
| [GetAssetGroup](https://docs-vip.apigo.ai/en/api-reference/endpoints/seedance/get-asset-group.md) | Get an asset group |
| [ListAssetGroups](https://docs-vip.apigo.ai/en/api-reference/endpoints/seedance/list-asset-groups.md) | List asset groups |
| [UpdateAssetGroup](https://docs-vip.apigo.ai/en/api-reference/endpoints/seedance/update-asset-group.md) | Update an asset group |
| [DeleteAssetGroup](https://docs-vip.apigo.ai/en/api-reference/endpoints/seedance/delete-asset-group.md) | Delete an asset group and all its assets |
| [CreateAsset](https://docs-vip.apigo.ai/en/api-reference/endpoints/seedance/create-asset.md) | Import an asset from a URL |
| [GetAsset](https://docs-vip.apigo.ai/en/api-reference/endpoints/seedance/get-asset.md) | Get an asset and its status |
| [ListAssets](https://docs-vip.apigo.ai/en/api-reference/endpoints/seedance/list-assets.md) | List assets |
| [UpdateAsset](https://docs-vip.apigo.ai/en/api-reference/endpoints/seedance/update-asset.md) | Rename an asset |
| [DeleteAsset](https://docs-vip.apigo.ai/en/api-reference/endpoints/seedance/delete-asset.md) | Delete a single asset |
| [CreateVisualValidateSession](https://docs-vip.apigo.ai/en/api-reference/endpoints/seedance/create-visual-validate-session.md) | Create a liveness verification session |
| [GetVisualValidateResult](https://docs-vip.apigo.ai/en/api-reference/endpoints/seedance/get-visual-validate-result.md) | Get the liveness verification result |

* `BaseUrl` is `https://api-vip.apigo.ai`. See [Usage overview](https://docs-vip.apigo.ai/en/api-reference/guide/interface-overview.md).
* `Version` is always `2024-01-01`.
* Request and response fields are case-sensitive, for example `URL`, `AssetType`, `GroupId`, `Status`.
* `ProjectName` is optional. Pass `default` or omit it.

## Authentication

> [!WARNING]
>
> The asset library management API uses HMAC-SHA256 signing and does **not** accept `Authorization: Bearer`. The video generation API uses Bearer. Both use the same ApiGo API key.

Signing parameters:

| Parameter | Value |
| - | - |
| AccessKey (AK) | ApiGo API key |
| SecretKey (SK) | The same ApiGo API key |
| Region | `ap-southeast-1` |
| Service | `ark` |
| SessionToken | Not used |

Example request:

```http theme={null}
POST /?Action=CreateAssetGroup&Version=2024-01-01 HTTP/1.1
Host: api-vip.apigo.ai
Content-Type: application/json
X-Date: 20261020T080000Z
X-Content-Sha256: <lowercase hex SHA-256 of the request body>
Authorization: HMAC-SHA256 Credential=<API_KEY>/20261020/ap-southeast-1/ark/request, SignedHeaders=content-type;host;x-content-sha256;x-date, Signature=<signature>

{"Name":"product-assets","GroupType":"AIGC"}
```

* `X-Date` is UTC in `YYYYMMDDTHHMMSSZ` format and must be within 15 minutes of server time.
* The signature covers the exact request body bytes sent. Changing the body, `Action`, `Host` or `X-Date` after signing requires a new signature.

### Signing algorithm

```text theme={null}
bodyHash         = SHA256_HEX(bodyBytes)
canonicalRequest = "POST\n"
                 + "/\n"
                 + "Action=<Action>&Version=2024-01-01\n"
                 + "content-type:application/json\n"
                 + "host:<Host>\n"
                 + "x-content-sha256:<bodyHash>\n"
                 + "x-date:<X-Date>\n"
                 + "\n"
                 + "content-type;host;x-content-sha256;x-date\n"
                 + bodyHash
scope            = "<YYYYMMDD>/ap-southeast-1/ark/request"
stringToSign     = "HMAC-SHA256\n<X-Date>\n" + scope + "\n" + SHA256_HEX(canonicalRequest)

kDate     = HMAC_SHA256(SK, "<YYYYMMDD>")
kRegion   = HMAC_SHA256(kDate, "ap-southeast-1")
kService  = HMAC_SHA256(kRegion, "ark")
kSigning  = HMAC_SHA256(kService, "request")
signature = HEX(HMAC_SHA256(kSigning, stringToSign))
```

`<YYYYMMDD>` is the date part of the same `X-Date`. Use the API key as SK directly, without any prefix.

### Signing code

> [tab] Go (Volcengine SDK)
>
> The Volcengine Go SDK signs requests automatically in `DoCall`. Requires Go 1.22 or later.
>
> ```shell theme={null}
> go mod init apigo-example
> go get github.com/volcengine/volcengine-go-sdk@v1.2.54
> go mod tidy
> ```
>
> ```go main.go theme={null}
> package main
>
> import (
>     "encoding/json"
>     "fmt"
>     "log"
>     "os"
>
>     "github.com/volcengine/volcengine-go-sdk/volcengine"
>     "github.com/volcengine/volcengine-go-sdk/volcengine/credentials"
>     "github.com/volcengine/volcengine-go-sdk/volcengine/session"
>     "github.com/volcengine/volcengine-go-sdk/volcengine/universal"
> )
>
> func main() {
>     key := os.Getenv("APIGO_API_KEY")
>     if key == "" {
>         log.Fatal("APIGO_API_KEY is not set")
>     }
>
>     config := volcengine.NewConfig().
>         WithCredentials(credentials.NewStaticCredentials(key, key, "")).
>         WithRegion("ap-southeast-1").
>         WithEndpoint("https://api-vip.apigo.ai")
>     sess, err := session.NewSession(config)
>     if err != nil {
>         log.Fatal(err)
>     }
>
>     resp, err := universal.New(sess).DoCall(
>         universal.RequestUniversal{
>             ServiceName: "ark",
>             Action:      "CreateAssetGroup",
>             Version:     "2024-01-01",
>             HttpMethod:  universal.POST,
>             ContentType: universal.ApplicationJSON,
>         },
>         &map[string]any{
>             "Name":      "product-assets",
>             "GroupType": "AIGC",
>         },
>     )
>     if err != nil {
>         log.Fatal(err)
>     }
>     data, _ := json.MarshalIndent(resp, "", "  ")
>     fmt.Println(string(data))
> }
> ```
>
> For other actions, change `Action` and the request body.

> [tab] Python (standard library)
>
> Python 3.9+, standard library only.
>
> ```python main.py theme={null}
> import hashlib
> import hmac
> import json
> import os
> from datetime import datetime, timezone
> from urllib.error import HTTPError
> from urllib.request import Request, urlopen
>
> HOST = "api-vip.apigo.ai"
> REGION, SERVICE, VERSION = "ap-southeast-1", "ark", "2024-01-01"
>
>
> def call(key, action, body):
>     payload = json.dumps(body, ensure_ascii=False, separators=(",", ":")).encode("utf-8")
>     x_date = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ")
>     day = x_date[:8]
>     query = f"Action={action}&Version={VERSION}"
>     body_hash = hashlib.sha256(payload).hexdigest()
>     signed_headers = "content-type;host;x-content-sha256;x-date"
>     canonical_request = "\n".join([
>         "POST", "/", query,
>         f"content-type:application/json\nhost:{HOST}\n"
>         f"x-content-sha256:{body_hash}\nx-date:{x_date}\n",
>         signed_headers, body_hash,
>     ])
>     scope = f"{day}/{REGION}/{SERVICE}/request"
>     string_to_sign = "\n".join([
>         "HMAC-SHA256", x_date, scope,
>         hashlib.sha256(canonical_request.encode("utf-8")).hexdigest(),
>     ])
>     signing_key = key.encode("utf-8")
>     for part in (day, REGION, SERVICE, "request"):
>         signing_key = hmac.new(signing_key, part.encode("utf-8"), hashlib.sha256).digest()
>     signature = hmac.new(signing_key, string_to_sign.encode("utf-8"), hashlib.sha256).hexdigest()
>
>     request = Request(f"https://{HOST}/?{query}", data=payload, method="POST", headers={
>         "Content-Type": "application/json",
>         "X-Date": x_date,
>         "X-Content-Sha256": body_hash,
>         "Authorization": f"HMAC-SHA256 Credential={key}/{scope}, "
>                          f"SignedHeaders={signed_headers}, Signature={signature}",
>     })
>     try:
>         with urlopen(request, timeout=60) as resp:
>             return resp.status, json.loads(resp.read())
>     except HTTPError as e:
>         return e.code, json.loads(e.read())
>
>
> if __name__ == "__main__":
>     status, data = call(os.environ["APIGO_API_KEY"], "CreateAssetGroup", {
>         "Name": "product-assets",
>         "GroupType": "AIGC",
>     })
>     print(status, json.dumps(data, ensure_ascii=False, indent=2))
> ```
>
> For other actions, change the `action` and `body` passed to `call`.

curl cannot compute the signature. To use curl, compute `X-Date`, `X-Content-Sha256` and `Authorization` with the code above, then send the identical request body.

## Response format

On success, HTTP 200 with the payload in `Result`:

```json theme={null}
{
  "ResponseMetadata": {
    "RequestId": "20261020080000ABCDEF",
    "Action": "CreateAssetGroup",
    "Version": "2024-01-01",
    "Service": "ark",
    "Region": "ap-southeast-1"
  },
  "Result": {
    "Id": "group-20261020080000-abcde"
  }
}
```

On failure there is no `Result`; the error is in `ResponseMetadata.Error`:

```json theme={null}
{
  "ResponseMetadata": {
    "RequestId": "20261020080000ABCDEF",
    "Action": "CreateAsset",
    "Version": "2024-01-01",
    "Service": "ark",
    "Region": "ap-southeast-1",
    "Error": {
      "Code": "InvalidParameter.TypeMismatch",
      "Message": "Expected audio, got image."
    }
  }
}
```

## Asset status

| Status | Description |
| - | - |
| `Processing` | Being processed. Check again later |
| `Active` | Ready to be referenced by video generation tasks |
| `Failed` | Processing failed. See `Error.Code` and `Error.Message` |

After `CreateAsset` returns an asset ID, the asset is usually `Processing`. Poll [GetAsset](https://docs-vip.apigo.ai/en/api-reference/endpoints/seedance/get-asset.md) until it becomes `Active` before using it. Processing time varies.

## Real-person assets

There are two asset group types:

| GroupType | How it is created | Purpose |
| - | - | - |
| `AIGC` | [CreateAssetGroup](https://docs-vip.apigo.ai/en/api-reference/endpoints/seedance/create-asset-group.md) | Regular reference assets |
| `LivenessFace` | Created automatically when a person completes liveness verification | Real-person portrait assets |

To generate videos with a real person's portrait as reference, the person must first complete liveness verification, then their photos are imported into the real-person group created by that verification:

1. Call [CreateVisualValidateSession](https://docs-vip.apigo.ai/en/api-reference/endpoints/seedance/create-visual-validate-session.md) to get `BytedToken` and `H5Link`.
2. The person opens `H5Link` in a mobile browser and completes the face scan.
3. Poll [GetVisualValidateResult](https://docs-vip.apigo.ai/en/api-reference/endpoints/seedance/get-visual-validate-result.md) until it returns `GroupId`.
4. Call [CreateAsset](https://docs-vip.apigo.ai/en/api-reference/endpoints/seedance/create-asset.md) with that real-person group ID as `GroupId` and the person's photo as `URL`.
5. Once [GetAsset](https://docs-vip.apigo.ai/en/api-reference/endpoints/seedance/get-asset.md) returns `Active`, reference it in generation tasks as `asset://<asset ID>`.

* Each successful verification creates a new real-person group with an empty name. Use [UpdateAssetGroup](https://docs-vip.apigo.ai/en/api-reference/endpoints/seedance/update-asset-group.md) to set a name and description.
* A real-person group only accepts photos of the verified person. If the photo does not match, the asset status becomes `Failed` with `Error.Code` `FaceMismatch`.
* To query real-person groups or their assets, pass `LivenessFace` as `Filter.GroupType` to `ListAssetGroups` / `ListAssets`.
* Real-person groups can be deleted with [DeleteAssetGroup](https://docs-vip.apigo.ai/en/api-reference/endpoints/seedance/delete-asset-group.md), which also deletes their assets.

## Pagination

`ListAssetGroups` and `ListAssets` support two pagination modes. Use only one per request.

### NextToken pagination (default)

| Parameter | Type | Description |
| - | - | - |
| `MaxResults` | integer | Items per page, 1–100, default 10 |
| `NextToken` | string | Omit on the first request; then pass the previous `Result.NextToken` |

* No `NextToken` in the response means you have reached the last page.
* Keep `Filter`, `SortBy` and `SortOrder` unchanged while paging, and pass `NextToken` back as-is.
* This mode does not return `TotalCount`.

### Page number pagination

| Parameter | Type | Description |
| - | - | - |
| `PageNumber` | integer | Page number, starting at 1 |
| `PageSize` | integer | Items per page, 1–100 |

* `PageNumber` and `PageSize` must be passed together and cannot be combined with `MaxResults` or `NextToken`.
* The response includes `TotalCount`, `PageNumber` and `PageSize`.
* Depth limit: `PageNumber × PageSize ≤ 10000` for `ListAssetGroups` and `≤ 20000` for `ListAssets`. Exceeding it returns `400 InvalidParameter`. Use NextToken pagination to go deeper.

## Error codes

| HTTP status | Code | Description |
| - | - | - |
| 400 | `MissingParameter` | A required parameter is missing |
| 400 | `InvalidParameter` | Invalid parameter type, value, length or pagination parameters |
| 400 | `InvalidParameter.TypeMismatch`, `InvalidParameter.DownloadFailed`, `InvalidParameter.FormatUnsupported`, etc. | File type does not match `AssetType`, the URL cannot be downloaded, or the format is unsupported |
| 400 | `InvalidAuthorization` | The `Authorization` header is missing `SignedHeaders`, `Signature` or other fields |
| 400 | `MissingRequestInfo` | `X-Date` is missing or malformed |
| 400 | `InvalidTimestamp` | `X-Date` differs from server time by more than 15 minutes |
| 401 | `InvalidAccessKey` | The API key is invalid or disabled. Also returned when `Authorization` is missing or Bearer is used |
| 403 | `SignatureDoesNotMatch` | Signature mismatch |
| 404 | `NotFound` | The asset or group does not exist, was deleted, or does not belong to your account; or the liveness result is not available yet |
| 404 | `InvalidActionOrVersion` | Unknown `Action`, or `Version` is not `2024-01-01` |
| 429 | `AccessDenied` | The caller IP is not in the API key's allowlist |
| 429 | `AccountOverdueError` | Insufficient balance (checked when creating groups, importing assets and creating liveness sessions) |
| 429 | `QuotaExceeded` | API key quota reached |
| 429 | `FlowLimitExceeded` | Too many requests |
| 500 | `InternalError` | Internal server error |
| 503 | `ServiceUnavailableTemp` | Service temporarily unavailable |

An import failure can surface in two ways: `CreateAsset` returns an error directly, or creation succeeds and `GetAsset` later returns `Status=Failed`.


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