WaAPI
WaAPI
Dokumentasi Integrasi API
Beranda Login Portal

WaAPI — Dokumentasi REST API

WaAPI mengirim pesan WhatsApp otomatis dari website atau aplikasi Anda. Model sederhana: 1 device = 1 token = 1 nomor WhatsApp. Semua response JSON memakai field status (boolean) dan detail (pesan) atau reason (jika gagal).

Base URL

https://wa-api.id/api/v1

Ringkasan fitur

FiturStatusAkses
Kirim pesan APIPOST /send
QR connectPortal /app
WABA (Cloud API)Portal /app → Hubungkan WABA
Inbox & kontakLive Portal /app
Auto reply / sapaanLive per tenant
Webhook pesan masukEvolution → /webhook/evolution
OTP APIPOST /otp/send · POST /otp/verify
BroadcastPOST /broadcast · Portal /app
Kirim gambar / fileFase 2

Quick Start

Kirim pesan pertama dalam 3 langkah:

  1. Login ke portal https://wa-api.id/app → menu Device → hubungkan WhatsApp (scan QR).
  2. Klik tombol Token pada device → salin token (format waapi_dev_...).
  3. Panggil API dengan header Authorization: Bearer <token>:
curl -X POST https://wa-api.id/api/v1/send \
  -H "Authorization: Bearer waapi_dev_TOKEN_ANDA" \
  -H "Content-Type: application/json" \
  -d '{"to":"6281234567890","message":"Halo dari WaAPI!"}'

Response sukses: HTTP 202 dengan field id (UUID pesan). Cek status via GET /messages/{id}.

Penting: Token harus diawali waapi_dev_. Jika token Anda terlihat seperti eyJpdiI6..., buka ulang modal Token di portal — sistem akan memperbarui ke format yang benar.

Autentikasi

Setiap request wajib menyertakan token di header. WaAPI mendukung dua jenis token — pilih sesuai kebutuhan integrasi Anda.

Jenis token

Token Format resmi Kapan dipakai Endpoint
Device token waapi_dev_ + 40 karakter acak
Contoh: waapi_dev_a8Kx9mP2...
Kirim dari satu nomor WA tertentu. Paling umum dipakai. Semua endpoint. Wajib untuk GET /device dan POST /device/disconnect.
Tenant API key waapi_ + 40 karakter acak
Contoh: waapi_zR3kL7nQ...
Kirim dari device default tenant. Cocok untuk backend multi-service. /send, /otp/*, /broadcast, /messages

Device token → portal Device → klik Token (sama persis dengan yang ditampilkan di modal).
Tenant API key → portal API Keys (ditampilkan sekali saat dibuat).

Cara kirim token (wajib)

Gunakan header Authorization dengan prefix Bearer. Ini format standar yang sama dengan instruksi di portal Device → Token.

Authorization: Bearer waapi_dev_TOKEN_ANDA
Content-Type: application/json

Alternatif (jarang dipakai)

Metode Contoh
Header X-Api-Key X-Api-Key: waapi_dev_xxxxx
Body / form token=waapi_dev_xxxxx

Untuk integrasi baru, gunakan selalu Authorization: Bearer. Content-Type: application/json direkomendasikan.

POST /api/v1/send

Kirim pesan teks ke satu nomor. Pesan masuk antrian dan dikirim di background. Untuk device WABA, bisa kirim template Meta dengan template_name.

Parameter

FieldWajibKeterangan
target / toYaNomor tujuan: 08xxx atau 628xxx
messageOpsional*Isi pesan teks (maks. 4096 karakter)
template_nameOpsional*Nama template Meta (device WABA)
template_languageOpsionalBahasa template, default id
variablesOpsionalArray variabel template, mis. ["Andi","12345"]
countryCodeOpsionalDefault 62

*Wajib isi message atau template_name (salah satu).

Contoh request

{
  "target": "6281234567890",
  "message": "Pesanan #12345 dikonfirmasi."
}

Response sukses

HTTP 202 Accepted — pesan masuk antrian, belum tentu sudah terkirim.

{
  "status": "queued",
  "detail": "Message queued",
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "to": "6281234567890",
  "message": "Pesanan #12345 dikonfirmasi.",
  "device": "abc1",
  "queued_at": "2026-06-13T08:00:00+00:00"
}

Simpan id (UUID) untuk cek status via GET /messages/{uuid}.

Response gagal

HTTP 400 / 422 / 429 — lihat field reason.

POST /api/v1/otp/send

Kirim kode OTP ke nomor WhatsApp. Kode di-generate server-side dan dikirim sebagai pesan WA. Simpan uuid dari response untuk verifikasi.

Alur OTP

1. Send Backend panggil POST /otp/send
2. User terima WA Pelanggan baca kode di WhatsApp
3. Verify Backend panggil POST /otp/verify dengan kode dari user
4. Webhook (opsional) WaAPI POST ke webhook_url saat verified/failed

Parameter

FieldWajibKeterangan
phone / toYaNomor tujuan: 08xxx atau 628xxx
countryCodeOpsionalDefault 62
lengthOpsionalPanjang kode angka (4–8, default 6)
expires_inOpsionalMasa berlaku detik (60–600, default 300 = 5 menit)
purposeOpsionalLabel, mis. login, reset-password
messageOpsionalTemplate pesan WA. Placeholder: {code}, {minutes}, {purpose}
webhook_urlOpsionalURL HTTPS yang menerima callback saat OTP verified atau failed

Contoh request

{
  "phone": "6281234567890",
  "purpose": "login",
  "length": 6,
  "expires_in": 300,
  "webhook_url": "https://app-anda.com/webhook/otp"
}

Response sukses

HTTP 202 Accepted

{
  "detail": "OTP sent",
  "uuid": "550e8400-e29b-41d4-a716-446655440000",
  "phone": "6281234567890",
  "expires_at": "2026-06-13T08:15:00+00:00",
  "status": "pending"
}
POST /api/v1/otp/verify

Verifikasi kode OTP yang dikirim user. Maksimal 3 percobaan per UUID (default).

Parameter

FieldWajibKeterangan
uuidYaUUID dari response /otp/send
codeYaKode angka yang diinput user (4–8 digit)

Contoh request

{
  "uuid": "550e8400-e29b-41d4-a716-446655440000",
  "code": "482910"
}

Response sukses

HTTP 200 OK

{
  "detail": "OTP verified",
  "uuid": "550e8400-e29b-41d4-a716-446655440000",
  "phone": "6281234567890",
  "status": "verified",
  "verified_at": "2026-06-13T08:12:30+00:00"
}

Response gagal

HTTP 422 Unprocessable Entity

{
  "status": false,
  "reason": "Kode OTP tidak valid."
}

Webhook callback (webhook_url)

Jika webhook_url diisi saat send, WaAPI akan POST JSON ke URL tersebut saat OTP verified atau failed (percobaan habis).

{
  "event": "otp.verified",
  "uuid": "550e8400-e29b-41d4-a716-446655440000",
  "phone": "6281234567890",
  "purpose": "login",
  "status": "verified",
  "verified_at": "2026-06-13T08:12:30+00:00",
  "timestamp": "2026-06-13T08:12:30+00:00"
}

Event otp.failed dikirim jika percobaan verifikasi melebihi batas.

POST /api/v1/broadcast

Kirim pesan yang sama ke banyak nomor sekaligus. Proses berjalan di background (antrian). Gunakan GET /broadcast/{uuid} untuk cek progress.

Parameter

FieldWajibKeterangan
messageYaIsi pesan broadcast (maks. 4096 karakter)
nameOpsionalNama kampanye (default: auto-generate)
phonesOpsional*Array nomor, contoh: ["628111","628222"]
contact_idsOpsional*Array ID kontak dari inbox (portal → Kontak)
all_contactsOpsional*true = kirim ke semua kontak tenant
scheduled_atOpsionalJadwal kirim ISO 8601, mis. 2026-06-14T09:00:00+07:00

*Wajib isi minimal satu: phones, contact_ids, atau all_contacts: true.

Contoh request

{
  "name": "Promo Ramadan",
  "message": "Halo! Diskon 20% berlaku hari ini saja.",
  "phones": ["6281234567890", "6289876543210"]
}

Response sukses

HTTP 202 Accepted

{
  "detail": "Broadcast queued",
  "uuid": "660e8400-e29b-41d4-a716-446655440001",
  "name": "Promo Ramadan",
  "status": "running",
  "total_recipients": 2,
  "scheduled_at": null
}
GET /api/v1/broadcast/{uuid}

Cek progress kampanye broadcast. Poll endpoint ini sampai status = completed.

{
  "detail": "ok",
  "uuid": "660e8400-e29b-41d4-a716-446655440001",
  "name": "Promo Ramadan",
  "status": "completed",
  "total_recipients": 100,
  "sent_count": 98,
  "failed_count": 2,
  "scheduled_at": null,
  "started_at": "2026-06-13T08:00:00+00:00",
  "completed_at": "2026-06-13T08:05:30+00:00"
}
GET /api/v1/device

Cek status koneksi device WhatsApp. Wajib pakai device token (waapi_dev_...).

{
  "status": true,
  "detail": "device profile",
  "device": {
    "code": "abc1",
    "name": "Toko Utama",
    "phone": "6281234567890",
    "status": "connected",
    "status_label": "Terhubung",
    "connected": true,
    "connected_at": "2026-06-10T12:00:00+00:00"
  }
}
POST /api/v1/device/disconnect

Putuskan sesi WhatsApp device. Wajib pakai device token (waapi_dev_...).

GET /api/v1/messages/{uuid}

Cek status pengiriman pesan. UUID didapat dari response POST /send field id.

queued sent delivered read atau failed
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "delivered",
  "to": "6281234567890",
  "message": "Tagihan WiFi jatuh tempo...",
  "sent_at": "2026-06-13T08:00:05+00:00",
  "delivered_at": "2026-06-13T08:00:12+00:00"
}

HTTP Status Code

Semua response memakai JSON. Field status: true = sukses, status: false = gagal.

CodeEndpointArti
200GET, verifyRequest berhasil (data langsung siap)
202POST send, otp/send, broadcastDiterima & masuk antrian background
400SemuaRequest invalid / device token required
401SemuaToken tidak ada atau salah
422POSTValidasi gagal (nomor invalid, OTP salah, dll.)
429POSTRate limit / kuota habis / anti-spam

Webhook

1. OTP callback (dari API Anda)

Set field webhook_url saat POST /otp/send. WaAPI akan POST ke URL tersebut. Lihat format di section OTP di atas.

2. Pesan masuk WhatsApp (Evolution API)

Agar inbox & auto reply berfungsi, arahkan webhook Evolution API ke:

POST https://wa-api.id/webhook/evolution

Event yang perlu diaktifkan di Evolution:

  • messages.upsert — pesan masuk (inbox, kontak, auto reply)
  • messages.update — status terkirim / dibaca
  • connection.update — status koneksi device
Webhook Evolution dikonfigurasi di server Evolution (bukan via API WaAPI). Hubungi admin platform jika perlu bantuan setup.

Contoh Integrasi

curl -X POST https://wa-api.id/api/v1/send \
  -H "Authorization: Bearer waapi_dev_TOKEN_ANDA" \
  -H "Content-Type: application/json" \
  -d '{"target":"6281234567890","message":"Pesanan #12345 dikonfirmasi."}'
$ch = curl_init('https://wa-api.id/api/v1/send');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer waapi_dev_TOKEN_ANDA',
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'target' => '6281234567890',
        'message' => 'Pesanan #12345 dikonfirmasi.',
    ]),
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
const res = await fetch('https://wa-api.id/api/v1/send', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer waapi_dev_TOKEN_ANDA',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    target: '6281234567890',
    message: 'Pesanan #12345 dikonfirmasi.',
  }),
});
const data = await res.json();
use Illuminate\Support\Facades\Http;

$response = Http::withHeaders([
    'Authorization' => 'Bearer waapi_dev_TOKEN_ANDA',
])->post('https://wa-api.id/api/v1/send', [
    'target' => '6281234567890',
    'message' => 'Pesanan #12345 dikonfirmasi.',
]);
# 1. Kirim OTP
curl -X POST https://wa-api.id/api/v1/otp/send \
  -H "Authorization: Bearer waapi_dev_TOKEN_ANDA" \
  -H "Content-Type: application/json" \
  -d '{"phone":"6281234567890","purpose":"login","webhook_url":"https://app-anda.com/webhook/otp"}'

# 2. Verifikasi (ganti UUID & code)
curl -X POST https://wa-api.id/api/v1/otp/verify \
  -H "Authorization: Bearer waapi_dev_TOKEN_ANDA" \
  -H "Content-Type: application/json" \
  -d '{"uuid":"550e8400-e29b-41d4-a716-446655440000","code":"482910"}'
// Kirim OTP
$send = json_decode(file_get_contents('https://wa-api.id/api/v1/otp/send', false, stream_context_create([
    'http' => [
        'method' => 'POST',
        'header' => "Authorization: Bearer waapi_dev_TOKEN_ANDA\r\nContent-Type: application/json",
        'content' => json_encode(['phone' => '6281234567890', 'purpose' => 'login']),
    ],
])), true);

$uuid = $send['uuid'];
const headers = {
  'Authorization': 'Bearer waapi_dev_TOKEN_ANDA',
  'Content-Type': 'application/json',
};

const sendRes = await fetch('https://wa-api.id/api/v1/otp/send', {
  method: 'POST', headers,
  body: JSON.stringify({ phone: '6281234567890', purpose: 'login' }),
});
const { uuid } = await sendRes.json();
use Illuminate\Support\Facades\Http;

$headers = ['Authorization' => 'Bearer waapi_dev_TOKEN_ANDA'];

$send = Http::withHeaders($headers)->post('https://wa-api.id/api/v1/otp/send', [
    'phone' => '6281234567890',
    'purpose' => 'login',
])->json();
curl -X POST https://wa-api.id/api/v1/broadcast \
  -H "Authorization: Bearer waapi_dev_TOKEN_ANDA" \
  -H "Content-Type: application/json" \
  -d '{"name":"Promo","message":"Diskon 20% hari ini!","phones":["628111","628222"]}'

curl https://wa-api.id/api/v1/broadcast/660e8400-e29b-41d4-a716-446655440001 \
  -H "Authorization: Bearer waapi_dev_TOKEN_ANDA"
$ctx = stream_context_create(['http' => [
    'method' => 'POST',
    'header' => "Authorization: Bearer waapi_dev_TOKEN_ANDA\r\nContent-Type: application/json",
    'content' => json_encode([
        'message' => 'Diskon 20% hari ini!',
        'phones' => ['6281234567890', '6289876543210'],
    ]),
]]);
$result = json_decode(file_get_contents('https://wa-api.id/api/v1/broadcast', false, $ctx), true);
const headers = {
  'Authorization': 'Bearer waapi_dev_TOKEN_ANDA',
  'Content-Type': 'application/json',
};

const create = await fetch('https://wa-api.id/api/v1/broadcast', {
  method: 'POST', headers,
  body: JSON.stringify({
    message: 'Diskon 20% hari ini!',
    phones: ['6281234567890', '6289876543210'],
  }),
});
const { uuid } = await create.json();
use Illuminate\Support\Facades\Http;

$headers = ['Authorization' => 'Bearer waapi_dev_TOKEN_ANDA'];

$campaign = Http::withHeaders($headers)->post('https://wa-api.id/api/v1/broadcast', [
    'name' => 'Promo Ramadan',
    'message' => 'Diskon 20% hari ini!',
    'phones' => ['6281234567890'],
])->json();

Kode Error (reason)

Field detail kadang berisi pesan manusia (Bahasa Indonesia). Field reason untuk logic di kode Anda.

reason HTTP Arti
token required401Header Authorization tidak dikirim, atau tanpa prefix Bearer
token invalid401Token salah, kedaluwarsa, atau format tidak valid (bukan waapi_dev_... / waapi_...)
target invalid422Format nomor tidak valid
input invalid422Parameter broadcast/OTP tidak lengkap
device disconnected400WhatsApp belum terhubung (scan QR dulu)
insufficient quota429Kuota pesan habis
rate limit exceeded429Terlalu banyak request per menit
duplicate message429Pesan duplikat ke nomor sama (anti-spam)
recipient cooldown429Nomor baru saja dikirimi pesan
recipient limit exceeded429Terlalu banyak penerima unik per jam
subscription expired429Paket / trial habis
device limit exceeded429Batas nomor WA paket tercapai
device rate limit exceeded429Device mengirim terlalu cepat
account suspended429Akun nonaktif atau ditangguhkan
trial expired429Masa trial habis
spam content detected429Konten terdeteksi spam (link/karakter berulang)
otp cooldown429OTP baru saja dikirim ke nomor ini
otp limit exceeded429Batas OTP per jam (per nomor / per akun)
broadcast limit exceeded429Terlalu banyak penerima atau kampanye broadcast per hari
autoreply limit exceeded429Batas auto reply per jam tercapai
autoreply cooldown429Auto reply ke kontak ini baru saja dikirim

Troubleshooting autentikasi

GejalaPenyebab umumSolusi
token required (401) Header Authorization kosong atau tanpa kata Bearer Pakai: Authorization: Bearer <token_dari_portal>
token required (401) Token hanya di body JSON, tanpa header Pindahkan token ke header Authorization: Bearer ...
token invalid (401) Token terpotong saat copy-paste atau pakai token lama Buka portal → Device → Token → salin ulang token lengkap
token invalid (401) Token tampil eyJpdiI6... (format lama/rusak) Buka ulang modal Token di portal — token akan diperbarui otomatis ke waapi_dev_...
token invalid (401) Pakai device token untuk tenant API key (atau sebaliknya) Cek prefix: waapi_dev_ = device, waapi_ = tenant key

Perlindungan Anti-Spam (otomatis)

WaAPI menerapkan batas kecepatan dan konten untuk mengurangi risiko nomor WhatsApp terkena ban:

  • Kirim pesan API — rate limit per menit, cooldown antar nomor, cegah pesan duplikat, batas penerima unik per jam (sesuai paket).
  • OTP — cooldown antar kirim, maks. 5 OTP/jam per nomor, maks. 100 OTP/jam per akun (default).
  • Broadcast — jeda ~3 detik antar penerima, maks. 500 penerima/kampanye, maks. 10 kampanye/hari (default).
  • Auto reply — maks. 60 balasan/jam per device, cooldown 30 menit per kontak (default).
  • Konten — panjang pesan dibatasi, terlalu banyak link atau karakter berulang ditolak.

Error OTP (HTTP 422, pesan di reason)

  • Kode OTP tidak valid.
  • Kode OTP sudah kedaluwarsa.
  • Permintaan OTP gagal. — percobaan verifikasi melebihi batas