Endpoint messaging mengirim pesan keluar ke WhatsApp. Semua operasi memerlukan instance yang sudah CONNECTED.
http://localhost:5002/api/api/send-messagecurl --request POST \
--url http://localhost:5002/api/api/send-messagePOST /api/send-message — Kirim teks
Handler: MessageController.sendText (src/controller/MessageController.ts:11)
Request fields
| Field | Wajib | Tipe | Catatan |
|---|---|---|---|
instance | ✅ | string | UUID sesi — harus CONNECTED |
number | ✅ | string | Nomor tujuan (628xxx) atau grup (<id>-<id>@g.us) |
message | ✅ | string | Isi pesan |
pesan_id | ✅ | string | ID pesan internal — diteruskan ke Socket.IO, bukan untuk kirim |
role | ✅ | string | Role pengirim (cs, user, dll.) — diteruskan ke Socket.IO |
data | ✅ | string|object | Data tambahan (JSON string atau object) — diteruskan ke Socket.IO |
quoted_raw_message | ❌ | string|object | Raw message untuk fitur reply (JSON string atau object) |
Contoh request
{
"instance": "0c1a968c-...",
"number": "628123456789",
"message": "Halo!",
"pesan_id": "uuid-pesan",
"role": "cs",
"data": "{}",
"quoted_raw_message": "{...}"
}Response data
{
"success": true,
"message": "Sukses kirim pesan",
"data": {
"id": "3EB0...",
"status": 1,
"raw_message": "{\"key\":{...},\"message\":{...},\"messageTimestamp\":170000}",
"number": "628123456789",
"message_content": "Halo!"
}
}raw_message adalah bentuk minimal pesan terkirim — simpan nilai ini bila ingin memakainya sebagai quoted_raw_message saat membalas.
Anti-ban presence
Sebelum kirim, engine mensimulasikan presence:
presenceSubscribe(formattedNumber)sendPresenceUpdate('composing', formattedNumber)— typing indicator- Jeda 1 detik
sendPresenceUpdate('paused', formattedNumber)— stop typingsock.sendMessage(...)
onWhatsApp validation
Untuk nomor personal (bukan grup), engine validasi nomor terdaftar di WhatsApp:
formatNumber(number) → jika bukan @g.us:
sock.onWhatsApp(formattedNumber)
jika !exists → throw "Nomor tujuan belum terdaftar Whatsapp"quoted_raw_message — reply
Jika quoted_raw_message ada, di-parse (JSON string → object) dan dipakai sebagai options.quoted di sock.sendMessage. Timestamp Long object dinormalisasi.
Socket emit
Setelah kirim, engine emit sendmessage + sendmessage_bubble ke room instance:
{
"id": "628123456789",
"type": "sendmessage",
"number": 0,
"instance": "0c1a968c-...",
"text": "uuid-pesan",
"status": "sent",
"role": "cs",
"data": { ... },
"timestamp": "2024-01-15T10:30:00.000Z"
}POST /api/send-media — Kirim media / link preview
Handler: MessageController.sendMedia (src/controller/MessageController.ts:98)
Request fields
| Field | Wajib | Tipe | Catatan |
|---|---|---|---|
instance | ✅ | string | UUID sesi |
number | ✅ | string | Nomor tujuan |
type | ✅ | string | image / video / document / audio / link |
url | ✅ | string | URL media (atau URL untuk link preview) |
caption | ❌ | string | Caption (untuk image/video/document) |
ptt | ❌ | boolean | true = voice note (khusus audio) |
filename | ❌ | string | Nama file (khusus document, default file.pdf) |
pesan_id | ❌ | string | ID pesan internal |
role | ❌ | string | Role pengirim |
data | ❌ | string|object | Data tambahan |
quoted_raw_message | ❌ | string|object | Raw message untuk reply |
Contoh request
{
"instance": "0c1a968c-...",
"number": "628123456789",
"type": "image",
"url": "https://example.com/photo.jpg",
"caption": "Lihat foto ini",
"pesan_id": "msg-1",
"role": "cs",
"data": "{}"
}Perilaku per tipe
type | Catatan |
|---|---|
image / video | Dikirim dengan caption |
document | Mimetype dipaksa application/pdf, nama default file.pdf |
audio | Mimetype audio/mp4; ptt: true → voice note (presence jadi recording) |
link | url di-fetch metadatanya via link-preview-js, dikirim sebagai teks + preview |
Validasi URL
Untuk image/video/document/audio, URL divalidasi lebih dulu via isMediaUrlValid:
const response = await fetch(url, { method: 'HEAD', signal: AbortSignal.timeout(5000) });
return response.ok; // 200-299Jika tidak dapat diakses, request gagal tanpa mengirim presence:
"Media URL tidak dapat diakses: <url>"Response data
{
"success": true,
"message": "Sukses kirim pesan",
"data": {
"id": "3EB0...",
"result": true,
"status": true,
"type": "image",
"raw_message": "{...}",
"url": "https://example.com/photo.jpg"
}
}POST /api/send-location — Kirim lokasi
Handler: MessageController.sendLocation (src/controller/MessageController.ts:201)
Request fields
| Field | Wajib | Tipe | Catatan |
|---|---|---|---|
instance | ✅ | string | UUID sesi |
number | ✅ | string | Nomor tujuan |
latitude | ✅ | string|number | Latitude |
longitude | ✅ | string|number | Longitude |
quoted_raw_message | ❌ | string|object | Diparsing tapi diabaikan service |
Contoh request
{
"instance": "0c1a968c-...",
"number": "628123456789",
"latitude": "-6.200000",
"longitude": "106.816666"
}Response
{
"success": true,
"message": "Location sent",
"data": { ...result.key }
}POST /api/send-broadcast — Kirim ke banyak nomor
Handler: MessageController.sendBroadcast (src/controller/MessageController.ts:268)
Request fields
| Field | Wajib | Tipe | Catatan |
|---|---|---|---|
instance | ✅ | string | UUID sesi |
number | ✅ | string|string[] | Array nomor atau string dipisah koma |
message | ✅ | string | Isi pesan |
Contoh request
{
"instance": "0c1a968c-...",
"number": ["628111", "628222"],
"message": "Promo hari ini!"
}Response (1 nomor — objek tunggal)
{
"success": true,
"message": "Broadcast selesai diproses",
"data": {
"result": true,
"id": "3EB0...",
"number": "628111",
"message": "Berhasil Terkirim"
}
}Response (>1 nomor — rekap)
{
"success": true,
"message": "Broadcast selesai diproses",
"data": {
"result": true,
"count": 2,
"success": 1,
"failed": 1,
"message": "Broadcast selesai diproses",
"details": [
{ "result": true, "id": "3EB0...", "number": "628111", "message": "Berhasil Terkirim" },
{ "result": false, "id": "", "number": "628222", "message": "Nomor tujuan belum terdaftar Whatsapp" }
]
}
}Anti-ban delay
Untuk broadcast >1 nomor, ada jeda acak 3-5 detik antar pengiriman:
const delayTime = Math.floor(Math.random() * 2000) + 3000; // 3000-5000msBroadcast memanggil sendTextMessage per nomor — yang berarti setiap nomor juga mendapat presence simulation (composing → paused).
Catatan
sendContactsudah diimplementasi diMessageController.ts:249tapi tidak didaftarkan di router — tidak dapat diaksesformatNumber()menormalkan input otomatis — lihat Conventions- Grup (deteksi: mengandung
-atau panjang >15 digit) tidak divalidasionWhatsApp - Presence simulation bisa gagal — ditangkap dengan try/catch, tidak menghentikan pengiriman
Langkah berikutnya
- Kelola grup? Baca Group API.
- Detail alur pesan? Baca Message Flow.
- Kembali ke API Reference.