# 地鼠数字人 OpenAPI 接入文档

版本：1.4.0
正式地址：`https://gateway.dspirit.cn/v1/digital-human`

## 1. 最短接入流程

客户只需提供三个必填输入：

1. API2 身份验证：`CLIENT_ID` 和 `CLIENT_SECRET`，使用 HTTP Basic Auth。
2. `image`：一张已授权的正面人物图片。
3. `audio`：一段成品音频。

调用一次 `POST /image-audio-videos`。平台自动完成令牌申请、素材上传和任务创建，并立即返回 `taskId`，不会等待视频生成完成：

```bash
curl --request POST \
  'https://gateway.dspirit.cn/v1/digital-human/image-audio-videos' \
  --user "${CLIENT_ID}:${CLIENT_SECRET}" \
  --header 'X-Idempotency-Key: order-20260819-0001' \
  --form 'image=@/absolute/path/avatar.png' \
  --form 'audio=@/absolute/path/narration.wav'
```

任务受理后返回 HTTP 202：

```json
{
  "ok": true,
  "code": 0,
  "message": "数字人任务已提交",
  "data": {
    "taskId": "dh_task_xxx",
    "operation": "image_avatar_video",
    "status": "processing",
    "billing": {
      "status": "reserved",
      "points": 5
    }
  }
}
```

响应头中的 `Location` 是轮询地址，`Retry-After` 是建议首次轮询等待秒数。也可以直接使用下面的地址轮询，仍然使用相同的 Basic Auth：

```bash
curl \
  'https://gateway.dspirit.cn/v1/digital-human/image-audio-videos/dh_task_xxx' \
  --user "${CLIENT_ID}:${CLIENT_SECRET}"
```

每隔 3-10 秒查询一次。`data.status=success` 时，从 `data.resultUrl` 下载 MP4；`failed` 时停止轮询，预扣积分进入退回流程。

如需回调，在同一个 multipart 请求中增加可选字段：

```bash
  --form 'callbackUrl=https://customer.example.com/callback/digital-human' \
  --form 'callbackSecret=客户自定义签名密钥'
```

任务进入终态后平台会向 `callbackUrl` 发送 POST 事件。传入 `callbackSecret` 时，回调包含 `X-Dishu-Signature: sha256=<HMAC-SHA256>`；轮询接口无论是否配置回调都始终可用。

除此以外不需要传 `org_id`、`user_id`、产品头、应用头、素材 URL、`mediaId` 或底层服务商 Key。`X-Idempotency-Key` 可省略，由服务端自动生成；同一业务请求重试时建议复用原值，避免重复创建和重复计费。

也可以把已经取得的短期 Token 作为验证值：

```bash
curl --request POST \
  'https://gateway.dspirit.cn/v1/digital-human/image-audio-videos' \
  --header "Authorization: Bearer ${ACCESS_TOKEN}" \
  --form 'image=@/absolute/path/avatar.png' \
  --form 'audio=@/absolute/path/narration.wav'
```

支持的图片格式：JPG、JPEG、PNG、WebP，最大 20MB。支持的音频格式：MP3、WAV、M4A、AAC、OGG，最大 100MB。

如需复用素材或调用更多高级能力，可继续使用后文的 `/media`、`/image-videos` 和 `/tasks/{taskId}` 流程。

机器可读规范：

- OpenAPI YAML：`https://gateway.dspirit.cn/v1/digital-human/openapi.yaml`
- OpenAPI JSON：`https://gateway.dspirit.cn/v1/digital-human/openapi.json`

## 2. 开通信息

地鼠为每个客户应用分配：

- `CLIENT_ID`
- `CLIENT_SECRET`
- `SITE_ID`，默认 `100000`
- 权限：`digital-human:read`、`digital-human:write`

凭证只能保存在客户服务端，不能写入网页、小程序、桌面安装包或公开仓库。凭证已绑定企业和服务账号，获取令牌时不要额外提交企业或用户 ID。

## 3. 获取访问令牌

```bash
curl --request POST \
  'https://dspirit.cn/authz/oidc/v2/100000/token' \
  --user "${CLIENT_ID}:${CLIENT_SECRET}" \
  --header 'Content-Type: application/x-www-form-urlencoded' \
  --data-urlencode 'grant_type=client_credentials' \
  --data-urlencode 'scope=ai:invoke digital-human:read digital-human:write'
```

后续高级接口统一携带：

```http
Authorization: Bearer ACCESS_TOKEN
X-Dishu-Product-Code: ai_avatar
X-Dishu-App-Key: avatar
```

使用第 1 节的 `/image-audio-videos` 单接口时不需要手动获取 Token，也不需要传产品头和应用头。

## 4. 上传一个素材

一个接口接收图片、音频或视频。使用 `multipart/form-data`，文件字段固定为 `file`。

上传图片：

```bash
curl --request POST \
  'https://gateway.dspirit.cn/v1/digital-human/media' \
  --header "Authorization: Bearer ${ACCESS_TOKEN}" \
  --header 'X-Dishu-Product-Code: ai_avatar' \
  --header 'X-Dishu-App-Key: avatar' \
  --form 'file=@/absolute/path/avatar.jpg'
```

上传音频：

```bash
curl --request POST \
  'https://gateway.dspirit.cn/v1/digital-human/media' \
  --header "Authorization: Bearer ${ACCESS_TOKEN}" \
  --header 'X-Dishu-Product-Code: ai_avatar' \
  --header 'X-Dishu-App-Key: avatar' \
  --form 'file=@/absolute/path/narration.mp3'
```

成功返回 HTTP `201`：

```json
{
  "ok": true,
  "code": 0,
  "message": "素材上传成功",
  "data": {
    "mediaId": "dh_media_0123456789abcdef0123456789abcdef",
    "kind": "image",
    "fileName": "avatar.jpg",
    "contentType": "image/jpeg",
    "sizeBytes": 123456,
    "expiresInSeconds": 604800
  }
}
```

`mediaId` 只属于当前企业和服务账号，不能跨账号复用。过期后重新上传即可。

## 5. 创建照片数字人视频

```bash
curl --request POST \
  'https://gateway.dspirit.cn/v1/digital-human/image-videos' \
  --header "Authorization: Bearer ${ACCESS_TOKEN}" \
  --header 'X-Dishu-Product-Code: ai_avatar' \
  --header 'X-Dishu-App-Key: avatar' \
  --header 'X-Idempotency-Key: customer-order-20260813-0001' \
  --header 'Content-Type: application/json' \
  --data '{
    "imageMediaId": "dh_media_IMAGE_ID",
    "audioMediaId": "dh_media_AUDIO_ID",
    "mode": 2,
    "title": "客户形象口播",
    "callbackUrl": "https://customer.example.com/callback/digital-human",
    "callbackSecret": "CUSTOMER_CALLBACK_SECRET"
  }'
```

`callbackUrl` 和 `callbackSecret` 都可省略。创建成功返回 HTTP `202`：

```json
{
  "ok": true,
  "code": 0,
  "message": "数字人任务已提交",
  "data": {
    "taskId": "dh_task_xxx",
    "operation": "image_avatar_video",
    "status": "processing",
    "callback": {
      "status": "pending",
      "attempts": 0
    },
    "billing": {
      "status": "reserved",
      "reserved": true,
      "committed": false,
      "refunded": false,
      "points": 5
    }
  }
}
```

同一个业务订单重试时必须复用原 `X-Idempotency-Key`，不要创建重复任务。

## 6. 接收回调

任务进入 `success` 或 `failed` 后，平台向 `callbackUrl` 发送 `POST application/json`：

```json
{
  "event": "digital_human.task.completed",
  "data": {
    "taskId": "dh_task_xxx",
    "operation": "image_avatar_video",
    "status": "success",
    "resultUrl": "https://.../result.mp4",
    "billing": {
      "status": "committed",
      "committed": true,
      "points": 5
    }
  }
}
```

回调请求头：

```http
X-Dishu-Event: digital_human.task.completed
X-Dishu-Task-Id: dh_task_xxx
X-Dishu-Signature: sha256=HEX_HMAC_SHA256
```

当创建任务传入 `callbackSecret` 时，签名计算方式为：

```text
hex(HMAC-SHA256(callbackSecret, 原始 HTTP 请求体字节))
```

验签成功后返回任意 `2xx`。非 `2xx` 或网络失败会重试，最多 5 次。回调地址必须是公网 HTTPS，不能指向内网、回环或保留地址。不要只凭回调判断计费，必要时用任务查询接口读回确认。

## 7. 查询任务结果

```bash
curl \
  'https://gateway.dspirit.cn/v1/digital-human/tasks/dh_task_xxx' \
  --header "Authorization: Bearer ${ACCESS_TOKEN}" \
  --header 'X-Dishu-Product-Code: ai_avatar' \
  --header 'X-Dishu-App-Key: avatar'
```

未使用回调时每隔 3-10 秒查询一次，达到终态后停止：

- `success`：生成成功，视频位于 `data.resultUrl`。
- `failed`：生成失败，预扣积分进入退回流程。
- `settlement_pending`：生成结果已确定，积分结算正在重试；不要重复创建任务。

`data.callback.status` 可为 `pending`、`scheduled`、`delivering`、`delivered`、`failed` 或 `exhausted`。回调失败不会把已生成成功的视频任务改成失败。

## 8. 其他能力

| 方法 | 路径 | 用途 |
| --- | --- | --- |
| POST | `/image-audio-videos` | 一次提交图片、音频和验证信息，异步返回 taskId |
| GET | `/image-audio-videos/{taskId}` | 使用相同 Basic/Bearer 凭证轮询状态和结果 |
| GET | `/status` | 服务、能力和积分状态 |
| GET | `/roles` | 查询角色 |
| GET | `/scenes`、`/models` | 查询数字人场景 |
| POST | `/models` | 训练数字人场景 |
| GET、POST | `/voice-clones` | 查询或克隆声音 |
| POST | `/voice-synthesis` | 合成克隆声音 |
| POST | `/videos` | 使用已训练场景生成数字人视频 |
| GET | `/sora/models` | 查询通用视频模型 |
| POST | `/sora/videos` | 生成通用视频素材 |

这些是兼容保留的高级接口；本次 `mediaId` 简化合同用于 `/image-videos` 照片数字人链路。完整请求结构以 OpenAPI JSON/YAML 为准。

## 9. 错误处理

```json
{
  "ok": false,
  "code": "DISHU_MEDIA_UNSUPPORTED_TYPE",
  "message": "文件扩展名与实际素材类型不一致。",
  "error": {
    "code": "DISHU_MEDIA_UNSUPPORTED_TYPE",
    "message": "文件扩展名与实际素材类型不一致。"
  },
  "requestId": "req_xxx"
}
```

| HTTP | code | 处理方式 |
| --- | --- | --- |
| 400 | `DISHU_DIGITAL_HUMAN_FILE_REQUIRED` | 检查单接口的 `image` 和 `audio` 文件字段 |
| 400 | `DISHU_MEDIA_INVALID_REQUEST` | 检查 multipart 字段是否为 `file` |
| 400 | `DISHU_DIGITAL_HUMAN_MEDIA_INVALID` | 检查 mediaId、素材类型、有效期和账号归属 |
| 401 | `API_KEY_REQUIRED` / `DISHU_LOGIN_REQUIRED` | 重新获取访问令牌 |
| 403 | `PRODUCT_NOT_ENABLED` / `DISHU_SCOPE_REQUIRED` | 检查产品头和接口权限 |
| 402 | `DISHU_POINTS_NOT_ENOUGH` | 充值或调整企业积分额度 |
| 413 | `DISHU_MEDIA_SIZE_INVALID` | 压缩素材或联系技术调整上限 |
| 415 | `DISHU_MEDIA_UNSUPPORTED_TYPE` | 使用受支持且扩展名匹配的媒体文件 |
| 429 | `QUOTA_NOT_ENOUGH` | 降低请求频率或申请额度 |
| 502 | `DISHU_DIGITAL_HUMAN_UPSTREAM_ERROR` | 复用原幂等键重试 |
| 503 | `DISHU_DIGITAL_HUMAN_NOT_CONFIGURED` | 联系地鼠技术支持 |

排查时提供 `requestId`、`taskId`、请求时间和接口路径，不要发送 Client Secret、访问令牌或回调密钥。

## 10. 上线检查

- Client Secret 与 callbackSecret 只保存在服务端密钥管理系统。
- 所有创建接口使用稳定幂等键。
- 回调先验签，再按 `taskId` 做幂等消费。
- 回调失败时使用 `/tasks/{taskId}` 主动查询，不重复创建视频。
- 记录 `requestId`、`taskId`、业务单号和计费状态，不记录令牌与原始声音文件。
- 取得人物形象、声音和内容的合法授权。
- 及时转存 `resultUrl`，并按客户数据保留政策清理素材。
