# Mobile API – Inbox Reference

Dokumentasi API untuk fitur **Inbox** di aplikasi mobile (Android/iOS). Base URL: `{BASE_URL}/api/mobile`.

**Autentikasi:** Semua endpoint di bawah memerlukan **Bearer token** (Sanctum). Header: `Authorization: Bearer {token}`.

**Role:** Endpoint ini mendukung user dengan role **client** atau **agent**. Response disesuaikan dengan role (client melihat semua percakapan milik perusahaannya; agent mengikuti permission `can_view_unassigned` / `can_view_all_inbox`).

---

## Daftar Endpoint Inbox

| Method | Endpoint | Deskripsi |
|--------|----------|-----------|
| GET | `/waba-list` | Daftar WABA (untuk filter dropdown) |
| GET | `/conversations` | Daftar percakapan (pagination lazy load) |
| GET | `/conversations/counts` | Total, read, unread, unreplied (badge & tab) |
| GET | `/conversations/{id}` | Detail satu percakapan |
| GET | `/conversations/{id}/messages` | Daftar pesan (cursor pagination) |
| GET | `/conversations/{id}/messages/search` | Cari dalam chat |
| PUT | `/conversations/{id}/notes` | Simpan catatan |
| PUT | `/conversations/{id}/contact` | Simpan/update kontak |
| POST | `/conversations/{id}/send` | Kirim pesan |
| POST | `/conversations/{id}/send-template` | Kirim pesan template |
| POST | `/conversations/{id}/mark-read` | Tandai dibaca |
| DELETE | `/conversations/{id}/messages/{messageId}` | Hapus pesan |
| PUT | `/conversations/{id}/messages/{messageId}` | Edit pesan (outgoing text) |
| POST | `/conversations/{id}/messages/{messageId}/retry-media` | Retry download media (antrian) |
| POST | `/conversations/{id}/messages/{messageId}/refetch-media` | Refetch media (synchronous) |
| POST | `/conversations/{id}/close` | Tutup chat |
| POST | `/conversations/{id}/reopen` | Buka kembali chat |
| POST | `/media/upload` | Upload media (untuk kirim gambar/dokumen) |

---

## 1. GET /waba-list

Mendapatkan daftar WABA (nomor WhatsApp Business) yang bisa dipakai untuk filter inbox.

**Response 200**

```json
{
  "success": true,
  "data": {
    "results": [
      {
        "id": 1,
        "text": "Nama Akun",
        "phoneNumber": "6281234567890",
        "type": "dedicated"
      }
    ],
    "clientId": 1
  }
}
```

- **results**: array untuk dropdown filter (id, text, phoneNumber, type).
- **clientId**: ID client (untuk keperluan tampilan).

---

## 2. GET /conversations

Daftar percakapan dengan **pagination lazy load** (cursor-based).

**Query params**

| Parameter | Tipe | Default | Keterangan |
|----------|------|---------|------------|
| limit | int | 20 | 1–50, jumlah per halaman |
| cursor | string | - | Dari `meta.nextCursor` response sebelumnya (untuk halaman berikutnya) |
| status | string | - | `open` \| `closed` |
| read_status | string | all | `all` \| `read` \| `unread` \| `unreplied` |
| unread_only | bool | false | Hanya percakapan dengan unread |
| waba_pool_id | int | - | Filter per WABA |
| search | string | - | Cari nama/nomor/kontak/isi pesan |
| only_contacts | bool | false | Hanya dari kontak (shared/agent) |
| assigned_to_me | bool | false | (Agent) hanya yang di-assign ke saya |

**Response 200**

```json
{
  "success": true,
  "data": [
    {
      "id": 123,
      "customerNumber": "6289876543210",
      "customerName": "Nama Pelanggan",
      "unreadCount": 2,
      "status": "open",
      "agent": { "id": 1, "name": "Agent A" },
      "waba": { "id": 1, "phoneNumber": "6281234567890", "name": "Akun WA" },
      "wabaPoolId": 1,
      "lastMessage": {
        "preview": "Preview pesan terakhir...",
        "type": "text",
        "timestamp": "2025-03-16T10:00:00.000000Z"
      },
      "lastMessageAt": "2025-03-16T10:00:00.000000Z"
    }
  ],
  "meta": {
    "limit": 20,
    "count": 20,
    "hasMore": true,
    "nextCursor": "eyJpZCI6MTAzfQ=="
  }
}
```

- **Lazy load:** Request pertama tanpa `cursor`. Untuk halaman berikutnya, kirim `cursor=meta.nextCursor` dari response sebelumnya. Jika `hasMore === false`, tidak ada halaman lagi.
- **nextCursor:** Base64-encoded; jangan diubah, langsung dikirim kembali sebagai query `cursor`.

---

## 3. GET /conversations/counts

Statistik untuk badge unread dan tab filter (All / Read / Unread / Unreplied).

**Query params**

| Parameter | Tipe | Keterangan |
|----------|------|------------|
| (optional) | - | Tidak ada parameter wajib |

**Response 200**

```json
{
  "success": true,
  "data": {
    "total": 100,
    "read": 60,
    "unread": 30,
    "unreplied": 10,
    "all": 100,
    "open": 80,
    "closed": 20
  }
}
```

- **total / all**: Total percakapan.
- **read**: Sudah dibaca (tanpa unread).
- **unread**: Ada pesan belum dibaca.
- **unreplied**: Terakhir dari customer, dalam 3 hari terakhir (belum dibalas).
- **open / closed**: Status percakapan.

Gunakan **unread** untuk badge; **total**, **read**, **unread**, **unreplied** untuk label tab filter.

---

## 4. GET /conversations/{id}

Detail satu percakapan (assignment). Wajib akses ke conversation tersebut (WabaScope).

**Response 200**

```json
{
  "success": true,
  "data": {
    "id": 123,
    "customerNumber": "6289876543210",
    "customerName": "Nama",
    "contactName": "Nama Kontak",
    "contactEmail": "email@example.com",
    "status": "open",
    "notes": "Catatan internal",
    "unreadCount": 2,
    "waba": { "id": 1, "phoneNumber": "6281234567890", "name": "Akun" },
    "wabaPoolId": 1,
    "agent": { "id": 1, "name": "Agent A" },
    "lastMessageAt": "2025-03-16T10:00:00.000000Z",
    "serviceWindow": { "open": true, "expiresAt": "..." }
  }
}
```

---

## 5. GET /conversations/{id}/messages

Daftar pesan dalam satu percakapan. Saat dipanggil, pesan unread otomatis di-mark read di backend.

**Query params**

| Parameter | Tipe | Default | Keterangan |
|----------|------|---------|------------|
| cursor | string | - | Cursor dari response sebelumnya |
| limit | int | 30 | 1–100 |
| direction | string | before | `before` \| `after` (relatif ke cursor) |

**Response 200**

```json
{
  "success": true,
  "data": [ ... ],
  "meta": {
    "nextCursor": "eyJ...",
    "hasMore": true,
    "limit": 30
  }
}
```

Struktur setiap item pesan: **id** (numeric untuk incoming, string `out_123` untuk outgoing), **direction**, **type**, **body**, **timestamp**, **mediaUrl**, **status** (outgoing), **template**, **interactive**, **quotedMessage**, **deletedAt**, dll.

---

## 6. GET /conversations/{id}/messages/search

Cari pesan dalam satu percakapan berdasarkan teks.

**Query params**

| Parameter | Tipe | Keterangan |
|----------|------|------------|
| q | string | **Required.** Kata kunci |
| page | int | 1 |
| per_page | int | 50 |

**Response 200**

```json
{
  "success": true,
  "data": [
    {
      "id": 456,
      "direction": "incoming",
      "type": "text",
      "body": "...",
      "timestamp": "2025-03-16T09:00:00.000000Z",
      "wa_message_id": "..."
    }
  ],
  "meta": { "total": 5, "current_page": 1, "per_page": 50 }
}
```

---

## 7. PUT /conversations/{id}/notes

Simpan catatan percakapan.

**Body (JSON)**

```json
{
  "notes": "Catatan internal maks 2000 karakter"
}
```

**Response 200**

```json
{
  "success": true,
  "message": "Notes saved successfully",
  "data": { "notes": "..." }
}
```

---

## 8. PUT /conversations/{id}/contact

Simpan atau update kontak (nama, email, tags) untuk nomor customer percakapan ini.

**Body (JSON)**

```json
{
  "name": "Nama Kontak",
  "email": "email@example.com",
  "tags": ["tag1", "tag2"]
}
```

- **name**: wajib.
- **email**: opsional.
- **tags**: array string, opsional.

**Response 200**

```json
{
  "success": true,
  "message": "Contact saved successfully",
  "data": { "contact": { ... } }
}
```

---

## 9. POST /conversations/{id}/send

Kirim pesan (teks, media, template, interactive).

**Body (JSON)** – tergantung tipe:

**Teks (dan reply/quoted)**
```json
{
  "type": "text",
  "content": "Isi pesan",
  "context_message_id": "wa_message_id_untuk_reply"
}
```
- **content**: isi pesan (wajib untuk type text).
- **context_message_id**: (opsional) WhatsApp message ID (`wa_message_id`) dari pesan yang ingin di-reply; mengaktifkan tampilan quoted/reply di WhatsApp.

**Gambar / video / dokumen / audio**
```json
{
  "type": "image",
  "media_url": "https://...",
  "caption": "Opsional",
  "context_message_id": "wa_message_id_untuk_reply"
}
```
atau `media_id` (setelah upload); untuk document bisa tambah `filename`. `context_message_id` opsional untuk reply.

**Template**
```json
{
  "type": "template",
  "template_name": "nama_template",
  "template_language": "id",
  "components": [ ... ]
}
```

**Interactive**
```json
{
  "type": "interactive",
  "interactive": { ... }
}
```

**Response 200**

```json
{
  "success": true,
  "message_id": "out_123",
  "wa_message_id": "wamid.xxx",
  "type": "text"
}
```

**Error 422** – service window 24 jam habis:
```json
{
  "error": "service_window_expired",
  "message": "The 24h customer service window has expired. Please use a template message.",
  "service_window": { ... },
  "require_template": true
}
```

**Error 422** – konten diblokir (Judol/keyword):
```json
{
  "error": "content_blocked",
  "message": "Pesan mengandung keyword terlarang: ...",
  "judol_blocked": true,
  "violation_count": 1,
  "violation_limit": 5
}
```

---

## 10. POST /conversations/{id}/send-template

Kirim pesan template (alternatif endpoint). Lihat dokumentasi request body di codebase atau spesifikasi template WhatsApp.

---

## 11. POST /conversations/{id}/mark-read

Tandai semua pesan dalam percakapan sebagai dibaca. (Membuka conversation dan memanggil GET messages juga menandai read di backend.)

**Response 200**

```json
{
  "success": true,
  "message": "Marked as read"
}
```

---

## 12. DELETE /conversations/{id}/messages/{messageId}

Hapus pesan (soft delete). Untuk pesan keluar (outgoing), jika masih dalam 1 jam akan coba revoke di WhatsApp.

- **messageId**: numeric = pesan masuk (WabaInboxMessage); `out_123` = pesan keluar (Message id 123).

**Response 200**

```json
{
  "success": true,
  "revoked_via_wa": true,
  "message": "Message deleted for everyone."
}
```

---

## 13. PUT /conversations/{id}/messages/{messageId}

Edit pesan keluar (hanya teks). messageId harus format `out_123`. Backend mengirim pesan koreksi dengan prefix "✏️ Perbaikan pesan sebelumnya:".

**Body (JSON)**

```json
{
  "message": "Teks yang sudah diperbaiki"
}
```

**Response 200**

```json
{
  "success": true,
  "message_id": "wamid.xxx",
  "new_message": {
    "id": "out_124",
    "body": "✏️ Perbaikan pesan sebelumnya:\nTeks baru",
    "timestamp": "...",
    "direction": "outgoing",
    "type": "text",
    "status": "sent"
  }
}
```

---

## 14. POST /conversations/{id}/messages/{messageId}/retry-media

Masukkan ulang download media pesan **masuk** ke antrian. messageId harus **numeric** (bukan `out_xxx`).

**Response 200**

```json
{
  "success": true,
  "message": "Media queued for download. It will be updated when processing completes."
}
```

**Error 400** – bukan pesan masuk atau tidak ada media:
```json
{
  "success": false,
  "error": "invalid_message_type",
  "message": "Only incoming messages can be retried for media download."
}
```

---

## 15. POST /conversations/{id}/messages/{messageId}/refetch-media

Download/refetch media pesan **masuk** secara synchronous. messageId harus **numeric**.

**Response 200**

```json
{
  "success": true,
  "message": "Media downloaded successfully.",
  "data": {
    "mediaUrl": "https://...",
    "mediaFilename": "file.jpg",
    "mediaSize": 12345,
    "mediaStatus": "completed"
  }
}
```

**Error 400** – gagal refetch:
```json
{
  "success": false,
  "error": "Refetch failed",
  "message": "..."
}
```

---

## 16. POST /conversations/{id}/close

Tutup percakapan (status = closed).

**Response 200**

```json
{
  "success": true,
  "message": "Chat closed successfully"
}
```

---

## 17. POST /conversations/{id}/reopen

Buka kembali percakapan yang sudah ditutup.

**Response 200**

```json
{
  "success": true,
  "message": "Chat reopened successfully"
}
```

---

## 18. POST /media/upload

Upload file untuk dikirim sebagai pesan media (gambar, video, dokumen, audio). Lihat dokumentasi Media Upload untuk format multipart dan limit ukuran.

**Response 200** – biasanya mengembalikan **media_id** atau **url** yang kemudian dipakai di body `POST /conversations/{id}/send`.

---

## Kode Error Umum

| HTTP | error_code / field | Arti |
|------|--------------------|------|
| 401 | - | Token tidak valid / kedaluwarsa |
| 403 | UNAUTHORIZED, ACCESS_DENIED | Tidak punya akses ke resource |
| 404 | CONVERSATION_NOT_FOUND, NOT_FOUND | Percakapan atau pesan tidak ditemukan |
| 400 | invalid_message_type, no_media | Request tidak valid (misalnya retry media untuk outgoing) |
| 422 | service_window_expired, content_blocked | Jendela 24 jam habis atau konten diblokir |

---

*Terakhir diperbarui sesuai penambahan: waba-list, pagination conversations (cursor), counts (read/unreplied), retry-media, refetch-media.*
