<!-- Generated by web/scripts/generate-public-discovery.mjs from docs/channel/grok-build-video.md. Do not edit by hand. -->

# Grok Build 视频

## 适用范围 / 一句话结论

这份文档描述 **BeefAPI 当前对用户开放的 Grok 视频合同**：只提供 `grok-imagine-video-1.5`，走 `POST /v1/videos`（及同等创建入口）异步生成，成功后用 `GET /v1/videos/{id}` 查状态、`GET /v1/videos/{id}/content` 取片。

它是 xAI **Build / 官方 Videos API** 的转发，不是 `grok.com/imagine` 网页协议，也不复刻网页 Projects、素材库、多代理或视频编辑 UI。旧模型 `grok-imagine-video` **不对用户提供**。

国内 `grok` / `workbuddy` 分组按人民币阶梯价扣费；价格以后台「视频阶梯价」与公开价格页的同一份配置为准。下文凡写「BeefAPI 当前合同」，均以本仓库实现和测试为准；若与官方文档不一致，以合同表为准，并标出来源文件。

---

## 1. 公开模型

| 模型 | 用户入口 | 说明 |
|---|---|---|
| `grok-imagine-video-1.5` | 开放 | 当前唯一对用户提供的 Grok 视频模型。人民币阶梯价默认只包含它；WorkBuddy 当前路由也只挂这一条。 |
| `grok-imagine-video` | **不提供** | 旧模型。不要在用户文档、价格页或示例里使用。 |
| `grok-imagine-video-1.5-preview` 等别名 | **不提供** | 官方模型页把 preview / 日期别名指向 1.5；用户请求必须写精确模型名 `grok-imagine-video-1.5`，加前缀或别名会 `invalid_model`。 |

渠道必须显式启用 `grok-imagine-video-1.5`。模型出现在某份上游 `/v1/models` 目录里，不等于 BeefAPI 已对该用户分组开放路由。

来源：`setting/ratio_setting/video_price_ladder.go`、`setting/ratio_setting/workbuddy_cny_pricebook.go`、`relay/channel/task/grok_build/adaptor.go`。

---

## 2. 接口

基址用你的 BeefAPI 主机（下称 `$BEEFAPI_BASE`），**不要**直连 `api.x.ai`。

| 方法 | 路径 | 作用 | 鉴权 |
|---|---|---|---|
| `POST` | `/v1/videos` | 创建任务 | API Key |
| `POST` | `/v1/videos/generations` | 同上，官方风格别名 | API Key |
| `GET` | `/v1/videos/{id}` | 查询状态 | API Key |
| `GET` | `/v1/videos/{id}/content` | 成功后取片 | API Key 或已登录控制台会话 |

另外还有兼容入口 `POST/GET /v1/video/generations`（单数 `video`）。Grok 视频请用上表四条。

`POST /v1/videos/{id}/remix` 是通用 OpenAI 风格路由，**本通路不支持**。xAI 的 `/v1/videos/edits`、`/v1/videos/extensions` 也 **暂不暴露**。

### 鉴权与请求头

创建与查询：

```http
Authorization: Bearer $BEEFAPI_KEY
Content-Type: application/json
```

- `$BEEFAPI_KEY` 是 BeefAPI 令牌，不是 xAI 账号 token。
- 创建必须是 JSON。不要用 `multipart/form-data` 调本通路。
- 可选 `x-idempotency-key`：原样转给上游。省略时，BeefAPI 用本次公开任务 ID 作为幂等键。同一任务重试会复用同一个受管上传目标。

取片 `GET /v1/videos/{id}/content` 可用同一 Bearer，或控制台登录态。成功且走受管存储时，响应是 **302**，`Cache-Control: no-store`，`Location` 是短时读取地址。用 curl 时加 `-L` 跟随跳转。

### 异步状态机与轮询

创建是异步的：

1. `POST` 立刻返回 BeefAPI 任务 `id`，`status` 一般为 `queued`。
2. 客户端轮询 `GET /v1/videos/{id}`，直到 `completed` 或 `failed`。
3. 成功后再请求 `GET /v1/videos/{id}/content`。

建议客户端每 **5 秒** 轮询一次（与官方 REST 示例一致）。生成常要数分钟，随时长、分辨率、参考图数量变化。不要把「几分钟内一定完成」写成 SLA。

BeefAPI 后台也会周期刷新未完成任务（默认约 15 秒一轮）。客户端仍应自己轮询；后台刷新不能替代 `GET`。

来源：`router/video-router.go`、`relay/channel/task/grok_build/adaptor.go`、`service/task_polling.go`、官方 [Video Generation](https://docs.x.ai/developers/model-capabilities/video/generation)。

---

## 3. 创建请求参数

`POST /v1/videos` 与 `POST /v1/videos/generations` 使用同一套 JSON。BeefAPI 先校验，再转成上游原生字段。

**模式判定（由参数组合决定，没有单独的 `mode` 字段）：**

| 模式 | 判定 | 互斥 |
|---|---|---|
| 文生视频 | 只有 `prompt`，没有 `image` / `input_reference` / `reference_images` / `reference_audios` | — |
| 图生视频 | 有 `image` 或 `input_reference` | 不能同时传 `reference_images` |
| 参考生视频 | 有 `reference_images` 和/或 `reference_audios` | 官方模式合同不与 `image` / `input_reference` 混用 |

`prompt` 一律必填。官方示例用 `<IMAGE_0>` / `<IMAGE_1>`、`<AUDIO_0>` 这类标签点名参考素材，序号约定以上游实际解析为准，不要把网页 Imagine 的素材库 ID 写进 prompt。

### 字段表

| 字段 | 类型 | 必填 | 允许值 | 省略 / 默认 | 限制 | 备注 |
|---|---|---|---|---|---|---|
| `model` | string | 必填 | 用户侧只允许 `grok-imagine-video-1.5` | 无默认 | 其他名字返回 `invalid_model` | 必须精确匹配，不要加 `xai/` 前缀，也不要传旧模型或 preview 别名 |
| `prompt` | string | 必填 | 非空字符串 | — | 去首尾空白后不能为空 | 描述画面、运动、对白 |
| `duration` | integer | 可选 | 1–15；参考模式 1–10 | 省略则按 **4** 发给上游 | 必须是整数（JSON number 或整数字符串 `"4"`）。`10.5` 非法。显式 `0` 非法，不会当成默认 4 | 见下方「BeefAPI 当前合同」 |
| `seconds` | integer / string | 可选 | 同 `duration` | 仅当 **没有** `duration` 时生效 | 与 `duration` 同语义的 OpenAI 风格别名 | 两者都写时以 `duration` 为准 |
| `aspect_ratio` | string | 可选 | `1:1` `16:9` `9:16` `4:3` `3:4` `3:2` `2:3`；另接受 `square`/`landscape`/`portrait` | 省略则 BeefAPI **发送** `16:9` | 大小写不敏感 | 官方图生视频在省略时跟原图比例；BeefAPI 当前会补成 16:9 |
| `resolution` | string | 可选 | `480p` `720p` `1080p` | 省略则 BeefAPI **发送** `480p` | 参考模式最高 `720p` | `1080p` 仅文生 / 单图生 |
| `size` | string | 可选 | `720x1280` `1280x720` `1024x1792` `1792x1024` | 仅当未写 `aspect_ratio` / `resolution` 时用来推断 | OpenAI 风格别名。`720x*` → 720p；`1024x*`/`1792x*` → 1080p | 不要与冲突的 `resolution` 混用 |
| `generate_audio` | boolean | 可选 | `true` / `false` | **省略则不发该字段**，上游默认出声（官方：默认 `true`） | 必须是 JSON 布尔。`"false"` 字符串非法 | **显式 `false` 会原样发给上游**，不会被 `omitempty` 丢掉 |
| `image` | string 或 object | 可选 | 见「图片输入格式」 | 无 | 与 `reference_images` 互斥；计 1 张参考图费 | 单图生视频（锁第一帧） |
| `input_reference` | string 或 object | 可选 | `{ "image_url": "..." }`，或与 `image` 相同的字符串/对象 | 无 `image` 时作为单图来源 | 与 `reference_images` 互斥 | OpenAI 风格别名，转成上游 `image` |
| `reference_images` | array | 可选 | 最多 **7** 项，格式同「图片输入格式」 | 无 | 与 `image`/`input_reference` 互斥；触发参考模式 | 也接受 `reference_image_urls` 作为别名 |
| `reference_audios` | array | 可选 | 最多 **3** 项，每项 `{"voice_id":"<预设ID>"}` | 无 | 仅 1.5；单独出现也算参考模式 | 自定义音频文件不支持。预设语音不加参考图费 |
| `output` | object | 可选 | 见 §7 | **普通客户端不要传** | `upload_url` 必须是 `https`、host 非空且不能带 userinfo；当前本地校验不判断目标是否公网可达 | 省略时由 BeefAPI 受管对象存储自动生成 |

未出现在上表的字段（例如网页 Imagine 的项目、素材库、扩展视频、编辑视频）不会作为本通路合同的一部分转发。渠道后台的「参数覆盖」**不作用于**本视频创建路径。

### 图片输入格式

以下三种都会被收成上游 `{ "url": "..." }` 或 `{ "file_id": "..." }`：

```json
"https://example.com/start.png"
```

```json
{ "url": "https://example.com/start.png" }
```

```json
{ "url": "data:image/jpeg;base64,...." }
```

```json
{ "file_id": "file_..." }
```

- **HTTPS URL**：公开可拉取的图片地址。本通路不在本地校验图片 URL 的协议，请使用公网 HTTPS。
- **data URL**：官方允许 `data:image/jpeg;base64,...` 这类 URI。BeefAPI 把它当作 `url` 字符串原样转发。
- **`file_id`**：仅当对象里带 `file_id` 时转发。BeefAPI **不提供** xAI Files 上传给普通用户；没有上游文件 ID 就不要用这个字段。
- OpenAI 风格 `input_reference.image_url` 会改写成 `image.url`。
- 数组项若无法解析成 url/file_id，会被丢掉；有效项超过 7 张会在校验阶段失败。

### 预设 voice ID

```json
"reference_audios": [
  { "voice_id": "eve" },
  { "voice_id": "leo" }
]
```

- 只接受对象里的 `voice_id` 字符串。空值会被丢掉。
- 官方：与 [Text to Speech 声线表](https://docs.x.ai/developers/model-capabilities/audio/text-to-speech#voices) 同一套预设，大小写不敏感；未知 ID 由上游返回 400 并附带可用列表。
- BeefAPI **不维护**本地声线白名单，也不支持用自己的音频文件当参考音。官方称自定义音频仅对受信任合作方开放。
- 示例里出现过的 ID 包括 `eve`、`leo`、`ara`。完整名单以官方 TTS 文档为准，不要把网页 App 里多出来的声线当成 Build API 已保证可用。

### 显式 false / 零值

| 写法 | 行为 |
|---|---|
| 不写 `generate_audio` | 不发给上游；官方默认出声 |
| `"generate_audio": false` | **会发给上游**，要静音片 |
| `"generate_audio": true` | 发给上游 |
| 不写 `duration` | 按 4 秒发给上游 |
| `"duration": 0` | 400：必须在 1–15（参考模式 1–10） |
| `"duration": 4` | 4 秒 |

来源：`relay/channel/task/grok_build/request.go`、`relay/channel/task/grok_build/adaptor_test.go`。

---

## 4. 能力矩阵（BeefAPI 当前合同）

对用户开放的模型只有 `grok-imagine-video-1.5`。

| | 文生视频 | 单图生视频 | 参考生视频（图和/或预设语音） |
|---|---|---|---|
| 480p | 允许 | 允许 | 允许 |
| 720p | 允许 | 允许 | 允许（参考模式上限） |
| 1080p | 允许 | 允许 | **拒绝**（`reference-to-video is limited to 720p`） |
| 时长 1–10 秒 | 允许 | 允许 | 允许 |
| 时长 11–15 秒 | 允许 | 允许 | **拒绝**（`reference-to-video duration must be between 1 and 10 seconds`） |
| 参考图 | 不要传 | 不要传 `reference_images` | 最多 7 张 |
| 预设语音 | 不要传 | 不要传 | 最多 3 个 `voice_id` |
| `image` + `reference_images` | — | **拒绝** | **拒绝** |

参考模式由「有 `reference_images` 或有 `reference_audios`」触发。只有预设语音、没有参考图，也按参考模式限制时长和分辨率。官方把图生视频与参考生视频定义为不同模式；因此不要把 `image` / `input_reference` 和 `reference_audios` 混用。当前 BeefAPI 本地校验只会明确拒绝 `image` + `reference_images`，`image` + `reference_audios` 若被提交，仍可能由上游按模式冲突拒绝，不属于本文承诺的可用组合。

### 与官方文档不一致之处

以 **BeefAPI 当前合同** 为准。官方页面会变，下表记录核对日 **2026-08-28** 的官方文档与本仓库实现的差异。

| 主题 | 官方文档 | BeefAPI 当前合同 | 来源 |
|---|---|---|---|
| 参考生视频最长时长 | `grok-imagine-video-1.5` 写 **15 秒** | **10 秒** | 官方 [Reference-to-Video](https://docs.x.ai/developers/model-capabilities/video/reference-to-video)；`request.go` `validateRequestMode`；公开价格里 `max_reference_duration_seconds = 10` |
| 省略 `aspect_ratio` 的图生视频 | 跟原图比例 | 仍发送 `16:9` | 官方 Video Generation；`buildNativeRequest` |
| 省略 `resolution` | 默认 480p | 发送 `480p` | 双方一致；BeefAPI 会显式带上该字段 |
| 省略 `duration` | REST 示例常不写；SDK/部分兼容层用 4 | 发送 `4` | `defaultDuration = 4` |
| `generate_audio` 省略 | 默认出声 | 不传字段，沿用上游默认 | 官方 Video Generation；`buildNativeRequest` |
| 旧模型 | 官方仍有 `grok-imagine-video` 页 | **不对用户提供** | 产品合同；阶梯价默认只有 1.5 |
| 视频编辑 / 延长 | 官方有独立文档和 API | **不暴露** | 适配器只实现 `generate` → `/v1/videos/generations` |

不要把内部 OpenAI 兼容转换层的默认值（例如把 `size` 缺省成 `720x1280`）当成 BeefAPI 用户合同。用户请求走 `relay/channel/task/grok_build/`。

---

## 5. 可复制 curl

把主机和密钥换成你的。**不要**把内部对象存储地址、Worker 地址或渠道号写进脚本。

```bash
export BEEFAPI_BASE="https://<你的 BeefAPI 主机>"
export BEEFAPI_KEY="sk-..."
```

### 文生视频

```bash
curl -sS -X POST "$BEEFAPI_BASE/v1/videos" \
  -H "Authorization: Bearer $BEEFAPI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "grok-imagine-video-1.5",
    "prompt": "纸船在雨后的水洼里漂，电影感微距",
    "duration": 4,
    "aspect_ratio": "16:9",
    "resolution": "720p",
    "generate_audio": false
  }'
```

### 单图生视频

`image` 可以是 HTTPS URL、data URL，或 `{"url":"..."}`。

```bash
curl -sS -X POST "$BEEFAPI_BASE/v1/videos" \
  -H "Authorization: Bearer $BEEFAPI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "grok-imagine-video-1.5",
    "prompt": "镜头缓慢前推，风吹动头发",
    "duration": 6,
    "aspect_ratio": "9:16",
    "resolution": "1080p",
    "image": {
      "url": "https://example.com/start.png"
    }
  }'
```

OpenAI 风格单图：

```bash
curl -sS -X POST "$BEEFAPI_BASE/v1/videos" \
  -H "Authorization: Bearer $BEEFAPI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "grok-imagine-video-1.5",
    "prompt": "把这张静帧做成缓慢延时",
    "duration": 8,
    "input_reference": {
      "image_url": "https://example.com/start.png"
    }
  }'
```

### 多参考图 / reference-to-video

参考模式最长 10 秒、最高 720p。`image` 与 `reference_images` 不能一起传。提示词里用 `<IMAGE_0>` 起的序号以上游实际为准。

```bash
curl -sS -X POST "$BEEFAPI_BASE/v1/videos" \
  -H "Authorization: Bearer $BEEFAPI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "grok-imagine-video-1.5",
    "prompt": "人物走出画面深处，穿上第二张图里的衣服，背景用第三张图的场景",
    "duration": 10,
    "aspect_ratio": "16:9",
    "resolution": "720p",
    "reference_images": [
      { "url": "https://example.com/person.png" },
      { "url": "https://example.com/outfit.png" },
      { "url": "https://example.com/set.png" }
    ]
  }'
```

### 带预设参考语音

```bash
curl -sS -X POST "$BEEFAPI_BASE/v1/videos" \
  -H "Authorization: Bearer $BEEFAPI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "grok-imagine-video-1.5",
    "prompt": "画面中的人用 <AUDIO_0> 的声音对镜头说话，第二个人用 <AUDIO_1> 的声音接话",
    "duration": 8,
    "aspect_ratio": "9:16",
    "resolution": "720p",
    "reference_images": [
      { "url": "https://example.com/person.png" },
      { "url": "https://example.com/product.png" },
      { "url": "https://example.com/set.png" }
    ],
    "reference_audios": [
      { "voice_id": "eve" },
      { "voice_id": "leo" }
    ]
  }'
```

### 查询状态

把 `{id}` 换成创建响应里的 `id`（BeefAPI 公开任务 ID，不是上游内部 ID）。

```bash
curl -sS "$BEEFAPI_BASE/v1/videos/{id}" \
  -H "Authorization: Bearer $BEEFAPI_KEY"
```

建议循环：

```bash
TASK_ID="<创建返回的 id>"
while true; do
  RESULT=$(curl -sS "$BEEFAPI_BASE/v1/videos/$TASK_ID" \
    -H "Authorization: Bearer $BEEFAPI_KEY")
  STATUS=$(printf '%s' "$RESULT" | jq -r '.status')
  echo "$STATUS"
  case "$STATUS" in
    completed) echo "$RESULT" | jq '{id,status,progress,metadata}'; break ;;
    failed) echo "$RESULT" | jq .; break ;;
    queued|in_progress|unknown) sleep 5 ;;
    *) echo "$RESULT"; sleep 5 ;;
  esac
done
```

### 下载 content

```bash
curl -sS -L "$BEEFAPI_BASE/v1/videos/{id}/content" \
  -H "Authorization: Bearer $BEEFAPI_KEY" \
  -o result.mp4
```

- `-L`：跟随 302。
- 任务未成功会得到 JSON 错误，不要把它存成 mp4。
- 跳转目标是短时读取地址，过期后重新请求 `/content`。
- 自带 `output.upload_url` 的任务 **不会** 经这个入口代取文件。

---

## 6. 响应字段和状态

客户端看到的是 BeefAPI 的 OpenAI 风格 `video` 对象，**不是** xAI 原生 `{"request_id":"..."}`。轮询请用创建响应里的 `id`。

### 创建成功（HTTP 200）

适配器写入的字段：

| 字段 | 类型 | 含义 |
|---|---|---|
| `id` | string | BeefAPI 公开任务 ID，后续查询/取片用这个 |
| `task_id` | string | 与 `id` 相同（旧字段，不要新依赖） |
| `object` | string | `video` |
| `model` | string | 请求的模型名 |
| `status` | string | 创建时固定写 `queued` |
| `progress` | int | 创建时为 `0` |
| `created_at` | int | Unix 秒 |
| `seconds` | string | 若请求带了 `duration`/`seconds` 则会回传；省略 duration 时本字段可能缺失 |
| `size` | string | 若请求带了 `size` 则回传 |

上游创建响应必须带 `request_id` 或 `id`，否则 BeefAPI 返回 `invalid_response`（502）。上游的 `request_id` **不会**出现在用户响应里。

未在上表列出的字段（例如上游 `usage`、原生 `video.url`）不要当成稳定合同。

### 查询中的状态映射

BeefAPI 对外状态（`dto.OpenAIVideo`）：

| 对外 `status` | 内部任务状态 | 上游状态（解析时） |
|---|---|---|
| `queued` | `QUEUED` / `SUBMITTED` | `queued`、`pending` |
| `in_progress` | `IN_PROGRESS` | `processing`、`in_progress`、`running` |
| `completed` | `SUCCESS` | `completed`、`done`、`succeeded`、`success` |
| `failed` | `FAILURE` | `failed`、`error`、`expired`、`cancelled`、`canceled` |
| `unknown` | 其他内部状态 | — |

查询响应还会有：

| 字段 | 何时出现 | 说明 |
|---|---|---|
| `progress` | 常有 | 由任务进度字符串去掉 `%` 得到整数。后台默认进度：排队约 20、处理中约 30、结束 100。若上游带 `progress`，会格式化成 `N%` 再解析 |
| `completed_at` | 有更新时间就有 | 当前实现写成任务 `UpdatedAt`，不要当成精确完成时刻 SLA |
| `seconds` | 成功且上游 body 含 `video.duration` | 以上游实际返回为准 |
| `error.code` / `error.message` | 失败且上游 JSON 含 `error` | 以上游为准 |
| `metadata.url` | 有结果地址时 | 受管存储成功后是 BeefAPI 的 `/v1/videos/{id}/content`，**不是**对象存储原始地址 |
| `metadata.content_url` | 成功且适配器补写 | 同样指向 content 代理 |

成功完成时，上游必须带 `video.url`（适配器才把任务标成功）；对用户来说，这个 URL 会被换成 content 代理，查询响应和日志里的 query string 会打码。不要依赖、保存或传播内部对象 URL。

自带 `output.upload_url` 且成功的任务：状态可以是 `completed`，但 **不会** 保存或返回你的 PUT URL，content 代理也不会去你的桶里取片。

### 处理中 / 成功 / 失败（查询示例形状）

字段集合以当前 DTO 为准；未出现的键不要假设稳定存在。

```json
{
  "id": "task_...",
  "object": "video",
  "model": "grok-imagine-video-1.5",
  "status": "in_progress",
  "progress": 30,
  "created_at": 0
}
```

```json
{
  "id": "task_...",
  "object": "video",
  "model": "grok-imagine-video-1.5",
  "status": "completed",
  "progress": 100,
  "seconds": "4",
  "metadata": {
    "url": "https://<BeefAPI主机>/v1/videos/task_.../content",
    "content_url": "https://<BeefAPI主机>/v1/videos/task_.../content"
  }
}
```

```json
{
  "id": "task_...",
  "object": "video",
  "model": "grok-imagine-video-1.5",
  "status": "failed",
  "error": {
    "code": "permission_denied",
    "message": "not entitled"
  }
}
```

失败示例里的 `code`/`message` 来自测试夹具，真实失败以上游为准。

### 创建阶段常见错误

HTTP 状态在响应头；JSON 主体是 `{"code":"...","message":"...","data":null}`（`TaskError`）。不要假设一定包在 `error` 对象里。

| HTTP | `code` | 典型 message | 何时 |
|---|---|---|---|
| 400 | `invalid_json` | 非法 JSON | 体解析失败 |
| 400 | `invalid_model` | `model must be grok-imagine-video or grok-imagine-video-1.5` | 模型名不在适配器列表。用户侧仍只应传 1.5 |
| 400 | `invalid_request` | `prompt is required` | 空 prompt |
| 400 | `invalid_request` | `duration must be between 1 and 15 seconds` | 时长越界 |
| 400 | `invalid_request` | `duration must be an integer` / `seconds must be an integer` | 非整数 |
| 400 | `invalid_request` | `aspect_ratio must be one of ...` | 比例非法 |
| 400 | `invalid_request` | `resolution must be 480p, 720p, or 1080p` | 分辨率非法 |
| 400 | `invalid_request` | `size must be one of 720x1280, 1280x720, 1024x1792, or 1792x1024` | size 非法 |
| 400 | `invalid_request` | `generate_audio must be a boolean` | 非布尔 |
| 400 | `invalid_request` | `output.upload_url must be a public https URL` | 自带 output 不合格 |
| 400 | `invalid_request` | `reference_images supports at most 7 images` | 超过 7 张 |
| 400 | `invalid_request` | `reference_audios supports at most 3 voices` | 超过 3 路语音 |
| 400 | `invalid_request` | `image and reference_images cannot be combined` | 模式冲突 |
| 400 | `invalid_request` | `reference-to-video duration must be between 1 and 10 seconds` | 参考模式超时长 |
| 400 | `invalid_request` | `reference-to-video is limited to 720p` | 参考模式用了 1080p |
| 401 | （鉴权中间件） | 未授权 | 缺令牌 / 令牌无效 |
| 402 或 403 | 预扣费错误码（常见 `insufficient_user_quota`） | 余额不足等 | 创建前预扣失败。国内钱包不足常见 402；其他账本失败可能是 403。以实际响应为准 |
| 503 | `managed_video_storage_unavailable` | 受管存储不可用 | 未传 `output` 且受管存储未配置或预签名失败。扣费和上游创建都不会发生 |

取片：

| HTTP | 含义 |
|---|---|
| 400 | 任务尚未成功 |
| 401 | 未授权 |
| 404 | 任务不存在或不属于当前用户 |
| 302 | 受管存储签发读取地址 |
| 502 | 受管对象校验失败，或非受管路径拿不到可取内容 |

上游创建非 200 时，错误体可能是上游原文（`fail_to_fetch_task`）。未知 `voice_id`、审核拒绝等 **以上游实际返回为准**，不要把本文的夹具文案当成稳定错误码表。

来源：`dto/openai_video.go`、`dto/task.go`、`model/task.go`、`relay/channel/task/grok_build/adaptor.go`、`controller/video_proxy.go`、`controller/relay.go`。

---

## 7. ZDR 与受管对象存储

Grok Build 账号走 Zero Data Retention 时，xAI **不替调用方托管成品**。每次创建都必须给上游一个一次性、S3 兼容的 HTTPS 预签名 **PUT** 地址。普通客户端 **不必、也不该** 自己传 `output`。

### 默认（推荐）：省略 `output`

BeefAPI 会：

1. 生成不透明对象键，前缀 `transient/video/v1/`（键本身不返回给用户）。
2. 签发 **15 分钟** PUT URL，写入上游请求的 `output.upload_url`。
3. 渠道重试复用同一上传目标与幂等键，避免重复对象。
4. 上游标完成后，用 **HeadObject** 校验对象存在、大小（必须 `> 0` 且 ≤ 256 MiB）、内容类型（空 / `application/octet-stream` / `video/*`）。校验失败则任务不能标成功。
5. 把用户侧结果固定为需鉴权的 `/v1/videos/{id}/content`。该入口签发约 **5 分钟** 的读取地址并 302 跳转。
6. 视频字节从对象存储/读取域名直接给客户端，**不经过 BeefAPI 应用主机**。

受管存储不可用、且请求又省略了 `output`：在预扣费和上游创建之前 **fail-closed**，HTTP 503 `managed_video_storage_unavailable`。不会退回到应用机磁盘或 Redis 放大对象。

### 高级：自带 `output.upload_url`

```json
{
  "model": "grok-imagine-video-1.5",
  "prompt": "纸船在水洼里漂",
  "duration": 4,
  "output": {
    "upload_url": "https://<你的桶>/object.mp4?<你的预签名参数>"
  }
}
```

责任边界：

- URL 必须是 `https`，主机非空，不能带 userinfo；`http://` 或缺少 host 会在本地校验失败。**当前本地校验不会识别内网地址，也不会验证签名是否有效**，调用方必须保证目标从 xAI 可访问、仅允许所需对象写入且在短时间内过期。
- BeefAPI 原样转发给上游；日志里的 `output.upload_url` 会打码。
- **不会**持久化这条 PUT URL，也 **不会** 用 content 代理去你的桶取片。
- 任务成功只表示上游认为写入完成；你必须自己从自己的对象系统读文件。
- 过期、权限、桶策略、CORS 全部由调用方负责。

不要在工单、文档或示例里粘贴真实预签名 URL、桶名或 Worker 路径。

来源：`service/video_delivery.go`、`service/r2_object_store.go`、`service/object_storage_config.go`、`relay/channel/task/grok_build/adaptor.go`、`controller/video_proxy.go`、官方 [ZDR video storage](https://docs.x.ai/build/settings/zdr-video-storage)。官方 ZDR 页面向 Grok Build 本机 `managed_config.toml`；BeefAPI 用站点受管存储完成同等「预签名 PUT 给上游」这件事，不要把本机 toml 配置抄给用户。

---

## 8. 国内售价与算例

对国内 `grok`、`workbuddy` 分组，**人民币视频阶梯价是唯一售价源**。管理员在后台改的数，同时驱动预扣费、`/api/pricing` 和公开价格页。海外 `global` 分组走独立 USD 价格簿，**不读**这张人民币阶梯。

默认（`grok-imagine-video-1.5`）：

| 项 | 单价 |
|---|---|
| 480p | ¥0.48 / 秒 |
| 720p | ¥0.84 / 秒 |
| 1080p | ¥1.50 / 秒 |
| 参考图 | ¥0.06 / 张 |

公式：

```
费用（元）= 秒数 × 该分辨率每秒价 + 参考图张数 × 0.06
```

- 文生：参考图张数 = 0。
- 单图生：`image` / `input_reference` 计 **1** 张。
- 多参考图：按有效 `reference_images` 张数。
- 预设 `reference_audios` **不加**这一项（官方：预设语音输入不计媒体输入费；BeefAPI 同样不把 voice 算进参考图张数）。

### 4 / 10 / 15 秒示例（不含参考图）

| 时长 | 480p | 720p | 1080p |
|---|---|---|---|
| 4 秒 | ¥1.92 | ¥3.36 | ¥6.00 |
| 10 秒 | ¥4.80 | ¥8.40 | ¥15.00 |
| 15 秒 | ¥7.20 | ¥12.60 | ¥22.50 |

参考模式最长 10 秒，因此 **没有** 合法的「15 秒 + 参考图」组合。15 秒一行只适用于文生 / 单图生。

### 参考图叠加

| 请求 | 计算 | 合计 |
|---|---|---|
| 4 秒 720p 文生 | 4 × 0.84 | ¥3.36 |
| 4 秒 720p + 1 张首帧 | 3.36 + 0.06 | ¥3.42 |
| 4 秒 720p + 2 张参考图 | 3.36 + 0.12 | ¥3.48 |
| 10 秒 720p + 7 张参考图 | 8.40 + 0.42 | ¥8.82 |
| 4 秒 1080p + 2 张参考图 | 非法（参考模式不能 1080p） | — |

金额以当时后台保存的阶梯为准。上表按仓库默认值计算。

### 用户在哪里看价

- 控制台价格表（`/pricing`）展示同一份阶梯，含 4/10/15 秒样例和「参考图每张」。
- `GET /api/pricing` 的 `video_price_ladders`。
- `GET /api/public/pricing` 与 `GET /pricing.json` 在模型行的 `video_price_ladder` 中给出每秒价、参考图价、`max_duration_seconds=15`、`max_reference_duration_seconds=10`。

后台入口：控制台设置 → 倍率/价格 → **视频阶梯价**（`/console/setting?tab=ratio`）。分辨率价格必须为正数且按 480p ≤ 720p ≤ 1080p 单调，参考图价格可为 0 但不能为负数；校验失败或缺少 1.5 时不会改扣费。

### 不要把上游美元成本写成用户售价

xAI 官方 1.5 标价（[模型页](https://docs.x.ai/developers/models/grok-imagine-video-1.5)，2026-08-28 核对）是上游 USD：**480p $0.08/s、720p $0.14/s、1080p $0.25/s，图片输入 $0.01/张**。这是账号成本权重，不是用户账单。

仓库里 `ModelPrice`（例如 1.5 的 `0.20`）只给 **不用人民币阶梯的分组** 做兼容底价。国内最终金额由阶梯覆盖，操作员 `ModelPrice` 和分组倍率不能悄悄改国内用户应付额。

来源：`setting/ratio_setting/video_price_ladder.go`、`relay/channel/task/grok_build/request.go`、`controller/pricing.go`、`controller/public_discovery.go`、`web/src/helpers/video-price-ladder.js`。

---

## 9. Heavy 百分比（运维口径）

用户账单只认 §8 的人民币阶梯，**不是** Grok 订阅剩余百分比。

运维可在 Grok Build 渠道账号页看到上游 `/v1/billing?format=credits` 解析结果，其中包括：

- `plan_name`（测试夹具出现过 `SuperGrok Heavy`）
- `remaining_percent` / `credit_usage_percent`
- `usage_percent_known`

可审计约束：

- 百分比是上游订阅账户的观测值，有延迟，也可能按整数/小数展示。
- 没有用量周期时，实现 **不会发明** 百分比（`usage_percent_known=false`）。
- 不能把剩余百分比写成用户额度、固定配额或 SLA。
- 一次生成消耗多少「百分点」会随分辨率、时长、参考图、上游权重变化，不能从整数百分比反推出固定额度。

### 2026-08-28 受控观测（不是换算合同）

一次隔离的 Heavy 生产实验从 `creditUsagePercent = 55` 开始，连续生成 6 条同提示词、16:9、静音视频：

| 时长 | 480p receipt 权重 | 720p receipt 权重 | 1080p receipt 权重 |
|---|---:|---:|---:|
| 1 秒 | $0.08 | $0.14 | $0.25 |
| 4 秒 | $0.32 | $0.56 | $1.00 |

这批请求的 receipt 权重合计 **$2.35**。前 5 次即时读取仍显示 55，最后一次请求约 20 秒后变为 56，并持续到 60 秒后的最终读回；实验窗口内该账号没有其他 BeefAPI 视频请求。

这只能证明「该批次跨过了一个**显示出来的**百分点」，**不能证明 `$2.35 = 1%`**。原因是上游只展示整数百分比，实验开始时隐藏的小数位置未知，且同步存在约 20 秒延迟。把单条请求按 $2.35 比例分摊成百分点，只能用于临时估算，不能写进售价、额度承诺或 SLA。实验在 Owner 批准的 `+1` 百分点上限处停止。

来源：`controller/grok_build_account.go`、`controller/grok_build_account_test.go`；2026-08-28 Heavy 受控生产实验回执。

---

## 10. 网页 Imagine 与 Build / 本通路

| | grok.com Imagine 网页 | BeefAPI（Build API 转发） |
|---|---|---|
| 入口 | 浏览器产品 | `POST /v1/videos` JSON |
| 模型 | 网页自己的产品目录 | 仅 `grok-imagine-video-1.5` |
| 文生 / 图生 / 参考图 / 预设语音 | 网页工作流 | 官方 API 字段，本通路可转发 |
| Projects、素材库、搜索、多代理 | 网页 UI | **不复制**，也不是请求参数 |
| 视频编辑、从末帧延长 | 网页/官方另有 API | **暂不暴露** |
| 输出托管 | 网页画廊 | ZDR：写入调用方或 BeefAPI 受管存储 |

不支持（当前合同）：

- `grok-imagine-video` 旧模型作为用户入口
- `/v1/videos/{id}/remix`
- xAI `videos/edits`、`videos/extensions`
- 自定义参考音频文件
- 渠道「参数覆盖」改写本通路 JSON
- 把网页积分、日次数、Projects 权限当成 API 配额

编辑和延长暂不开放：现有任务计费按「时长 × 分辨率 + 参考图张数」预扣，表达不了「输入视频时长」这类额外成本；适配器也只实现 generate。账单合同能准确表达之前，不能用错误价格上线。

---

## 11. 管理员配置

1. 渠道类型为 Grok Build，模型列表 **显式包含** `grok-imagine-video-1.5`，并挂到应对用户开放的分组（国内常见 `grok` / `workbuddy`）。
2. 受管对象存储必须可用（否则无 `output` 的请求 503）。
3. 「视频阶梯价」必须包含 `grok-imagine-video-1.5`，480p/720p/1080p 为正数且单调，参考图价 ≥ 0。
4. 不要只改 `ModelPrice` 或分组倍率来调国内视频售价。
5. 不要把旧 `grok-imagine-video` 加进用户可见目录或阶梯价。
6. 渠道参数覆盖不要指望能改视频 JSON。
7. Grok Build 账号页的剩余百分比只用于运维观察，不驱动用户扣费。

用户发现价格：控制台价格表、`/api/pricing`、`/api/public/pricing`、`/pricing.json`。国内页应能看到每秒价和参考图加价，而不是上游美元。

---

## 12. 验收清单

下列每一项都要单独留证据。HTTP 200、单测绿、或另一项成功，都不能替代缺失项。

| 项 | 做什么 |
|---|---|
| 本地转换 | `go test ./relay/channel/task/grok_build/ ./service/ -count=1` 中与视频相关的用例（显式 `false`、互斥、阶梯价、受管 PUT、Head 校验、打码） |
| 公开价格 | `go test ./controller/ -count=1 -run 'PublicPricing\|VideoPrice'`；确认默认阶梯 0.48/0.84/1.50/0.06，且公开 JSON 含 15/10 秒上限 |
| 协议/转发 | `cpa_runtime_sidecar/` 与 `relay/channel/task/grok_build/` 相关测试；确认创建打到 `/v1/videos/generations`，日志打码 `upload_url` |
| 模型目录 | 用当前 Grok Build 账号看上游 `/v1/models`，再核对 BeefAPI 渠道实际启用的是 1.5，而不是「目录里有就等于已开放」 |
| 真实创建 | 文生、单图、多参考图、带 `generate_audio: false`、带预设 voice 各至少一次；确认 1080p+参考图、15 秒+参考图被 BeefAPI 拒绝 |
| 轮询 | `GET /v1/videos/{id}` 从 `queued`/`in_progress` 到 `completed` 或 `failed` |
| 读回 | `GET /v1/videos/{id}/content` 302 后得到可播放 mp4；确认响应和文档都没有内部对象 URL |
| 扣费 | 预扣金额 = 秒数×阶梯 + 张数×参考图价；失败退回；国内不受 `ModelPrice` 悄悄偏移 |
| 上游 usage | 如需核对成本，用账号侧 receipt/usage，**不要**把它写成用户售价 |
| 部署 | 记录实际部署 commit；本地测试不能当生产已上线 |

---

## 13. 排障

| 现象 | 先查 |
|---|---|
| `invalid_model` | 模型名是否精确为 `grok-imagine-video-1.5` |
| `prompt is required` | JSON 是否空、字段是否拼成 `promt` |
| `image and reference_images cannot be combined` | 单图和参考图是否写在同一请求 |
| 参考模式 1080p / 15 秒被拒 | 这是 BeefAPI 合同，不是漏转发 |
| `generate_audio must be a boolean` | 不要传字符串 `"false"` |
| `managed_video_storage_unavailable` | 受管存储未配，或预签名失败；不要改去「先扣费再试」 |
| 创建成功但一直 `queued` | 继续轮询；看后台任务轮询是否在跑；是否已超时失败 |
| `completed` 但 `/content` 502 | HeadObject 失败（空对象、过大、错误 Content-Type）；不要把内部键返回给用户排障 |
| `/content` 400「尚未完成」 | 先查 `GET /v1/videos/{id}` 的 `status` |
| 302 后 403/过期 | 读取地址约 5 分钟有效，重新 GET `/content` |
| 自带 `output` 成功却没有 content | 预期行为：自己去自己的桶读 |
| 扣费和价格页不一致 | 是否改了阶梯价却没保存成功；是否看的是海外分组 |
| 未知 `voice_id` | 以上游 400 为准；查官方 TTS 声线表 |
| 审核失败 | 以上游 `error.message` 为准 |

任务超时默认由 `TASK_TIMEOUT_MINUTES` 控制（代码默认 1440 分钟）。超时会失败并退还未完成任务的预扣（遗留任务规则除外）。这不是「保证 24 小时内出片」。

---

## 14. 安全注意事项

- 不要把渠道号、账号邮箱、refresh/access token、真实对象 URL、预签名 query、用户 ID 写入文档、工单摘要或示例。
- 日志会打码 `output.upload_url` 和结果 URL 的 query；排障时用任务 ID 和状态，不要把完整签名 URL 贴到群里。
- `/content` 必须带令牌或登录态；短时读取地址仍视为秘密，不要公开分发。
- 图片 URL 会由上游去拉取。不要把内网地址、带凭证的 URL 塞进 `image` / `reference_images`。
- data URL 会进入请求体，注意网关 body 大小限制。
- 本通路 fail-closed：没有受管存储就不能静默把成品写到应用机。

---

## 官方一手链接

- [Video Generation](https://docs.x.ai/developers/model-capabilities/video/generation)
- [Image-to-Video](https://docs.x.ai/developers/model-capabilities/video/image-to-video)
- [Reference-to-Video](https://docs.x.ai/developers/model-capabilities/video/reference-to-video)
- [grok-imagine-video-1.5](https://docs.x.ai/developers/models/grok-imagine-video-1.5)
- [ZDR video storage](https://docs.x.ai/build/settings/zdr-video-storage)
- [Pricing](https://docs.x.ai/developers/pricing)
- [Text to Speech voices](https://docs.x.ai/developers/model-capabilities/audio/text-to-speech#voices)

官方页面变更时，先对照 `relay/channel/task/grok_build/request.go` 再改本文。用户合同以 BeefAPI 当前实现为准。
