W WA Gateway CRM ← Beranda

Dokumentasi API

Halaman ini menjelaskan cara memakai endpoint api.php dan kirim.php untuk mengirim & menerima pesan WhatsApp lewat hosting ini. Endpoint ini dipakai oleh local app (yang benar-benar terhubung ke WhatsApp) dan bisa juga dipanggil dari sistem eksternal untuk menitip pesan keluar.

1. Autentikasi

Semua endpoint kecuali webhook wajib mengirim header:

X-API-Secret: <isi API_SECRET dari .env>

Kalau header tidak ada → 401. Kalau isinya salah → 403. Nilai ini harus sama persis dengan API_SECRET di .env (lihat halaman Admin untuk cek beberapa karakter pertama/terakhirnya).

Endpoint webhook memakai token yang berbeda (WEBHOOK_TOKEN), dikirim lewat parameter token di query string atau body — supaya kalau token ini bocor, penyerang cuma bisa menitip pesan ke outbox, tidak bisa mengakses endpoint lain.

POST kirim.php (disarankan untuk aplikasi luar)

File terpisah dari api.php, khusus untuk dipanggil dari aplikasi/sistem pihak ketiga yang ingin mengirim pesan WhatsApp. Parameternya hampir sama persis dengan form Tes Kirim Pesan di dashboard — bedanya, di sini Device ID tidak perlu dikirim manual.

Token sekarang per-device. Setiap kali perusahaan mendaftarkan Device ID baru di dashboard, sistem otomatis membuat token unik untuk device tersebut (kolom company_devices.token), yang ditampilkan sekali di halaman dashboard dan bisa dibuat ulang kapan saja. Token inilah yang dipakai untuk autentikasi di sini — device_id tujuan otomatis ditentukan dari token, tidak perlu dikirim terpisah. WEBHOOK_TOKEN di .env tidak dipakai lagi di sini (masih dipakai khusus untuk action=webhook versi lama).

Catatan: karena ini pesan keluar, nomor tujuan tidak otomatis dicatat sebagai kontak — pencatatan kontak hanya terjadi untuk chat masuk (lihat Pencatatan kontak).

FieldWajibKeterangan
tokenYaToken unik device, didapat dari dashboard perusahaan saat device didaftarkan (atau setelah "Buat Ulang Token"). Menentukan device_id tujuan secara otomatis
to_numberYaNomor tujuan (boleh format 08xx, otomatis dinormalisasi ke 62xx)
message_textSalah satu wajib*Isi pesan / caption media
media_fileTidakUpload file langsung (multipart/form-data) — gambar/video/dokumen, maks 20MB. Kalau diisi, media_url/media_mimetype/media_filename di bawah diabaikan
media_urlSalah satu wajib*Alternatif kalau file sudah ada di URL publik (tidak upload langsung)
media_mimetypeTidakMimetype media, mis. image/jpeg (dipakai bersama media_url)
media_filenameTidakNama file media (dipakai bersama media_url)

*Minimal salah satu dari message_text, media_file, atau media_url harus diisi.

Contoh 1 — kirim pesan teks saja:

curl -s -X POST "https://crm.kiosq.id/kirim.php" \
  -d "token=TOKEN_DEVICE_ANDA" \
  -d "to_number=081234567890" \
  -d "message_text=Halo dari aplikasi luar"

Contoh 2 — upload file media langsung (multipart):

curl -s -X POST "https://crm.kiosq.id/kirim.php" \
  -F "token=TOKEN_DEVICE_ANDA" \
  -F "to_number=081234567890" \
  -F "message_text=Cek lampiran ya" \
  -F "media_file=@/path/ke/file.jpg"

Contoh 3 — pakai media yang sudah ada di URL publik:

curl -s -X POST "https://crm.kiosq.id/kirim.php" \
  -d "token=TOKEN_DEVICE_ANDA" \
  -d "to_number=081234567890" \
  -d "media_url=https://domain-anda.com/file.pdf" \
  -d "media_mimetype=application/pdf" \
  -d "media_filename=invoice.pdf"

Respons sukses — pesan langsung masuk ke tabel wa_messages dengan status pending, menunggu local app mengambil & mengirimnya:

{ "ok": true, "id": 103, "status": "pending" }

Respons gagal (parameter kurang / token salah / dll), selalu dalam format JSON dengan kode HTTP sesuai (lihat tabel error):

{ "error": "to_number, dan message_text/media_file/media_url wajib diisi" }

Kalau token tidak dikenali:

{ "error": "invalid token" }

GET action=pending

Dipakai local app untuk mengambil (dan langsung meng-klaim) pesan outbox yang berstatus pending untuk satu device.

ParameterWajibKeterangan
session / deviceYaDevice ID (mis. A1B2C3)
limitTidakJumlah maksimal pesan diambil sekali panggil (default 10, maks 50)
curl -s "https://crm.kiosq.id/api.php?action=pending&session=A1B2C3&limit=10" \
  -H "X-API-Secret: SECRET_ANDA"

Respons (pesan langsung berubah status jadi processing begitu diambil, supaya tidak diambil dobel):

{
  "items": [
    {
      "id": 101,
      "to_number": "6281234567890",
      "message_text": "Halo dari hosting",
      "media_url": null,
      "media_mimetype": null,
      "media_filename": null
    }
  ]
}

POST action=report

Dipakai local app untuk melaporkan hasil pengiriman satu pesan yang sudah diambil lewat pending.

ParameterWajibKeterangan
session / deviceYa (query string)Device ID

Body JSON:

FieldWajibKeterangan
idYaID pesan (dari respons pending)
statusYasent atau failed
wa_message_idTidakID pesan dari WhatsApp (untuk pelacakan status lanjutan)
chat_jidTidakJID chat WhatsApp tujuan
errorTidakPesan error kalau status=failed
curl -s -X POST "https://crm.kiosq.id/api.php?action=report&session=A1B2C3" \
  -H "X-API-Secret: SECRET_ANDA" \
  -H "Content-Type: application/json" \
  -d '{"id":101,"status":"sent","wa_message_id":"3EB0...","chat_jid":"6281234567890@s.whatsapp.net"}'

POST action=push

Dipakai local app untuk mengirim pesan masuk (chat baru dari lawan bicara) atau update status pengiriman (terkirim/sampai/dibaca) ke hosting.

Field bodyWajibKeterangan
message_idYaID unik pesan dari WhatsApp
chat_jidYaJID chat
directionTidakin (default) atau out
from_meTidaktrue/false — menentukan status awal (sent/received)
wa_statusTidakAngka 0-5 (lihat tabel status WA di bawah)
sender_number, recipient_number, push_nameTidakInfo pengirim. Kalau direction=in, sender_number + push_name otomatis dicatat sebagai kontak baru (lihat Pencatatan kontak)
message_type, message_textTidakJenis & isi pesan
is_groupTidakBoolean (true/false) -- tandai apakah chat ini dari grup WhatsApp. Kalau tidak dikirim, otomatis dideteksi dari akhiran chat_jid (@g.us = grup, @s.whatsapp.net = pribadi) -- kirim eksplisit kalau local app sudah tahu pasti
media_base64, media_mimetype, media_filenameTidakMedia di-encode base64 untuk disimpan di database
media_truncatedTidaktrue kalau media dipotong karena kebesaran
wa_timestampTidakUnix timestamp pesan dari WhatsApp
curl -s -X POST "https://crm.kiosq.id/api.php?action=push&session=A1B2C3" \
  -H "X-API-Secret: SECRET_ANDA" \
  -H "Content-Type: application/json" \
  -d '{
    "message_id": "3EB0...",
    "chat_jid": "6281234567890@s.whatsapp.net",
    "direction": "in",
    "sender_number": "6281234567890",
    "push_name": "Budi",
    "message_type": "text",
    "message_text": "Halo, ini balasan dari pelanggan",
    "is_group": false
  }'

Tabel kode wa_status:

NilaiArti
0Error
1Pending
2Terkirim ke server WhatsApp
3Sampai di HP tujuan
4Sudah dibaca
5Voice note diputar
Pencatatan kontak otomatis. Nomor dicatat sebagai kontak perusahaan (tabel contacts, dipakai untuk fitur Blast Pesan) hanya dari chat masuk — yaitu hit action=push dengan direction=in. Nomor tujuan dari pesan keluar (kirim.php, action=webhook, atau action=push dengan direction=out) tidak dicatat sebagai kontak. Nama kontak diisi dari push_name, dan sekali tersimpan tidak akan ditimpa.

GET POST action=webhook (versi lama, masih didukung)

Fungsinya sama dengan kirim.php di atas (masuk ke outbox lewat token WEBHOOK_TOKEN), tapi lewat api.php dan tidak mendukung upload file langsung (hanya media_url). Untuk integrasi baru, disarankan pakai kirim.php. Sama seperti kirim.php, ini pesan keluar sehingga nomor tujuan tidak dicatat sebagai kontak.

Endpoint untuk pihak eksternal (Zapier, sistem lain) menitip pesan ke outbox tanpa perlu header custom — cukup token di query string atau body, supaya kompatibel dengan tool yang tidak bisa kirim header sendiri.

FieldWajibKeterangan
tokenYaHarus sama dengan WEBHOOK_TOKEN di .env
device_idYaDevice ID tujuan
to_number / toYaNomor tujuan (boleh format 08xx, otomatis dinormalisasi ke 62xx)
message_text / message / textSalah satu wajib*Isi pesan
media_urlSalah satu wajib*URL media publik yang mau dikirim
media_mimetype, media_filenameTidakMetadata media

*Minimal salah satu dari message_text atau media_url harus diisi.

curl -s -X POST "https://crm.kiosq.id/api.php?action=webhook" \
  -H "Content-Type: application/json" \
  -d '{
    "token": "WEBHOOK_TOKEN_ANDA",
    "device_id": "A1B2C3",
    "to_number": "081234567890",
    "message_text": "Halo dari sistem eksternal"
  }'

Atau lewat GET (query string), cocok untuk tool yang cuma bisa hit URL:

https://crm.kiosq.id/api.php?action=webhook&token=WEBHOOK_TOKEN_ANDA&device_id=A1B2C3&to_number=081234567890&message=Halo

Respons sukses:

{ "ok": true, "id": 102 }

GET action=media

Mengambil isi file media (gambar/video/dokumen) yang tersimpan di database, mis. untuk ditampilkan di aplikasi lain.

ParameterWajibKeterangan
session / deviceYaDevice ID
idYaID baris pesan (kolom id)
curl -s "https://crm.kiosq.id/api.php?action=media&session=A1B2C3&id=101" \
  -H "X-API-Secret: SECRET_ANDA" \
  -o file-hasil.jpg

Respons berupa file binari langsung (bukan JSON), dengan header Content-Type & Content-Disposition sesuai media aslinya.

POST action=log

Dipakai local app untuk mengirim log status/aktivitas device ke hosting — mis. koneksi WhatsApp putus, berhasil reconnect, error saat kirim, QR code discan ulang, dsb. Log ini terpisah dari riwayat pesan (wa_messages) dan bisa dilihat perusahaan lewat halaman Log Device di dashboard mereka.

ParameterWajibKeterangan
session / deviceYa (query string)Device ID

Body JSON:

FieldWajibKeterangan
messageYaIsi log (maks 5000 karakter, dipotong otomatis kalau lebih)
levelTidakdebug / info (default) / warn / error. Nilai lain otomatis jadi info
contextTidakObject bebas untuk detail tambahan (disimpan sebagai JSON), mis. {"attempt":2,"reason":"timeout"}
curl -s -X POST "https://crm.kiosq.id/api.php?action=log&session=A1B2C3" \
  -H "X-API-Secret: SECRET_ANDA" \
  -H "Content-Type: application/json" \
  -d '{
    "level": "warn",
    "message": "Koneksi WhatsApp terputus, mencoba reconnect",
    "context": { "attempt": 2, "reason": "timeout" }
  }'

Respons sukses:

{ "ok": true }
Log otomatis dibersihkan: baris yang lebih tua dari 30 hari per device akan terhapus otomatis secara berkala setiap ada request masuk (tidak perlu cron job terpisah). Jangan kirim log terlalu sering/verbose (mis. tiap detik) — kirim hanya untuk event yang berarti (connect/disconnect/error), bukan setiap polling.

9. Kode error umum

KodeArti
400Parameter/body wajib tidak lengkap atau tidak valid
401Header X-API-Secret tidak dikirim
403Secret / token salah
404Action tidak dikenal, atau media tidak ditemukan
500Error internal — cek logs/app.log lewat halaman Admin

10. Alur kirim pesan end-to-end

  1. Pesan keluar dibuat: lewat form Tes Kirim Pesan di dashboard perusahaan, atau lewat kirim.php/action=webhook dari sistem eksternal → masuk ke tabel wa_messages dengan status=pending.
  2. Local app polling action=pending secara berkala → pesan otomatis berubah jadi status=processing begitu diambil.
  3. Local app benar-benar mengirim pesan lewat WhatsApp, lalu melapor lewat action=report → status akhir jadi sent atau failed.
  4. Kalau ada balasan dari pelanggan, atau update status seperti "sudah dibaca", local app kirim lewat action=push.
  5. Media yang diterima bisa diambil lagi lewat action=media.
Nomor telepon otomatis dinormalisasi: awalan 0 diganti jadi 62 (mis. 0812345678906281234567890). Tidak perlu format ini di sisi pengirim.
Jangan sebarkan nilai API_SECRET / WEBHOOK_TOKEN di tempat publik (repo, chat, dsb). Kalau bocor, ganti segera lewat .env.

11. Bikin script local app (curl + prompt AI)

"Local app" adalah proses yang jalan terus di komputer/server Anda sendiri, terhubung langsung ke WhatsApp (lewat library semacam whatsapp-web.js atau Baileys di Node.js), yang secara berkala polling & push data ke hosting ini lewat api.php. Dua cara untuk membuatnya:

A. Coba manual dulu pakai curl

Sebelum menulis kode, pastikan alurnya jalan dengan curl langsung dari terminal:

# 1. Cek ada pesan pending untuk device
curl -s "https://crm.kiosq.id/api.php?action=pending&session=A1B2C3&limit=10" \
  -H "X-API-Secret: SECRET_ANDA"

# 2. Lapor pesan #101 sudah terkirim
curl -s -X POST "https://crm.kiosq.id/api.php?action=report&session=A1B2C3" \
  -H "X-API-Secret: SECRET_ANDA" -H "Content-Type: application/json" \
  -d '{"id":101,"status":"sent","chat_jid":"6281234567890@s.whatsapp.net"}'

# 3. Kirim balasan pelanggan yang masuk
curl -s -X POST "https://crm.kiosq.id/api.php?action=push&session=A1B2C3" \
  -H "X-API-Secret: SECRET_ANDA" -H "Content-Type: application/json" \
  -d '{"message_id":"3EB0...","chat_jid":"6281234567890@s.whatsapp.net","direction":"in","message_text":"Halo"}'

# 4. Lapor device baru saja reconnect
curl -s -X POST "https://crm.kiosq.id/api.php?action=log&session=A1B2C3" \
  -H "X-API-Secret: SECRET_ANDA" -H "Content-Type: application/json" \
  -d '{"level":"info","message":"Device reconnect berhasil"}'

B. Generate script-nya pakai AI

Salin prompt di bawah ini ke Claude/ChatGPT (atau AI assistant lain) untuk membuatkan script Node.js lengkap — sudah mencakup semua endpoint (pending, report, push, log). URL API sudah otomatis terisi sesuai domain hosting ini — tinggal ganti <ISI_DEVICE_ID_SAYA> dan <ISI_API_SECRET_SAYA> sebelum dikirim.

Prompt siap-salin
Buatkan saya script (bahasa: Node.js) yang berjalan terus-menerus (long-running
process / daemon) di komputer lokal saya untuk menjembatani WhatsApp (via
library whatsapp-web.js atau Baileys) dengan REST API hosting berikut:

Base URL API: https://crm.kiosq.id/api.php
Device ID: <ISI_DEVICE_ID_SAYA>          # 4-6 karakter, dari dashboard perusahaan
API Secret: <ISI_API_SECRET_SAYA>        # header X-API-Secret, dari admin hosting

Script harus melakukan hal-hal berikut dalam loop:

1. POLLING OUTBOX (tiap 3-5 detik):
   GET https://crm.kiosq.id/api.php?action=pending&session=<ISI_DEVICE_ID_SAYA>&limit=10
   Header: X-API-Secret: <ISI_API_SECRET_SAYA>
   -> Ambil array "items". Untuk tiap item, kirim pesan (message_text, dan
      kalau ada media_url/media_mimetype/media_filename kirim juga medianya)
      lewat WhatsApp ke to_number.

2. LAPOR HASIL KIRIM (setelah tiap pesan selesai dikirim/gagal):
   POST https://crm.kiosq.id/api.php?action=report&session=<ISI_DEVICE_ID_SAYA>
   Header: X-API-Secret: <ISI_API_SECRET_SAYA>, Content-Type: application/json
   Body: { "id": <id item dari langkah 1>, "status": "sent" atau "failed",
           "wa_message_id": <id pesan dari WhatsApp jika ada>,
           "chat_jid": <JID chat tujuan>,
           "error": <pesan error kalau status=failed, null kalau sukses> }

3. TERUSKAN PESAN MASUK (event message dari WhatsApp) & UPDATE STATUS
   (event ack/read):
   POST https://crm.kiosq.id/api.php?action=push&session=<ISI_DEVICE_ID_SAYA>
   Header: X-API-Secret: <ISI_API_SECRET_SAYA>, Content-Type: application/json
   Body: { "message_id", "chat_jid", "direction": "in", "from_me": false,
           "sender_number", "push_name", "message_type", "message_text",
           "wa_status" (0-5, kalau ini update status pesan keluar),
           "media_base64"/"media_mimetype"/"media_filename" kalau ada media }

4. KIRIM LOG STATUS DEVICE (saat event penting: connected, disconnected,
   reconnecting, error, qr_generated -- JANGAN kirim log tiap detik/polling):
   POST https://crm.kiosq.id/api.php?action=log&session=<ISI_DEVICE_ID_SAYA>
   Header: X-API-Secret: <ISI_API_SECRET_SAYA>, Content-Type: application/json
   Body: { "level": "info"|"warn"|"error"|"debug", "message": "<deskripsi
           singkat event>", "context": { ...detail tambahan opsional... } }

Ketentuan tambahan:
- Semua request pakai header X-API-Secret di atas, KECUALI kalau nanti saya
  sebutkan endpoint lain yang pakai token per-device (jangan diasumsikan).
- Tangani error jaringan dengan retry + backoff sederhana, jangan sampai
  proses polling berhenti total kalau satu request gagal.
- Nomor telepon dari WhatsApp (format 62xxxxxxxxxx@s.whatsapp.net) perlu
  diubah ke format polos (62xxxxxxxxxx) sebelum dikirim sebagai
  sender_number/recipient_number.
- Simpan konfigurasi (base URL, device ID, secret) di file .env terpisah,
  jangan hardcode di source code.
- Tambahkan graceful shutdown (SIGINT/SIGTERM) yang menutup koneksi
  WhatsApp dengan bersih.

Tolong buatkan strukturnya modular (terpisah: client WhatsApp, client API
hosting, loop polling) supaya mudah saya modifikasi.
Setelah script jadi, jalankan dulu di lingkungan development dan pantau lewat halaman Log Device di dashboard — kirim log level info saat script berhasil connect, supaya Anda tahu integrasinya jalan tanpa perlu cek terminal terus-menerus.