# Permintaan

Permintaan adalah satu panggilan ke satu model. Permintaan memiliki id (`req_…`), status, biaya dalam kredit API (1000 = 1 USD), dan, jika berhasil, file yang dihasilkan. Objek yang sama dikembalikan oleh setiap endpoint di bawah ini, untuk setiap model.

## Menemukan model

Katalog semua 1.496 model tersedia sebagai JSON publik (tidak memerlukan kunci):

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

| Parameter kueri | Arti |
|---|---|
| `q` | Kata-kata yang semuanya harus muncul dalam nama, id, pembuat, atau tag, misalnya `kling 3`. |
| `category` | Satu id kategori, misalnya `text-to-video` atau `upscale`. `GET https://api.doitong.com/v1/categories` mencantumkan semua 21 kategori beserta jumlahnya. |
| `lab` | Pembuat model, berdasarkan nama atau id, misalnya `Google` atau `black-forest-labs`. |
| `family` | Id `family` dari baris sebelumnya: setiap varian dari satu model (pro, fast, image-to-video, …). |
| `mode` | `exact` (harga dihitung dari input), `measured` (dari panjang atau ukuran file input), `estimate` (perkiraan harga per eksekusi), atau `metered` (ditagih berdasarkan penggunaan aktual, hingga maksimum per eksekusi). |
| `limit`, `cursor` | Ukuran halaman (1 hingga 100, bawaan 50) dan `next_cursor` dari halaman sebelumnya. |

Setiap baris memiliki `id`, `name`, `lab`, `category`, `family`, `summary`, `tags`, `output_kind`, `docs_url`, dan objek `price` (lihat [Harga](https://doitong.com/developers/docs/pricing#price-objects)). `GET https://api.doitong.com/v1/models/{model_id}` menambahkan JSON Schema input dan output lengkap, nama parameter kanonis, aturan harga, dan contoh input. `category` atau `mode` yang tidak dikenal menghasilkan `400 invalid_parameter`.

## Menjalankan model

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

- Body adalah input model, berupa objek JSON. Setiap halaman model mencantumkan parameternya.
- Input diperiksa sebelum biaya apa pun dibebankan. Input yang tidak valid menghasilkan `422 invalid_input` dengan `param` yang bermasalah, dan tidak dikenai biaya.
- `?wait=N` (hingga 20 detik) menahan panggilan hingga permintaan selesai atau waktunya habis.
- `?webhook_url=https://…` mengirimkan POST bertanda tangan ke URL tersebut saat permintaan selesai (lihat [Webhook](https://doitong.com/developers/docs/webhooks)).
- `?storage=permanent` atau `?storage=temporary` memilih berapa lama file output disimpan; jika tidak digunakan, pengaturan akun akan diterapkan (lihat [Lokasi file](#storage)).

Responsnya adalah `202 Accepted` saat permintaan masih diproses, dan `200 OK` jika selesai dalam waktu tunggu (atau jika merupakan pemutaran ulang, lihat di bawah). Header `Location` dan `Doitong-Request-Id` membawa URL dan id permintaan.

## Status

Siklus hidup permintaan: `queued` → `running` → `succeeded` | `failed` | `canceled` (final; yang gagal dan dibatalkan mendapatkan pengembalian dana).

| Status | Arti | Final |
|---|---|---|
| `queued` | Diterima dan ditagih, menunggu worker. `queue_position` mungkin menampilkan posisinya dalam antrean. | tidak |
| `running` | Model sedang menghasilkan output. | tidak |
| `succeeded` | Selesai. `outputs` mencantumkan file. | ya |
| `failed` | Model atau batas menghentikannya. `error` menjelaskan alasannya. Kredit API dikembalikan. | ya |
| `canceled` | Dibatalkan sebelum menghasilkan hasil. Kredit API dikembalikan. | ya |

Permintaan yang berjalan lebih lama dari batas waktunya gagal dengan kode `timeout` dan dana dikembalikan: 15 menit untuk gambar, 60 menit untuk video, 30 menit untuk audio, dan semua jenis lainnya.

## Objek permintaan

```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` sama untuk setiap model: satu entri per file yang dihasilkan dengan `type` (`image`, `video`, `audio`, `file` atau `3d`), `url` pada penyimpanan kami, `content_type`, dan jika diketahui, `file_size`, `width`, `height`, `duration`.
- `output` adalah objek hasil milik model (field-nya tercantum di setiap halaman model) dengan URL yang sama.
- `storage` menjelaskan cara file disimpan: `mode` (`permanent` atau `temporary`), `bytes` yang disimpan, `expires_at` (saat file sementara dihapus, `null` untuk file permanen), dan `expired` (`true` setelah file dihapus).
- `cost.credits` adalah harga panggilan dalam kredit API (`cost.usd` dalam USD), `cost.charged` adalah jumlah yang didebit dari saldo API, dan `cost.refunded` adalah jumlah yang dikembalikan. Untuk model `metered`, `cost.credits` adalah maksimum yang ditahan di awal (`cost.held`); setelah eksekusi berhasil, `cost.settled` menunjukkan jumlah penggunaan, dan sisa maksimum berada di `cost.refunded` (lihat [Ditagih berdasarkan penggunaan](https://doitong.com/developers/docs/pricing#metered)).
- Jika permintaan gagal, `error` berbentuk `{ "type": …, "code": …, "message": … }`.

## Membaca permintaan

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

- `?wait=N` (hingga 20 detik) melakukan long-poll: panggilan dikembalikan segera setelah permintaan final, atau setelah N detik dengan status saat ini. Melakukan loop dengan cara ini adalah cara paling sederhana untuk menunggu hasil.
- `?logs=1` menambahkan baris log kemajuan model (50 terakhir) sebagai `logs`.

Anda hanya dapat membaca permintaan dari akun Anda sendiri; id lain apa pun menghasilkan `404 request_not_found`.

## Mencantumkan permintaan

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

Yang terbaru ditampilkan terlebih dahulu. `limit` bernilai 1 hingga 100 (bawaan 20), sedangkan `status` dan `model` berfungsi sebagai filter. Responsnya adalah `{ "object": "list", "data": [ … ], "has_more": true, "next_cursor": "req_…" }`; teruskan `cursor=<next_cursor>` untuk halaman berikutnya.

## Membatalkan permintaan

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

- Dibatalkan sebelum model dimulai: status menjadi `canceled`, kredit API dikembalikan, dan Anda menerima `200`.
- Sudah dalam proses pembuatan: pembatalan diteruskan dan Anda menerima `202` dengan `"cancel_requested": true`. Jika model tetap selesai, permintaan berhasil seperti biasa dan ditagih.
- Sudah final: `409 not_cancelable` (permintaan yang sudah `canceled` cukup mengembalikan `200`).

## Menghapus file permintaan

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

Segera menghapus salinan file output permintaan kami dan menghentikan biaya penyimpanannya. Responsnya adalah objek permintaan dengan `storage.expired: true`; URL-nya tetap berada dalam catatan, tetapi tidak lagi berfungsi. Pemanggilan ulang tidak mengubah apa pun (`200`). Permintaan yang masih `queued` atau `running` belum memiliki file: `409 request_active`. Memerlukan `models:run`.

## Idempotensi: mencoba ulang tanpa membayar dua kali

Jaringan dapat gagal. Untuk mencoba ulang eksekusi dengan aman, kirim header `Idempotency-Key` (string apa pun hingga 255 karakter, unik per operasi, misalnya id pesanan Anda sendiri):

- Kunci dan body yang sama seperti sebelumnya: Anda menerima kembali permintaan asli (`200`, header `Idempotent-Replayed: true`), dan tidak ada biaya tambahan.
- Kunci dan body yang sama setelah eksekusi gagal sebelum mencapai model (`upstream_error` atau `internal_error` saat pengiriman, `insufficient_credits`, `budget_exceeded`, `too_many_inflight`): tidak ada yang dihasilkan dan tidak ada biaya, sehingga percobaan ulang menjalankan model lagi alih-alih mengembalikan kegagalan.
- Kunci yang sama dengan body berbeda: `409 idempotency_mismatch`.
- Kunci dibatasi pada kunci API yang mengirimkannya.

## Lokasi file

Setiap file output disalin ke penyimpanan kami sebelum permintaan ditandai `succeeded`, sehingga URL dalam `outputs` berada di host kami sendiri dan dapat ditautkan langsung. Anda dapat memilih berapa lama file tersebut disimpan:

| Mode | Durasi | Harga |
|---|---|---|
| `permanent` (bawaan) | Hingga Anda menghapusnya | 100 kredit API ($0,10) per GB per bulan, ditagih setiap hari dari saldo API |
| `temporary` | 7 hari, lalu dihapus | Gratis |

- Pengaturan akun menentukan perilaku untuk permintaan baru: tombol "Simpan file selamanya" di [dasbor](https://doitong.com/developers/dashboard#balance), atau `PATCH https://api.doitong.com/v1/account/storage` dengan `{"mode": "temporary"}`. `GET https://api.doitong.com/v1/account` menampilkannya di bawah `storage`, bersama byte yang Anda simpan dan perkiraan bulanan.
- `?storage=permanent|temporary` pada eksekusi (argumen `storage` dari tool MCP `run`) menggantikan pengaturan untuk eksekusi tersebut.
- Mengubah pengaturan tidak pernah memengaruhi file yang sudah Anda miliki; hapus file tersebut dengan [`DELETE /requests/{id}/files`](#delete-files).
- Jika saldo API tidak dapat membayar penyimpanan, tidak ada biaya yang dikenakan dan Anda menerima email: isi ulang dalam 30 hari, atau file yang disimpan menjadi sementara dan dihapus 7 hari kemudian.

Dalam kasus yang jarang terjadi ketika penyalinan kami gagal, entri memiliki `"stored": false` dan URL-nya adalah URL asli yang berumur pendek: segera unduh.
