# Yêu cầu

Một yêu cầu là một lần gọi một mô hình. Yêu cầu có id (`req_…`), trạng thái, chi phí tính bằng tín dụng API (1000 = 1 USD) và, khi thành công, các tệp được tạo. Cùng một đối tượng được trả về từ mọi điểm cuối bên dưới, với mọi mô hình.

## Tìm mô hình

Danh mục gồm tất cả 1.496 mô hình là JSON công khai (không cần khóa):

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

| Tham số truy vấn | Ý nghĩa |
|---|---|
| `q` | Các từ phải xuất hiện trong tên, id, nhà phát triển hoặc thẻ, ví dụ `kling 3`. |
| `category` | Một id danh mục, ví dụ `text-to-video` hoặc `upscale`. `GET https://api.doitong.com/v1/categories` liệt kê tất cả 21 danh mục cùng số lượng. |
| `lab` | Nhà phát triển của mô hình, theo tên hoặc id, ví dụ `Google` hoặc `black-forest-labs`. |
| `family` | Một id `family` từ hàng trước: mọi biến thể của một mô hình (pro, fast, image-to-video, …). |
| `mode` | `exact` (giá được tính từ đầu vào), `measured` (từ độ dài hoặc kích thước của các tệp đầu vào), `estimate` (giá ước tính cho mỗi lần chạy) hoặc `metered` (tính phí theo mức sử dụng thực tế, tối đa cho mỗi lần chạy). |
| `limit`, `cursor` | Kích thước trang (1 đến 100, mặc định 50) và `next_cursor` của trang trước. |

Mỗi hàng có `id`, `name`, `lab`, `category`, `family`, `summary`, `tags`, `output_kind`, `docs_url` và một đối tượng `price` (xem [Định giá](https://doitong.com/developers/docs/pricing#price-objects)). `GET https://api.doitong.com/v1/models/{model_id}` bổ sung JSON Schema đầy đủ cho đầu vào và đầu ra, tên tham số chuẩn, quy tắc giá và một đầu vào mẫu. `category` hoặc `mode` không hợp lệ sẽ trả về `400 invalid_parameter`.

## Chạy mô hình

```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": "..."}'
```

- Phần thân là đầu vào của mô hình, một đối tượng JSON. Trang của từng mô hình liệt kê các tham số.
- Đầu vào được kiểm tra trước khi tính phí. Đầu vào không hợp lệ trả về `422 invalid_input` cùng `param` gây lỗi và không tốn phí.
- `?wait=N` (tối đa 20 giây) giữ cuộc gọi cho đến khi yêu cầu hoàn tất hoặc hết thời gian.
- `?webhook_url=https://…` gửi một `POST` có chữ ký đến URL đó khi yêu cầu hoàn tất (xem [Webhook](https://doitong.com/developers/docs/webhooks)).
- `?storage=permanent` hoặc `?storage=temporary` chọn thời gian giữ các tệp đầu ra; nếu không có, cài đặt tài khoản sẽ được áp dụng (xem [Nơi lưu tệp](#storage)).

Phản hồi là `202 Accepted` khi yêu cầu vẫn đang được xử lý và `200 OK` khi yêu cầu hoàn tất trong thời gian chờ (hoặc khi đây là lần phát lại, xem bên dưới). Các header `Location` và `Doitong-Request-Id` chứa URL và id của yêu cầu.

## Trạng thái

Vòng đời yêu cầu: `queued` → `running` → `succeeded` | `failed` | `canceled` (hoàn tất; các yêu cầu failed và canceled được hoàn tiền).

| Trạng thái | Ý nghĩa | Cuối cùng |
|---|---|---|
| `queued` | Đã chấp nhận và tính phí, đang chờ worker. `queue_position` có thể hiển thị vị trí trong hàng đợi. | không |
| `running` | Mô hình đang tạo kết quả. | không |
| `succeeded` | Hoàn tất. `outputs` liệt kê các tệp. | có |
| `failed` | Mô hình hoặc một giới hạn đã dừng tác vụ. `error` cho biết lý do. Tín dụng API được hoàn tiền. | có |
| `canceled` | Đã hủy trước khi tạo kết quả. Tín dụng API được hoàn tiền. | có |

Yêu cầu chạy quá giới hạn thời gian sẽ thất bại với mã `timeout` và được hoàn tiền: 15 phút cho hình ảnh, 60 phút cho video, 30 phút cho âm thanh và mọi trường hợp khác.

## Đối tượng yêu cầu

```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` giống nhau với mọi mô hình: mỗi tệp được tạo có một mục gồm `type` (`image`, `video`, `audio`, `file` hoặc `3d`), `url` trên hệ thống lưu trữ của chúng tôi, `content_type` và, khi biết, `file_size`, `width`, `height`, `duration`.
- `output` là đối tượng kết quả riêng của mô hình (các trường được liệt kê trên trang của từng mô hình), với cùng các URL.
- `storage` cho biết cách giữ các tệp: `mode` (`permanent` hoặc `temporary`), số `bytes` đã lưu, `expires_at` (thời điểm tệp tạm thời bị xóa, `null` đối với tệp vĩnh viễn) và `expired` (`true` khi các tệp đã biến mất).
- `cost.credits` là giá của cuộc gọi tính bằng tín dụng API (`cost.usd` tính bằng USD), `cost.charged` là số tiền đã trừ khỏi số dư API, `cost.refunded` là số tiền được hoàn lại. Với mô hình `metered`, `cost.credits` là mức tối đa được giữ lúc bắt đầu (`cost.held`); khi lần chạy thành công, `cost.settled` cho biết mức sử dụng thực tế và phần còn lại của mức tối đa nằm trong `cost.refunded` (xem [Tính phí theo mức sử dụng](https://doitong.com/developers/docs/pricing#metered)).
- Khi yêu cầu thất bại, `error` có dạng `{ "type": …, "code": …, "message": … }`.

## Đọc một yêu cầu

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

- `?wait=N` (tối đa 20 giây) thực hiện long-poll: cuộc gọi trả về ngay khi yêu cầu có trạng thái cuối cùng, hoặc sau N giây với trạng thái hiện tại. Lặp cách này là phương pháp đơn giản nhất để chờ kết quả.
- `?logs=1` thêm các dòng nhật ký tiến trình của mô hình (50 dòng cuối) dưới dạng `logs`.

Bạn chỉ có thể đọc các yêu cầu thuộc tài khoản của mình; mọi id khác trả về `404 request_not_found`.

## Liệt kê yêu cầu

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

Mới nhất trước. `limit` từ 1 đến 100 (mặc định 20), `status` và `model` dùng để lọc. Phản hồi có dạng `{ "object": "list", "data": [ … ], "has_more": true, "next_cursor": "req_…" }`; truyền `cursor=<next_cursor>` để lấy trang tiếp theo.

## Hủy một yêu cầu

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

- Đã hủy trước khi mô hình bắt đầu: trạng thái trở thành `canceled`, tín dụng API được hoàn tiền và bạn nhận `200`.
- Đang tạo kết quả: yêu cầu hủy được chuyển tiếp và bạn nhận `202` với `"cancel_requested": true`. Nếu mô hình vẫn hoàn tất, yêu cầu thành công bình thường và bị tính phí.
- Đã ở trạng thái cuối: `409 not_cancelable` (yêu cầu đã `canceled` chỉ đơn giản trả về `200`).

## Xóa các tệp của một yêu cầu

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

Xóa ngay các bản sao tệp đầu ra của yêu cầu trên hệ thống của chúng tôi và dừng phí lưu trữ. Phản hồi là đối tượng yêu cầu với `storage.expired: true`; URL vẫn nằm trong bản ghi nhưng không còn hoạt động. Gọi lại không thay đổi gì (`200`). Yêu cầu vẫn đang `queued` hoặc `running` chưa có tệp: `409 request_active`. Cần `models:run`.

## Tính bất biến: thử lại mà không trả phí hai lần

Mạng có thể gặp lỗi. Để thử lại an toàn, hãy gửi header `Idempotency-Key` (bất kỳ chuỗi nào tối đa 255 ký tự, duy nhất cho mỗi thao tác, ví dụ id đơn hàng của bạn):

- Cùng khóa và cùng phần thân như trước: bạn nhận lại yêu cầu ban đầu (`200`, header `Idempotent-Replayed: true`) và không bị tính phí lần nữa.
- Cùng khóa và phần thân sau một lần chạy thất bại trước khi đến mô hình (`upstream_error` hoặc `internal_error` khi gửi, `insufficient_credits`, `budget_exceeded`, `too_many_inflight`): không có gì được tạo và không bị tính phí, nên lần thử lại sẽ chạy mô hình thay vì trả về lỗi.
- Cùng khóa nhưng phần thân khác: `409 idempotency_mismatch`.
- Khóa được giới hạn trong khóa API đã gửi chúng.

## Nơi lưu tệp

Mỗi tệp đầu ra được sao chép vào hệ thống lưu trữ trước khi yêu cầu được đánh dấu `succeeded`, vì vậy các URL trong `outputs` nằm trên máy chủ của chúng tôi và bạn có thể liên kết trực tiếp đến chúng. Bạn tự chọn thời gian lưu:

| Chế độ | Thời gian | Giá |
|---|---|---|
| `permanent` (mặc định) | Cho đến khi bạn xóa | 100 tín dụng API (0,10 $) mỗi GB mỗi tháng, tính phí hằng ngày từ số dư API |
| `temporary` | 7 ngày, sau đó bị xóa | Miễn phí |

- Cài đặt tài khoản quyết định cho các yêu cầu mới: công tắc "Lưu tệp vĩnh viễn" trong [bảng điều khiển](https://doitong.com/developers/dashboard#balance), hoặc `PATCH https://api.doitong.com/v1/account/storage` với `{"mode": "temporary"}`. `GET https://api.doitong.com/v1/account` hiển thị cài đặt này trong `storage`, cùng số byte bạn giữ và ước tính hàng tháng.
- `?storage=permanent|temporary` trong một lần chạy (đối số `storage` của công cụ MCP `run`) sẽ ghi đè cài đặt cho lần chạy đó.
- Thay đổi cài đặt không bao giờ tác động đến các tệp bạn đã có; hãy xóa chúng bằng [`DELETE /requests/{id}/files`](#delete-files).
- Khi số dư API không đủ trả phí lưu trữ, không có khoản phí nào được tính và bạn nhận email: hãy nạp tiền trong 30 ngày, nếu không các tệp đang giữ sẽ trở thành tạm thời và bị xóa sau 7 ngày.

Trong trường hợp hiếm gặp khi bản sao của chúng tôi bị lỗi, mục đó có `"stored": false` và URL là URL gốc có thời hạn ngắn: hãy tải xuống sớm.
