# Sunghub API — Hướng Dẫn Tích Hợp

Base URL:

```text
https://sunghub.com
```

Mọi request cần header:

```http
X-Auth-Key: <api_key_sunghub>
Content-Type: application/json
```

API key lấy ở trang **Tài khoản** (`https://sunghub.com/app#tai-khoan`). Giữ kín key — ai có key là dùng được số dư của bạn.

Tiền tính bằng **credit, 1 credit = 1 đồng**. Credit bị trừ ngay khi gửi job; job lỗi do hệ thống, bị huỷ hoặc quá thời gian chờ được **hoàn đủ tự động**. Job bị từ chối vì **nội dung vi phạm chính sách** (người nổi tiếng, bạo lực, khiêu dâm, trẻ em…) **không được hoàn**.

Mọi job tạo ảnh, video, giọng đọc đều **bất đồng bộ**: gửi job → nhận `job_id` → hỏi trạng thái mỗi 3–5 giây cho tới khi xong → tải file về.

## 1. Tạo Ảnh / Video Google Flow

```http
POST /api/flow
```

Body:

```json
{
  "type": "flow_text_to_image",
  "prompt": "A small wooden lighthouse on a rocky island at sunset",
  "params": { "aspectRatio": "16:9", "imageModel": "Nano_Banana_2" }
}
```

### Ví Dụ cURL

```bash
curl -X POST "https://sunghub.com/api/flow" \
  -H "X-Auth-Key: <api_key>" \
  -H "Content-Type: application/json" \
  --data '{
    "type": "flow_text_to_image",
    "prompt": "A small wooden lighthouse on a rocky island at sunset",
    "params": { "aspectRatio": "16:9" }
  }'
```

### Response Khi Tạo Job (202)

```json
{
  "ok": true,
  "job_id": "7d0a4da8-3016-468d-96b2-d3fded787b4a",
  "status": "queued",
  "credits_used": 220,
  "credits_remaining": 49780
}
```

### Response Lỗi

```json
{ "error": "prompt required" }
```

| HTTP | Ý nghĩa |
|---|---|
| `400` | Thiếu / sai tham số (type, tỉ lệ, model, số giây…) — không trừ credit |
| `401` | Thiếu hoặc sai `X-Auth-Key` |
| `402` | Không đủ credit — `{ "error", "required", "balance" }` |
| `429` | Gửi quá nhanh (mặc định 60 request/phút mỗi key) |
| `503` | Tính năng đang tạm ngừng, ví dụ video: `{ "error": "Tạo video đang tạm ngừng…", "video_disabled": true }` — không trừ credit |

## 2. Kiểm Tra Trạng Thái Job

```http
GET /api/flow/{job_id}
```

Hỏi mỗi 3–5 giây cho tới khi `status` là `done` hoặc `failed`.

### Response Đang Xử Lý

```json
{
  "job_id": "7d0a4da8-3016-468d-96b2-d3fded787b4a",
  "type": "flow_text_to_image",
  "status": "processing",
  "prompt": "A small wooden lighthouse…",
  "credit_cost": 220,
  "created_at": "2026-10-16 08:00:00"
}
```

`status`: `queued` → `processing` → `done` | `failed`.

### Response Thành Công

```json
{
  "job_id": "7d0a4da8-3016-468d-96b2-d3fded787b4a",
  "type": "flow_text_to_image",
  "status": "done",
  "credit_cost": 220,
  "created_at": "2026-10-16 08:00:00",
  "completed_at": "2026-10-16 08:00:39",
  "urls": ["https://flow-content.google/image/…?Expires=…"]
}
```

- `urls[0]` là **link gốc của Google**, tải được không cần đăng nhập, **sống khoảng 6 giờ**. Sunghub không lưu file — tải về ngay khi job xong.
- Job video trả thêm `media_id` để nâng lên 1080p (mục 5).

### Response Thất Bại

```json
{
  "job_id": "7d0a4da8-3016-468d-96b2-d3fded787b4a",
  "status": "failed",
  "credit_cost": 220,
  "error": "Máy chủ tạo ảnh/video đang gặp sự cố, credit đã được hoàn. Vui lòng thử lại sau."
}
```

Job `failed` đã được hoàn đủ `credit_cost`, **trừ** khi lỗi do nội dung vi phạm chính sách — khi đó `error` ghi rõ "vi phạm chính sách nội dung… KHÔNG hoàn credit".

### Huỷ Job

```http
DELETE /api/flow/{job_id}
```

| Trạng thái job | Kết quả |
|---|---|
| `queued` / `processing` | `200 { "ok": true, "cancelled": true, "refunded": 220 }` — hoàn credit ngay |
| `done` / `failed` | `409` — không huỷ được |
| không phải job của bạn | `404` |

Nếu chờ quá lâu và muốn gửi lại: **huỷ job cũ trước rồi mới gửi job mới**, nếu không job cũ vẫn chạy và vẫn tính tiền.

### Lịch Sử Job

```http
GET /api/flow/history?limit=20&offset=0
```

Trả `{ "jobs": [...], "limit", "offset", "total" }`, `limit` tối đa 100.

## 3. Các `type` Được Hỗ Trợ

| `type` | Làm gì | Bắt buộc thêm |
|---|---|---|
| `flow_text_to_image` | Chữ → ảnh | `prompt` |
| `flow_image_to_image` | Ảnh mẫu → ảnh mới (giữ nhân vật / bố cục) | `prompt`, `params.imageUrls` (1 ảnh trở lên) |
| `flow_text_to_video` | Chữ → video Omni Flash 720p, có âm thanh | `prompt` |
| `flow_reference_images_to_video` | 1–3 ảnh mẫu → video Omni Flash | `prompt`, `params.imageUrls` (1–3 ảnh) |
| `flow_upscale_video` | Nâng video đã tạo lên 1920×1080 | `params.mediaId` (hoặc `params.jobId`) |

## 4. Tham Số `params`

| Field | Kiểu | Áp dụng | Giá trị | Mặc định |
|---|---|---|---|---|
| `aspectRatio` | string | ảnh | `16:9`, `4:3`, `1:1`, `3:4`, `9:16` | `16:9` |
| `aspectRatio` | string | video | `16:9`, `9:16` | `16:9` |
| `imageModel` | string | ảnh | `Nano_Banana_2`, `Nano_Banana_Pro` | `Nano_Banana_2` |
| `resolution` | string | ảnh | `1K` (gốc), `2K`, `4K` — 2K/4K thêm ~20–25 giây | `1K` |
| `imageUrls` | string[] | ảnh mẫu | URL ảnh công khai hoặc data URI base64 (`data:image/jpeg;base64,…`) | — |
| `durationSec` | number | video | `4`, `6`, `8`, `10` (giá trị khác → `400`) | `8` |
| *(độ phân giải)* | — | video | Video tạo ra luôn **720p** (1280×720 / 720×1280). Muốn **1080p**: khi job `done`, gửi tiếp `flow_upscale_video` với `media_id` của job đó | 720p |
| `mediaId` | string | nâng 1080p | `media_id` của job video đã `done` | — |
| `cookie` | string | mọi type | Cookie Google của bạn (mục 6) — không gửi = dùng tài khoản Sunghub | — |

- Body JSON tối đa **10 MB**. Ảnh mẫu nên thu về cạnh dài ≤ 1600px JPEG, hoặc tải lên trước bằng `POST /api/flow/upload` rồi gửi URL.
- Prompt dài vài nghìn ký tự vẫn chạy.

### Tải Ảnh Mẫu Lên Trước

```bash
curl -X POST "https://sunghub.com/api/flow/upload" \
  -H "X-Auth-Key: <api_key>" \
  -F "files=@nhanvat.jpg" -F "files=@boicanh.png"
```

```json
{ "ok": true, "urls": ["https://sunghub.com/media/…jpg", "https://sunghub.com/media/…png"] }
```

Tối đa 4 file, 25 MB mỗi file, JPEG/PNG/WEBP. File lưu **24 giờ**.

## 5. Ví Dụ Payload Theo `type`

### flow_text_to_image

```json
{ "type": "flow_text_to_image", "prompt": "A red ceramic mug on a grey table, studio light",
  "params": { "aspectRatio": "1:1", "imageModel": "Nano_Banana_Pro" } }
```

Ảnh 2K / 4K: thêm `"resolution": "2K"` hoặc `"4K"` vào `params`. Khi xong, `urls[0]` là bản 2K/4K; phản hồi có thêm `resolution`, `url_1k` (link bản 1K gốc) và `media_id_1k`. Ví dụ 16:9 → 2K = 2752×1536, 4K = 5504×3072; 1:1 → 4K = 4096×4096. Nâng độ phân giải hỏng thì cả job `failed` và hoàn credit.

### flow_image_to_image

```json
{ "type": "flow_image_to_image", "prompt": "Make the mug bright green, keep everything else the same",
  "params": { "aspectRatio": "16:9", "imageUrls": ["https://sunghub.com/media/…jpg"] } }
```

### flow_text_to_video

```json
{ "type": "flow_text_to_video", "prompt": "Slow aerial shot over a misty pine forest at dawn",
  "params": { "aspectRatio": "16:9", "durationSec": 6 } }
```

### flow_reference_images_to_video

```json
{ "type": "flow_reference_images_to_video", "prompt": "The girl walks through the old temple gate",
  "params": { "aspectRatio": "9:16", "durationSec": 8,
              "imageUrls": ["data:image/jpeg;base64,…", "https://sunghub.com/media/…png"] } }
```

### flow_upscale_video

```json
{ "type": "flow_upscale_video", "params": { "mediaId": "cc70cd49-eedc-4a57-b1ca-b97c7e17c088" } }
```

`mediaId` lấy từ `media_id` của job video **của chính bạn** đã `done`. Kết quả cùng độ dài video gốc. Tách job riêng: nâng cấp hỏng thì bản 720p vẫn còn.

## 6. Dùng Tài Khoản Google Của Bạn

Nếu bạn có gói Google AI trả phí, gửi kèm cookie Google để tạo bằng hạn mức của bạn — Sunghub chỉ thu **50 credit / lượt** cho mọi `type`, video mọi độ dài.

```json
{ "type": "flow_text_to_image", "prompt": "…",
  "params": { "aspectRatio": "16:9", "cookie": "SID=…; HSID=…; SSID=…; SAPISID=…; __Secure-1PSID=…; OSID=…; __Secure-OSID=…; SIDCC=…" } }
```

- `cookie`: chuỗi Cookie header lấy từ trình duyệt đã đăng nhập `flow.google.com`. Nhận cả `cookies` dạng mảng (định dạng extension xuất cookie).
- Phải có đủ cookie đăng nhập (ít nhất `SID`, `__Secure-1PSID`…), thiếu → `400`.
- Cookie chỉ dùng cho đúng job đó, **không lưu vào cơ sở dữ liệu, không ghi log**.
- Google đổi cookie liên tục: khi job `done`, `GET /api/flow/{job_id}` trả thêm **một lần** trường `cookies` (đã được Google làm mới) — **lưu lại và dùng cho job sau** thay bản cũ.
- Mỗi tài khoản Google chạy tối đa **3 job cùng lúc**; job dư được nhận và xếp hàng (tối đa 30 phút, quá thì `failed` + hoàn credit).
- Cookie hết hạn → job `failed` với thông báo xuất cookie mới, credit được hoàn. Trong lúc chạy, phiên trên trình duyệt của bạn có thể bị Google đăng xuất.

### Cách Gọi Dạng Task (tuỳ chọn)

Cùng chức năng, dạng `create-task` / `check-status`, dùng cookie của bạn:

```http
POST /api/flow/create-task/{endpoint}
GET  /api/flow/check-status/{taskid}
```

| `{endpoint}` | Field |
|---|---|
| `text-to-image` | `cookie`, `prompt`, `model` (`Nano_Banana_2` \| `Nano_Banana_Pro`), `ratio` |
| `image-to-image` | như trên + `imageUrl` (URL hoặc data URI, chuỗi hoặc mảng) |
| `text-to-video` | `cookie`, `prompt`, `model: "Omni_Flash"`, `ratio` (`16:9` \| `9:16`), `length` (4/6/8/10, mặc định 4) — ra 720p; 1080p gọi tiếp `upscale-video` |
| `reference-images-to-video` | như trên + `imageUrl` 1–3 ảnh |
| `upscale-video` | `cookie`, `media_id` |

Response tạo task: `{ "code": 200, "taskid", "status": "pending", "amount", "balance" }`. `check-status` trả `status`: `pending` → `processing` → `successfully` | `failed`; thành công có `result.fifeUrl` (link ảnh/video), `result.mediaGenerationId` và `cookie` đã làm mới (một lần). Lỗi cookie: `code: 4012`.

## 7. Ảnh OpenAI GPT Image

```http
POST /api/openai-image
GET  /api/openai-image/{job_id}
```

```bash
curl -X POST "https://sunghub.com/api/openai-image" \
  -H "X-Auth-Key: <api_key>" -H "Content-Type: application/json" \
  --data '{ "prompt": "A flat illustration of a coffee shop logo with the text SUNG CAFE",
            "params": { "aspectRatio": "1:1" } }'
```

```json
{ "ok": true, "job_id": "…", "status": "queued", "model": "gpt-image-2", "size": "1024x1024",
  "credits_used": 500, "credits_remaining": 49500 }
```

| Field `params` | Giá trị |
|---|---|
| `aspectRatio` | `1:1` → 1024×1024 · `16:9` → 1536×1024 · `9:16` → 1024×1536 · bỏ trống → 2560×1440 |
| `size` | Ghi thẳng `rộngxcao`, ví dụ `2560x1440` |
| `images` | Ảnh mẫu, tối đa **4**: mảng `{ "b64": "data:image/jpeg;base64,…", "name": "ref1.jpg" }` |

- Trong prompt, gọi ảnh mẫu theo **thứ tự gửi lên**: `Image 1: …. Image 2: ….` rồi nói rõ ghép gì vào đâu. Tên file không được dùng.
- `GET` khi xong: `{ "status": "done", "urls": ["https://sunghub.com/media/oai_….png"] }`. File lưu **24 giờ**; trạng thái job giữ **1 giờ** — tải về ngay.
- Ảnh bị từ chối vì nội dung vi phạm chính sách → `failed`, **không hoàn credit**.

## 8. Ảnh / Video Muse

Nguồn ảnh/video giá rẻ, chạy theo hàng chờ (ít luồng — job có thể chờ vài phút khi đông). Tối đa **3 job chưa xong** mỗi tài khoản; vượt → `429`.

```http
POST   /api/muse
GET    /api/muse/{job_id}
DELETE /api/muse/{job_id}      (huỷ job ĐANG CHỜ, hoàn credit; job đã chạy không huỷ được)
GET    /api/muse/status        (số job đang chờ / đang chạy)
```

| `type` | Mô tả | `params` |
|---|---|---|
| `muse_text_to_image` | Chữ → ảnh | `aspectRatio`: `9:16`, `16:9`, `1:1`, `4:3`, `3:4` |
| `muse_image_to_image` | Sửa ảnh từ 1 ảnh mẫu | `image` (data URI base64, hoặc link `/media/…` đã tải lên bằng `POST /api/flow/upload`), `aspectRatio` |
| `muse_video` | Video 1 đoạn, tối đa 10 giây | `durationSec` 2–10 (mặc định 10), `aspectRatio`, `image` (khung đầu, tuỳ chọn) |
| `muse_video_long` | Video dài: 2–8 đoạn nối tiếp | `segments`: mảng 2–8 câu lệnh (mỗi câu một đoạn), `durationSec` mỗi đoạn, `aspectRatio`, `image`, `keepAudio` (mặc định `false` = bỏ tiếng) |

```bash
curl -X POST "https://sunghub.com/api/muse" \
  -H "X-Auth-Key: <api_key>" -H "Content-Type: application/json" \
  --data '{ "type": "muse_text_to_image", "prompt": "A red fox in a snowy forest, watercolor",
            "params": { "aspectRatio": "16:9" } }'
```

```json
{ "ok": true, "job_id": "…", "status": "queued", "credits_used": 50, "credits_remaining": 49950 }
```

`GET /api/muse/{job_id}` khi xong: `{ "status": "done", "urls": ["https://sunghub.com/media/muse_….png"] }`. File lưu tạm trên máy chủ — tải về ngay. Job lỗi do hệ thống → hoàn credit; bị từ chối vì nội dung vi phạm chính sách → **không hoàn**.

Giá: ảnh `muse_image` 50 · sửa ảnh `muse_image_edit` 50 · video `muse_video` 500 **mỗi đoạn** (video dài = 500 × số đoạn).

## 9. Giọng Đọc (Text-to-Speech)

```http
GET  /api/labs/models?provider=elevenlabs|minimax
GET  /api/labs/voices?provider=…
GET  /api/labs/languages?provider=minimax
GET  /api/labs/quote?text=…&provider=…&model_id=…&export_transcript=1
POST /api/labs/create
GET  /api/labs/task/{id}
GET  /api/labs/task/{id}/download[?format=srt]
GET  /api/labs/tasks
```

### Model Được Hỗ Trợ

| Provider | `model_id` | Giá / 1.000 ký tự | Tối đa / lần |
|---|---|---|---|
| `elevenlabs` | `eleven_v4` | 400 | 10.000 |
| `elevenlabs` | `eleven_v4_turbo` | 400 | 10.000 |
| `elevenlabs` | `eleven_v3` | 400 | 5.000 |
| `elevenlabs` | `eleven_turbo_v2_5` | 200 | 40.000 |
| `minimax` | `speech-2.8-hd` | 400 | 5.000 |
| `minimax` | `speech-2.8-turbo` | 200 | 10.000 |

Model khác → `400`. Bỏ trống `model_id` = model đầu bảng của provider. Xuất phụ đề SRT (`export_transcript: true`) **+15%**. Tính theo số ký tự thực tế của `text`, làm tròn lên.

### Danh Sách Giọng

| Request | Trả về |
|---|---|
| `?provider=elevenlabs` | Giọng có sẵn: `{ "voices": [{ voice_id, name, gender, language, preview_url, … }] }` (chỉ lọc được bằng `search`) |
| `?provider=elevenlabs&scope=shared` | Giọng cộng đồng: `{ "voices", "has_more" }` — `page` bắt đầu từ **0**, `page_size` ≤ 100, lọc `search`, `gender`, `age`, `accent`, ngôn ngữ bằng `required_languages=vi` |
| `?provider=minimax&language=Vietnamese` | `{ "voice_list": [{ voice_id, voice_name, tag_list, sample_audio, … }], "total" }` |
| `?provider=minimax&scope=cloned` | **Giọng tiếng Việt clone sẵn của Sunghub** (hơn 100 giọng): `{ "voice_list", "total" }` — dùng với `provider: "minimax"` |

### Tạo Giọng Đọc

```bash
curl -X POST "https://sunghub.com/api/labs/create" \
  -H "X-Auth-Key: <api_key>" -H "Content-Type: application/json" \
  --data '{
    "text": "Xin chào, đây là giọng đọc mẫu.",
    "voice_id": "<voice_id>",
    "provider": "minimax",
    "model_id": "speech-2.8-hd",
    "language_code": "Vietnamese",
    "voice_settings": { "speed": 1.0, "pitch": 0, "vol": 1.0, "use_speaker_boost": true },
    "export_transcript": false
  }'
```

```json
{ "success": true, "id": 495, "task_id": "…", "chars": 31, "creditCost": 13, "priceKey": "voice_minimax_hd" }
```

- Dùng **`id`** (số) cho các bước sau.
- `language_code`: ElevenLabs dùng **mã** (`vi`, `en`); MiniMax dùng **tên** (`Vietnamese`, `English`). Luôn gửi.
- `voice_settings` — ElevenLabs: `stability` 0–1, `similarity_boost` 0–1, `speed` 0.7–1.2, `use_speaker_boost`. MiniMax: `speed` 0.5–2.0, `pitch` −12…12 (số nguyên), `vol` 0.01–10, `use_speaker_boost`.
- Văn bản dài hơn mức tối đa của model: tự chia thành nhiều lần gọi.
- Không đủ credit → `400 { "error": "Không đủ credits", "required", "current" }`.

### Theo Dõi & Tải File

```json
// GET /api/labs/task/495
{ "success": true, "id": 495, "status": "completed", "progress": 100 }
```

`status`: `pending` → `processing` → `completed` | `failed`. Hỏi mỗi 5–10 giây, tối đa 30 phút. Task `failed` hoặc quá 30 phút được **hoàn credit tự động**. Tải file qua Sunghub:

```bash
curl -o out.mp3 "https://sunghub.com/api/labs/task/495/download" -H "X-Auth-Key: <api_key>"
curl -o out.srt "https://sunghub.com/api/labs/task/495/download?format=srt" -H "X-Auth-Key: <api_key>"
```

## 10. Tài Khoản & Giá

| Request | Trả về |
|---|---|
| `GET /api/credits` | `{ "credits": 49780 }` |
| `GET /api/credits/history` | `{ "success": true, "history": [{ id, type, amount, description, task_id, created_at }] }` — 200 dòng gần nhất. `description` dễ đọc ("Tạo ảnh Nano Banana 2", "Hoàn tiền 7d0a4da8 - Tạo ảnh Nano Banana 2 lỗi"); `task_id` = mã job ảnh/video (`job_id`) hoặc `id` task giọng đọc, `null` nếu không phải dòng tạo media |
| `GET /api/pricing/{key}` | `{ "success": true, "key", "credits", "label" }` |

### Pricing Key

| Dịch vụ | `key` | Giá |
|---|---|---|
| Ảnh Nano Banana 2 / ảnh mẫu → ảnh | `flowsell_text_to_image` / `flowsell_image_to_image` | 220 / ảnh |
| Ảnh Nano Banana Pro | `flowsell_text_to_image_pro` / `flowsell_image_to_image_pro` | 250 / ảnh |
| Phụ thu ảnh 2K / 4K (cộng vào giá ảnh) | `flowsell_image_2k` / `flowsell_image_4k` (kèm cookie: `flow_image_2k` / `flow_image_4k`) | xem `GET /api/pricing/{key}` |
| Video Omni Flash (chữ / ảnh mẫu) | `flowsell_text_to_video` / `flowsell_reference_images_to_video` | 500 / **giây** |
| Nâng video 1080p | `flowsell_upscale_video` | 1.000 / video |
| Mọi `type` kèm cookie của bạn | `flow_text_to_image`, `flow_image_to_image`, `flow_text_to_video`, `flow_reference_images_to_video`, `flow_upscale_video` | 50 / lượt |
| Ảnh OpenAI | `openai_image` + `openai_image_ref` × số ảnh mẫu | 500 + 350 / ảnh mẫu |
| Ảnh / video Muse | `muse_image` / `muse_image_edit` / `muse_video` | 50 / 50 / 500 mỗi đoạn video |
| Giọng đọc | `voice_eleven_v3`, `voice_eleven_turbo_v25`, `voice_minimax_hd`, `voice_minimax` | / 1.000 ký tự (mục 9) |

Giá có thể thay đổi — đọc `GET /api/pricing/{key}` để hiển thị giá đúng thời điểm.

## 11. Lưu Ý Khi Tích Hợp

- Chỉ hiển thị cho người dùng trường `error` trong JSON. Lỗi mạng hoặc phản hồi không đọc được: báo "không kết nối được máy chủ" và thử lại sau.
- Hỏi trạng thái mỗi 3–5 giây (ảnh/video) hoặc 5–10 giây (giọng). Nên chờ tối đa **10 phút** cho ảnh, **16 phút** cho video, **30 phút** cho giọng đọc.
- Gửi nhiều job cùng lúc được: job vượt số luồng đang rảnh sẽ **xếp hàng**, không báo lỗi.
- Link ảnh/video Google sống ~6 giờ, file trên `sunghub.com/media` sống 24 giờ: **tải về và tự lưu ngay khi job xong**.
- Video có thể tạm ngừng khi hết hạn mức phía Google — khi đó `POST` video trả `503` `video_disabled`, không trừ credit. Kiểm trước bằng `GET /api/app-status/sungmedia` → `features.video`.
- Mỗi API key mặc định tối đa 60 request/phút.
