Referensi lengkap untuk mengintegrasikan autentikasi OTP WhatsApp, pesan, perangkat, broadcast, dan webhook ke aplikasi Anda.
https://api.loginwa.com
Autentikasi: Bearer <API_KEY>
Harga dalam USD. Base URL adalah https://api.loginwa.com (juga dapat diakses di https://loginwa.com/api). Endpoint berversi berada di bawah /api/v1/…; endpoint produk OTP adalah /api/auth/start & /api/auth/verify.
POST /api/auth/start dengan nomor telepon pengguna.POST /api/auth/verify dengan session_id dan kode OTP.# Send OTP
curl -X POST https://api.loginwa.com/api/auth/start \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"phone":"6281234567890"}'
# Verify OTP
curl -X POST https://api.loginwa.com/api/auth/verify \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"session_id":"SESSION_ID","otp_code":"123456"}'
Authorization: Bearer <API_KEY> .X-Api-Key: <API_KEY> header.application/json.Untuk aplikasi mobile/SPA, panggil LoginWA dari server backend Anda agar API key tetap aman.
// Example header
Authorization: Bearer sk_live_abc123xyz789
// or
X-Api-Key: sk_live_abc123xyz789
Produk OTP WhatsApp. Endpoint ini memerlukan API key yang valid dan langganan aktif (dibatasi 60 permintaan/menit). Path yang disarankan: /api/auth/…. Alias yang kompatibel: /api/v1/auth/… (dipakai SDK; start juga mengembalikan sent_via_engine dan quota_remaining; verify mengembalikan phone bukan phone_number).
Setelah tautan atau rescan WhatsApp baru, ponsel bisa menampilkan “Waiting for this message”. Itu kunci sesi yang belum lengkap, bukan string OTP yang rusak. LoginWA tidak melihat placeholder itu; jika kiriman pertama tidak mendapat ack pengiriman dalam waktu singkat, kode OTP yang sama dikirim otomatis dari perangkat online lain di pool yang sama (pool platform, atau perangkat milik Anda jika ada dua atau lebih). Tombol Resend tetap ada. Dua pesan bisa sampai jika yang pertama baru terbaca belakangan.
Kirim OTP ke nomor telepon via WhatsApp.
| Parameter | Tipe | Wajib | Deskripsi |
|---|---|---|---|
| phone | string | Ya | Nomor telepon dengan kode negara (mis. 6281234567890) |
| country_code | string | Tidak | Kode negara default jika tidak ada di phone |
| otp_length | int | Tidak | Panjang OTP, 4–8 (default: 6) |
| message_template | string | Tidak | Pesan kustom dengan placeholder {code} |
| meta | object | Tidak | Metadata kustom untuk dilacak |
{
"session_id": "3bbaaf0b-3c11-44a2-8a7e-4edc426c5fcd",
"expires_in": 300
}
Kode OTP hanya dikirim via WhatsApp. Tidak pernah dikembalikan di JSON ini.
Verifikasi kode OTP yang dimasukkan pengguna.
| Parameter | Tipe | Wajib | Deskripsi |
|---|---|---|---|
| session_id | string | Ya | Session ID dari /api/auth/start |
| otp_code | string | Ya | Kode OTP yang dimasukkan pengguna |
{
"status": "verified",
"phone_number": "6281234567890"
}
Pemeriksaan gagal mengembalikan 422 dengan status invalid_code, expired, blocked, atau invalid_session.
Kirim pesan WhatsApp kustom di luar OTP. Jika perangkat pertama tidak mendapat ack pengiriman, LoginWA mengirim ulang pesan yang sama dari perangkat online Anda yang lain — bukan dari nomor orang lain.
Kirim pesan WhatsApp teks atau media (gambar, video, dokumen, audio) ke sebuah nomor atau grup. Setiap pengiriman sukses mengurangi satu pesan dari kuota Anda.
| Parameter | Tipe | Wajib | Deskripsi |
|---|---|---|---|
| phone | string | Ya | Nomor penerima dengan kode negara, atau JID grup (…@g.us) |
| type | string | Tidak | text (default), image, video, document, audio |
| message | string | Untuk teks | Isi pesan, 1–4096 karakter (tipe text) |
| media_url | string | Untuk media | URL https publik dari file media (pakai POST /uploads/media dari dashboard, atau hosting sendiri) |
| caption | string | Tidak | Caption di bawah gambar/video/dokumen (maks 1024 karakter) |
| filename | string | Tidak | Nama file tampilan untuk tipe document |
| device_id | string | Tidak | Perangkat tertentu untuk mengirim (dipilih otomatis bila dikosongkan) |
| meta | object | Tidak | Metadata kustom yang dilampirkan ke log pesan |
| reply_to | object | Tidak | Kutip pesan sebelumnya: { id, remote_jid?, from_me? }. Alias: replyTo, quoted. |
curl -X POST https://api.loginwa.com/api/v1/messages/send \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"phone":"6281234567890","message":"Hello from LoginWA!"}'
curl -X POST https://api.loginwa.com/api/v1/messages/send \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"phone":"6281234567890","type":"image","media_url":"https://example.com/promo.jpg","caption":"Promo hari ini!"}'
{
"success": true,
"message_id": "3EB0538DA65B",
"remote_jid": "[email protected]",
"trace_id": "9f1c2e7a-1b3d-4f5a-9c8e-2a1b3c4d5e6f",
"device_id": "dev_abc123",
"phone": "6281234567890",
"quota_remaining": 4987
}
Simpan message_id dan remote_jid untuk menghapus nanti. message_id adalah id protokol WhatsApp (sama dengan kunci idempotensi kirim). Error: 422 validation_failed, 429 quota_exceeded, 502 send_failed, 503 no_device_connected. Lihat Kode Error.
Cabut pesan keluar (hapus untuk semua). Hanya pesan yang dikirim device ini yang bisa dihapus. Tidak memakai kuota. Penerima tetap melihat “This message was deleted”. WhatsApp bisa menolak setelah jendela waktunya (422 too_late). JID grup (…@g.us) diterima di phone.
curl -X POST https://api.loginwa.com/api/v1/messages/delete \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"message_id":"3EB0538DA65B","phone":"6281234567890"}'
{
"success": true,
"status": "revoked",
"message_id": "3EB0538DA65B"
}
Cek satu pesan milik app ini. Status: queued, sent, delivered, read, failed, revoked.
curl -X GET https://api.loginwa.com/api/v1/messages/3EB0538DA65B \
-H "Authorization: Bearer YOUR_API_KEY"
Cek apakah nomor terdaftar di WhatsApp sebelum mengirim (mengurangi pesan gagal dan risiko banned). Tidak memakai kuota.
curl -X POST https://api.loginwa.com/api/v1/numbers/check \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"phones":["6281234567890","6289876543210"]}'
{
"device_id": "dev_abc123",
"results": [
{"phone": "6281234567890", "registered": true, "jid": "[email protected]"},
{"phone": "6289876543210", "registered": false, "jid": null}
]
}
Maksimal 20 nomor per permintaan.
Lihat daftar grup WhatsApp yang diikuti device. Pakai id grup (…@g.us) sebagai field phone di /messages/send untuk mengirim ke grup.
{
"device_id": "dev_abc123",
"groups": [
{"id": "[email protected]", "subject": "Tim Marketing", "participants": 12, "is_announce": false}
]
}
Memanggil POST /api/v1/messages/send dalam empat bahasa. Simpan API key di sisi server.
curl -X POST https://api.loginwa.com/api/v1/messages/send \
-H "Authorization: Bearer $LOGINWA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"phone": "6281234567890",
"message": "Hello from LoginWA!"
}'
$client = new \GuzzleHttp\Client();
$res = $client->post('https://api.loginwa.com/api/v1/messages/send', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('LOGINWA_API_KEY'),
'Content-Type' => 'application/json',
],
'json' => [
'phone' => '6281234567890',
'message' => 'Hello from LoginWA!',
],
]);
$data = json_decode((string) $res->getBody(), true);
echo $data['message_id'];
const res = await fetch(
'https://api.loginwa.com/api/v1/messages/send',
{
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.LOGINWA_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
phone: '6281234567890',
message: 'Hello from LoginWA!',
}),
}
);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
console.log(data.message_id);
import os, requests
res = requests.post(
'https://api.loginwa.com/api/v1/messages/send',
headers={
'Authorization': f"Bearer {os.environ['LOGINWA_API_KEY']}",
'Content-Type': 'application/json',
},
json={
'phone': '6281234567890',
'message': 'Hello from LoginWA!',
},
)
res.raise_for_status()
print(res.json()['message_id'])
Kelola perangkat WhatsApp yang terhubung ke app Anda. ID perangkat memakai engine_device_id (mis. dev_abc123). Pairing bersifat asinkron: membuat atau menyegarkan perangkat mengembalikan 202 dan Anda melakukan polling GET /api/v1/devices/{id} untuk token QR.
Daftar semua perangkat untuk app Anda.
// Response 200
{
"devices": [
{
"id": "dev_abc123",
"label": "My WhatsApp",
"status": "online",
"phone_number": "6281234567890",
"is_online": true,
"last_seen_at": "2026-06-01T10:00:00+00:00",
"created_at": "2026-01-15T10:00:00+00:00"
}
],
"count": 1
}
Daftarkan perangkat baru dan mulai pairing QR.
| Parameter | Tipe | Wajib |
|---|---|---|
| label | string | Tidak |
// Response 202
{
"device_id": "dev_abc123",
"engine_device_id": "dev_abc123",
"label": "My WhatsApp",
"status": "qr_waiting",
"qr_code": "2@A1b2C3...",
"job_id": "job_xyz",
"qr_status_url": "https://api.loginwa.com/api/v1/devices/dev_abc123",
"message": "Scan QR code with WhatsApp to connect device"
}
Ambil status perangkat, termasuk token QR selama status qr_waiting.
Hapus sesi lama, lalu minta QR baru (hanya saat qr_waiting / offline / registering). Mengembalikan 202. Poll GET /api/v1/devices/{id} sampai status online, lalu sembunyikan QR.
Tautkan TANPA scan QR: kirim {"phone":"62812xxxx"} dan Anda mendapat kode pairing 8 karakter. Di HP: WhatsApp → Perangkat Tertaut → Tautkan Perangkat → "Tautkan dengan nomor telepon", lalu masukkan kodenya (kedaluwarsa ±1 menit).
{
"device_id": "dev_abc123",
"pairing_code": "ABCD-1234",
"expires_in": 60
}
Putuskan dan hapus perangkat.
Kirim pesan WhatsApp massal ke banyak penerima dengan jeda cerdas, penjadwalan, dan pelacakan progres. Semua respons dibungkus dalam { "success": true, "data": … }.
Hello {name}!| Status | Deskripsi |
|---|---|
draft | Kampanye dibuat, belum dimulai |
queued | Menunggu diproses |
sending | Sedang mengirim pesan |
paused | Dihentikan sementara |
completed | Semua pesan terkirim |
failed | Kampanye gagal |
Daftar semua kampanye broadcast Anda (paginasi, 20/halaman).
Buat kampanye broadcast baru. Mengembalikan 201; mengembalikan 402 jika jumlah kontak melebihi sisa kuota.
| Parameter | Tipe | Wajib |
|---|---|---|
| name | string | Ya |
| message | string | Ya |
| contacts | array (1–10000) | Ya |
| contacts.*.phone | string | Ya |
| contacts.*.name | string | Tidak |
| contacts.*.variables | object | Tidak |
| media_url | string | Tidak |
| media_type | string | Tidak (image/video/document/audio) |
| delay_seconds | integer | Tidak (1–60, default: 5) |
| schedule_at | datetime | Tidak (harus di masa depan) |
Ambil detail dan progres kampanye.
Mulai kirim kampanye (membutuhkan perangkat online).
Jeda kampanye yang sedang mengirim.
Lanjutkan kampanye yang dijeda.
Daftar kontak dengan status pengiriman. Saring dengan ?status=sent|failed|pending
Hapus kampanye (tidak bisa saat sending, jeda dulu).
// 1. Create campaign
POST /api/v1/broadcast/campaigns
{
"name": "Summer Sale 2026",
"message": "Hello {name}!\n\nSpecial offer for you:\n- 50% discount\n- Free shipping\n\nClick: https://shop.com/promo",
"contacts": [
{"phone": "081234567890", "name": "Budi"},
{"phone": "081234567891", "name": "Ani", "variables": {"city": "Jakarta"}},
{"phone": "081234567892", "name": "Citra"}
],
"delay_seconds": 5,
"media_url": "https://example.com/promo.jpg",
"media_type": "image"
}
// Response
{
"success": true,
"data": {
"id": "camp_abc123xyz",
"name": "Summer Sale 2026",
"status": "draft",
"total_contacts": 3,
"estimated_duration": "15 seconds"
}
}
// 2. Start sending
POST /api/v1/broadcast/campaigns/camp_abc123xyz/send
// 3. Check progress
GET /api/v1/broadcast/campaigns/camp_abc123xyz
{
"success": true,
"data": {
"id": "camp_abc123xyz",
"status": "sending",
"progress": {
"total": 3,
"sent": 2,
"delivered": 1,
"failed": 0,
"pending": 1,
"percentage": 66.7
}
}
}
Konfigurasikan webhook untuk menerima event real-time (pesan masuk, status pengiriman, event perangkat dan OTP). Setiap app dapat mendaftarkan hingga 5 webhook. Signing secret hanya ditampilkan sekali saat pembuatan dan melalui regenerate-secret.
| Event | Deskripsi |
|---|---|
message.incoming | Pesan WhatsApp masuk baru |
message.sent | Pesan berhasil dikirim |
message.delivered | Pesan terkirim ke penerima |
message.read | Pesan dibaca oleh penerima |
message.failed | Pengiriman pesan gagal |
message.deleted | Pesan keluar dicabut (hapus untuk semua) |
device.connected | Perangkat berhasil dipasangkan |
device.disconnected | Perangkat terputus |
device.qr_ready | QR code siap dipindai |
otp.verified | Verifikasi OTP berhasil |
otp.expired | Sesi OTP kedaluwarsa |
Kosongkan events (atau kirim array kosong) untuk berlangganan semua event.
LoginWA mengirim POST JSON ke URL Anda dengan header X-LoginWA-Event dan X-LoginWA-Signature (HMAC-SHA256 dari body mentah menggunakan secret Anda).
{
"event": "message.incoming",
"timestamp": "2026-06-01T12:00:00+00:00",
"data": {
"message_id": "msg_abc123",
"from": "6281234567890",
"body": "Hello!",
"device_id": "dev_xyz"
}
}
Daftar semua webhook (juga mengembalikan available_events).
Buat webhook baru. Mengembalikan 201 dengan secret (ditampilkan sekali).
| Parameter | Tipe | Wajib |
|---|---|---|
| url | string (public https) | Ya |
| events | array | Tidak |
| secret | string | Tidak (dibuat otomatis) |
| retry_count | integer | Tidak (0–10, default: 3) |
Ambil detail webhook dan kesehatan pengiriman.
Perbarui url, events, active, secret, atau retry_count.
Hapus webhook.
Kirim event webhook.test yang ditandatangani ke URL.
Buat signing secret baru.
Setiap pengiriman menyertakan tanda tangan di header X-LoginWA-Signature (HMAC-SHA256 dari body permintaan mentah). Verifikasi untuk memastikan keasliannya:
// PHP/Laravel example
$signature = $request->header('X-LoginWA-Signature');
$payload = $request->getContent();
$secret = 'your_webhook_secret';
$expected = hash_hmac('sha256', $payload, $secret);
if (! hash_equals($expected, (string) $signature)) {
abort(403, 'Invalid signature');
}
Batasi API key ke alamat IP sumber tertentu. Saat pembatasan aktif, permintaan dari IP yang tidak di-whitelist ditolak dengan 403 ip_not_allowed. Endpoint ini dikecualikan dari pemeriksaan IP agar Anda selalu dapat mengelola daftar Anda.
Daftar IP yang di-whitelist dan apakah pembatasan aktif.
Tambahkan IP. Body: ip_address (wajib), label (opsional). Mengembalikan 409 jika sudah ada.
Hapus IP dari whitelist.
Aktif/nonaktifkan pembatasan. Body: enabled (boolean). Mengembalikan 422 jika mengaktifkan tanpa IP.
Balasan otomatis dan alur chatbot dikonfigurasi per app di Dashboard, tidak ada API terpisah untuk mengelolanya. Saat pesan masuk tiba, LoginWA mengevaluasi aturan Anda dan dapat membalas otomatis, lalu tetap mengirim event message.incoming ke webhook Anda agar logika Anda sendiri juga bisa berjalan.
Kelola auto-reply di Dashboard →Harga dalam USD. Paket dan kuota dikelola di Dashboard, bukan via API. Checkout memakai broker pembayaran Satuapps (kartu, QRIS, dan virtual account) sepenuhnya di UI penagihan; tidak ada API penagihan publik. Langganan yang tidak aktif, ditangguhkan, atau menunggak menyebabkan permintaan API mengembalikan 402 subscription_suspended.
Kelola Penagihan →Jalankan npx -y @loginwa/mcp dan biarkan agent coding Anda memasangnya. Termasuk prompt siap tempel.
Lihat Dokumentasi →Mengirim OTP via POST /api/auth/start. Verifikasi dengan POST /api/auth/verify memakai session_id yang dikembalikan.
$client = new \GuzzleHttp\Client();
// Send OTP
$res = $client->post('https://api.loginwa.com/api/auth/start', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('LOGINWA_API_KEY'),
'Content-Type' => 'application/json',
],
'json' => [
'phone' => '6281234567890',
'otp_length' => 6,
],
]);
$data = json_decode((string) $res->getBody(), true);
$sessionId = $data['session_id'];
const BASE_URL = 'https://api.loginwa.com/api';
const API_KEY = process.env.LOGINWA_API_KEY;
async function sendOtp(phone) {
const res = await fetch(`${BASE_URL}/auth/start`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ phone }),
});
if (!res.ok) throw new Error('Failed to send OTP');
return res.json();
}
import requests
import os
BASE_URL = 'https://api.loginwa.com/api'
API_KEY = os.environ.get('LOGINWA_API_KEY')
def send_otp(phone):
res = requests.post(
f'{BASE_URL}/auth/start',
headers={
'Authorization': f'Bearer {API_KEY}',
'Content-Type': 'application/json',
},
json={'phone': phone}
)
res.raise_for_status()
return res.json()
# Send OTP
curl -X POST https://api.loginwa.com/api/auth/start \
-H "Authorization: Bearer $LOGINWA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"phone":"6281234567890"}'
# Verify OTP
curl -X POST https://api.loginwa.com/api/auth/verify \
-H "Authorization: Bearer $LOGINWA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"session_id":"xxx","otp_code":"123456"}'
Mulai gratis, lalu skalakan seiring pertumbuhan. Harga dalam USD, bayar kartu atau QRIS, aktivasi instan. Semua paket mencakup akses API penuh, Widget, SDK, Dashboard, Webhook, dan proyek tanpa batas.
| Jenis Batas | Nilai |
|---|---|
| Batas laju, /api/v1/* | 120 permintaan/menit per API key |
| Batas laju, /api/auth/* | 60 permintaan/menit per API key |
| Panjang OTP | 6 digit (4–8 dapat diatur) |
| OTP TTL | 5 menit (300 detik) |
| Maks. Percobaan Verifikasi | 5 percobaan |
| Kuota Bulanan | Berdasarkan paket Anda (lihat harga) |
Batas per-key dapat dikustomisasi. Melebihi batas laju mengembalikan 429 rate_limited dengan header Retry-After . Melebihi kuota mengembalikan 429 quota_exceeded.
| Kode | HTTP | Deskripsi |
|---|---|---|
unauthorized | 401 | API key tidak valid atau hilang |
subscription_suspended | 402 | Langganan tidak aktif, ditangguhkan, atau menunggak |
ip_not_allowed | 403 | IP sumber tidak ada di whitelist API key |
validation_failed | 422 | Body permintaan gagal validasi (lihat errors) |
invalid_code | 422 | Kode OTP salah |
expired | 422 | Sesi OTP kedaluwarsa |
blocked | 422 | Terlalu banyak percobaan verifikasi untuk sesi OTP ini |
too_late | 422 | WhatsApp tidak lagi mengizinkan pesan ini dihapus |
quota_exceeded | 429 | Kuota pesan bulanan terlampaui |
rate_limited | 429 | Terlalu banyak permintaan per menit |
send_failed | 502 | Engine WhatsApp gagal mengirim pesan |
delete_failed | 502 | Engine WhatsApp gagal menghapus pesan |
no_device_connected | 503 | Tidak ada perangkat WhatsApp online untuk aplikasi ini |
device_not_found | 404 | Sesi perangkat WhatsApp tidak ditemukan di engine |
// 422 validation_failed
{
"error": "validation_failed",
"message": "Invalid request parameters",
"errors": { "phone": ["The phone field is required."] }
}
// 429 quota_exceeded
{ "error": "quota_exceeded", "message": "Monthly message quota exceeded", "quota_remaining": 0 }
// 403 ip_not_allowed
{ "error": "ip_not_allowed", "message": "Your IP address is not whitelisted for this API key.", "ip": "203.0.113.5" }
Endpoint macet? Email di bawah, atau buka tiket di /support.