Kedua API Webchat ini bukan bagian dari /api/mobile/v1 dan tidak memakai Bearer token, jadi tidak muncul di tester "Test Endpoint" di bawah — dokumentasi di section ini murni referensi (contoh cURL/JS manual). Public Widget API tidak butuh autentikasi sama sekali (dibatasi lewat token widget + rate limit); Agent Inbox API butuh sesi login browser (cookie), bukan token yang bisa dipakai dari luar browser/Postman tanpa sesi tersebut.
1. Public Widget API (tanpa autentikasi)
Base Path
/api/webchat/{token}
Token
Token publik widget (lihat Manage Webchat Widgets)
Auth
Tidak ada — dibatasi honeypot field + rate limit
Rate Limit (umum)
30/menit per IP, 60/menit per widget token
Rate Limit (otp/request)
5/menit per IP, 3/10 menit per alamat email
| Method |
Path |
Auth Tambahan |
Deskripsi |
| POST |
/check-history |
— |
Cek apakah email/phone yang diketik sudah punya percakapan sebelumnya. Dipakai untuk menentukan apakah step OTP perlu ditampilkan (visitor baru = tidak perlu OTP sama sekali). |
| POST |
/otp/request |
— |
Kirim kode OTP 6 digit ke email lewat email sender default milik client (fallback ke email account pertama jika tidak ada default). Kode berlaku 10 menit. |
| POST |
/otp/verify |
— |
Verifikasi kode OTP. Maks 5x percobaan salah sebelum kode harus diminta ulang. Status verified tersimpan 30 menit dan sekali pakai (langsung terpakai habis saat /start berhasil). |
| POST |
/start |
— |
Mulai atau lanjutkan sesi chat. Wajib sudah OTP-verified hanya jika email/phone ini sudah punya history webchat sebelumnya; visitor baru langsung lolos. Mengembalikan visitor_token yang disimpan visitor (localStorage) untuk semua request berikutnya. |
| GET |
/messages |
Header X-Visitor-Token |
Ambil seluruh pesan (lintas semua conversation, satu thread berkelanjutan) beserta data visitor (name/email/phone) untuk sesi yang sedang berjalan. |
| POST |
/messages |
Header X-Visitor-Token |
Kirim pesan teks dari visitor ke agent. Body: content (wajib, maks 4096 karakter). |
| POST |
/upload |
Header X-Visitor-Token |
Upload lampiran dari visitor. Multipart/form-data, field file (wajib, maks 20MB) — gambar/video/audio/dokumen otomatis dideteksi dari mime type. |
Contoh: cek history → kirim OTP → verifikasi → mulai chat
TOKEN=nscs6iwivknjno4dsvouekhx3yyh32m9
# 1. Cek apakah email ini sudah punya history (menentukan perlu OTP atau tidak)
curl -X POST "https://apps.rndsolusi.com/api/webchat/$TOKEN/check-history" \
-H "Content-Type: application/json" \
--data '{"email":"budi@example.com","phone":"081234567890"}'
# -> {"success":true,"has_history":false}
# 2. Jika has_history=true, minta kode OTP dulu
curl -X POST "https://apps.rndsolusi.com/api/webchat/$TOKEN/otp/request" \
-H "Content-Type: application/json" \
--data '{"email":"budi@example.com","website":""}'
# -> {"success":true}
# 3. Verifikasi kode yang diterima lewat email
curl -X POST "https://apps.rndsolusi.com/api/webchat/$TOKEN/otp/verify" \
-H "Content-Type: application/json" \
--data '{"email":"budi@example.com","code":"123456"}'
# -> {"success":true}
# 4. Mulai sesi chat (name/email/phone; website = honeypot, kosongkan)
curl -X POST "https://apps.rndsolusi.com/api/webchat/$TOKEN/start" \
-H "Content-Type: application/json" \
--data '{"name":"Budi Santoso","email":"budi@example.com","phone":"081234567890","website":""}'
# -> {"success":true,"visitor_token":"...","conversation_id":123,"visitor_name":"Budi Santoso"}
# 5. Simpan visitor_token, lalu pakai untuk polling/kirim pesan
VISITOR_TOKEN="...dari langkah 4..."
curl "https://apps.rndsolusi.com/api/webchat/$TOKEN/messages" \
-H "X-Visitor-Token: $VISITOR_TOKEN"
curl -X POST "https://apps.rndsolusi.com/api/webchat/$TOKEN/messages" \
-H "Content-Type: application/json" \
-H "X-Visitor-Token: $VISITOR_TOKEN" \
--data '{"content":"Halo, saya butuh bantuan"}'
2. Agent Inbox API (Webchat Beta — sesi login browser)
Endpoint ini dipanggil oleh SPA Webchat Beta (/webchat-beta) memakai sesi cookie hasil login web biasa (role admin/manager/agent), bukan Bearer token — tidak bisa dites langsung dari cURL/Postman tanpa membawa cookie sesi tersebut. Semua endpoint menerima query client_id (hanya efektif untuk role admin memilih client lain; manager/agent selalu terikat ke client mereka sendiri).
Base Path
/webchat-beta/api
Auth
Sesi web (cookie) — middleware role:admin,manager,agent
| Method |
Path |
Deskripsi |
| GET |
/contacts |
List + filter kontak webchat. Query: search, filter_status (open/closed/blacklisted), team_id, assigned_to, assigned_me, unassigned_only, tag_filter (Custom Inbox), sort, page. Response menyertakan open_count, unassigned_count, blacklisted_count, team_open_counts. |
| GET |
/contacts/{contact} |
Detail kontak: pesan halaman terbaru, timeline_items (pesan diselingi marker "Conversation opened/closed"), has_more_history, oldest_id. |
| GET |
/contacts/{contact}/history |
Pagination pesan lama (infinite scroll ke atas). Query: before_id, limit (default 30, maks 100). |
| PUT |
/contacts/{contact} |
Update panel Linked Contact. Body: name (wajib), email, address, notes, tags (array — tag protected seperti webchat-inbox otomatis dipertahankan). |
| POST |
/contacts/{contact}/send |
Kirim balasan agent. Body: content dan/atau file (multipart, maks 20MB) — minimal salah satu wajib diisi. Jika conversation aktif sudah closed, otomatis membuat conversation baru ter-assign ke agent yang membalas (history lama tetap sebagai riwayat). |
| POST |
/contacts/{contact}/assign |
Assign ke user (user_id — team ikut otomatis dari team user tsb) atau langsung ke team (team_id), atau kosongkan keduanya untuk unassign. |
| POST |
/contacts/{contact}/close |
Tutup percakapan. Body: category_id (wajib jika client punya master kategori), sub_category_id, summary (opsional). |
Contoh (dari dalam sesi browser yang sudah login)
// Kirim balasan teks ke contact tertentu
fetch('/webchat-beta/api/contacts/123/send', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-Requested-With': 'XMLHttpRequest' },
credentials: 'same-origin',
body: JSON.stringify({ client_id: 3, content: 'Halo, ada yang bisa dibantu?' })
});
// Tutup percakapan dengan kategori + ringkasan
fetch('/webchat-beta/api/contacts/123/close', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-Requested-With': 'XMLHttpRequest' },
credentials: 'same-origin',
body: JSON.stringify({ client_id: 3, category_id: 1, sub_category_id: 2, summary: 'Sudah selesai dibantu' })
});
3. Realtime Events (Reverb / WebSocket)
| Channel |
Tipe |
Event |
Payload Ringkas |
| webchat.chat.{contactId} |
Private (auth via /broadcasting/auth) |
.NewMessage |
{ message: {...} } — dipakai Webchat Beta (agent) |
| webchat.widget.{visitorToken} |
Public (tanpa auth) |
NewMessage |
{ message: {...} } — dipakai halaman widget visitor, di-key pakai visitor_token yang unguessable, bukan contact ID |
Contoh Listen — Agent (Laravel Echo + Reverb)
window.Echo.private(`webchat.chat.${contactId}`)
.listen('.NewMessage', (payload) => console.log('NewMessage', payload.message));
Contoh Listen — Visitor Widget (pusher-js langsung, tanpa Echo/auth)
const pusher = new Pusher(REVERB_APP_KEY, {
wsHost: REVERB_HOST, wsPort: REVERB_PORT, wssPort: REVERB_PORT,
forceTLS: true, enabledTransports: ['ws', 'wss'], disableStats: true,
});
pusher.subscribe(`webchat.widget.${visitorToken}`)
.bind('NewMessage', (data) => console.log('NewMessage', data.message));