# Menangani error

> Pahami respons error dan kegagalan transkripsi, lalu tentukan kapan request perlu diperbaiki atau dicoba ulang.

Canonical: https://transcript1.com/id/docs/errors
Language: id
Last updated: 2026-11-05

Bedakan request yang ditolak API dengan transkripsi yang gagal setelah diterima. Pada kasus pertama, Anda mendapat respons error langsung. Pada kasus kedua, job sudah dibuat, tetapi akhirnya memiliki `status` bernilai `failed`.

## Struktur respons error

Jika request ke `/v1` ditolak, respons error mengikuti format berikut:

```json
{
  "error": {
    "code": "insufficient_minutes",
    "message": "Not enough minutes",
    "retryable": false,
    "action_url": "https://transcript1.com/dashboard/billing",
    "minutes_needed": 20,
    "minutes_remaining": 3
  }
}
```

Gunakan `code` untuk menentukan penanganan di aplikasi. Teks `message` boleh ditampilkan kepada pengguna, tetapi jangan jadikan teksnya patokan karena bisa berubah. Nilai `retryable: true` menandakan request yang sama mungkin berhasil jika dicoba lagi nanti.

Selain `retryable`, beberapa error menyertakan informasi tambahan:

| Field | Kode error | Keterangan |
|---|---|---|
| `action_url` | `insufficient_minutes`, `no_active_plan`, `topup_needs_user` | Link untuk membeli paket atau top-up. |
| `minutes_needed`, `minutes_remaining` | `insufficient_minutes` | Jumlah menit yang dibutuhkan request dan saldo menit Anda. Kebutuhan menit bernilai `null` jika belum diketahui. |
| `reason`, `packs` | `topup_needs_user` | Alasan kartu tidak ditagih, dan jumlah bundel yang diminta. |
| `supported_platforms` | `unsupported_platform` | Daftar nilai platform yang diterima: `["youtube", "x", "tiktok", "instagram", "facebook", "bluesky", "twitch", "dailymotion", "pinterest", "linkedin", "snapchat"]`. |
| `missing_chunks` | `upload_incomplete` | Daftar indeks chunk yang belum sampai ke server. |
| `max_bytes` | `media_too_large` | Batas ukuran file yang berlaku untuk Anda, dalam byte. |
| `params` | `invalid_request` | Rincian validasi body atau query: setiap field dipasangkan dengan penjelasan masalahnya. |

## Saat API menolak request

| Status HTTP | `code` | Bisa retry | Penyebab dan tindakan |
|---|---|---|---|
| 400, 422 | `invalid_request` | Tidak | Ada parameter atau field body yang belum diisi atau tidak valid. Contohnya `diarize` bersama `source: "captions"`, atau `reuse_recent` bersama `webhook_url`. Body yang bukan JSON valid juga memakai kode ini. |
| 422 | `invalid_url` | Tidak | Link tidak diterima atau `platform` tidak sesuai dengan link. Gunakan YouTube, X, TikTok, Instagram, Facebook, Bluesky, atau platform lain dalam [daftar sumber](https://transcript1.com/id/docs/sources-and-options). |
| 422 | `unsupported_platform` | Tidak | Nilai `platform` tidak termasuk platform yang didukung. |
| 422 | `unsupported_media` | Tidak | Ekstensi file unggahan tidak didukung. |
| 401 | `unauthorized` | Tidak | Kredensial belum dikirim, tidak valid, sudah kedaluwarsa, atau sudah dicabut. Kode ini juga berlaku untuk token unggahan yang tidak valid atau kedaluwarsa. |
| 403 | `insufficient_scope` | Tidak | Token OAuth belum memiliki scope `transcriptions:read` atau `transcriptions:write` yang diperlukan. |
| 403 | `email_not_verified` | Tidak | Alamat email belum diverifikasi. Selesaikan verifikasi sebelum membuat transkripsi atau sesi langsung. |
| 402 | `insufficient_minutes` | Tidak | Saldo menit yang bisa dipakai sudah 0 atau tidak cukup untuk transkripsi ini, atau kurang dari satu menit yang dimulai pada sesi langsung. Jika [top-up otomatis](https://transcript1.com/id/docs/billing-and-limits#auto-top-up) aktif, transcript1 mencoba top-up lebih dulu. Beli menit di `action_url`, atau biarkan agen membelinya dengan `POST /v1/topups` atau tool MCP `top_up`. |
| 402 | `no_active_plan` | Tidak | Pembelian top-up memerlukan paket berbayar aktif. Kode ini berasal dari `POST /v1/topups` dan endpoint penagihan dasbor, bukan endpoint transkripsi. |
| 402 | `topup_needs_user` | Hanya untuk `pending` dan `in_progress` | `POST /v1/topups` tidak menagih kartu. `reason` menjelaskan alasannya: `not_allowed` (kredensial tidak boleh membeli top-up, atau pemiliknya bukan admin tim), `cap_reached` (batas bulanan untuk agen sudah tidak cukup), `card_declined`, `authentication_required`, atau `no_card`. Pengguna bisa membeli bundel di `action_url`. Untuk `pending` (pembayaran belum dikonfirmasi) dan `in_progress` (top-up lain sedang berjalan), tagihan masih mungkin berhasil: coba lagi nanti dengan `Idempotency-Key` yang sama, bukan meminta pengguna membeli. |
| 404 | `not_found` | Tidak | Tidak ada transkripsi dengan ID tersebut, atau transkripsi milik tim lain. |
| 409 | `upload_incomplete` | Tidak | Belum semua chunk diterima. Kirim bagian yang kurang, lalu ulangi langkah penyelesaian unggahan. |
| 409 | `invalid_state` | Tidak | Transkripsi tidak sedang menunggu unggahan. Anda tidak bisa mengirim chunk atau menyelesaikan unggahan. |
| 409 | `session_ended` | Tidak | Sesi langsung sudah berakhir, sehingga `POST /v1/live/sessions/:id/token` tidak bisa menerbitkan token. Juga dikirim sebagai event `error` WebSocket. |
| 422 | `unsupported_language` | Tidak | `language` pada sesi langsung tidak didukung. |
| 422 | `unsupported_translation` | Tidak | Bahasa `translate_to` pada sesi langsung tidak didukung, atau terjemahan langsung tidak tersedia. |
| 409 | `idempotency_conflict` | Tidak | Header `Idempotency-Key` sudah dipakai untuk parameter yang berbeda. |
| 413 | `media_too_large` | Tidak | File melewati batas ukuran Anda. Periksa [batas unggahan](https://transcript1.com/id/docs/uploads). |
| 413 | `invalid_request` | Tidak | Body JSON melebihi 1 MB. Untuk file yang terlalu besar, kodenya `media_too_large`. |
| 429 | `rate_limited` | Ya | Jumlah request per menit melewati batas. Tunggu sesuai jumlah detik pada `Retry-After`. |
| 429 | `too_many_active_jobs` | Ya | Terlalu banyak transkripsi berjalan atau sesi unggahan terbuka. Tunggu sesuai jumlah detik pada `Retry-After`. |
| 429 | `too_many_live_sessions` | Ya | Semua sesi langsung bersamaan pada paket Anda sedang terpakai. Tunggu sesuai jumlah detik pada `Retry-After` (30). |
| 500 | `internal_error` | Ya | Ada masalah pada sistem kami. Coba kembali beberapa saat lagi. |
| 503 | `service_unavailable` | Ya | transcript1 sementara tidak bisa diakses. Coba kembali beberapa saat lagi. |
| 503 | `live_capacity` | Ya | Seluruh kapasitas langsung sedang terpakai. Tunggu sesuai jumlah detik pada `Retry-After` (30), lalu buat sesi lagi. |
| 503 | `live_unavailable` | Tidak | Transkripsi langsung sedang tidak tersedia. |

Jika browser memakai cookie login sebagai autentikasi, API juga memeriksa origin request. Akses dari situs lain mendapat `403 forbidden_origin`. Pemeriksaan ini tidak berlaku untuk request API yang memakai header `Authorization` dengan kredensial bearer.

Semua respons `429` menyertakan `Retry-After` dalam detik. Rincian batasnya ada di [biaya dan batas penggunaan](https://transcript1.com/id/docs/billing-and-limits).

## Tentukan kapan perlu retry

- Retry hanya untuk `retryable` bernilai `true`: `rate_limited`, `too_many_active_jobs`, `too_many_live_sessions`, `live_capacity`, `internal_error`, `service_unavailable`, dan `topup_needs_user` dengan `reason` `pending` atau `in_progress`. Jika ada `Retry-After`, tunggu sesuai nilainya. Jika tidak, tambah jeda pada setiap percobaan.
- Saat mengulang `POST /v1/transcriptions` atau `POST /v1/topups`, pakai header `Idempotency-Key` yang sama. Dengan begitu, Anda tidak terkena biaya kedua jika request pertama ternyata sudah berhasil.
- Untuk kode lain, perbaiki request terlebih dahulu. Mengirim request yang sama tanpa perubahan tidak menyelesaikan masalahnya.

## Transkripsi langsung

`POST /v1/live/sessions` dan endpoint langsung lainnya memakai struktur error yang sama. `invalid_request`, `unsupported_language`, `insufficient_minutes`, dan `email_not_verified` juga berlaku untuk endpoint tersebut, bersama kode khusus transkripsi langsung pada tabel di atas. Pembuatan sesi tidak idempoten, jadi percobaan ulang setelah `too_many_live_sessions` atau `live_capacity` membuat sesi baru.

Setelah sesi ada, masalah datang lewat WebSocket sebagai event `error` dan kode penutupan, bukan sebagai error HTTP. Lihat [Transkripsi langsung](https://transcript1.com/id/docs/live#websocket-protocol) untuk semua event.

| Kode penutupan | Arti |
|---|---|
| `1000` | Penutupan normal, setelah `session.ended`. |
| `1011` | Error server yang tidak terduga. Lanjutkan dengan token baru. |
| `1012` | Server restart, setelah `reconnect`. Lanjutkan dengan token baru. |
| `4001` | Token tidak valid, kedaluwarsa, atau sudah dipakai. |
| `4004` | Sesi tidak ditemukan atau sudah berakhir. |
| `4008` | Pelanggaran protokol: frame audio berukuran ganjil atau terlalu besar. |
| `4010` | Digantikan oleh koneksi yang lebih baru ke sesi yang sama. |

Sesi langsung yang berakhir karena alasan seperti `balance_exhausted` atau `idle_timeout` bukanlah error: sesi mengirim `session.ended` dengan alasan berakhir dan disimpan. Lihat [Alasan berakhir](https://transcript1.com/id/docs/live#end-reasons).

## Saat proses transkripsi gagal

Job bisa gagal setelah API menerima request dan mengirim respons. Jika `status` berubah menjadi `failed`, objek transkripsi memuat `error: { "code", "message" }`. Periksa `error` saat polling atau ketika menerima [webhook](https://transcript1.com/id/docs/webhooks) `transcription.failed`. Jika terjemahan gagal, webhook `transcription.translation.failed` dikirim dan transkrip tidak pernah ikut gagal.

| `error.code` | Penyebab | Langkah berikutnya |
|---|---|---|
| `media_unavailable` | Video tidak ditemukan atau sudah tidak tersedia. | Jangan langsung retry. Periksa URL. |
| `media_private` | Video hanya bisa diakses secara privat. | Gunakan postingan publik, atau video YouTube berstatus publik maupun unlisted. |
| `login_required` | Platform meminta Anda login untuk mengakses video. | Tidak ada retry untuk kegagalan permanen ini. |
| `no_audio` | Video tidak memiliki track audio. | Tidak ada retry untuk kegagalan permanen ini. |
| `age_restricted` | Video memiliki batasan usia. | Unggah file rekamannya, bukan mengulang link yang sama. |
| `live_stream` | Siaran masih berlangsung atau belum dimulai. | Coba lagi setelah siaran berakhir. |
| `no_captions` | Mode `captions` pada `source` tidak menemukan track subtitle yang bisa dipakai. | Ubah ke `source: "auto"` atau `"stt"`. |
| `media_too_long` | Durasi melebihi batas speech to text, yaitu 5 jam. | Bagi rekaman menjadi beberapa bagian sebelum mengirimnya lagi. |
| `unsupported_media` | File tidak bisa dibaca atau tidak memiliki track audio. | Kirim file dengan format yang didukung. |
| `no_speech` | Tidak ada ucapan yang terdeteksi. | Jangan retry. |
| `unsupported_language` | Bahasa dalam rekaman tidak didukung. | Ubah `language` ke kode yang didukung atau `"auto"` sebelum mencoba lagi. |
| `insufficient_minutes` | Saldo menit habis sebelum transkripsi bisa ditagih, dan top-up otomatis, jika aktif, tidak berhasil menambah menit. | Isi saldo dengan top-up, lalu coba lagi. |
| `fetch_failed` | Media tetap gagal diambil setelah beberapa percobaan. | Boleh dicoba lagi nanti. |
| `transcription_failed` | Speech to text tetap gagal setelah beberapa percobaan. | Boleh dicoba lagi nanti. |

Jika transkripsi gagal atau dibatalkan, Anda tidak membayar. Menit yang sudah dipotong untuk job tersebut dikembalikan ke saldo.
