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

# Lapisan dan Pengeditan Wilayah Grok Imagine 2.0 Ext

> Gunakan segment untuk mengambil lapisan objek dan mask presisi, lalu edit poligon, kotak, atau objek terdeteksi dengan region_edit.

<Info>
  `segment` dan `region_edit` menggunakan endpoint gambar asinkron yang sama. Simpan `task_id`, lalu polling [Dapatkan status tugas](/id/api-reference/tasks/status); permintaan pembuatan tidak langsung mengembalikan lapisan atau gambar final.
</Info>

<Warning>
  Jangan pernah mengekspos API Key di bundle browser, LocalStorage, URL, atau log frontend. Panggil APIMart melalui backend atau BFF Anda.
</Warning>

<RequestExample>
  ```bash cURL theme={null}
  curl --request POST \
    --url https://api.apimart.ai/v1/images/generations \
    --header 'Authorization: Bearer <token>' \
    --header 'Content-Type: application/json' \
    --header 'Accept: application/json' \
    --header 'Idempotency-Key: 6baf0940-25d6-4ec2-9131-925250840fa7' \
    --data '{
      "model": "grok-imagine-2.0-ext",
      "operation": "segment",
      "source_task_id": "<COMPLETED_SINGLE_IMAGE_TASK_ID>",
      "include_mask_rle": true,
      "cached_only": false
    }'
  ```
</RequestExample>

<ResponseExample>
  ```json 200 theme={null}
  {
    "code": 200,
    "data": [{ "status": "submitted", "task_id": "task_..." }]
  }
  ```
</ResponseExample>

## Ringkasan operasi

| Tujuan                                                                       | Input utama                   | Hasil selesai                      | Penagihan                 |
| ---------------------------------------------------------------------------- | ----------------------------- | ---------------------------------- | ------------------------- |
| `segment`: Mendeteksi objek serta mengambil lapisan, kotak, dan mask presisi | `source_task_id`              | `image_id`, `image_url`, `objects` | Gratis                    |
| `region_edit`: Mengedit poligon, persegi panjang, atau objek terdeteksi      | `image_id`, `prompt`, pilihan | URL baru dan `image_id`            | Ditagih per tugas selesai |

```text theme={null}
task_id → segment(source_task_id) → image_id + mask_rle
        → selection_regions → region_edit → new task_id + image_id
```

<Note>
  `source_task_id` dan `image_id` tidak dapat dipertukarkan. `segment` memakai ID tugas sumber; `region_edit` memakai ID aset gambar. Untuk melakukan segmentasi ulang gambar hasil edit, gunakan ID tugas `region_edit` yang selesai sebagai `source_task_id` baru.
</Note>

## Header permintaan

Gunakan `Authorization: Bearer <APIMART_API_KEY>`, `Content-Type: application/json`, dan `Accept: application/json`.

`Idempotency-Key` bersifat opsional dan sangat disarankan untuk permintaan `region_edit` berbayar. Nilainya mendukung 1–191 karakter ASCII yang terlihat; UUID disarankan. Gunakan key baru untuk setiap operasi logis baru. Retry jaringan untuk permintaan yang sama harus memakai ulang key dan body yang identik. Jika hasil tidak pasti, jangan retry otomatis dengan key baru.

## Alur tugas asinkron

Pembuatan yang berhasil mengembalikan HTTP `200` dan `data[0].task_id`. Poll `GET /v1/tasks/{task_id}?language=id` mulai tiap 2 detik, naik hingga maksimum 5 detik, dengan batas total 10 menit. Hentikan polling lama saat gambar sumber berubah.

<Warning>
  Kueri tugas dapat mengembalikan HTTP `200` ketika `data.status` bernilai `failed`. Tentukan hasil dari `data.status` dan tampilkan `data.error` bila ada.
</Warning>

## `segment`

### Parameter permintaan

| Kolom              | Tipe    | Wajib | Default | Deskripsi                                                             |
| ------------------ | ------- | :---: | ------- | --------------------------------------------------------------------- |
| `model`            | string  |   ✅   | —       | Tetap `grok-imagine-2.0-ext`                                          |
| `operation`        | string  |   ✅   | —       | Tetap `segment`                                                       |
| `source_task_id`   | string  |   ✅   | —       | Tugas Grok satu gambar yang selesai dan dimiliki pengguna saat ini    |
| `include_mask_rle` | boolean |   —   | `true`  | Kembalikan COCO compressed RLE; pertahankan `true` untuk edit presisi |
| `cache_only`       | boolean |   —   | `false` | Hanya periksa cache segmentasi; jangan panggil upstream jika miss     |
| `cached_only`      | boolean |   —   | `false` | Petunjuk cache upstream, bukan jaminan cache lokal                    |
| `refresh`          | boolean |   —   | `false` | Lewati cache; jangan gunakan dalam alur editor normal                 |

`segment` tidak memerlukan `prompt`. Jangan kirim `image_id`, `image_index`, `billing_model_name`, `n`, `size`, atau `response_format`. `cache_only=true` dan `refresh=true` saling eksklusif.

### Contoh permintaan

<Tabs>
  <Tab title="Ambil lapisan">
    ```json theme={null}
    {
      "model": "grok-imagine-2.0-ext",
      "operation": "segment",
      "source_task_id": "<COMPLETED_SINGLE_IMAGE_TASK_ID>",
      "include_mask_rle": true,
      "cached_only": false
    }
    ```
  </Tab>

  <Tab title="Periksa cache">
    ```json theme={null}
    {
      "model": "grok-imagine-2.0-ext",
      "operation": "segment",
      "source_task_id": "<COMPLETED_SINGLE_IMAGE_TASK_ID>",
      "include_mask_rle": true,
      "cache_only": true
    }
    ```
  </Tab>
</Tabs>

Cache miss tetap merupakan tugas berhasil. Gunakan `cache_status` (`hit` atau `miss`) atau `from_cache`; jangan menyimpulkan hit dari `cached`.

### Respons selesai

Untuk `segment`, `data.result` langsung berisi hasil segmentasi dan tidak dibungkus dalam `images`.

```json theme={null}
{
  "code": 200,
  "data": {
    "id": "task_...",
    "status": "completed",
    "progress": 100,
    "cost": 0,
    "credits_cost": 0,
    "result": {
      "source_task_id": "task_...",
      "image_id": "6b78a7c3-5f9e-4a3d-a928-9e9b42113a3f",
      "image_url": "https://.../source.jpg",
      "from_cache": true,
      "cache_status": "hit",
      "objects": [{
        "index": 0,
        "name": "red sports car",
        "box_xyxy": [38.1, 689.8, 945.8, 1065.4],
        "score": 0.9765625,
        "mask_size": [1792, 1008],
        "mask_url": "",
        "mask_rle": { "size": [1792, 1008], "counts": "..." }
      }]
    }
  }
}
```

| Kolom                 | Deskripsi                                              |
| --------------------- | ------------------------------------------------------ |
| `result.image_id`     | ID aset untuk `region_edit`                            |
| `result.image_url`    | URL HTTP(S) yang sejajar dengan `image_id`             |
| `objects[].index`     | Indeks server asli; pertahankan untuk `object_indices` |
| `objects[].box_xyxy`  | Kotak piksel mask `[x1,y1,x2,y2]`                      |
| `objects[].score`     | Skor keyakinan; dapat berupa `null`                    |
| `objects[].mask_size` | Selalu `[height,width]`; jangan hard-code dimensi      |
| `objects[].mask_rle`  | COCO compressed RLE untuk kontur presisi               |
| `objects[].mask_url`  | URL gambar mask opsional; dapat kosong                 |

Objek tanpa `mask_rle` atau `mask_url` yang valid hanya dapat memakai edit kotak perkiraan.

## Dekode `mask_rle`

`mask_rle.counts` adalah string hitungan terkompresi COCO, bukan Base64 atau zlib. Data dibuka per kolom; run pertama adalah latar, lalu bergantian antara depan dan latar.

TypeScript berikut mengubahnya menjadi mask biner per baris yang sesuai untuk browser:

```ts theme={null}
export interface CocoRLE {
  size: [height: number, width: number];
  counts: string;
}

export interface BinaryMask {
  width: number;
  height: number;
  data: Uint8Array; // data[y * width + x]
}

function decodeCompressedCounts(counts: string): number[] {
  const runs: number[] = [];
  let cursor = 0;
  while (cursor < counts.length) {
    let value = 0;
    let shift = 0;
    let more = true;
    while (more) {
      if (cursor >= counts.length) throw new Error("Truncated COCO RLE counts");
      const current = counts.charCodeAt(cursor++) - 48;
      value |= (current & 0x1f) << shift;
      more = (current & 0x20) !== 0;
      shift += 5;
      if (!more && (current & 0x10) !== 0) value |= -1 << shift;
    }
    if (runs.length > 2) value += runs[runs.length - 2] ?? 0;
    if (value < 0) throw new Error(\`Invalid COCO RLE run: \${value}\`);
    runs.push(value);
  }
  return runs;
}

export function decodeCocoRLE(rle: CocoRLE): BinaryMask {
  const [height, width] = rle.size;
  if (!Number.isInteger(height) || !Number.isInteger(width) || height <= 0 || width <= 0) {
    throw new Error(\`Invalid mask size: \${JSON.stringify(rle.size)}\`);
  }
  const pixelCount = width * height;
  if (!Number.isSafeInteger(pixelCount) || pixelCount > 32_000_000) {
    throw new Error(\`Mask exceeds the frontend safety limit: \${pixelCount}\`);
  }
  if (!rle.counts) throw new Error("Missing COCO RLE counts");

  const data = new Uint8Array(pixelCount);
  const runs = decodeCompressedCounts(rle.counts);
  let position = 0;
  let foreground = false;
  for (const run of runs) {
    if (position + run > data.length) throw new Error("COCO RLE exceeds mask_size");
    if (foreground) {
      for (let offset = 0; offset < run; offset++) {
        const index = position + offset;
        const y = index % height;
        const x = (index - y) / height;
        data[y * width + x] = 1;
      }
    }
    position += run;
    foreground = !foreground;
  }
  if (position !== data.length) throw new Error("COCO RLE does not cover the mask");
  return { width, height, data };
}
```

Dekode mask besar di Web Worker. Jangan kirim nilai lengkap `mask_rle.counts` ke log, analitik, URL, atau pelaporan error.

### Ubah mask menjadi pilihan presisi

```ts theme={null}
interface SelectionBoundary { points: number[] }
interface SelectionRegion { outer: SelectionBoundary; holes?: SelectionBoundary[] }
```

Telusuri komponen terhubung dan lubang, sederhanakan kontur, lalu normalkan setiap titik ke `0–1`. Tiap ring memerlukan minimal 3 titik berbeda, area tidak nol, dan tidak boleh berpotongan sendiri. Simpan maksimal 16 region terbesar per lapisan dan 400 titik per ring.

<Warning>
  `mask_size` adalah `[height,width]` dan memakai koordinat mask sumber, bukan ukuran CSS. Dengan `object-fit: contain`, kurangi offset letterbox, skala sesuai area gambar aktual, dan batasi hasil ke `0–1`.
</Warning>

Membaca piksel gambar sumber atau `mask_url` memerlukan CORS. Tetapkan `crossOrigin = "anonymous"` sebelum `src`, atau fetch Blob. Dekode `mask_rle` langsung menghindari dependensi ini.

## Edit wilayah: `region_edit`

### Parameter permintaan

| Kolom               | Tipe         | Wajib | Deskripsi                                                                          |
| ------------------- | ------------ | :---: | ---------------------------------------------------------------------------------- |
| `model`             | string       |   ✅   | Tetap `grok-imagine-2.0-ext`                                                       |
| `operation`         | string       |   ✅   | `region_edit`                                                                      |
| `image_id`          | string       |   ✅   | ID aset sumber; pertama gunakan `image_id` dari `segment`, lalu hasil edit terbaru |
| `prompt`            | string       |   ✅   | Instruksi tidak kosong yang menjelaskan perubahan                                  |
| `selection_regions` | array        |   \*  | Poligon `0–1` dengan `outer` dan `holes` opsional; direkomendasikan                |
| `boxes`             | number\[]\[] |   \*  | Persegi panjang `[x1,y1,x2,y2]`; kotak piksel memerlukan `mask_size`               |
| `object_indices`    | integer\[]   |   \*  | Nilai asli `objects[].index`; hanya edit kotak perkiraan                           |
| `mask_size`         | integer\[]   |   \*  | Wajib untuk kotak piksel; `[height,width]` berupa integer positif                  |

Minimal satu dari `selection_regions`, `boxes`, atau `object_indices` harus tidak kosong. API menerima kombinasi, tetapi frontend sebaiknya memakai satu metode per permintaan.

<Warning>
  Jangan kirim `billing_model_name`, `size`, `aspect_ratio`, `source_aspect_ratio`, `source_size`, atau `image_urls`. Hilangkan `n` atau isi `1`; hilangkan `claim_asset` atau isi `false`; hilangkan `response_format` atau isi `url`. Base64 dan `stream=true` tidak didukung.
</Warning>

### Metode pilihan

| Metode              | Sumber pilihan           | Presisi                  | Penggunaan                      |
| ------------------- | ------------------------ | ------------------------ | ------------------------------- |
| `selection_regions` | Poligon frontend         | Presisi, termasuk lubang | Edit lapisan atau kuas produksi |
| `boxes`             | Persegi panjang frontend | Perkiraan kotak          | Alat kotak atau MVP             |
| `object_indices`    | Indeks segment asli      | Perkiraan kotak          | Uji integrasi cepat             |

<Tabs>
  <Tab title="Poligon presisi">
    ```json theme={null}
    {
      "model": "grok-imagine-2.0-ext",
      "operation": "region_edit",
      "image_id": "<SOURCE_IMAGE_ID>",
      "prompt": "Change the selected car to bright red and preserve the rest",
      "selection_regions": [{
        "outer": { "points": [0.12, 0.20, 0.48, 0.20, 0.48, 0.61, 0.12, 0.61] },
        "holes": [{ "points": [0.30, 0.35, 0.38, 0.35, 0.38, 0.45, 0.30, 0.45] }]
      }]
    }
    ```

    `points` dapat berupa array datar atau pasangan bertingkat. Setiap nilai harus terbatas dan berada pada `0–1`; tiap ring membutuhkan minimal 3 pasangan.
  </Tab>

  <Tab title="Kotak ternormalisasi">
    ```json theme={null}
    {
      "model": "grok-imagine-2.0-ext",
      "operation": "region_edit",
      "image_id": "<SOURCE_IMAGE_ID>",
      "prompt": "Change the car inside the box to bright red",
      "boxes": [[0.04, 0.385, 0.938, 0.594]]
    }
    ```
  </Tab>

  <Tab title="Kotak piksel">
    ```json theme={null}
    {
      "model": "grok-imagine-2.0-ext",
      "operation": "region_edit",
      "image_id": "<SOURCE_IMAGE_ID>",
      "prompt": "Change the car inside the box to bright red",
      "boxes": [[40, 689.6, 945.9, 1064.4]],
      "mask_size": [1792, 1008]
    }
    ```
  </Tab>

  <Tab title="Indeks objek">
    ```json theme={null}
    {
      "model": "grok-imagine-2.0-ext",
      "operation": "region_edit",
      "image_id": "<SOURCE_IMAGE_ID>",
      "prompt": "Change the selected car to bright red",
      "object_indices": [0]
    }
    ```

    Indeks harus berasal dari respons segment untuk `image_id` yang sama. Jangan ganti dengan indeks dari array frontend yang difilter, diurutkan, atau dikelompokkan.
  </Tab>
</Tabs>

### Respons selesai

```json theme={null}
{
  "code": 200,
  "data": {
    "id": "task_...",
    "status": "completed",
    "progress": 100,
    "cost": 0.016,
    "credits_cost": 0.16,
    "result": {
      "images": [{
        "url": ["https://.../result.jpg"],
        "image_ids": ["<NEW_IMAGE_ID>"],
        "items": [{
          "url": "https://.../result.jpg",
          "image_id": "<NEW_IMAGE_ID>",
          "source_image_id": "<SOURCE_IMAGE_ID>",
          "role": "region_edit"
        }],
        "expires_at": 1787040000
      }]
    }
  }
}
```

Utamakan `result.images[0].items[0]`. Untuk respons lama, pasangkan `url[0]` dan `image_ids[0]` hanya jika panjang array sama. Lanjutkan hanya setelah memperoleh URL HTTP(S) dan `image_id` baru.

Gunakan `expires_at` sebagai acuan kedaluwarsa URL; jangan hard-code jumlah jam. Unduh atau simpan aset yang dibutuhkan jangka panjang.

## Pengeditan berkelanjutan

Setelah edit selesai, perbarui URL tampilan, ID aset saat ini, dan ID tugas sumber secara bersamaan, lalu hapus lapisan dan status polling lama.

* Segmentasi ulang: gunakan ID tugas `region_edit` ini sebagai `source_task_id`
* Edit ulang: gunakan `image_id` baru yang dikembalikan
* Jangan pernah mengirim `image_id` ke `segment` dan jangan terus mengedit ID gambar sebelumnya.

## Penanganan error

| HTTP / status                       | Penyebab umum                                                                                   | Penanganan                                                                     |
| ----------------------------------- | ----------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
| 400 sumber atau operasi tidak valid | Operasi salah, tugas sumber tidak dapat dipakai, atau `image_id/image_index` dikirim ke segment | Validasi operasi dan gunakan tugas satu gambar selesai milik pengguna saat ini |
| 400 pilihan tidak valid             | Prompt kosong, pilihan tidak ada, atau poligon, kotak, indeks tidak valid                       | Validasi prompt dan pilihan sebelum mengirim                                   |
| 400 opsi tidak didukung             | `claim_asset`, `n`, format, ukuran, atau stream tidak valid                                     | Hapus field tidak didukung dan gunakan output URL                              |
| 401 / 403                           | Key tidak valid atau izin model tidak ada                                                       | Periksa key server dan akses akun                                              |
| 402                                 | Saldo tidak cukup                                                                               | Minta top up sebelum retry                                                     |
| 409                                 | Permintaan idempoten sedang berjalan, berubah, atau tidak pasti                                 | Ikuti respons; jangan ganti key otomatis                                       |
| 429 / 5xx                           | Rate limit atau gangguan sementara                                                              | Ikuti `Retry-After` dan lakukan backoff terbatas                               |
| failed / task\_failed               | Eksekusi asinkron gagal                                                                         | Hentikan polling dan tampilkan `data.error.message`                            |

## Penagihan

* `segment` gratis dan selesai dengan `cost=0` serta `credits_cost=0`, tetapi tetap memerlukan autentikasi dan tugas sumber valid.
* `region_edit` berbayar. Gunakan `cost` dan `credits_cost` dari tugas selesai; jangan hard-code harga di frontend.
* Jangan pernah mengirim field internal `billing_model_name`.

## Daftar periksa frontend

* Simpan API Key hanya di backend atau BFF.
* Kirim hanya `source_task_id` ke `segment`; jangan kirim `image_id` atau `image_index`.
* Gunakan `image_id` dari segment untuk `region_edit` dan sediakan minimal satu metode pilihan.
* Gunakan `selection_regions` untuk edit presisi; `object_indices` hanya perkiraan kotak.
* Selalu baca `mask_size` sebagai `[height,width]` dan tangani skala serta letterbox.
* Gunakan ulang idempotency key asli untuk retry yang sama dan validasi URL serta `image_id` baru.
