API Reference

WhatsApp Unofficial Gateway API

REST API untuk mengirim & menerima pesan WhatsApp dari sistem Anda sendiri. Semua endpoint relatif terhadap base URL berikut.

base url
https://wau.jasaonline.net

Butuh API key? Buat di portal setelah berlangganan.

Autentikasi

Setiap permintaan menyertakan header X-API-Key berisi API key penuh Anda (format jo_live_<…>). Bukan bearer token.

curl
curl https://wau.jasaonline.net/v1/ping \
  -H "X-API-Key: jo_live_xxxxxxxx_xxxxxxxx…"

Key tidak valid atau dicabut → 401 unauthorized. Tenant ditangguhkan → 403 tenant_suspended. Key disimpan sebagai hash (argon2id) — simpan baik-baik saat dibuat, tidak dapat ditampilkan ulang oleh gateway (portal menyimpan salinan terenkripsi untuk Anda).

Rate limit & idempotensi

Batas default 60 permintaan/menit per key. Jika terlampaui → 429 rate_limit_exceeded disertai header Retry-After: 60.

Untuk mencegah duplikasi saat retry, sertakan header Idempotency-Key yang unik per operasi. Respons yang diputar ulang membawa Idempotent-Replay: true.

Perangkat & pairing

Sebuah "device" = satu nomor WhatsApp yang tertaut. Siklus statusnya:

status
NEW → PAIRING → ACTIVE → DISCONNECTED | LOGGED_OUT | BANNED
POST /v1/devicesGET /v1/devicesGET /v1/devices/{id}DELETE /v1/devices/{id}POST /v1/devices/{id}/pairGET /v1/devices/{id}/pair/qr.pngGET /v1/devices/{id}/pair/stream

Buat device, mulai pairing, lalu pindai QR (PNG di /pair/qr.png, atau stream SSE di /pair/stream). QR berotasi ~20 detik.

Setelah QR dipindai, status tetap PAIRING sekitar 25 detik(proses upload prekey) sebelum berubah ke ACTIVE. Jangan timeout lebih cepat. Tidak ada endpoint status terpisah — baca status via GET /v1/devices/{id}.

Pakai portal kami jika Anda tidak ingin membangun UI pairing sendiri.

Kirim pesan

POST /v1/messages
curl · text
curl -X POST https://wau.jasaonline.net/v1/messages \
  -H "X-API-Key: jo_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "6281234567890",
    "type": "text",
    "text": "Halo dari gateway"
  }'

Nomor to boleh berupa digit polos (otomatis ditambah @s.whatsapp.net) atau JID grup …@g.us. Sukses → 202 Accepted:

202
{
  "message_id": "…",
  "wa_message_id": "…",
  "status": "queued",
  "device_id": "…"
}

Nomor pengirim & round-robin

Anda tidak mengirim daftar nomor pengirim — tidak ada field from. Pemilihan device diatur oleh satu field opsional device_id:

  • Kosongkan device_id → gateway memilih otomatis. Untuk chat pribadi, percakapan baru disebar (round-robin) ke device aktif yang paling lama tak dipakai, lalu percakapan itu menempel ke device tersebut agar penerima selalu melihat nomor yang sama. Untuk grup, dipilih device yang menjadi anggota grup itu.
  • Isi device_id: "<uuid>" → paksa satu device tertentu (harus berstatus ACTIVE). Ambil id dari GET /v1/devices.

Jadi round-robin lintas semua nomor aktif terjadi dengan sendirinya saat device_id dikosongkan; device_idhanya dipakai bila Anda ingin memilih satu pengirim spesifik. Respons & webhook selalu menyertakan device_id yang benar-benar dipakai.

202 bukan berarti terkirim. Gateway hanya memvalidasi bahwa to dan type terisi. Tipe tak dikenal, atau media tanpa media_id, tetap menerima 202 — kegagalannya muncul asinkron lewat webhook message.status (status failed + status_detail).

Tipe yang didukung: text, image, video, audio, document, sticker, location, contact, reaction, edit, delete. Media memakai media_id (unggah dulu, lihat bagian Media) + caption. Reaction & delete memakai target_id. Lintas-tipe: reply_to, mentions, ttl, dan callback_url per-pesan.

Catatan: tipe buttons saat ini di-render sebagai teks bernomor (WhatsApp membatasi tombol untuk akun tidak resmi), bukan tombol interaktif — jangan mengandalkannya sebagai tombol.

Media

POST /v1/mediaGET /v1/media/{id}

Unggah via multipart/form-data (field file) atau JSON {"url": "…"}. Respons berisi media_id yang dipakai saat mengirim pesan media.

curl · upload
curl -X POST https://wau.jasaonline.net/v1/media \
  -H "X-API-Key: jo_live_…" \
  -F "file=@foto.jpg"
# → { "media_id": "…", "mime_type": "image/jpeg", "size_bytes": 12345 }

Webhook

POST /v1/webhooksGET /v1/webhooksPATCH /v1/webhooks/{id}DELETE /v1/webhooks/{id}POST /v1/webhooks/{id}/rotate-secretGET /v1/webhooks/{id}/deliveries

Daftarkan URL untuk menerima event. secret hanya dikembalikan sekali (saat create / rotate-secret). Event yang tersedia: message.received & message.status.

Verifikasi tanda tangan (WAJIB)

Setiap kiriman membawa header X-JO-Signature, X-JO-Timestamp, dan X-JO-Event-Id. Tanda tangan = HMAC-SHA256 atas timestamp + "." + rawBody:

node · verifikasi
import crypto from "node:crypto";

function verify(req, secret) {
  const ts = req.headers["x-jo-timestamp"];
  const sig = req.headers["x-jo-signature"]; // "sha256=<hex>"
  const expected =
    "sha256=" +
    crypto.createHmac("sha256", secret)
      .update(ts + "." + req.rawBody)   // rawBody = bytes mentah, bukan JSON re-serialize
      .digest("hex");
  return crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
}

Balas 2xx untuk menandai sukses. Jika gagal, gateway mengulang dengan jadwal 0s · 30s · 2m · 10m · 1h · 6h · 24h, lalu berhenti (dead). Lima kegagalan beruntun membuka circuit breaker ~60 detik.

Callback per-pesan (callback_url pada POST /v1/messages) ditandatangani dengan secret callback tenant, berbeda dari secret endpoint di atas. Secret itu hanya bisa dilihat & dirotasi dari portal.

Payload webhook

message.received

json
{
  "event": "message.received",
  "data": {
    "message_id": "…",
    "wa_message_id": "…",
    "device_id": "…",
    "chat_jid": "6281…@s.whatsapp.net",
    "sender_jid": "6281…@s.whatsapp.net",
    "is_group": false,
    "type": "text",
    "body": { "text": "isi pesan" },
    "media": { "media_id": "…", "url": "/v1/media/…" }
  }
}

media.url adalah path relatif — gabungkan dengan base URL untuk mengunduhnya (https://wau.jasaonline.net/v1/media/…). Bentuk body berbeda per tipe (mis. image → caption/mime; location → latitude/longitude).

message.status

json
{
  "event": "message.status",
  "data": {
    "message_id": "…",
    "wa_message_id": "…",
    "to": "6281…@s.whatsapp.net",
    "status": "delivered",           // sent | delivered | read | played | failed | expired
    "status_detail": "…"             // ada saat failed/expired
  }
}

Group, chat, kontak

Tersedia set lengkap untuk grup (/v1/groups/*: join, info, participants, invite-link, leave), chat (/v1/chats/*: mute, archive, read, disappearing, typing, history), dan kontak (/v1/contacts/*: check, block, info, avatar, presence, business). Semua memakai auth & konvensi yang sama.

GET /v1/contacts/check?phones=628xx,629xx mengecek apakah nomor terdaftar di WhatsApp — berguna sebelum mengirim massal.

Kode error

StatusArti
401 unauthorizedAPI key hilang / tidak valid / dicabut
403 tenant_suspendedTenant ditangguhkan
429 rate_limit_exceededBatas laju terlampaui (lihat Retry-After)
400 bad_requestBody / parameter tidak valid
404 device_not_foundDevice tidak ada / bukan milik Anda
409 device_not_activeDevice belum ACTIVE
503 no_eligible_deviceTidak ada device aktif di bawah batas harian

Siap mulai? Buat API key atau lihat harga.