> 以下是 Azimo 开发者文档「进阶用法」的 Markdown 版本，供 AI 助手参考。
> - Azimo（https://azimo.ai）是 AI 商业视频制作服务：一段文字加几份资料，交付一条通过质检的商用视频；提供异步 REST API、Python / TypeScript SDK、命令行 azimo 和 MCP 服务。
> - Base URL：https://azimo.ai
> - 鉴权：每个 /v1 请求带 `Authorization: Bearer <API key>`（在 https://azimo.ai/console#account 创建 Key）；用 `X-Project-ID` 选择项目，省略时使用默认项目。
> - 本页地址：https://azimo.ai/developers/advanced
> - 价格不公开：请联系 sales@azimo.ai。

# 进阶用法

接入正式环境前需要了解的几件事：Webhook、安全重试、错误处理、限流、项目与权限、预算批准、修改版本和删除文件。

完整的接口和字段见 [API 参考](https://azimo.ai/developers/api)，这里只讲怎么用。

## 用 Webhook 接收结果

不想轮询，就登记一个回调地址。视频状态每次变化，Azimo 都会 POST 一个事件过来。登记需要 `owner` 角色的 Key：

```bash
curl -s https://azimo.ai/v1/webhooks \
  -H "Authorization: Bearer $AZIMO_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: webhook-001" \
  -d '{"url": "https://example.com/hooks/azimo", "name": "Production updates"}'
```

返回里的 `signing_secret`（以 `whsec_` 开头）只显示这一次，请保存好。

事件长这样，`type` 是 `video.` 加上状态，例如 `video.progress`、`video.needs_input`、`video.complete`、`video.rejected`、`video.failed`、`video.cancelled`：

```json
{"id": "d8009f54780243ea", "sequence": 12, "type": "video.complete", "api_version": "v1", "project_id": "c56eb945e4464aec", "created_at": 1790000000, "data": {"id": "b6bf4689035d474f", "status": "complete", "stage": "complete", "progress": 100}}
```

每个请求带两个头：`Vidgen-Event-Id`（事件 ID）和 `Vidgen-Signature: t=<时间戳>,v1=<签名>`。签名是 `HMAC-SHA256(signing_secret, "<时间戳>." + 原始请求体)` 的十六进制。一定要对收到的原始字节验签，不要先解析 JSON 再序列化：

```python
import hashlib, hmac, time

def verify(secret: str, header: str, body: bytes, tolerance: int = 300) -> bool:
    try:
        parts = dict(item.split("=", 1) for item in header.split(","))
        timestamp = int(parts["t"])
    except (KeyError, ValueError):
        return False
    if abs(time.time() - timestamp) > tolerance:  # 拒绝 5 分钟以前的请求
        return False
    expected = hmac.new(secret.encode(), f"{timestamp}.".encode() + body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, parts.get("v1", ""))
```

Python SDK 自带同样的函数 `Vidgen.verify_webhook(secret, header, body)`，TypeScript SDK 是 `Vidgen.verifyWebhook(secret, header, body)`。

收到事件后：

- 验签通过就尽快返回 2xx，耗时的处理放到自己的队列里。
- 投递失败会自动重试，最长持续约 72 小时，所以同一个事件可能到达多次：请按事件 `id` **去重**。
- 事件不保证按顺序到达。拿不准时，用 `GET /v1/videos/{id}` 查最新状态。
- 以后可能增加新的事件类型，遇到不认识的 `type` 直接忽略即可。
- 下载链接会过期；需要时重新查询视频，拿新链接。

也可以只给某一条视频设回调：在 `POST /v1/videos` 里加 `webhook_url`，响应里的 `webhook_signing_secret` 就是验签用的密钥。不用 Webhook 的话，可以用 `GET /v1/events?after=<sequence>` 按顺序拉取事件。

## 安全重试：Idempotency-Key

所有创建类的 POST（上传、方案、制作、修改、取消、回答、Webhook 等）都接受请求头 `Idempotency-Key`，长度 1–128 个字符。

- 同一个 Key、同样的请求体：返回第一次的结果，不会再做一次。
- 同一个 Key、不同的请求体：返回 `409`，错误码 `idempotency_conflict`。
- 换一个新 Key：就是一个新请求。对同一个方案换 Key 再提交，会再做一条视频。

做法：在发请求前生成 Key，和你的业务记录一起保存；超时或断线后，用同一个 Key 重试。

## 处理错误

所有错误都是同一种格式：

```json
{"error": {"code": "validation_error", "message": "request validation failed", "details": [{"loc": ["body", "options", "duration_max"], "msg": "Input should be less than or equal to 180", "type": "less_than_equal"}], "request_id": "64ceb19e540b438f8b9a4df3bfe62ba7"}}
```

按 `code` 判断，不要按 `message` 的文字判断。每个响应都有 `X-Request-ID` 头；联系我们时请附上 `request_id`。

| HTTP | `code` | 怎么办 |
| --- | --- | --- |
| 401 | `unauthenticated` | Key 缺失、错误或已撤销，换一把有效的 Key |
| 402 | `insufficient_balance` / `budget_exceeded` | 余额不足或达到额度上限；充值或联系 sales@azimo.ai，不要反复重试 |
| 403 | `forbidden` | 角色或项目不允许，例如 `read-only key` |
| 403 | `contact_sales` | 与价格相关的请求，请联系 sales@azimo.ai |
| 404 | `not_found` | ID 不存在，或不在当前项目里 |
| 409 | `conflict` / `idempotency_conflict` | 状态不允许（例如视频已结束），或 Key 被用于不同的请求 |
| 413 | `payload_too_large` / `storage_quota_exceeded` | 文件超过 60 MB，或存储空间已满，删掉不用的素材 |
| 415 | `unsupported_media_type` | 不支持的文件类型 |
| 422 | `validation_error` | 请求字段不对，`details` 列出具体问题 |
| 429 | `rate_limited` | 请求太频繁，按 `Retry-After` 等待后重试 |
| 500 | `internal_error` | 我们这边出错，带上 `request_id` 联系我们 |
| 503 | `backend_unconfigured` | 这个工作流暂时不可用 |

## 限流与 429

- 每把 Key 默认每分钟最多 600 次 API 请求。
- 每个组织每小时能开始的制作数量有上限；Key 也可以单独设更低的上限。
- 超出时返回 `429`，响应头 `Retry-After` 是需要等待的秒数。

Python SDK 会对 GET 和带 `Idempotency-Key` 的 POST 自动重试 429 和 5xx，并遵守 `Retry-After`。轮询视频状态时，每 10 秒左右一次就够了。

## 项目与 Key 的角色

资源（素材、方案、视频、Webhook）都属于某个项目。请求头 `X-Project-ID` 选择项目，不传就用默认项目。组织 owner 可以用 `POST /v1/projects` 新建项目，用 `POST /v1/api-keys` 创建 Key 并指定 `role` 和 `project_id`。

| 角色 | 能做什么 |
| --- | --- |
| `viewer` | 只读：查看视频、方案、素材、事件和余额 |
| `editor` | 另外可以上传、创建方案、开始制作、取消、回答和修改 |
| `owner`（绑定项目） | 另外可以管理本项目的 Webhook |
| `owner`（不绑定项目） | 组织 owner：管理所有项目和 Key |

每个应用或客户端单独用一把 Key，按最小权限选择角色。

## 预算批准

制作过程中如果要超出这条视频的花费上限，视频会暂停在 `needs_input`，`result.input_required` 是：

```json
{"code": "budget_approval", "actor": "customer", "message": "…", "approvable": true}
```

同意继续，就回答：

```bash
curl -s https://azimo.ai/v1/videos/b6bf4689035d474f/resume \
  -H "Authorization: Bearer $AZIMO_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: approve-b6bf-001" \
  -d '{"approve_budget": true}'
```

SDK 是 `azimo.approve_budget(video_id)`，命令行是 `azimo resume ID --approve-budget`。批准后仍然需要余额足够，否则返回 `402`；已经完成的步骤不会重做。

其他 `needs_input` 用文字回答：`{"message": "..."}`，需要补资料时加上 `"assets": ["<素材 ID>"]`。长时间不回应（默认 7 天）的视频会自动取消。

## 修改版本

对已结束的视频（`complete`、`rejected`、`failed`、`cancelled` 或 `needs_input`）可以做一个修改版：

```bash
curl -s https://azimo.ai/v1/videos/b6bf4689035d474f/revisions \
  -H "Authorization: Bearer $AZIMO_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: revise-b6bf-001" \
  -d '{"prompt": "完整的新 brief：同样的产品，结尾强调一次能烧两杯水。", "options": {"subtitles": true}}'
```

- 返回一条新视频，`parent_id` 指向原视频，原视频保留不变。
- `prompt` 会整体替换原来的 brief，请写完整；`options` 只改你传的字段；`assets` 不传就沿用原来的素材（原素材已删除时会返回 `404`，这时请重新上传并传 `assets`）。
- 修改版是一次完整的新制作，会使用余额。视频还在制作中时返回 `409`。

## 取消与删除文件

- 取消：`POST /v1/videos/{id}/cancel`。排队或等待中的视频立即取消；正在制作的会在下一步开始前停下。取消的视频按已经花掉的用量收费。
- 质检不过不收费：以 `rejected`（未通过质检）或 `failed`（系统未能完成）结束的视频不扣余额，冻结额度全部退回，钱包流水里有一条金额为 0 的 `settle` 记录说明原因。
- 删除素材：`DELETE /v1/assets/{id}`。已经用它做好的视频不受影响，但之后基于这条视频做修改版时要重新提供素材。
- 删除视频文件：`DELETE /v1/videos/{id}`，只能删已结束的视频（先取消）。成片、字幕和封面会被清除，下载链接失效，视频记录保留。
- 品牌、模板和 Webhook 也可以用 `DELETE` 删除。
- 成片只保留一段时间，请及时下载保存。
