API 文档
素材库 API
AccessKey 签名、虚拟素材入库、单个状态查询与真人扫码授权完整格式
接口路径、认证方式、参数与任务状态说明
素材库 API
素材 API 用于创建虚拟人物分组、提交图片/视频/音频、查询审核状态,以及通过二维码完成真人素材授权。所有 ID 都与 AccessKey 所属用户绑定,跨用户访问会被拒绝。
基础信息
| 项目 | 值 |
|---|---|
| 请求方法 | POST |
| 基础路径 | https://locopalai.com/api/v3 |
| 公共查询参数 | Action=<接口名>&Version=2024-01-01 |
| 请求格式 | application/json |
| 鉴权 | AccessKey / SecretKey + HMAC-SHA256 |
| 区域与服务 | cn-beijing / ark |
AccessKey
登录平台后打开“AccessKey 管理”,点击创建。创建响应会显示 AccessKey 与 SecretKey,SecretKey 只完整显示一次。
- AccessKey 用于标识调用方,可在列表中查看掩码。
- SecretKey 用于签名,无法从列表再次读取。
- 停用、过期或删除后不能继续调用。
- 每个 AccessKey 只能访问本账户的分组、素材和真人授权会话。
AccessKey 只用于素材接口,不能代替模型 API Key。两者用途如下:
| 凭据 | 使用位置 | 请求头/签名 | 用途 |
|---|---|---|---|
| API Key | /v1/* | Authorization: Bearer sk-... | 查询模型、创建图片/视频任务、查询生成结果。 |
| AccessKey + SecretKey | /api/v3 | HMAC-SHA256 | 创建分组、素材入库、查询审核状态、真人扫码授权。 |
典型业务会同时使用两套凭据:先用 AccessKey 完成素材入库并等待可用,再用 API Key 提交包含 asset://<素材ID> 的视频请求。
HMAC-SHA256 签名
Content-Type: application/json
Host: locopalai.com
X-Date: 20260908T120000Z
X-Content-Sha256: <实际请求体的 SHA-256 小写十六进制>
Authorization: HMAC-SHA256 Credential=<AccessKey>/<YYYYMMDD>/cn-beijing/ark/request, SignedHeaders=content-type;host;x-content-sha256;x-date, Signature=<signature>规范化请求:
CanonicalRequest = Method + "\n" + Path + "\n" + CanonicalQuery + "\n" +
CanonicalHeaders + "\n" + SignedHeaders + "\n" + SHA256(Payload)
Scope = YYYYMMDD/cn-beijing/ark/request
StringToSign = HMAC-SHA256 + "\n" + X-Date + "\n" + Scope + "\n" + SHA256(CanonicalRequest)CanonicalQuery 按键排序。对 /api/v3?Action=GetAsset&Version=2024-01-01,值为 Action=GetAsset&Version=2024-01-01。签名派生顺序是日期 → 区域 → 服务 → request → StringToSign。请求时间与服务器相差不能超过 15 分钟。
Node.js 完整签名客户端
import crypto from 'node:crypto'
const host = 'locopalai.com'
const accessKey = process.env.LOCOPAL_ACCESS_KEY!
const secretKey = process.env.LOCOPAL_SECRET_KEY!
const sha256 = (value: string | Buffer) =>
crypto.createHash('sha256').update(value).digest('hex')
const hmac = (key: string | Buffer, value: string) =>
crypto.createHmac('sha256', key).update(value).digest()
async function materialRequest(action: string, input: Record<string, unknown>) {
const body = JSON.stringify(input)
const xDate = new Date().toISOString().replace(/[-:]/g, '').replace(/\.\d{3}Z$/, 'Z')
const shortDate = xDate.slice(0, 8)
const payloadHash = sha256(body)
const query = `Action=${encodeURIComponent(action)}&Version=2024-01-01`
const signedHeaders = 'content-type;host;x-content-sha256;x-date'
const canonicalHeaders =
`content-type:application/json\nhost:${host}\n` +
`x-content-sha256:${payloadHash}\nx-date:${xDate}\n`
const canonicalRequest = [
'POST', '/api/v3', query, canonicalHeaders, signedHeaders, payloadHash,
].join('\n')
const scope = `${shortDate}/cn-beijing/ark/request`
const stringToSign = `HMAC-SHA256\n${xDate}\n${scope}\n${sha256(canonicalRequest)}`
const signingKey = hmac(hmac(hmac(hmac(secretKey, shortDate), 'cn-beijing'), 'ark'), 'request')
const signature = crypto.createHmac('sha256', signingKey).update(stringToSign).digest('hex')
const response = await fetch(`https://${host}/api/v3?${query}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Date': xDate,
'X-Content-Sha256': payloadHash,
Authorization: `HMAC-SHA256 Credential=${accessKey}/${scope}, SignedHeaders=${signedHeaders}, Signature=${signature}`,
},
body,
})
const payload = await response.json()
if (!response.ok || payload.ResponseMetadata?.Error) {
throw new Error(payload.ResponseMetadata?.Error?.Message ?? `HTTP ${response.status}`)
}
return payload.Result
}
console.log(await materialRequest('ListAssetGroups', {
Filter: { GroupType: 'AIGC' },
PageNumber: 1,
PageSize: 20,
}))Python 完整签名客户端
import datetime
import hashlib
import hmac
import json
import os
import urllib.parse
import requests
HOST = "locopalai.com"
ACCESS_KEY = os.environ["LOCOPAL_ACCESS_KEY"]
SECRET_KEY = os.environ["LOCOPAL_SECRET_KEY"]
def sign(key: bytes, value: str) -> bytes:
return hmac.new(key, value.encode("utf-8"), hashlib.sha256).digest()
def material_request(action: str, payload: dict) -> dict:
body = json.dumps(payload, ensure_ascii=False, separators=(",", ":")).encode("utf-8")
x_date = datetime.datetime.now(datetime.timezone.utc).strftime("%Y%m%dT%H%M%SZ")
short_date = x_date[:8]
payload_hash = hashlib.sha256(body).hexdigest()
query = urllib.parse.urlencode({"Action": action, "Version": "2024-01-01"})
signed_headers = "content-type;host;x-content-sha256;x-date"
canonical_headers = (
f"content-type:application/json\nhost:{HOST}\n"
f"x-content-sha256:{payload_hash}\nx-date:{x_date}\n"
)
canonical_request = "\n".join([
"POST", "/api/v3", query, canonical_headers,
signed_headers, payload_hash,
])
scope = f"{short_date}/cn-beijing/ark/request"
request_hash = hashlib.sha256(canonical_request.encode("utf-8")).hexdigest()
string_to_sign = f"HMAC-SHA256\n{x_date}\n{scope}\n{request_hash}"
signing_key = sign(sign(sign(sign(SECRET_KEY.encode(), short_date), "cn-beijing"), "ark"), "request")
signature = hmac.new(signing_key, string_to_sign.encode(), hashlib.sha256).hexdigest()
authorization = (
f"HMAC-SHA256 Credential={ACCESS_KEY}/{scope}, "
f"SignedHeaders={signed_headers}, Signature={signature}"
)
response = requests.post(
f"https://{HOST}/api/v3?{query}",
data=body,
headers={
"Content-Type": "application/json",
"X-Date": x_date,
"X-Content-Sha256": payload_hash,
"Authorization": authorization,
},
timeout=30,
)
response.raise_for_status()
result = response.json()
error = result.get("ResponseMetadata", {}).get("Error")
if error:
raise RuntimeError(f"{error.get('Code')}: {error.get('Message')}")
return result["Result"]Go 完整签名客户端
func materialRequest(ctx context.Context, action string, input any) (map[string]any, error) {
host := "locopalai.com"
accessKey := os.Getenv("LOCOPAL_ACCESS_KEY")
secretKey := os.Getenv("LOCOPAL_SECRET_KEY")
body, _ := json.Marshal(input)
xDate := time.Now().UTC().Format("20060102T150405Z")
shortDate := xDate[:8]
shaHex := func(value []byte) string {
sum := sha256.Sum256(value)
return hex.EncodeToString(sum[:])
}
sign := func(key []byte, value string) []byte {
mac := hmac.New(sha256.New, key)
mac.Write([]byte(value))
return mac.Sum(nil)
}
query := "Action=" + url.QueryEscape(action) + "&Version=2024-01-01"
payloadHash := shaHex(body)
signedHeaders := "content-type;host;x-content-sha256;x-date"
canonicalHeaders := "content-type:application/json\nhost:" + host +
"\nx-content-sha256:" + payloadHash + "\nx-date:" + xDate + "\n"
canonicalRequest := strings.Join([]string{
"POST", "/api/v3", query, canonicalHeaders, signedHeaders, payloadHash,
}, "\n")
scope := shortDate + "/cn-beijing/ark/request"
stringToSign := "HMAC-SHA256\n" + xDate + "\n" + scope + "\n" + shaHex([]byte(canonicalRequest))
key := sign(sign(sign(sign([]byte(secretKey), shortDate), "cn-beijing"), "ark"), "request")
signature := hex.EncodeToString(sign(key, stringToSign))
req, _ := http.NewRequestWithContext(ctx, http.MethodPost,
"https://"+host+"/api/v3?"+query, bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Date", xDate)
req.Header.Set("X-Content-Sha256", payloadHash)
req.Header.Set("Authorization", "HMAC-SHA256 Credential="+accessKey+"/"+scope+
", SignedHeaders="+signedHeaders+", Signature="+signature)
response, err := http.DefaultClient.Do(req)
if err != nil { return nil, err }
defer response.Body.Close()
var envelope struct {
Result map[string]any `json:"Result"`
ResponseMetadata struct {
Error *struct { Code, Message string } `json:"Error"`
} `json:"ResponseMetadata"`
}
if err := json.NewDecoder(response.Body).Decode(&envelope); err != nil { return nil, err }
if envelope.ResponseMetadata.Error != nil {
return nil, fmt.Errorf("%s: %s", envelope.ResponseMetadata.Error.Code, envelope.ResponseMetadata.Error.Message)
}
return envelope.Result, nil
}签名必须基于最终发送的原始字节计算。不要先对一个 JSON 字符串签名,再让 HTTP SDK 重新格式化请求体。
通用响应体
成功:
{
"ResponseMetadata": {
"RequestId": "request-id",
"Action": "CreateAssetGroup",
"Version": "2024-01-01",
"Service": "ark",
"Region": "cn-beijing"
},
"Result": {}
}失败:
{
"ResponseMetadata": {
"RequestId": "request-id",
"Action": "GetAsset",
"Version": "2024-01-01",
"Service": "ark",
"Region": "cn-beijing",
"Error": {
"Code": "AccessDenied",
"Message": "素材不存在或无权访问"
}
}
}业务校验错误可能仍返回 HTTP 200,因此必须同时检查 ResponseMetadata.Error。
CreateAssetGroup - 创建虚拟分组
POST /api/v3?Action=CreateAssetGroup&Version=2024-01-01
认证方式: AccessKey HMAC-SHA256
请求体: application/json
成功判定: HTTP 成功且 ResponseMetadata.Error 不存在
该接口只创建虚拟人物分组。真人分组必须使用后文的二维码授权接口。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
Name | string | 是 | 分组名称,1 至 64 个字符。 |
Description | string | 否 | 分组说明,最多 300 个字符。 |
GroupType | string | 否 | 仅使用 AIGC;省略时默认 AIGC。 |
{
"Name": "品牌虚拟角色",
"Description": "产品宣传片角色",
"GroupType": "AIGC"
}响应字段
| 字段 | 类型 | 说明 |
|---|---|---|
ResponseMetadata.RequestId | string | 本次请求标识,排查问题时保留。 |
Result.Id | string | 平台分组 ID,后续分组和素材接口都使用它。 |
分组创建后只需保存一个 Result.Id,不要为不同模型重复创建同名分组。
{
"ResponseMetadata": {
"RequestId": "request-create-group",
"Action": "CreateAssetGroup",
"Version": "2024-01-01",
"Service": "ark",
"Region": "cn-beijing"
},
"Result": { "Id": "group-virtual-id" }
}ListAssetGroups - 查询分组列表
POST /api/v3?Action=ListAssetGroups&Version=2024-01-01
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
Filter.GroupIds | string[] | 否 | 只查询指定分组。 |
Filter.GroupType | string | 否 | AIGC 或 LivenessFace。 |
Filter.Name | string | 否 | 名称筛选。 |
PageNumber | integer | 否 | 页码,默认 1。 |
PageSize | integer | 否 | 每页数量,默认 20,最大 100。 |
SortBy | string | 否 | CreateTime 或 UpdateTime。 |
SortOrder | string | 否 | Desc 或 Asc。 |
{
"Filter": { "GroupType": "AIGC", "Name": "品牌" },
"PageNumber": 1,
"PageSize": 20,
"SortBy": "CreateTime",
"SortOrder": "Desc"
}{
"ResponseMetadata": {
"RequestId": "request-list-groups",
"Action": "ListAssetGroups",
"Version": "2024-01-01",
"Service": "ark",
"Region": "cn-beijing"
},
"Result": {
"Items": [
{
"Id": "group-virtual-id",
"Name": "品牌虚拟角色",
"Description": "产品宣传片角色",
"GroupType": "AIGC",
"CreateTime": "2026-09-08T12:00:00Z",
"UpdateTime": "2026-09-08T12:00:00Z"
}
],
"TotalCount": 1,
"PageNumber": 1,
"PageSize": 20
}
}GetAssetGroup - 查询单个分组
POST /api/v3?Action=GetAssetGroup&Version=2024-01-01
认证方式: AccessKey HMAC-SHA256
请求体: application/json
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
Id | string | 是 | CreateAssetGroup 或真人授权解析返回的分组 ID。 |
{ "Id": "group-virtual-id" }{
"ResponseMetadata": {
"RequestId": "request-get-group",
"Action": "GetAssetGroup",
"Version": "2024-01-01",
"Service": "ark",
"Region": "cn-beijing"
},
"Result": {
"Id": "group-virtual-id",
"Name": "品牌虚拟角色",
"Description": "产品宣传片角色",
"GroupType": "AIGC"
}
}UpdateAssetGroup - 更新分组
POST /api/v3?Action=UpdateAssetGroup&Version=2024-01-01
认证方式: AccessKey HMAC-SHA256
请求体: application/json
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
Id | string | 是 | 本账户拥有的分组 ID。 |
Name | string | 否 | 新名称;省略时保留原名称。 |
Description | string | 否 | 新说明;传空字符串可清空说明。 |
{
"Id": "group-virtual-id",
"Name": "品牌角色库",
"Description": "更新后的说明"
}{
"ResponseMetadata": {
"RequestId": "request-update-group",
"Action": "UpdateAssetGroup",
"Version": "2024-01-01",
"Service": "ark",
"Region": "cn-beijing"
},
"Result": { "Id": "group-virtual-id" }
}DeleteAssetGroup - 删除分组
POST /api/v3?Action=DeleteAssetGroup&Version=2024-01-01
认证方式: AccessKey HMAC-SHA256
请求体: application/json
{ "Id": "group-virtual-id" }{
"ResponseMetadata": {
"RequestId": "request-delete-group",
"Action": "DeleteAssetGroup",
"Version": "2024-01-01",
"Service": "ark",
"Region": "cn-beijing"
},
"Result": {}
}删除分组会使组内素材不再可用。业务系统应在删除前确认没有运行中的任务仍引用这些素材。
CreateAsset - 往分组插入素材
POST /api/v3?Action=CreateAsset&Version=2024-01-01
认证方式: AccessKey HMAC-SHA256
请求体: application/json
成功判定: 返回素材 ID 表示已受理,最终可用性以 GetAsset 为准
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
GroupId | string | 是 | 当前账户拥有的虚拟素材分组 ID。 |
AssetType | string | 是 | Image、Video 或 Audio。 |
Name | string | 否 | 素材显示名称。 |
URL | string | 是 | 可被服务端直接下载的公网 HTTPS 地址。 |
{
"GroupId": "group-virtual-id",
"AssetType": "Image",
"Name": "角色正面图",
"URL": "https://cdn.example.com/character-front.png"
}{
"ResponseMetadata": {
"RequestId": "request-create-asset",
"Action": "CreateAsset",
"Version": "2024-01-01",
"Service": "ark",
"Region": "cn-beijing"
},
"Result": { "Id": "asset-id" }
}Result.Id 必须持久化。创建成功只表示已经接收,不表示审核和预处理完成。
调用方切换模型时仍提交同一个 asset://<Result.Id>,不需要查询或替换其他 ID。
URL 必须是公网 HTTPS,不能依赖 Cookie、Referer 或临时浏览器会话;Content-Type 应与真实文件一致。图片使用 JPEG、PNG、WebP 等位图格式,不要提交 SVG 页面。链接有效期应覆盖下载和处理时间。
GetAsset - 查询单个素材状态
POST /api/v3?Action=GetAsset&Version=2024-01-01
认证方式: AccessKey HMAC-SHA256
请求体: application/json
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
Id | string | 是 | CreateAsset 返回的素材 ID。 |
{ "Id": "asset-id" }处理中:
{
"ResponseMetadata": {
"RequestId": "request-get-asset",
"Action": "GetAsset",
"Version": "2024-01-01",
"Service": "ark",
"Region": "cn-beijing"
},
"Result": {
"Id": "asset-id",
"GroupId": "group-virtual-id",
"Name": "角色正面图",
"URL": "https://cdn.example.com/character-front.png",
"AssetType": "Image",
"Status": "Processing",
"Error": { "Code": "", "Message": "" },
"ModelReadiness": [
{
"Models": ["doubao-seedance-2.0-B", "doubao-seedance-2.0-B-fast"],
"Status": "Active"
},
{
"Models": ["doubao-seedance-2.0"],
"Status": "Processing"
}
]
}
}可用:
{
"Result": {
"Id": "asset-id",
"GroupId": "group-virtual-id",
"AssetType": "Image",
"Status": "Active",
"Error": { "Code": "", "Message": "" }
}
}失败:
{
"Result": {
"Id": "asset-id",
"Status": "Failed",
"Error": {
"Code": "ResourceDownloadFailed",
"Message": "resource download failed"
}
}
}程序应在 Processing 时继续查询同一素材 ID,在 Active 后允许进入生成,在 Failed 时向用户展示 Error.Message 并停止自动重试。
ModelReadiness 字段
| 字段 | 类型 | 说明 |
|---|---|---|
Models | string[] | 当前准备状态对应的模型 ID。 |
Status | string | Preparing、Processing、Active、Failed 或 Unavailable。 |
Error.Message | string | 当前模型组不可用时的具体原因。 |
若请求即将使用模型 doubao-seedance-2.0,应找到 Models 包含该 ID 的项目,并等待其 Status 为 Active。没有读取 ModelReadiness 的客户端也可以直接提交视频;平台会在提交前再次校验,并在尚未可用时返回可重试错误,不会把错误的素材 ID交给视频模型。
async function checkModelAsset(assetId: string, model: string) {
const asset = await materialRequest('GetAsset', { Id: assetId })
const readiness = asset.ModelReadiness?.find((item: any) =>
item.Models?.includes(model),
)
const status = readiness?.Status ?? asset.Status
if (status === 'Active') return { ready: true, asset }
if (['Failed', 'Unavailable'].includes(status)) {
throw new Error(readiness?.Error?.Message ?? asset.Error?.Message ?? '素材不可用')
}
return { ready: false, status }
}未就绪时由业务队列按自身调度策略再次调用,不要重复执行 CreateAsset。页面刷新、进程重启或网络恢复后仍查询同一个素材 ID。
ListAssets - 查询素材列表
POST /api/v3?Action=ListAssets&Version=2024-01-01
认证方式: AccessKey HMAC-SHA256
请求体: application/json
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
Filter.GroupIds | string[] | 否 | 分组 ID 列表。 |
Filter.GroupType | string | 否 | AIGC 或 LivenessFace。 |
Filter.Statuses | string[] | 否 | Processing、Active、Failed。 |
Filter.Name | string | 否 | 名称筛选。 |
PageNumber | integer | 否 | 默认 1。 |
PageSize | integer | 否 | 默认 20,最大 100。 |
SortBy | string | 否 | CreateTime、UpdateTime 或 GroupId。 |
SortOrder | string | 否 | Desc 或 Asc。 |
{
"Filter": {
"GroupIds": ["group-virtual-id"],
"Statuses": ["Active"]
},
"PageNumber": 1,
"PageSize": 20,
"SortBy": "CreateTime",
"SortOrder": "Desc"
}{
"ResponseMetadata": {
"RequestId": "request-list-assets",
"Action": "ListAssets",
"Version": "2024-01-01",
"Service": "ark",
"Region": "cn-beijing"
},
"Result": {
"Items": [
{
"Id": "asset-id",
"GroupId": "group-virtual-id",
"Name": "角色正面图",
"URL": "https://cdn.example.com/character-front.png",
"AssetType": "Image",
"Status": "Active",
"Error": { "Code": "", "Message": "" }
}
],
"TotalCount": 1,
"PageNumber": 1,
"PageSize": 20
}
}UpdateAsset 与 DeleteAsset
两个接口都使用 AccessKey HMAC-SHA256 和 application/json 请求体。修改名称和删除都只需要使用平台返回的公开素材 ID。
更新素材名称:
{ "Id": "asset-id", "Name": "角色正面定妆图" }{
"ResponseMetadata": {
"RequestId": "request-update-asset",
"Action": "UpdateAsset",
"Version": "2024-01-01",
"Service": "ark",
"Region": "cn-beijing"
},
"Result": { "Id": "asset-id" }
}删除素材:
{ "Id": "asset-id" }{
"ResponseMetadata": {
"RequestId": "request-delete-asset",
"Action": "DeleteAsset",
"Version": "2024-01-01",
"Service": "ark",
"Region": "cn-beijing"
},
"Result": {}
}真人素材授权与同步
真人素材分组不能通过 CreateAssetGroup 创建,也不需要在网页或 API 中提前填写名称和说明。用户在手机采集流程中填写,解析成功后返回。
1. 用户同意协议
调用方必须先向用户展示真人素材授权与个人信息处理规则,并记录用户主动同意。不要在用户未同意时生成采集二维码。
2. CreatePortraitSession
POST /api/v3?Action=CreatePortraitSession&Version=2024-01-01
认证方式: AccessKey HMAC-SHA256
请求体: application/json
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
ContinuousCreate | boolean | 否 | 是否允许在同一采集流程中连续创建,默认 false。 |
{ "ContinuousCreate": false }{
"ResponseMetadata": {
"RequestId": "request-create-portrait-session",
"Action": "CreatePortraitSession",
"Version": "2024-01-01",
"Service": "ark",
"Region": "cn-beijing"
},
"Result": {
"H5Link": "https://example.com/portrait-session",
"BytedToken": "portrait-session-token",
"ExpiresIn": 600,
"ExpireTime": "2026-09-08T12:10:00Z"
}
}调用方应将 H5Link 生成为二维码,同时把 BytedToken 只保存在创建会话的用户服务端状态中。二维码过期后重新创建,不要把 Token 转给其他账户。
3. 用户手机采集
用户本人扫码,在手机端完成授权、活体采集、分组名称和说明。该步骤不能由服务端接口或普通图片上传代替。
4. ResolveBytedToken
POST /api/v3?Action=ResolveBytedToken&Version=2024-01-01
认证方式: AccessKey HMAC-SHA256
请求体: application/json
{ "BytedToken": "portrait-session-token" }采集尚未完成:
{
"ResponseMetadata": {
"RequestId": "request-resolve-pending",
"Action": "ResolveBytedToken",
"Version": "2024-01-01",
"Service": "ark",
"Region": "cn-beijing",
"Error": {
"Code": "ValidationError",
"Message": "真人授权尚未完成,请完成扫码后重试"
}
}
}采集完成:
{
"ResponseMetadata": {
"RequestId": "request-resolve-success",
"Action": "ResolveBytedToken",
"Version": "2024-01-01",
"Service": "ark",
"Region": "cn-beijing"
},
"Result": {
"GroupId": "group-liveness-id",
"GroupName": "演员 A",
"GroupDescription": "手机端填写的说明"
}
}解析成功后会话即失效。保存 GroupId,然后用 ListAssets 查询真人素材:
{
"Filter": {
"GroupIds": ["group-liveness-id"],
"GroupType": "LivenessFace"
},
"PageNumber": 1,
"PageSize": 20
}素材由手机采集流程产生,不要再用普通上传重复插入真人素材。每个素材仍需检查 Status;只有 Active 才能用于支持真人素材的模型。
在视频中引用素材
{
"type": "image_url",
"role": "reference_image",
"image_url": { "url": "asset://asset-id" }
}直接上传的临时参考素材使用公网 HTTPS URL 或接口明确支持的内联数据,不需要先进入素材库。素材库引用与本次临时引用是两种不同方式。
在不同视频模型中使用素材
视频请求使用 /v1/models 当前返回的模型 ID。更换模型时只替换请求中的 model,素材仍使用 CreateAsset 返回的同一个 ID:
{
"model": "doubao-seedance-2.0",
"content": [
{ "type": "text", "text": "保持角色外观一致,完成自然转身" },
{
"type": "image_url",
"role": "reference_image",
"image_url": { "url": "asset://asset-id" }
}
],
"resolution": "720p",
"ratio": "16:9",
"duration": 5
}asset://asset-id 保持不变。提交前通过 GetAsset 确认素材为 Active,并在模型详情中确认所选模型支持当前参考素材类型。
从入库到视频的完整调用顺序
- 使用 AccessKey 调用
CreateAssetGroup,保存Result.Id。 - 使用同一 AccessKey 调用
CreateAsset,保存Result.Id。 - 使用
GetAsset查询素材状态。 - 素材状态为
Active后,使用 API Key 调用/v1/video/generations。 - 在
content中提交asset://<素材ID>;多图时每张图各占一个条目。 - 保存视频任务 ID,调用
/v1/video/generations/{task_id}查询结果。 - 切换模型时只改变
model,继续使用同一个平台素材 ID。
状态与客户端行为
| 状态 | 含义 | 客户端行为 |
|---|---|---|
Processing | 下载、解析或审核中 | 保留原素材 ID,稍后查询。 |
Active | 可用于模型请求 | 允许选择并提交 asset://。 |
Failed | 处理失败 | 展示 Error.Code 和 Error.Message,修正后重新创建素材。 |
不要用固定等待秒数代替状态查询,也不要在 Processing 时重复创建相同素材。
安全与用户隔离
- AccessKey 只能访问所属账户的分组和素材。
- 真人会话 Token 只能由创建该会话的账户解析。
- 同一真人分组不能绑定多个平台账户。
- 不要在日志、工单或前端代码中记录 SecretKey、完整 Token 或身份证明信息。
- 面向终端用户时展示可理解的失败原因,同时保留 RequestId 供技术排查。
