> ## Documentation Index
> Fetch the complete documentation index at: https://gccai.heqingsong.uk/llms.txt
> Use this file to discover all available pages before exploring further.

# Callback Penyelesaian Tugas (Webhook)

> Sertakan URL callback saat mengirim tugas generasi asinkron, dan kami akan mem-POST hasilnya kepada Anda setelah tugas selesai (berhasil atau gagal) — tanpa perlu polling.

Saat mengirim tugas generasi asinkron seperti video / gambar / audio, Anda dapat menyertakan URL callback. **Setelah tugas selesai (berhasil atau gagal)**, kami akan secara aktif mem-POST hasilnya ke URL Anda, sehingga Anda tidak perlu terus-menerus melakukan polling.

## Mulai cepat

Saat mengirim tugas, tambahkan field `webhook` ke body permintaan:

```bash theme={null}
curl -X POST https://domain-akses-anda/v1/images/generations \
  -H "Authorization: Bearer API_KEY_ANDA" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-image-2",
    "prompt": "a red apple on a table",
    "size": "1024x1024",
    "webhook": "https://your-server.com"
  }'
```

Setelah tugas selesai, kami akan mengirim permintaan POST ke **`URL Anda + /callback`**.

<Note>
  Endpoint tugas asinkron lainnya (video, audio, dll.) bekerja dengan cara yang sama — cukup tambahkan field `webhook` ke body permintaan.
</Note>

## Aturan URL

`webhook` yang Anda berikan adalah **URL dasar (base)**, dan kami otomatis menambahkan `/callback` di belakangnya:

| `webhook` Anda                 | Ke mana kami benar-benar mem-POST      |
| ------------------------------ | -------------------------------------- |
| `https://your-server.com`      | `https://your-server.com/callback`     |
| `https://your-server.com/api`  | `https://your-server.com/api/callback` |
| `https://your-server.com/api/` | `https://your-server.com/api/callback` |

Jadi server Anda perlu menyediakan endpoint yang menerima `POST .../callback`.

## Apa yang akan Anda terima

Konten yang dikirim **persis sama dengan yang dikembalikan oleh endpoint "[Dapatkan Status Tugas](/id/api-reference/tasks/status)"** — Anda dapat memprosesnya dengan logika parsing yang sama.

<CodeGroup>
  ```json Berhasil (status: completed) theme={null}
  {
    "id": "task_01KV7FXR8BEYS1BWHJCT3JMCJ5",
    "status": "completed",
    "progress": 100,
    "created": 1781589029,
    "completed": 1781589058,
    "actual_time": 29,
    "cost": 0.006,
    "credits_cost": 0.06,
    "result": {
      "images": [{ "url": ["https://.../result.png"], "expires_at": 1781675458 }]
    }
  }
  ```

  ```json Gagal (status: failed) theme={null}
  {
    "id": "task_xxx",
    "status": "failed",
    "progress": 100,
    "created": 1781589029,
    "completed": 1781589050,
    "error": { "message": "alasan kegagalan...", "code": "" }
  }
  ```
</CodeGroup>

<Note>
  Untuk tugas video, hasilnya ada di `result.videos`, dan untuk audio di `result.audios`.
</Note>

<Warning>
  Kami hanya mengirim ketika tugas mencapai **status final** (`completed` / `failed`); kami tidak mengirim saat sedang diproses.
</Warning>

## Percobaan ulang dan deduplikasi (penting)

* **Percobaan ulang**: Jika server Anda tidak mengembalikan `2xx` dalam sekitar 10 detik, atau mengembalikan `5xx`, kami akan otomatis mencoba lagi, hingga **3 kali**, dengan interval sekitar **10 dtk, 30 dtk, dan 60 dtk**. Jika ketiganya gagal, kami menyerah (dalam sekitar 2 menit).
* **Tidak dicoba ulang**: Jika endpoint Anda mengembalikan `4xx` (dianggap URL / permintaan bermasalah), kami langsung menyerah tanpa mencoba lagi.
* **Deduplikasi**: Biasanya satu tugas hanya dikirim sekali. Namun dalam kasus ekstrem (mis. restart di sisi kami setelah pengiriman tetapi sebelum konfirmasi) Anda **mungkin menerima pengiriman ganda**. Pastikan untuk **melakukan deduplikasi secara idempoten berdasarkan `id` (task\_id)** untuk menghindari pemrosesan ganda.

**Rekomendasi untuk endpoint penerima Anda:**

<Steps>
  <Step title="Kembalikan 2xx secepat mungkin">
    Terima dan masukkan ke antrean dulu, lalu proses secara asinkron — jangan biarkan kami menunggu Anda selesai memproses.
  </Step>

  <Step title="Deduplikasi berdasarkan id">
    Gunakan `id` (task\_id) sebagai kunci idempotensi untuk menghindari pemrosesan ganda.
  </Step>

  <Step title="Konfigurasi dan verifikasi tanda tangan">
    Di produksi, verifikasi asal permintaan callback dan tolak permintaan palsu.
  </Step>
</Steps>

## Persyaratan untuk URL callback

Demi keamanan, URL callback harus memenuhi:

| Persyaratan          | Keterangan                                                                                          |
| -------------------- | --------------------------------------------------------------------------------------------------- |
| Dapat diakses publik | **Tidak boleh** berupa alamat internal / lokal (mis. `127.0.0.1`, `10.x`, `192.168.x` akan ditolak) |
| Protokol             | `http` atau `https` (disarankan `https`)                                                            |
| Port                 | Gunakan port standar (`80` / `443`); port non-standar mungkin diblokir                              |
| Domain               | Tidak boleh menunjuk ke domain layanan kami sendiri                                                 |

URL yang tidak memenuhi persyaratan ini akan langsung dibuang (tidak dikirim, tidak dicoba ulang).

## FAQ

<AccordionGroup>
  <Accordion title="Saya mengirim tugas dengan webhook tetapi tidak menerima pengiriman?">
    Periksa satu per satu:

    1. Apakah tugas **benar-benar selesai**? Periksa detail tugas — apakah `status` sudah `completed` / `failed` (tidak dikirim saat diproses)?
    2. Apakah URL Anda **dapat diakses publik**? Bisakah kami menjangkau `/callback` Anda?
    3. Apakah port-nya standar (80 / 443)? Port non-standar mungkin diblokir oleh kebijakan keamanan.
    4. Apakah `/callback` Anda **mengembalikan 2xx tepat waktu**? Mengembalikan 4xx akan langsung ditinggalkan.
    5. Apakah Anda menggunakan `https`? Apakah sertifikatnya valid?
  </Accordion>

  <Accordion title="Mengapa url di dalam result berupa array?">
    Beberapa model menghasilkan banyak gambar sekaligus, jadi `images[].url` bisa berupa array — cukup perlakukan sebagai array.
  </Accordion>

  <Accordion title="Apakah tautan hasil memiliki masa berlaku?">
    Jika `result` menyertakan `expires_at` (timestamp Unix), itu menunjukkan waktu kedaluwarsa tautan — segera transfer/simpan.
  </Accordion>

  <Accordion title="Apakah status 'sedang diproses' dikirim?">
    Tidak. Kami hanya mengirim sekali, saat tugas akhirnya berhasil atau gagal.
  </Accordion>
</AccordionGroup>

## Contoh penerima minimal

```python Python theme={null}
from http.server import BaseHTTPRequestHandler, HTTPServer
import json

class H(BaseHTTPRequestHandler):
    def do_POST(self):
        n = int(self.headers.get("Content-Length") or 0)
        body = self.rfile.read(n)
        data = json.loads(body)
        print("Callback tugas diterima:", data["id"], data["status"])
        # TODO: deduplikasi berdasarkan id, verifikasi tanda tangan, lalu proses
        self.send_response(200); self.end_headers()
        self.wfile.write(b'{"ok":true}')

HTTPServer(("0.0.0.0", 443), H).serve_forever()
```

Kembalikan `200` secepat mungkin, dan jalankan logika pemrosesan Anda secara asinkron di latar belakang.
