# 请求

一次请求是对一个模型的一次调用。它包含 id（`req_…`）、状态、以 API 积分计的成本（1000 = 1 USD），以及在成功时生成的文件。以下每个端点对每个模型返回的都是同一种对象。

## 查找模型

包含全部 1,496 个模型的目录是公开 JSON（无需密钥）：

```bash
curl "https://api.doitong.com/v1/models?category=text-to-video&lab=Google&limit=20"
```

| 查询参数 | 含义 |
|---|---|
| `q` | 名称、id、制作者或标签中必须全部出现的词，例如 `kling 3`。 |
| `category` | 一个类别 id，例如 `text-to-video` 或 `upscale`。`GET https://api.doitong.com/v1/categories` 会列出全部 21 个类别及其数量。 |
| `lab` | 模型的制作者，可按名称或 id 查询，例如 `Google` 或 `black-forest-labs`。 |
| `family` | 上一行中的 `family` id：同一模型的所有变体（pro、fast、image-to-video 等）。 |
| `mode` | `exact`（根据输入计算价格）、`measured`（根据输入文件的长度或大小计算）、`estimate`（每次运行的预估价格）或 `metered`（按实际用量计费，每次运行不超过最大值）。 |
| `limit`、`cursor` | 页面大小（1 到 100，默认 50）以及上一页的 `next_cursor`。 |

每行包含 `id`、`name`、`lab`、`category`、`family`、`summary`、`tags`、`output_kind`、`docs_url` 和一个 `price` 对象（参见 [定价](https://doitong.com/developers/docs/pricing#price-objects)）。`GET https://api.doitong.com/v1/models/{model_id}` 还会返回完整的输入和输出 JSON Schema、规范参数名称、价格规则以及输入示例。未知的 `category` 或 `mode` 会返回 `400 invalid_parameter`。

## 运行模型

```bash
curl -X POST "https://api.doitong.com/v1/run/{model_id}?wait=20" \
  -H "Authorization: Bearer $DOITONG_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-1842-cover" \
  -d '{"prompt": "..."}'
```

- 请求体是模型输入，即一个 JSON 对象。每个模型页面都会列出其参数。
- 输入会在扣费前进行检查。无效输入会返回带有问题 `param` 的 `422 invalid_input`，且不会产生费用。
- `?wait=N`（最多 20 秒）会保持调用，直到请求完成或时间到期。
- `?webhook_url=https://…` 会在请求完成时向该 URL 发送签名 `POST`（参见 [Webhook](https://doitong.com/developers/docs/webhooks)）。
- `?storage=permanent` 或 `?storage=temporary` 用于选择输出文件的保留时长；未指定时使用账户设置（参见[文件存储位置](#storage)）。

请求仍在处理时，响应为 `202 Accepted`；在等待时间内完成时（或在重放时，见下文），响应为 `200 OK`。`Location` 和 `Doitong-Request-Id` 标头携带请求 URL 和 id。

## 状态

请求生命周期: `queued` → `running` → `succeeded` | `failed` | `canceled` (最终状态；失败和已取消的请求会退款).

| 状态 | 含义 | 最终状态 |
|---|---|---|
| `queued` | 已接受并扣费，正在等待工作进程。`queue_position` 可能会显示排队位置。 | 否 |
| `running` | 模型正在生成。 | 否 |
| `succeeded` | 已完成。`outputs` 列出文件。 | 是 |
| `failed` | 模型或某项限制使其停止。`error` 说明原因。API 积分会退款。 | 是 |
| `canceled` | 在生成结果前取消。API 积分会退款。 | 是 |

运行时间超过时限的请求会失败并返回代码 `timeout`，同时退款：图像 15 分钟、视频 60 分钟、音频及其他内容 30 分钟。

## 请求对象

```json
{
  "id": "req_8k2m4q7x1z9c3v5b6n0p2r4t",
  "object": "request",
  "model": "flux/schnell",
  "status": "succeeded",
  "created_at": "2026-10-20T12:00:00Z",
  "started_at": "2026-10-20T12:00:01Z",
  "completed_at": "2026-10-20T12:00:02Z",
  "queue_position": null,
  "cost": { "credits": 3, "usd": "0.003", "charged": 3, "refunded": 0 },
  "input": { "prompt": "..." },
  "output": { "images": [ { "url": "https://content.doitong.com/dev-api/…/0-output.jpeg", "width": 1024, "height": 768 } ] },
  "outputs": [
    { "type": "image", "url": "https://content.doitong.com/dev-api/…/0-output.jpeg", "content_type": "image/jpeg",
      "width": 1024, "height": 768, "file_size": 183920, "stored": true }
  ],
  "error": null,
  "metrics": { "inference_seconds": 1.2 },
  "storage": { "mode": "permanent", "bytes": 183920, "expires_at": null, "expired": false },
  "urls": {
    "self": "https://api.doitong.com/v1/requests/req_8k2m4q7x1z9c3v5b6n0p2r4t",
    "cancel": "https://api.doitong.com/v1/requests/req_8k2m4q7x1z9c3v5b6n0p2r4t/cancel"
  }
}
```

- `outputs` 对所有模型都相同：每个生成文件对应一个条目，包含 `type`（`image`、`video`、`audio`、`file` 或 `3d`）、我们存储中的 `url`、`content_type`，以及已知时的 `file_size`、`width`、`height`、`duration`。
- `output` 是模型自己的结果对象（其字段列在每个模型页面上），其中包含相同的 URL。
- `storage` 说明文件的保留方式：`mode`（`permanent` 或 `temporary`）、已存储的 `bytes`、`expires_at`（临时文件删除的时间，永久文件为 `null`）以及 `expired`（文件消失后为 `true`）。
- `cost.credits` 是调用所需的 API 积分价格（`cost.usd` 以 USD 计），`cost.charged` 是从 API 余额中扣除的金额，`cost.refunded` 是退回的金额。对于 `metered` 模型，`cost.credits` 是开始时冻结的最大值（`cost.held`）；运行成功后，`cost.settled` 表示实际用量，其余最大值会计入 `cost.refunded`（参见[按用量计费](https://doitong.com/developers/docs/pricing#metered)）。
- 请求失败时，`error` 为 `{ "type": …, "code": …, "message": … }`。

## 读取请求

```bash
curl "https://api.doitong.com/v1/requests/req_8k2m4q7x1z9c3v5b6n0p2r4t?wait=20" \
  -H "Authorization: Bearer $DOITONG_API_KEY"
```

- `?wait=N`（最多 20 秒）会执行长轮询：请求达到最终状态时立即返回，否则在 N 秒后返回当前状态。循环调用是等待结果的最简单方式。
- `?logs=1` 会将模型的进度日志行（最后 50 行）作为 `logs` 添加。

你只能读取自己账户的请求；其他 id 会返回 `404 request_not_found`。

## 列出请求

```bash
curl "https://api.doitong.com/v1/requests?limit=20&status=succeeded&model=flux/schnell" \
  -H "Authorization: Bearer $DOITONG_API_KEY"
```

按最新优先。`limit` 范围为 1 到 100（默认 20），`status` 和 `model` 用于筛选。响应为 `{ "object": "list", "data": [ … ], "has_more": true, "next_cursor": "req_…" }`；传入 `cursor=<next_cursor>` 获取下一页。

## 取消请求

```bash
curl -X POST "https://api.doitong.com/v1/requests/req_8k2m4q7x1z9c3v5b6n0p2r4t/cancel" \
  -H "Authorization: Bearer $DOITONG_API_KEY"
```

- 在模型启动前取消：状态变为 `canceled`，API 积分退款，并返回 `200`。
- 已开始生成：取消请求会被转发，并返回带有 `"cancel_requested": true` 的 `202`。如果模型仍然完成，请求会正常成功并计费。
- 已处于最终状态：返回 `409 not_cancelable`（已经 `canceled` 的请求会直接返回 `200`）。

## 删除请求的文件

```bash
curl -X DELETE "https://api.doitong.com/v1/requests/req_8k2m4q7x1z9c3v5b6n0p2r4t/files" \
  -H "Authorization: Bearer $DOITONG_API_KEY"
```

立即删除我们保存的请求输出文件，并停止收取存储费用。响应是 `storage.expired: true` 的请求对象；其 URL 仍保留在记录中，但不再有效。再次调用不会产生变化（`200`）。仍处于 `queued` 或 `running` 状态的请求尚无文件：`409 request_active`。需要 `models:run`。

## 幂等性：重试而不重复付费

网络可能会失败。要安全地重试运行，请发送 `Idempotency-Key` 标头（长度不超过 255 个字符的任意字符串，每个操作唯一，例如你自己的订单 id）：

- 相同密钥和相同请求体：返回原始请求（`200`，标头 `Idempotent-Replayed: true`），不会再次扣费。
- 相同密钥和请求体，但之前的运行在到达模型前失败（提交时出现 `upstream_error` 或 `internal_error`、`insufficient_credits`、`budget_exceeded`、`too_many_inflight`）：未生成任何内容，也未扣费，因此重试会再次运行模型，而不是返回失败结果。
- 相同密钥但请求体不同：`409 idempotency_mismatch`。
- 密钥的作用域限定为发送该密钥的 API 密钥。

## 文件存储位置

每个输出文件都会在请求标记为 `succeeded` 前复制到我们的存储中，因此 `outputs` 中的 URL 位于我们自己的主机上，你可以直接链接到它们。保留时长由你选择：

| 模式 | 时长 | 价格 |
|---|---|---|
| `permanent`（默认） | 直到你删除它们 | 每 GB 每月 100 API 积分（$0.10），每天从 API 余额中扣费 |
| `temporary` | 7 天，然后删除 | 免费 |

- 账户设置决定新请求的模式： [控制台](https://doitong.com/developers/dashboard#balance)中的“永久保存文件”开关，或使用 `PATCH https://api.doitong.com/v1/account/storage` 发送 `{"mode": "temporary"}`。`GET https://api.doitong.com/v1/account` 会在 `storage` 下显示该设置、已保留的字节数和月度预估费用。
- 运行时的 `?storage=permanent|temporary`（MCP `run` 工具的 `storage` 参数）会覆盖该运行的设置。
- 更改设置不会影响已有文件；请使用 [`DELETE /requests/{id}/files`](#delete-files) 删除它们。
- API 余额无法支付存储费用时，不会扣费，并会向你发送邮件：请在 30 天内充值，否则保留的文件会变为临时文件，并在之后 7 天被删除。

在极少数情况下，如果我们的副本创建失败，条目会包含 `"stored": false`，URL 则是原始的短时有效 URL：请尽快下载。
