Dokumentasi API
Referensi lengkap REST API WA Client 1 — gateway WhatsApp multi-nomor dari PT Surya Digital Nusantara. Satu akun bisa mengelola beberapa nomor WhatsApp sekaligus, masing-masing dengan API key, riwayat chat, dan webhook sendiri yang terisolasi penuh dari nomor/akun lain.
Gambaran Umum
Base URL untuk seluruh endpoint di bawah ini:
https://pesan-client1.suryacloud.my.id
Setiap request dan response menggunakan JSON (content-type: application/json), kecuali endpoint upload media yang menerima multipart/form-data.
Mulai Cepat
- Login dengan akun Google — akun dibuat otomatis saat pertama kali login.
- Di halaman Nomor Saya, klik + Tambah Nomor dan beri nama.
- Salin API key yang ditampilkan — hanya muncul sekali saat itu juga.
- Buka dashboard nomor tersebut, scan QR dari WhatsApp di HP (Perangkat Tertaut → Tautkan Perangkat).
- Panggil endpoint di bawah menggunakan API key nomor tersebut.
Autentikasi
Endpoint pengiriman pesan (/api/send/*, /api/groups, /media/*) diautentikasi dengan API key per-nomor, dikirim lewat header x-api-key (atau parameter query ?key= jika header tidak memungkinkan, mis. embed <img>).
x-api-key: API_KEY_NOMOR_ANDA
Login dashboard (halaman web) menggunakan akun Google, terpisah sepenuhnya dari API key — API key tidak pernah digunakan untuk login ke dashboard, dan sebaliknya.
Mengirim pesan teks ke nomor personal atau grup.
| Field | Tipe | Keterangan |
|---|---|---|
to | string | Nomor tujuan (mis. 628123456789) atau JID grup (...@g.us). |
message | string | Isi pesan teks. |
curl -X POST https://pesan-client1.suryacloud.my.id/api/send/text \ -H "x-api-key: API_KEY_ANDA" \ -H "content-type: application/json" \ -d '{"to":"628123456789","message":"Halo dari WA Client 1"}'
const res = await fetch('https://pesan-client1.suryacloud.my.id/api/send/text', { method: 'POST', headers: { 'x-api-key': 'API_KEY_ANDA', 'content-type': 'application/json' }, body: JSON.stringify({ to: '628123456789', message: 'Halo dari WA Client 1' }), });
import requests requests.post( 'https://pesan-client1.suryacloud.my.id/api/send/text', headers={'x-api-key': 'API_KEY_ANDA'}, json={'to': '628123456789', 'message': 'Halo dari WA Client 1'}, )
Response 200
{ "success": true, "messageId": "3EB0..." }Mengirim gambar, video, dokumen, atau audio. Terima file langsung (multipart/form-data) atau URL media yang sudah publik.
| Field | Tipe | Keterangan |
|---|---|---|
to | string | Nomor atau JID grup tujuan. |
file | file | Upload langsung (opsional jika mediaUrl diisi). |
mediaUrl | string | URL http(s) ke file media (opsional jika file diisi). |
type | string | image / video / document / audio — wajib jika pakai mediaUrl, opsional (auto-deteksi dari mimetype) jika upload file. |
caption | string | Opsional, teks penyerta. |
fileName | string | Opsional, nama file untuk dokumen. |
curl -X POST https://pesan-client1.suryacloud.my.id/api/send/media \ -H "x-api-key: API_KEY_ANDA" \ -F "to=628123456789" \ -F "file=@/path/ke/gambar.jpg" \ -F "caption=Contoh gambar"
const form = new FormData(); form.append('to', '628123456789'); form.append('mediaUrl', 'https://contoh.com/gambar.jpg'); form.append('type', 'image'); await fetch('https://pesan-client1.suryacloud.my.id/api/send/media', { method: 'POST', headers: { 'x-api-key': 'API_KEY_ANDA' }, body: form, });
Daftar grup WhatsApp yang diikuti nomor ini.
{ "groups": [ { "id": "1234-5678@g.us", "subject": "Tim Support", "participants": 12 } ] }Webhook
Dikelola dari halaman Webhook di dashboard nomor Anda (bukan lewat API key). Satu nomor bisa punya lebih dari satu tujuan webhook sekaligus — misalnya satu ke Google Apps Script untuk otomasi, satu lagi ke server OTP login — setiap event dikirim ke semua endpoint yang aktif secara paralel dan independen. Tiap endpoint punya URL dan secret HMAC-nya sendiri, jadi merotasi atau menghapus satu integrasi tidak memengaruhi yang lain.
Event yang Dikirim
| Event | Kapan Terjadi |
|---|---|
message.incoming | Ada pesan masuk (personal atau grup). |
message.status | Status pesan keluar berubah (sent / delivered / read / failed). |
identity.merged | Kontak yang sebelumnya dikenali lewat @lid berhasil diketahui nomor HP aslinya (lihat penjelasan LID di bawah) — pakai ini untuk menggabungkan kontak/thread lama Anda. |
device.connected | Nomor berhasil terhubung ke WhatsApp. |
device.disconnected | Koneksi terputus (cek field needsQrScan). |
Setiap payload otomatis disisipi numberId dan phoneNumber milik nomor WA Client1 Anda sendiri (bukan lawan bicara) — jadi satu URL webhook bisa dipakai untuk beberapa nomor sekaligus dan tetap bisa dibedakan asalnya. Untuk identitas pengirim/lawan bicara, selalu pakai chatId/from, bukan phoneNumber di level atas ini.
{
"event": "message.incoming",
"chatId": "628123456789@s.whatsapp.net",
"from": "628123456789@s.whatsapp.net",
"isGroup": false,
"isFromMe": false,
"type": "text",
"text": "Halo, ada info?",
"numberId": "b3f1...",
"phoneNumber": "6281234567890"
}chatId/from
pada message.incoming bisa berisi identitas WhatsApp internal berformat
<angka-acak>@lid alih-alih <nomor-hp>@s.whatsapp.net — biasanya
untuk kontak yang belum pernah mengirim lewat nomor HP aslinya dan belum tersimpan di
address book nomor WA Client1 Anda. Ini bukan bug pengiriman: WhatsApp memang tidak selalu
membocorkan nomor HP asli kontak seperti itu di level protokol, bahkan aplikasi WhatsApp
resmi si pengirim pun menampilkannya sebagai kontak terpisah sampai nomornya "ketemu".
WA Client1 otomatis meresolusi
@lid → nomor HP asli begitu nomornya
diketahui (lewat sinkronisasi kontak, atau kontak tsb membagikan nomornya), lalu memakai
nomor asli itu untuk seluruh pesan berikutnya. Kalau resolusi ini terjadi setelah Anda
sudah menerima satu/lebih message.incoming dengan chatId ber-@lid,
Anda akan menerima event identity.merged berisi oldChatId (yang lama, @lid)
dan newChatId (nomor HP asli) — pakai ini untuk menggabungkan kontak/thread yang
sempat tercatat terpisah di sisi Anda. Jangan asumsikan chatId/from selalu
berformat @s.whatsapp.net, dan jangan pakai field phoneNumber di level atas
sebagai pengganti — field itu identitas nomor WA Client1 Anda sendiri, bukan lawan bicara.
Contoh payload message.status — perhatikan nama field-nya chatId, bukan to:
{
"event": "message.status",
"messageId": "3EB0...",
"chatId": "628123456789@s.whatsapp.net",
"status": "delivered",
"timestamp": 1786450000,
"numberId": "b3f1...",
"phoneNumber": "6281234567890"
}Contoh payload identity.merged:
{
"event": "identity.merged",
"oldChatId": "44294803030187@lid",
"newChatId": "628123456789@s.whatsapp.net",
"numberId": "b3f1...",
"phoneNumber": "6281234567890"
}Verifikasi Signature
Setiap request webhook menyertakan header x-webhook-signature berisi HMAC-SHA256 dari body mentah, ditandatangani dengan secret milik endpoint tujuan itu sendiri (beda dari API key, dan beda per endpoint kalau Anda punya lebih dari satu — lihat halaman Webhook di dashboard).
const crypto = require('crypto'); function isValid(rawBody, header, secret) { const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(rawBody).digest('hex'); return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(header)); }
import hmac, hashlib def is_valid(raw_body: bytes, header: str, secret: str) -> bool: expected = 'sha256=' + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest() return hmac.compare_digest(expected, header)
numberId/phoneNumber di body tanpa verifikasi terlebih dahulu.Rate Limit
Endpoint /api/send/* dibatasi 20 pesan per menit per nomor (bisa berbeda per deployment) untuk mengurangi risiko deteksi otomatisasi oleh WhatsApp. Response melebihi batas mengembalikan status 429 dengan header retry-after.
Kode Error
| Status | Arti |
|---|---|
400 | Parameter request tidak valid atau kurang. |
401 | API key tidak ada / tidak valid, atau sesi login tidak valid. |
404 | Resource tidak ditemukan (termasuk mengakses nomor yang bukan milik Anda). |
409 | Konflik — mis. batas maksimum jumlah nomor per akun tercapai. |
429 | Melebihi rate limit. |
500 | Kesalahan tak terduga di server. |
{ "error": "pesan penjelasan singkat" }