# Khái niệm

> **Tóm tắt** — Run là một lệnh gọi một style. Priority đúng hạn hoặc miễn phí; Relax xếp hàng chờ, không hạn chót, giá không bao giờ cao hơn Priority. Run không có kết quả thì miễn phí. Webhook có ký; lệnh gửi idempotent; `end_user_id` bắt buộc.

Các khái niệm này đúng cho cả hai cửa. Tích hợp mới bắt đầu bằng [Jobs API](https://docs.wandura.ai/vi/developers/api-v2/jobs), dùng chữ của job (`processing`, `succeeded`, `cancelled`, `timeout`); trang này mô tả cả runs, vẫn được hỗ trợ.

## Run
Một **run** là một lệnh gọi một style cho ra một kết quả. Bạn gửi, run ở `queued`, rồi `running`, rồi trạng thái cuối.

Priority: bắt đầu ở **2 s**, nhân 1.5 mỗi lần, tối đa **10 s**, cộng thêm 0–0.5 s ngẫu nhiên. Relax: 10 s đến 60 s. Trạng thái cuối: `completed`, `failed`, `nsfw`, `canceled`.

Run mang `price` và `charge`: `held` khi đang chạy, `charged` khi hoàn tất, `released` khi miễn phí, `none` với key test. Huỷ run đang xếp hàng bằng `POST` vào `cancel_url`; tiền giữ được trả lại.

### Chấm điểm kết quả
**Đang triển khai, không bắt buộc.** Báo cho Wandura biết kết quả tốt hay không:

```bash
curl -X POST https://api.wandura.ai/v2/runs/run_Qk2m9XaB7Lc4/feedback \
  -H "Authorization: Bearer $WANDURA_KEY" -H "Content-Type: application/json" \
  -d '{"rating": "down", "reason": "Face does not match"}'
```

- `rating`: `up` hoặc `down`. `reason`: không bắt buộc, tối đa 280 ký tự.
- Id là `id` của run hoặc `job_id` của job, tạo bằng key cùng product.
- Mỗi run, mỗi API key một lần chấm. Gọi lại thì thay lần trước.
- Câu trả lời là lần chấm đã lưu: `object` `run_feedback`, `run`, `rating`, `reason`, `created_at`, `updated_at`.
- Lỗi: `400 invalid_request` (`rating` hoặc `reason` sai), `404 run_not_found`.

Wandura đọc các lần chấm để làm style tốt hơn. Chúng không hiện công khai và không có gì khác phụ thuộc vào chúng. Trong console, trang Runs và Playground có cùng nút thích / không thích.

## Style và lane
**Style** là một kết quả làm sẵn (ví dụ kiểu chân dung hay một clip chuyển động). Bạn không chọn model: Wandura giữ các nhà cung cấp phía sau mỗi style và đổi được mà bạn không phải sửa code. Mỗi style có schema `inputs` tại `/v2/public/styles/{style}/openapi.json`.

Mỗi run chọn một **lane**:

- **Priority**: xong trong thời gian cam kết của style (xem [danh sách style](https://docs.wandura.ai/vi/developers/api-v2/styles)) nếu không run miễn phí.
- **Relax**: xếp hàng chờ, không hạn chót, mỗi end user một run chạy cùng lúc; giá không bao giờ cao hơn Priority.

`POST /v2/quote` nhận cùng body như run và trả giá và ETA mà không tính tiền.

## Run miễn phí
Bạn không bao giờ trả tiền cho run không có kết quả. Run miễn phí kết thúc với `free_run.reason`:

| `free_run.reason` | Khi nào |
|---|---|
| `priority_late` | Priority did not finish within its promise. |
| `provider_error` | The run failed on our side. |
| `bad_input` | An input could not be used (for example a URL that is not http(s)). |
| `content_blocked` | The input or output was blocked by the content filter (status nsfw). |

## Webhook
Đặt một URL webhook cho mỗi product trong [console](https://open.wandura.ai/webhooks). Mỗi lần run đổi trạng thái được POST như một event, **có ký**:

```
Wandura-Signature: t=1791600000,v1=5f2b…
```

`v1` là HMAC-SHA256 dạng hex của `t + "." + raw body` với webhook secret của bạn. Kiểm trên raw body, từ chối `t` lệch quá 5 phút so với bây giờ, rồi trả 2xx nhanh. Non-2xx sẽ được gửi lại (30 s, 2 phút, 10 phút, 1 giờ, 6 giờ, 24 giờ). Có thể nhận lặp: khử trùng theo `Wandura-Event-Id`, và giữ `sequence` cao nhất cho mỗi run. [SDK](https://docs.wandura.ai/vi/developers/api-v2/sdks) có sẵn hàm kiểm.

Loại event: `run.running`, `run.completed`, `run.failed`, `run.nsfw`, `run.canceled`. Link kết quả mở được ít nhất 7 ngày (`expires_at`).

Product của bạn cũng có thể đăng ký event nghẽn, `queue.congestion_started` và `queue.congestion_ended`: xem [Hàng chờ và nghẽn](https://docs.wandura.ai/vi/developers/api-v2/queue-and-congestion#events).

## Giới hạn
| Giới hạn | Mặc định | Điều xảy ra |
|---|---|---|
| Run mỗi end user mỗi ngày | 20 | Vượt: 429 `end_user_cap_reached` kèm `Retry-After` (đặt lại 00:00 UTC). Tổ chức của bạn có thể đổi mức trần. |
| Run đồng thời mỗi product | 20 | Run thừa xếp hàng (queued); Priority đi trước. Hàng chờ chứa số run priority bằng số suất chạy; quá mức đó, run priority mới bị trả 429 `product_busy` kèm `Retry-After`. Câu trả lời submit cho thấy các số này: xem [Hàng chờ và nghẽn](https://docs.wandura.ai/vi/developers/api-v2/queue-and-congestion). |
| Run Relax đang chạy mỗi end user | 1 | Run Relax kế tiếp của end user đó chạy khi run trước kết thúc. |
| Cửa sổ chữ ký webhook | 300 s | Từ chối webhook có timestamp cũ hơn hoặc mới hơn mức này. |

Cần nhiều hơn? Cho chúng tôi biết khối lượng của bạn, chúng tôi nâng giới hạn của tổ chức.

### Run đồng thời theo mức nạp
> **Đang triển khai:** bậc theo mức nạp đã xây xong nhưng chưa bật. Trước khi bật, mọi tài khoản dùng mức mặc định ở trên.

Khi bật, product đang ở mức mặc định được chạy nhiều run cùng lúc hơn khi tổ chức của bạn nạp tiền bằng thẻ. Phần được tính là các lần nạp thẻ đã vào ví trong 28 ngày gần nhất, trừ phần đã hoàn. Giới hạn chỉ tăng: không bậc nào thấp hơn mức mặc định, và giới hạn chúng tôi đặt riêng cho product của bạn theo thoả thuận giữ nguyên. Bảng bậc hiện tại ở [open.wandura.ai/pricing](https://open.wandura.ai/pricing). Bậc của bạn, số đã nạp và mức cần cho bậc kế tiếp hiện ở `GET /v2/queue` (trường `spend_tier`) và trang Limits của console.

## Idempotency
Mỗi lệnh gửi (`POST /v2/styles/{style}` hoặc `POST /v2/runs`) phải có header `Idempotency-Key`, một chuỗi duy nhất tối đa 255 ký tự. Nếu mạng đứt và bạn thử lại với cùng key và body, bạn nhận lại run đầu tiên (`200`, header `Idempotent-Replayed: true`): không bao giờ chạy hay tính tiền hai lần. Cùng key mà body khác là `409 idempotency_key_reused`. Dùng key mới cho mỗi run mới.

## End-user ID
`end_user_id` bắt buộc trên mọi run: id của người trong app của bạn mà run này dành cho (chuỗi tự chọn, tối đa 255 ký tự; đừng gửi email hay tên). Trần theo ngày và quy tắc Relax tính theo end user, nên một người dùng nặng không ăn hết cả product. Dùng nó trong console để trả lời khiếu nại của người dùng.

## Tải tệp lên
Không có chỗ lưu trữ riêng? `POST /v2/uploads` với `{content_type, size_bytes}` trả `upload_url` đã ký sẵn. PUT dữ liệu vào đó, rồi truyền `file_url` trong `inputs`. Tệp được giữ 24–48 giờ.

## Khoá API
> **Đang triển khai:** quyền, style được phép và tag dưới đây lên api.wandura.ai và console ở bản phát hành tới.

Mỗi key thuộc một product. Khi tạo key trong console ở mục **Keys**, bạn có thể thu hẹp nó cho một app hoặc một người:

| Thiết lập | Làm gì | Khi nào bị từ chối |
|---|---|---|
| **Permission** (quyền) | **Run styles** (mặc định): gửi run và job, đọc run và style. **Read only** (chỉ đọc): đọc run và style. | Key Read only mà gửi, huỷ, chia sẻ hoặc tải tệp lên thì nhận `403 insufficient_scope` (Jobs API: `403 FORBIDDEN`). Không gì được tạo hay tính tiền. |
| **Allowed styles** (style được phép) | Những style key này được chạy. Để trống là mọi style org của bạn chạy được. | Style khác nhận `403 style_not_allowed` khi chạy và khi hỏi giá (Jobs API: `403 FORBIDDEN`). |
| **Tags** | Nhãn để bạn tự sắp xếp, ví dụ `android` hoặc tên một đồng nghiệp. | Không bao giờ: tag không đổi gì. |
| **Expires** (hết hạn) | Từ 1 ngày tới 1 năm, hoặc không bao giờ. Danh sách hiện **Expires soon** từ 7 ngày trước. | Sau mốc đó: `401 api_key_expired`. |

Nếu org của bạn chỉ chạy được một số style, style được phép của key vẫn nằm trong tập đó. Key tạo không kèm các thiết lập này chạy như mọi key xưa nay và không bao giờ nhận `403`. Rotate (xoay) key giữ nguyên thiết lập; muốn đổi thì tạo key mới rồi thu hồi key cũ. Đối tượng key và webhook `key.expiring` / `key.revoked` chỉ có `preset` (`run_styles` hoặc `read_only`), `allowed_styles` và `tags` trên key có đặt chúng.

## Test và live
Key test (`wdr_test_…`) trả kết quả mẫu với giá $0 và theo cùng quy tắc (idempotency, end-user ID, webhook), nên code bạn thử là code bạn chạy thật. Key live chạy thật.
