# KOKO 视频生成 API

> 最后更新：2026-10-23。模型启停状态、账号实时积分价格和参考素材限制以 `GET /models` 返回为准。
> 公开 API 只允许调用模型列表中当前启用并开放 OpenAPI 的模型；模型列表已删除/停用/退役的模型不能再调用（返回 `MODEL_NOT_SUPPORTED`）。

Base URL：

```text
https://20saminos.com/openapi/v1
```

当前公开 API 提供以下 19 个视频模型。图片生成（`gpt-image-2`）走站点登录态接口，见文末「图片生成」一节；音频生成、文本生成和已下线模型不在此接口范围内。每个模型的实时价格（`duration_prices`）、可选时长和参考素材上限以账号 `GET /models` 返回为准；已从模型列表删除/停用/退役的模型不能再调用。

| 模型 ID（`model`） | 名称 | 可选时长（秒） | 分辨率 | 参考上限（图 / 视频 / 音频） |
| --- | --- | --- | --- | --- |
| `seedance-2.0` | Seedance 2.0·卡人脸满血版 | 5 / 10 / 15 | 720p | 9 / 0 / 3 |
| `minimaxh3` | MiniMaxH3 2K满血版 | 5–15 任意整数秒 | 2K | 8 / 0 / 3 |
| `chaomeng-video-345b30093bb2` | 超梦速冲 Seedance 2.5 满血 | 30 | 720p | 9 / 0 / 0 |
| `chaomeng-video-4e7c9cbaf92a` | 晚线1 Seedance 2.5 过人脸 | 30 | 720p | 9 / 0 / 0 |
| `chaomeng-video-89115b278079` | 晚线2 Seedance 2.5 900 过人脸 | 30 | 720p | 9 / 0 / 0 |
| `chaomeng-video-88b0bf9d5c3f` | Seedance 2.5 特价 1080p 930 | 5–30 任意整数秒 | 1080p | 9 / 0 / 0 |
| `chaomeng-video-cd673cf9f19b` | 超梦晚线6 301010 | 4–30 任意整数秒 | 720p | 30 / 0 / 0 |
| `lingjing-seedance-2.0-fast-vertical` | Seedance 2.0 Fast 竖屏 | 15 | 720p | 9 / 3 / 0 |
| `lingjing-seedance-2.0-mini-vertical` | Seedance 2.0 Mini 竖屏 | 15 | 720p | 9 / 0 / 0 |
| `sd2_480p_real` | SD2.0 | 15 | 480p | 9 / 3 / 3 |
| `sd_2_5_once_720p` | SD2.5 满血全参 | 30 | 720p | 30 / 10 / 10 |
| `bb2.5` | Seedance 2.5 全能参考 | 30 | 720p | 30 / 10 / 10 |
| `AL-seedance-2.5-900` | Seedance 2.5·AL | 15 | 720p | 9 / 0 / 0 |
| `kk-sd25-full-720p` | KKSeedance2.5 满参满血 | 30 | 720p | 30 / 10 / 10 |
| `nh-seedance2.5-720p` | 官转 Seedance 2.5 · NH 720P | 4–30 任意整数秒（按秒计费） | 720p | 30 / 10 / 10 |
| `nh-seedance2.5-1080p` | 官转 Seedance 2.5 · NH 1080P | 4–30 任意整数秒（按秒计费） | 1080p | 30 / 10 / 10 |
| `wan3-720p-fast` | KKWAN3.0-720P快速 | 30 | 720p | 10 / 0 / 5 |
| `wan3.0-vedio-deal` | wan3.0-普通通道 | 5 / 10 / 15 / 30（同价） | 480p / 720p / 1080p | 10 / 0 / 5 |
| `wan3.0-video-prime-deal` | wan3.0-快速通道 | 5 / 10 / 15 / 30（同价） | 480p / 720p / 1080p | 10 / 0 / 5 |

## 1. API Key

客户登录 KOKO Studio 后，在账号 API Key 管理入口创建密钥。完整密钥只在创建成功时返回一次，后续只能查看前缀。

```bash
curl -X POST https://20saminos.com/api/account/api-keys \
  -H "Content-Type: application/json" \
  -H "Cookie: koko_session=客户登录后的会话 Cookie" \
  -d '{"name":"我的 Seedance 服务"}'
```

管理接口：

- `GET /api/account/api-keys`：查看密钥列表和当前账号 API 价格。
- `POST /api/account/api-keys`：创建密钥。
- `DELETE /api/account/api-keys/{key_id}`：停用密钥。

公开 API 请求头：

```http
Authorization: Bearer koko_live_xxxxxxxxx
```

API Key 等同于账号访问凭证，只应保存在服务端环境变量中。

## 2. 查看模型和价格

```bash
curl https://20saminos.com/openapi/v1/models \
  -H "Authorization: Bearer koko_live_xxxxxxxxx"
```

返回结构示例（节选 2 个模型；完整字段、全部 12 个模型及当前账号价格请实时查询）：

```json
{
  "models": [
    {
      "id": "seedance-2.0",
      "name": "Seedance 2.0·卡人脸满血版",
      "description": "Seedance 2.0·卡人脸满血版",
      "type": "video",
      "duration": 15,
      "durations": [5, 10, 15],
      "reference_images": {"supported": true, "max": 9, "formats": ["jpeg", "png", "webp"]},
      "reference_audios": {"supported": true, "max": 3},
      "ratio": ["9:16", "1:1", "3:4", "4:3", "16:9"],
      "resolutions": ["720p"],
      "enabled": true,
      "maintenance": false
    },
    {
      "id": "minimaxh3",
      "name": "MiniMaxH3 2K满血版(支持参考音频，不支持参考视频)",
      "description": "MiniMaxH3 2K满血版(支持参考音频，不支持参考视频)",
      "type": "video",
      "duration": 15,
      "durations": [5, 10, 15],
      "reference_images": {"supported": true, "max": 8, "formats": ["jpeg", "png", "webp"]},
      "reference_audios": {"supported": true, "max": 3},
      "ratio": ["9:16", "1:1", "3:4", "4:3", "16:9"],
      "resolutions": ["2K"],
      "enabled": true,
      "maintenance": false
    }
  ]
}
```

`duration_prices` 和 `points` 的单位都是积分。所有模型的实际价格以当前账号 `GET /models` 的实时返回为准，不应硬编码示例价格。

## 3. 创建视频任务

```bash
curl -X POST https://20saminos.com/openapi/v1/videos \
  -H "Authorization: Bearer koko_live_xxxxxxxxx" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: customer_order_20260917_001" \
  -d '{
    "model": "seedance-2.0",
    "prompt": "一只小狗在草地上奔跑，镜头缓慢推进，阳光自然，画面稳定",
    "duration": 5,
    "ratio": "9:16",
    "resolution": "720p",
    "mode": "reference",
    "count": 1
  }'
```

创建成功返回 `202`。相同账号使用相同 `Idempotency-Key` 重试时返回原任务，不会重复扣积分；此时也可能返回 `200`。

minimaxh3 示例（15 秒、带参考图）：

```bash
curl -X POST https://20saminos.com/openapi/v1/videos \
  -H "Authorization: Bearer koko_live_xxxxxxxxx" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: minimax_h3_4s_20260919_001" \
  -d '{
    "model": "minimaxh3",
    "prompt": "雨后的城市街道倒映霓虹，一辆复古汽车缓慢驶过，镜头平稳跟拍",
    "duration": 15,
    "ratio": "16:9",
    "resolution": "2K",
    "mode": "reference",
    "count": 1
  }'
```

注意：minimaxh3 必须提供 1–8 张参考图，可附参考音频（最多 3 个），不支持参考视频。

```bash
curl -X POST https://20saminos.com/openapi/v1/videos \
  -H "Authorization: Bearer koko_live_xxxxxxxxx" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: sd2_av_20260926_001" \
  -d '{
    "model": "sd2_480p_real",
    "prompt": "参考视频中的动作和镜头节奏，并跟随参考音频生成自然连贯的视频",
    "duration": 15,
    "ratio": "16:9",
    "resolution": "720p",
    "attachments": [
      {
        "type": "video",
        "url": "https://example.com/reference.mp4",
        "mime": "video/mp4",
        "duration_seconds": 10
      },
      {
        "type": "audio",
        "url": "https://example.com/reference.mp3",
        "mime": "audio/mpeg"
      }
    ]
  }'
```

也可以分别使用 `video_urls` / `reference_videos` 和 `audio_urls` / `reference_audios`。参考视频、音频的支持类型及数量以 `GET /models` 为准；视频应提供准确的 `duration_seconds`，时长限制取决于所选模型。minimaxh3 不支持参考视频，参考图最多 8 张、参考音频最多 3 个，混合素材合计最多 10 个。

```json
{
  "id": "upstream_task_id",
  "status": "running",
  "model": "seedance-2.0",
  "prompt": "一只小狗在草地上奔跑，镜头缓慢推进，阳光自然，画面稳定",
  "prompt_complete": true,
  "points": 15,
  "balance": 185,
  "video_url": null,
  "failure_reason": null,
  "created_at": "2026-09-17T12:00:00.000Z",
  "updated_at": "2026-09-17T12:00:00.000Z"
}
```

### 参数规则

下表为两个常用模型的参数规则；其余 10 个模型的参数以 `GET /models` 返回的 `durations`、`resolutions`、`reference_images`、`reference_videos`、`reference_audios` 字段为准。

| 参数 | Seedance 2.0（`seedance-2.0`） | MiniMax H3（`minimaxh3`） |
| --- | --- | --- |
| `duration` / `seconds` | `5`、`10`、`15` | `5`、`10`、`15` |
| `resolution` | 固定 `720p` | 固定 `2K` |
| `ratio` / `aspect_ratio` | `9:16`、`1:1`、`3:4`、`4:3`、`16:9` | 同左 |
| `count` | 固定 `1` | 固定 `1` |
| 参考图片 | 最多 9 张 | 最多 8 张 |
| 参考视频 | 不支持 | 不支持 |
| 参考音频 | 支持，最多 3 个 | 支持，最多 3 个 |
| 计费 | 以 `GET /models` 的 `duration_prices` 为准 | 同左 |

`prompt` 必填。Seedance 2.0 最多 15000 个字符，MiniMax H3 最多 7000 个字符。字符长度按 JavaScript UTF-16 单元计算。

### 幂等键和请求编号

- `Idempotency-Key` 长度为 8-100，只允许字母、数字、下划线和横线。
- 无法设置请求头时，可在 JSON 中传同规格的 `request_id`。
- 两者都未提供时平台会自动生成任务编号，但客户端无法用自己的订单号安全重试。
- 可选请求头 `X-Request-Id` 使用相同格式，响应会在 `X-Request-Id` 和错误体中回传。

### 参考图

Seedance 2.0 每次最多支持 9 张参考图；MiniMax H3 最多支持 8 张参考图。三种字段只能选一种：

```json
{"reference_images":[{"url":"https://example.com/a.jpg"}]}
```

```json
{"image_urls":["https://example.com/a.jpg"]}
```

```json
{"images":["https://example.com/a.jpg"]}
```

对象写法也兼容 `image_url`。

**图片有两种给法，任选其一，也可以混用：**

1. **公网 HTTPS 链接**（上面的写法）—— 需上游能直接访问到。
2. **直接传图片内容（base64）** —— 本地软件不必先上传到公网：

```json
{"image_urls":["data:image/png;base64,iVBORw0KGgoAAAANSUhEUg..."]}
```

```json
{"image_urls":[{"base64":"iVBORw0KGgoAAAANSUhEUg...","mime":"image/png"}]}
```

图片限 JPG、PNG、WebP，单张不超过 50 MB。**base64 现在也支持参考视频与参考音频**（2026-10-15 起）：视频用 MP4、音频用 MP3/WAV/M4A/AAC/FLAC，单个素材同样不超过 50 MB，我们会落盘后转成公网链接，无需你先上传。任一图片下载或上传失败会终止提交并自动退回积分（`400 INVALID_REFERENCE_MEDIA`）。

### MiniMax H3 参考视频和音频

- 推荐使用 `attachments` 传混合参考素材，每项通过 `type` 标明 `image`、`video` 或 `audio`。
- 也兼容 `video_urls`、`reference_videos`、`audio_urls` 和 `reference_audios`。
- 参考视频与参考音频可以给公网 HTTPS URL，**也可以直接传 base64**（与参考图同款写法，见上文「参考图」）。
- 参考视频应提供正确的 `duration_seconds`；仅选择目录声明支持参考视频的模型，并遵守对应时长限制。MiniMax H3 当前不支持参考视频。
- MiniMax H3 最多 8 张参考图、3 个参考音频，不支持参考视频。

### 素材引用、编号与真实资源绑定

本节更新：2026-11-02。`@` 的颜色是编辑器显示状态；有效引用需要真实媒体文件、确定的附件顺序和与附件对应的编号。OpenAPI 不读取客户端素材库中的名称或实体 ID。客户端应先解析 `@角色:名称`、`@场景:名称`、`@道具:名称`、`@音色:名称`、`@视频:名称`，再提交编号和真实媒体；API 不会根据任意名称猜测所指文件。

人物、场景、道具、音频、视频均为可选素材。客户端空占位不发送附件，也不占用引用编号；只有实际上传或生成并已关联真实媒体的条目才参与识别、编号和颜色标记。所选模型的最低素材要求仍然生效，例如模型要求至少一张参考图时，应按模型规则提供真实图片。

客户端名称解析（清影4.8.6）：素材名末尾的数字属于完整名称，人物1、人物11、人物111及同名音色必须按各自实体ID和真实文件匹配，真实长名称未选时按普通文字发送，不回落到已选短名称。替换素材使用单遍最长匹配和完整名称边界，重复操作保持结果不变。仅当前分镜明确选择的真实文件加入附件。旧版音色替换重复追加1时，仅在显式音色引用、唯一对应已选音色、整个项目不存在真实长名称及其他可能原名的条件下兼容；其他数字或字母后缀保持严格检查。实际图片、音频、视频依最终附件顺序分别编号，颜色本身不等于资源绑定。

#### 独立编号与重复资源

图片、音频、视频各自从 1 编号。编号对应网关规范化后最终同类附件的顺序，不是混合 `attachments` 数组的总位置。

| 提示词 | 对应资源 |
| --- | --- |
| `@图片1`、`@图片2` | 最终图片列表的第 1、2 张图片 |
| `@音频1` | 最终音频列表的第 1 段音频 |
| `@视频1` | 最终视频列表的第 1 段视频 |

推荐每次只用一份 `attachments` 数组提交全部素材，避免同时混用 `image_urls`、`reference_images`、`audio_urls`、`video_urls` 等别名字段造成重复或顺序误解。当前网关的重复资源规则如下：

- 图片：同一来源数组内部重复图片保留独立槽位；合并多个别名来源时会合并重复资源。若两个图片槽使用同一张图，仍按两个槽编号。
- 音频、视频：相同类型、相同 URL 的资源合并为同一槽；相同 `data:` URL 也会合并。多个客户端实体若指向同一段音频或视频，应共用该最终编号。
- 重复使用 `@图片1`、`@音频1` 或 `@视频1` 始终指向同一槽，不创建新媒体。客户端应先完成与网关一致的去重，再生成编号。

素材选择、重命名、清除、替换、重新排序以及保存后重开时，应同步维护稳定实体 ID、媒体文件和编号映射。发送期间固定使用同一份附件快照。例如图片顺序从 `[红色机器人, 蓝色机器人]` 改为 `[蓝色机器人, 红色机器人]` 后，名称引用 `@角色:红色机器人` 应编译为 `@图片2`；手写编号则始终按本次最终顺序解释。

#### 混合附件示例

先通过当前账号 `GET /models` 确认模型支持图片、视频和音频，以及可用分辨率、时长和限制。目录中的 `reference_images`、`reference_videos`、`reference_audios` 与提交入口使用同一份能力规则；`supported: false` 或 `max: 0` 表示该类素材不开放。如返回 `reference_media_max_total`，混合素材合计也应符合该上限。H3 当前支持图片和音频，视频上限为 0；Lingjing fast 当前支持图片和视频，音频上限为 0。下面使用 `sd_2_5_once_720p` 展示三类素材的字段组织；参数以实际模型列表为准，示例 URL 应替换为真实素材。

```json
{
  "model": "sd_2_5_once_720p",
  "prompt": "@图片1 中的红色机器人向 @图片2 中的蓝色机器人挥手，参考 @视频1 的动作，使用 @音频1 的旁白。保持两个机器人的外观和颜色。",
  "duration": 30,
  "ratio": "16:9",
  "resolution": "720p",
  "mode": "reference",
  "count": 1,
  "attachments": [
    {"type": "image", "url": "https://example.com/red.png", "mime": "image/png"},
    {"type": "audio", "url": "https://example.com/narration.wav", "mime": "audio/wav"},
    {"type": "video", "url": "https://example.com/motion.mp4", "mime": "video/mp4", "duration_seconds": 3},
    {"type": "image", "url": "https://example.com/blue.png", "mime": "image/png"}
  ]
}
```

这个请求中，混合数组第 2 项仍是 `@音频1`，第 3 项是 `@视频1`，第 4 项是 `@图片2`。如果再附加一项相同 URL 的旁白音频，它仍共用 `@音频1`，不产生 `@音频2`。

#### 文件上传与供应商适配

本地磁盘路径和 `http://127.0.0.1/...` 只在客户端可访问。客户端应读取文件后通过 `data:<mime>;base64,...` 发送字节，或提供服务器可访问的公网 HTTPS 地址。视频同时提供准确的 `duration_seconds`，媒体类型和数量必须符合所选模型。

真实处理链路是：客户端绑定文件并编译编号 → OpenAPI 收集和规范化附件 → 对 base64 素材落盘并生成可访问的站内 HTTPS 地址 → 对应模型的供应商适配层转发真实媒体 → 查询任务终态。

不同供应商使用不同底层协议。例如 Chaomeng 通道先上传文件取得 `file_ids`；MiniMax H3 通道使用图片和音频数组；部分 Seedance 通道使用 `image_urls` 和 `audio_b64s`。具体供应商若使用 `attachment://...` 等资源标注，由该供应商适配层在真实上传后生成；它不是所有模型统一使用的 OpenAPI 请求字段。编号与实际附件一致后，还应按所选模型验证上游原生引用语法和实际生成效果，不能将某一个底层协议的转换规则泛化到全部供应商。

#### 预检、错误与验收

客户端发送前应统一拒绝编号为 0、超出最终同类数量或缺少真实文件的引用，例如一张图片配 `@图片99`，一段去重后的音频配 `@音频2`。同名歧义、缺失文件、不受支持的媒体、数量超限及参考视频时长异常也应先处理。

当前创建请求在扣费前校验实际附件数量和引用编号：图片编号错误可返回 `400 INVALID_IMAGE_REFERENCE`，并注明未扣积分；音频、视频编号以及媒体类型、字段、数量异常可返回 `400 INVALID_REFERENCE_MEDIA`。转发时也会复核同一份能力限制，不静默取前 3 段。所有失败以实际 API 错误体、任务状态和账单为准；保留原任务与幂等键，先核对扣费或退款状态，再决定是否重试。

验收应分别检查实际发送字节、最终附件顺序、服务端素材处理与供应商资源绑定、任务终态和计费状态。HTTP 接收成功不代表任务完成，颜色显示不代表资源已绑定。绑定准确能减少客户端错绑因素；人物外观、物体、动作、声音和画面穿插仍需查看实际生成结果。


## 4. 查询任务

```bash
curl https://20saminos.com/openapi/v1/videos/upstream_task_id \
  -H "Authorization: Bearer koko_live_xxxxxxxxx"
```

状态：

- `submitting`：已接收，正在处理参考素材或向上游提交，不代表生成排队。
- `queued`：兼容历史任务状态；上游自身的排队无法由本站取消。
- `running`：上游生成中。
- `completed`：生成完成，可以读取 `video_url`。
- `failed`：生成失败，积分已自动退回，原因在 `failure_reason`。

上游明确返回余额不足、审核拒绝等错误时，任务结束并按失败规则退款，不会伪装成无限排队。网络超时等结果不确定的情况仍使用同一幂等键恢复，避免重复生成。

任务只能由创建它的账号查询。

创建任务、幂等重试及查询任务的成功响应均返回 `prompt` 和 `prompt_complete`。`prompt` 是本账号提交并被服务接受的完整提示词（沿用提交时去除首尾空白的规则，保留内部换行和中文），不追加上游内部指令。运行中、成功、失败任务查询均支持；已归档的历史提示词会从归档读取。若历史归档缺失、损坏或校验失败，返回 `prompt: null, prompt_complete: false`，不会将截断摘要冒充完整提示词。此变更不修改计费或生成参数。

## 5. 提取视频地址与下载原文件

> 下载协议更新：2026-10-07（原文件下载改为直连上游 CDN）。适用于本平台公开视频 API，包括 Seedance 与 MiniMax H3；不是上游供应商的接口文档。

### 5.1 推荐方式：查询任务，提取 video_url，再下载

1. 使用 API Key 查询 `GET /openapi/v1/videos/{task_id}`。
2. 确认 `status` 为 `completed` 且 `video_url` 非空。
3. 使用返回的完整 `video_url` 发起普通 GET，保存二进制响应。

以下是 Bash 示例，需安装 `curl` 和 `jq`；`KOKO_API_KEY` 请通过服务端环境变量配置。示例不会创建新任务或产生生成费用。

```bash
BASE="https://20saminos.com/openapi/v1"
TASK_ID="替换为已有任务ID"
: "${KOKO_API_KEY:?请先配置 KOKO_API_KEY}"

task_json="$(curl --fail --silent --show-error \
  --connect-timeout 15 --max-time 60 \
  -H "Authorization: Bearer ${KOKO_API_KEY}" \
  "${BASE}/videos/${TASK_ID}")" || exit 1

VIDEO_URL="$(printf '%s' "$task_json" | jq -er \
  'select(.status == "completed") | .video_url | select(type == "string" and length > 0)')" \
  || { printf '任务未完成或尚未返回下载地址\n' >&2; exit 1; }

# 签名下载地址本身已经授权，不再发送 API Key、Cookie 或 Referer。
# 先写临时文件，仅在完整传输成功后改名，避免将中断文件当作成品。
curl --fail --location --show-error \
  --proto '=https' --proto-redir '=https' --max-redirs 5 \
  --connect-timeout 15 --max-time 600 \
  --output "koko-video.mp4.part" "$VIDEO_URL" \
  && mv -- "koko-video.mp4.part" "koko-video.mp4"
```

下载超时值是客户端示例配置，不是服务端速度或完成时间保证。`completed` 表示生成成功，不代表所有来源文件在任何网络下都能立即下载；下载失败请重新查询原任务，不要重新创建收费生成任务。

### 5.2 下载鉴权、域名与有效期

- 查询任务：需要 `Authorization: Bearer <你的 API Key>`。
- `video_url`：带短期签名的下载地址，有效期为生成该地址后约1小时；无需额外 Authorization、Cookie 或 Referer。请像临时访问凭证一样保护它。
- 原样保留整个 URL，不能删除签名参数、替换域名、截断查询串或修改 `media_intent`。过期返回401时，用 API Key 重新查询任务取得新地址。
- **原文件下载会先返回 307，`Location` 指向上游 CDN 直链**（`*.dola.com`、`*.douyinvod.com`）；
  浏览器与 HTTP 客户端直接连到该 CDN 取流，**视频字节不经过本平台服务器**。调用方不需要调用上游
  `signed_url`，也不需要自行添加 `passthrough=1`；对非该渠道的任务，平台内部仍按原方式中继，调用方无感。
- ⚠️ 上游直链**时效很短（约 30 秒）**：必须在拿到 `Location` 后**立即**发起请求，不要缓存、排队或稍后再下。
  失效后请对同一 `/content` 重新请求，平台会重新签发一条。
- 未带签名的 `/videos/{task_id}/content` 需要 API Key，正常先返回 **307**，`Location` 是短期签名地址（**可能位于上游 CDN 域名**）；这不是下载失败。HTTP 客户端应正确处理重定向。
- ⚠️ **不要把 API Key、Cookie 或任何本平台凭证发往跳转目标域名**，**不要使用 `curl --location-trusted`**。
  跳转目标确为第三方 CDN，携带凭证等于把你的 API Key 泄露给该域名。正确做法：只跟随跳转、不带 Authorization。
  需要浏览器播放时，可以使用签名地址作为媒体源，但跨域 JavaScript 读取还取决于该响应的 CORS；下载附件与网页预览并非同一接口契约。
- 下载目标是原始视频文件，不保证统一转码为 H.264；网页预览可能采用不同版本。不要用网页预览文件大小或哈希校验原文件。

### 5.3 另一入口：Bearer content

```bash
# GET：自动跟随307取得签名地址并下载原文件
curl --fail --location --show-error \
  --proto '=https' --proto-redir '=https' --max-redirs 5 \
  --connect-timeout 15 --max-time 600 \
  -H "Authorization: Bearer ${KOKO_API_KEY}" \
  --output "koko-video.mp4.part" \
  "${BASE}/videos/${TASK_ID}/content" \
  && mv -- "koko-video.mp4.part" "koko-video.mp4"
```

也可以关闭客户端的自动跳转，读取307响应的 `Location`，再用**不带 Authorization**的新请求下载签名地址。不要把307响应体保存成MP4。

### 5.4 HEAD、Range 与断点续传

```bash
# 查看长度、类型、ETag等；HEAD没有视频响应体
curl --fail --head --location --show-error "$VIDEO_URL"

# 只取前1024字节，用于连通性探测，不是完整视频
curl --fail --location --show-error \
  -H "Range: bytes=0-1023" \
  --output "koko-video-prefix.bin" "$VIDEO_URL"
```

- 完整GET通常返回 `200`；单段Range被接受时返回 `206`，检查 `Content-Range` 和本段实际字节数。
- 示例 `bytes=0-1023` 正常应为1024字节。不能把它保存为“完整视频”后判定视频损坏。
- 无效或越界范围可能返回 `416`。若请求Range却返回200，应按完整文件处理，不能直接追加到旧文件。
- 断点续传应保存同一文件的强ETag或Last-Modified，配合 `If-Range`；仅在响应范围、校验标识一致时追加。标识不可用、已改变或服务端返回完整200时，重新完整下载，避免拼接不同文件版本。
- 完整传输结束后，若有 `Content-Length`，实际字节数必须一致；必要时用播放器或 `ffmpeg` 验证完整可解码。响应可能使用 `video/mp4`、`application/octet-stream` 或 `binary/octet-stream`，不能只依赖扩展名判断。

### 5.5 错误排查

| 现象/状态 | 处理 |
| --- | --- |
| `307` | 读取Location继续请求，或开启自动跳转。 |
| `401` | 无签名入口检查API Key；签名地址检查过期/参数是否完整，重新查询任务。 |
| `404` | 检查任务ID以及是否属于当前API Key账号。来源文件不可用时也可能收到相应错误。 |
| `409 VIDEO_NOT_READY` | 任务尚未完成，继续查询原任务。 |
| `416` | Range越界或格式不支持，先HEAD核对长度。 |
| `429` | 降低请求频率，按响应提示退避重试。 |
| `502 / 503 / 504`、连接超时 | 记录时间、任务ID和请求ID，重新查询并有限次数重试；不要重新提交生成或认为已自动退款。 |
| 返回JSON/HTML而非视频 | 读取错误内容，不要把错误响应保存成MP4。 |
| 下载中断/长度不足 | 保留为临时文件，核验同一版本后续传或重新下载，不要宣布成功。 |

定位问题时提供：任务ID、发生时间及时区、客户端出口IP、HTTP状态码、响应的 `X-Request-Id`（如有），以及DNS/连接/首字节/总耗时。不要公开 API Key、Cookie、完整签名URL或客户提示词。


## 6. 查询账号

```bash
curl https://20saminos.com/openapi/v1/account \
  -H "Authorization: Bearer koko_live_xxxxxxxxx"
```

```json
{
  "user": {
    "id": "usr_xxx",
    "username": "customer",
    "balance": 185,
    "status": "active"
  },
  "balance": 185,
  "currency": "points"
}
```

## 7. 错误格式

```json
{
  "error": {
    "code": "INSUFFICIENT_POINTS",
    "message": "积分不足，本次需要 20 积分",
    "request_id": "req_xxx"
  }
}
```

| HTTP | code | 含义 |
| ---: | --- | --- |
| 400 | `INVALID_JSON` | 请求体不是有效 JSON |
| 400 | `MODEL_NOT_SUPPORTED` | 模型不受支持 |
| 400 | `INVALID_PROMPT` | 提示词为空或超过该模型 `max_prompt_length` |
| 400 | `INVALID_DURATION` | 时长不符合模型规格 |
| 400 | `INVALID_RATIO` | 比例不受支持 |
| 400 | `INVALID_RESOLUTION` | 清晰度不受支持 |
| 400 | `INVALID_MODE` | mode 不受支持 |
| 400 | `INVALID_COUNT` | count 不是 1 |
| 400 | `INVALID_IDEMPOTENCY_KEY` | 幂等键格式无效 |
| 400 | `INVALID_REFERENCE_MEDIA` | 参考素材字段、数量、URL 或 base64 不符合要求 |
| 400 | `REFERENCE_UPLOAD_FAILED` | 参考图处理失败，积分已退回 |
| 400 | `CHARGE_REJECTED` | 当前账号价格或扣费规则不允许提交 |
| 401 | `INVALID_API_KEY` | API Key 无效或已停用 |
| 402 | `INSUFFICIENT_POINTS` | 积分不足 |
| 404 | `TASK_NOT_FOUND` | 任务不存在或不属于当前账号 |
| 404 | `NOT_FOUND` | API 路径不存在 |
| 429 | `RATE_LIMITED` | 单个 API Key 超过每分钟 120 次请求 |
| 502 | `UPSTREAM_ERROR` | 上游提交、查询或视频处理失败，失败任务自动退款 |
| 503 | `MODEL_MAINTENANCE` | 模型维护中，未扣积分 |
| 504 | `UPSTREAM_TIMEOUT` | 上游提交超时，积分已退回 |

## 8. 扣费和退款

1. 创建任务前检查积分。
2. 每个幂等键最多成功扣费一次。
3. 上游提交失败、参考图处理失败或任务最终失败时自动退款。
4. 维护模型和参数校验失败不会扣积分。
5. 只有任务成功完成才保留扣费。

## 9. 客户端建议

- 创建任务后保存 `id`，每 5-10 秒查询一次状态。
- 网络超时后使用相同 `Idempotency-Key` 重试。
- 只在 `completed` 时下载 `video_url`。
- 不要保存或直连上游临时文件地址。
- 不要把平台 API Key 当作上游厂商密钥使用。

## 10. New API / 第三方网关对接

New API（new-api）在「渠道」里填写 KOKO 的 Base URL 时，不要带最后的 `/v1`（new-api 会自己追加）：

```text
Base URL: https://20saminos.com/openapi
```

new-api 实际请求的是：

```text
GET https://20saminos.com/openapi/v1/models
Authorization: Bearer koko_live_xxxxxxxxx
```

如果网关要求填写完整的 OpenAPI 根地址，也可以直接填 `https://20saminos.com/openapi/v1`。
服务端同时兼容 `.../openapi/v1/v1/models` 和根路径 `.../v1/models`，上述两种 Base URL 都能获取模型列表。

使用 API CDN 时，把域名换成 `https://api.kokoaii.top/openapi`，路径规则相同。

返回的 `data[].id` 就是渠道里应填的模型名，例如 `seedance-2.0`、`minimaxh3`、`bb2.5`。
模型是否可用以当前 API Key 调用 `GET /models` 的实时返回为准。

## 下载有效期

成片走上游直链透传，请尽快下载：

- `minimaxh3`：约 72 小时
- `seedance-2.0`：约 2 小时

以 `GET /models` 的 `downloadTtlHours` 为准；过期后 `/content` 会返回上游错误，平台不做本地缓存。


## 图片生成（gpt-image-2）

图片生成支持 Bearer 密钥调用，模型 `gpt-image-2`，**5 积分/张**（1K / 2K / 4K 同价），支持文生图与参考图生图（参考图最多 16 张），与视频接口共用同一把 API Key 和积分账户。

### 提交任务

`POST https://20saminos.com/openapi/v1/images/generations`

请求头：`Authorization: Bearer <API Key>`、`Content-Type: application/json`；可选 `Idempotency-Key: <唯一值>`（8–100 位字母、数字、`-`、`_`；用同一 Key 重复提交不会重复扣费，会返回原任务）。

body 示例：

`{"model":"gpt-image-2","prompt":"一只橘猫坐在窗台上看夕阳","ratio":"1:1","resolution":"1K","reference_images":[]}`

| 参数 | 必填 | 说明 |
| --- | --- | --- |
| model | 否 | 固定 `gpt-image-2`（缺省即此值） |
| prompt | 是 | 图片提示词 |
| ratio（或 aspect_ratio） | 否 | `9:16` / `1:1` / `16:9`，默认 `1:1` |
| resolution | 否 | `1K` / `2K` / `4K`，默认 `1K`，三档同价 |
| reference_images | 否 | 参考图 HTTPS URL 数组，最多 16 张（图生图时传入） |
| count | 否 | 固定 1，每次生成 1 张 |

提交成功返回 `202`：`{"id":"<任务ID>","task_id":"<任务ID>","status":"running"}`。任务 ID 即 Idempotency-Key（未传时为系统生成的 `api-…`），积分在提交时扣除。

### 查询状态

`GET https://20saminos.com/openapi/v1/images/generations/<任务ID>`（Bearer）

返回示例：`{"id":"…","object":"image.generation","status":"completed","model":"gpt-image-2","prompt":"…","points":5,"balance":1234,"image_url":"https://20saminos.com/openapi/v1/images/generations/<任务ID>/content","failure_reason":null,"created_at":"…","updated_at":"…"}`

`status` 取值：`submitting` / `running` / `completed` / `failed`。生成失败自动全额退回积分，失败原因见 `failure_reason`。

### 下载图片

`GET <image_url>`（带同一把 Bearer 密钥）直接返回图片文件。结果有效期约 24 小时，请及时保存。

> 站点工作台的登录态调用方式保持不变：先 `POST /api/usage/charge`（kind=image）扣费，再 `POST /api/image/images/generations` 提交并轮询，结果页为 `/api/image/results/<referenceId>`。
