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

# 素材库鉴权与通用约定

> Seedance 素材库管理接口的地址、HMAC-SHA256 签名、响应结构、分页与错误码。

素材库用于管理视频生成的参考图片、音频和视频。素材导入并处于 `Active` 状态后，可以在[创建视频生成任务](https://docs-vip.apigo.ai/api-reference/endpoints/seedance/create-task.md)时以 `asset://<素材ID>` 引用。

## 接口列表

所有管理动作使用同一个地址，通过 `Action` 区分：

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

| Action | 说明 |
| - | - |
| [CreateAssetGroup](https://docs-vip.apigo.ai/api-reference/endpoints/seedance/create-asset-group.md) | 创建素材组 |
| [GetAssetGroup](https://docs-vip.apigo.ai/api-reference/endpoints/seedance/get-asset-group.md) | 查询素材组详情 |
| [ListAssetGroups](https://docs-vip.apigo.ai/api-reference/endpoints/seedance/list-asset-groups.md) | 查询素材组列表 |
| [UpdateAssetGroup](https://docs-vip.apigo.ai/api-reference/endpoints/seedance/update-asset-group.md) | 修改素材组 |
| [DeleteAssetGroup](https://docs-vip.apigo.ai/api-reference/endpoints/seedance/delete-asset-group.md) | 删除素材组及组内素材 |
| [CreateAsset](https://docs-vip.apigo.ai/api-reference/endpoints/seedance/create-asset.md) | 通过 URL 导入素材 |
| [GetAsset](https://docs-vip.apigo.ai/api-reference/endpoints/seedance/get-asset.md) | 查询素材详情与状态 |
| [ListAssets](https://docs-vip.apigo.ai/api-reference/endpoints/seedance/list-assets.md) | 查询素材列表 |
| [UpdateAsset](https://docs-vip.apigo.ai/api-reference/endpoints/seedance/update-asset.md) | 修改素材名称 |
| [DeleteAsset](https://docs-vip.apigo.ai/api-reference/endpoints/seedance/delete-asset.md) | 删除单个素材 |
| [CreateVisualValidateSession](https://docs-vip.apigo.ai/api-reference/endpoints/seedance/create-visual-validate-session.md) | 创建真人活体认证会话 |
| [GetVisualValidateResult](https://docs-vip.apigo.ai/api-reference/endpoints/seedance/get-visual-validate-result.md) | 查询活体认证结果 |

* `BaseUrl` 为 `https://api-vip.apigo.ai`，见[使用概述](https://docs-vip.apigo.ai/api-reference/guide/interface-overview.md)。
* `Version` 固定为 `2024-01-01`。
* 请求和响应字段区分大小写，例如 `URL`、`AssetType`、`GroupId`、`Status`。
* `ProjectName` 为可选参数，可传 `default` 或不传。

## 鉴权

> [!WARNING]
>
> 素材库管理接口使用 HMAC-SHA256 签名，**不支持** `Authorization: Bearer`。视频生成接口使用 Bearer。两者使用同一个 ApiGo API Key。

签名参数：

| 参数 | 取值 |
| - | - |
| AccessKey（AK） | ApiGo API Key |
| SecretKey（SK） | 同一个 ApiGo API Key |
| Region | `ap-southeast-1` |
| Service | `ark` |
| SessionToken | 不传 |

请求示例：

```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: <请求体 SHA-256 小写十六进制>
Authorization: HMAC-SHA256 Credential=<API_KEY>/20261020/ap-southeast-1/ark/request, SignedHeaders=content-type;host;x-content-sha256;x-date, Signature=<签名>

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

* `X-Date` 为 UTC 时间，格式 `YYYYMMDDTHHMMSSZ`，与服务端时间相差不能超过 15 分钟。
* 签名基于实际发送的请求体字节。签名后修改请求体、`Action`、`Host` 或 `X-Date` 都需要重新签名。

### 签名算法

```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>` 取同一个 `X-Date` 的日期部分。SK 直接使用 API Key，不加任何前缀。

### 签名代码

> [tab] Go（火山 SDK）
>
> 使用火山引擎 Go SDK，`DoCall` 自动完成签名。需要 Go 1.22 或更高版本。
>
> ```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")
>     }
>
>     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))
> }
> ```
>
> 调用其他动作时，修改 `Action` 和请求体即可。

> [tab] Python（标准库）
>
> Python 3.9+，仅使用标准库。
>
> ```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))
> ```
>
> 调用其他动作时，修改 `call` 的 `action` 和 `body` 即可。

curl 不能自动生成签名。使用 curl 时，先用上面的代码算出 `X-Date`、`X-Content-Sha256` 和 `Authorization`，再按原样发送同一份请求体。

## 响应结构

成功时 HTTP 200，业务数据位于 `Result`：

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

失败时没有 `Result`，错误位于 `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."
    }
  }
}
```

## 素材状态

| Status | 说明 |
| - | - |
| `Processing` | 处理中，稍后再查 |
| `Active` | 已就绪，可被视频生成任务引用 |
| `Failed` | 处理失败，原因见 `Error.Code` 与 `Error.Message` |

`CreateAsset` 返回素材 ID 后，素材通常处于 `Processing`。通过 [GetAsset](https://docs-vip.apigo.ai/api-reference/endpoints/seedance/get-asset.md) 轮询到 `Active` 后再用于生成，处理时长不固定。

## 真人素材

素材组分为两种类型：

| GroupType | 创建方式 | 用途 |
| - | - | - |
| `AIGC` | [CreateAssetGroup](https://docs-vip.apigo.ai/api-reference/endpoints/seedance/create-asset-group.md) | 普通参考素材 |
| `LivenessFace` | 真人完成活体认证后自动生成 | 真人人像素材 |

以真人人像作为参考生成视频时，需先由本人完成活体认证，再把本人照片导入认证生成的真人素材组：

1. 调用 [CreateVisualValidateSession](https://docs-vip.apigo.ai/api-reference/endpoints/seedance/create-visual-validate-session.md)，取得 `BytedToken` 和 `H5Link`。
2. 本人在手机浏览器中打开 `H5Link` 完成刷脸。
3. 调用 [GetVisualValidateResult](https://docs-vip.apigo.ai/api-reference/endpoints/seedance/get-visual-validate-result.md) 轮询，成功后取得 `GroupId`。
4. 调用 [CreateAsset](https://docs-vip.apigo.ai/api-reference/endpoints/seedance/create-asset.md)，`GroupId` 传上一步的真人素材组 ID，`URL` 传本人照片。
5. [GetAsset](https://docs-vip.apigo.ai/api-reference/endpoints/seedance/get-asset.md) 返回 `Active` 后，在生成任务中以 `asset://<素材ID>` 引用。

* 每次认证成功都会生成一个新的真人素材组，组名称为空，可用 [UpdateAssetGroup](https://docs-vip.apigo.ai/api-reference/endpoints/seedance/update-asset-group.md) 设置名称和描述。
* 真人素材组只能导入认证本人的照片。照片与认证人不一致时，素材状态变为 `Failed`，`Error.Code` 为 `FaceMismatch`。
* 查询真人素材组或其中的素材时，`ListAssetGroups` / `ListAssets` 的 `Filter.GroupType` 传 `LivenessFace`。
* 真人素材组可通过 [DeleteAssetGroup](https://docs-vip.apigo.ai/api-reference/endpoints/seedance/delete-asset-group.md) 删除，组内素材一并删除。

## 分页

`ListAssetGroups` 和 `ListAssets` 支持两种分页方式，每次请求只能选择一种。

### NextToken 分页（默认）

| 参数 | 类型 | 说明 |
| - | - | - |
| `MaxResults` | integer | 每页条数，1–100，默认 10 |
| `NextToken` | string | 首次请求不传；之后传上一页返回的 `Result.NextToken` |

* 响应不含 `NextToken` 时表示已到最后一页。
* 翻页时保持 `Filter`、`SortBy`、`SortOrder` 不变，`NextToken` 原样传回。
* 此方式不返回 `TotalCount`。

### 页码分页

| 参数 | 类型 | 说明 |
| - | - | - |
| `PageNumber` | integer | 页码，从 1 开始 |
| `PageSize` | integer | 每页条数，1–100 |

* `PageNumber` 和 `PageSize` 必须同时传，且不能与 `MaxResults`、`NextToken` 同时使用。
* 响应返回 `TotalCount`、`PageNumber`、`PageSize`。
* 翻页深度上限：`ListAssetGroups` 为 `PageNumber × PageSize ≤ 10000`，`ListAssets` 为 `PageNumber × PageSize ≤ 20000`，超出返回 `400 InvalidParameter`。需要遍历更多数据时使用 NextToken 分页。

## 错误码

| HTTP 状态码 | Code | 说明 |
| - | - | - |
| 400 | `MissingParameter` | 缺少必填参数 |
| 400 | `InvalidParameter` | 参数类型、取值、长度或分页参数不合法 |
| 400 | `InvalidParameter.TypeMismatch`、`InvalidParameter.DownloadFailed`、`InvalidParameter.FormatUnsupported` 等 | 素材类型与 `AssetType` 不符、URL 无法下载或格式不支持 |
| 400 | `InvalidAuthorization` | `Authorization` 请求头缺少 `SignedHeaders`、`Signature` 等字段 |
| 400 | `MissingRequestInfo` | 缺少 `X-Date` 或格式错误 |
| 400 | `InvalidTimestamp` | `X-Date` 与服务端时间相差超过 15 分钟 |
| 401 | `InvalidAccessKey` | API Key 无效或已停用；缺少 `Authorization` 或使用 Bearer 鉴权时也返回此错误 |
| 403 | `SignatureDoesNotMatch` | 签名不匹配 |
| 404 | `NotFound` | 素材、素材组不存在、已删除或不属于当前账号；活体认证结果尚未生成 |
| 404 | `InvalidActionOrVersion` | `Action` 不存在，或 `Version` 不是 `2024-01-01` |
| 429 | `AccessDenied` | 调用 IP 不在 API Key 的白名单内 |
| 429 | `AccountOverdueError` | 余额不足（创建素材组、导入素材、创建活体认证会话时校验） |
| 429 | `QuotaExceeded` | 达到 API Key 设置的额度上限 |
| 429 | `FlowLimitExceeded` | 请求过于频繁 |
| 500 | `InternalError` | 服务内部错误 |
| 503 | `ServiceUnavailableTemp` | 服务暂时不可用 |

素材导入失败可能以两种形式出现：`CreateAsset` 直接返回错误，或创建成功后 `GetAsset` 返回 `Status=Failed`。


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