Dokumentasi API SmartWA

Panduan bagi sistem/aplikasi lain yang ingin mengirim dan menerima pesan WhatsApp lewat gateway SmartWA — satu API, banyak nomor, dilindungi kebijakan anti-blokir otomatis.

Ringkasan

SmartWA adalah gateway WhatsApp berbasis Baileys yang bisa dipakai bersama oleh banyak aplikasi (multi-tenant) dan banyak nomor WhatsApp sekaligus. Aplikasi Anda cukup memanggil satu REST API untuk mengirim pesan — SmartWA yang menangani pemilihan nomor, jam kirim, kuota harian, dan seluruh kebijakan anti-blokir di baliknya.

Penting: permintaan kirim pesan yang berhasil (202) berarti pesan masuk antrian, bukan langsung terkirim. Sistem kami (disebut Guard) memutuskan kapan pesan benar-benar dikirim berdasarkan jam operasional, kuota, dan riwayat hubungan dengan nomor tujuan — supaya nomor WhatsApp tidak diblokir. Pantau status pengiriman lewat endpoint status atau webhook.

Base URL

https://smartwa.indosmartmedia.com/api/v1

Cara mendapatkan akses

Hubungi administrator SmartWA untuk didaftarkan sebagai aplikasi baru. Admin akan membuatkan API key dan meng-assign nomor WhatsApp yang boleh dipakai aplikasi Anda lewat dashboard. API key hanya ditampilkan sekali saat dibuat — simpan dengan aman, tidak bisa dilihat ulang setelahnya (hanya bisa diputar/diganti).

Autentikasi

Sertakan API key sebagai Bearer token di header Authorization pada setiap permintaan:

Authorization: Bearer sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Alternatif (setara): header X-API-Key: sk_xxx....

Permintaan tanpa key atau dengan key yang tidak valid/nonaktif akan mendapat 401.

Endpoint

Semua endpoint mengembalikan JSON dengan field ok (boolean) yang menandakan berhasil/tidaknya permintaan.

POST /api/v1/messages

Mengirim pesan teks atau media ke satu nomor. Pesan masuk antrian dan dievaluasi oleh Guard sebelum benar-benar dikirim.

Body (JSON)

FieldTipeKeterangan
phonewajibstring Nomor tujuan. Format bebas (08123..., +62812..., 62812...) — dinormalisasi otomatis.
textopsional*string Isi pesan, maks 4096 karakter. Mendukung spintax {a|b|c} — lihat Kategori & Kebijakan. *Wajib diisi kalau media.url kosong.
mediaopsionalobject { url, type, caption }type salah satu dari image, video, audio, document. url harus alamat publik (bukan IP privat/internal).
categoryopsionalstring transactional, cs, atau marketing. Default cs. Menentukan nomor mana yang dipakai dan aturan mana yang berlaku.
sessionopsionalstring Kode nomor spesifik (lihat Daftar Nomor). Kosongkan untuk membiarkan SmartWA memilih nomor tersehat secara otomatis sesuai category.
idempotency_keyopsionalstring Kunci unik dari sisi Anda. Permintaan berulang dengan kunci sama tidak menghasilkan pesan ganda — aman dipakai untuk retry.

Contoh permintaan

curl -X POST https://smartwa.indosmartmedia.com/api/v1/messages \
  -H "Authorization: Bearer sk_xxx..." \
  -H "Content-Type: application/json" \
  -d '{
    "phone": "081234567890",
    "text": "{Halo|Hai} Pak Budi, invoice INV-882 sudah terbit.",
    "category": "transactional",
    "idempotency_key": "inv-882-notif"
  }'

Respons

202 Masuk antrian atau dijadwalkan
{
  "ok": true,
  "session": "cs-utama",
  "id": 1042,
  "status": "queued",  // atau "scheduled"
  "reason": null,        // alasan jika "scheduled", mis. di luar jam kirim
  "lane": "warm",       // "reply-window" | "warm" | "cold" | null
  "scheduled_at": "2026-09-13 08:30:12"
}
200 Idempotency key sudah pernah dipakai (bukan pesan baru)
{ "ok": true, "duplicate": true, "id": 1042, "status": "sent", "reject_reason": null }
422 Ditolak Guard atau validasi gagal — lihat field error
{ "ok": false, "status": "rejected", "error": "Dilarang memakai URL shortener — pakai domain sendiri" }
403 Nomor (session) tidak di-assign ke aplikasi Anda
404 session yang disebut tidak ditemukan
409 Tidak ada nomor tersedia untuk kategori/permintaan ini
GET /api/v1/messages/:id

Mengambil status terbaru satu pesan yang pernah dikirim lewat aplikasi Anda.

curl https://smartwa.indosmartmedia.com/api/v1/messages/1042 \
  -H "Authorization: Bearer sk_xxx..."
200
{
  "ok": true,
  "message": {
    "id": 1042, "phone": "6281234567890", "category": "transactional",
    "status": "delivered",  // queued|scheduled|sending|sent|delivered|read|failed|rejected|cancelled
    "reject_reason": null, "wa_message_id": "3EB0...",
    "scheduled_at": "2026-09-13 08:30:12", "sent_at": "2026-09-13 08:30:19",
    "created_at": "2026-09-13 08:29:55"
  }
}
404 Pesan tidak ditemukan (atau bukan milik aplikasi Anda)
GET /api/v1/sessions

Daftar nomor WhatsApp yang di-assign ke aplikasi Anda, beserta status dan sisa kuota — berguna untuk mengatur ritme pengiriman dari sisi aplikasi Anda sendiri.

200
{
  "ok": true,
  "sessions": [
    {
      "session": "cs-utama", "label": "CS Utama", "phone": "6281111111111",
      "role": "cs", "status": "connected", "health_score": 96,
      "paused_until": null,
      "quota": {
        "day": { "used": 42, "cap": 600 },
        "hour": { "used": 3, "cap": 75 },
        "cold": { "used": 1, "cap": 100 },
        "warmupDay": 366
      }
    }
  ]
}

Aplikasi Anda hanya melihat nomor yang memang di-assign kepadanya — nomor milik aplikasi lain tidak akan muncul.

POST /api/v1/contacts/check

Memeriksa apakah sebuah nomor terdaftar di WhatsApp, sebelum mengirim pesan ke sana.

FieldTipeKeterangan
phonewajibstringNomor yang dicek.
sessionopsionalstringNomor pemeriksa spesifik. Kosongkan untuk otomatis.
200
{ "ok": true, "phone": "6281234567890", "on_whatsapp": true }
409 Nomor pemeriksa sedang tidak tersambung ke WhatsApp
Dibatasi 30 permintaan/menit per aplikasi — lebih ketat dari endpoint lain karena berpotensi disalahgunakan untuk menyapu (scan) banyak nomor sekaligus.
POST /api/v1/blacklist

Menambahkan nomor ke daftar cekal — nomor yang dicekal tidak akan pernah menerima pesan dari nomor WhatsApp mana pun di SmartWA, lintas aplikasi.

FieldTipeKeterangan
phonewajibstringNomor yang dicekal.
reasonopsionalstringAlasan, untuk catatan admin.
200
{ "ok": true, "phone": "6281234567890" }
403 Nomor belum pernah berhubungan dengan aplikasi Anda
Karena daftar cekal berlaku global lintas aplikasi, endpoint ini hanya menerima nomor yang memang pernah berkirim pesan dengan aplikasi Anda — mencegah satu aplikasi mencekal nomor milik aplikasi lain secara sembarangan. Opt-out dari balasan "STOP"/"BERHENTI" penerima ditangani otomatis oleh sistem, tidak perlu dipanggil manual lewat endpoint ini.
GET /api/v1/health

Memeriksa apakah API key masih valid dan aktif — berguna untuk uji koneksi awal.

200
{ "ok": true, "app": "Nama Aplikasi Anda" }

Kategori & Kebijakan Pengiriman

Setiap pesan wajib punya category. Ini bukan sekadar label — menentukan nomor mana yang boleh dipakai dan seberapa ketat aturannya:

KategoriDipakai untukCatatan
transactionalOTP, notifikasi transaksi, invoiceHanya lewat nomor berperan sama; paling longgar kuotanya.
csBalasan layanan pelangganDefault kalau tidak diisi.
marketingPromosi, broadcastPaling ketat — tautan ke kontak baru ditolak, kuota kontak dingin dibatasi.
Nomor transactional menolak pesan berkategori marketing, dan sebaliknya. Ini sengaja — satu laporan spam di nomor marketing tidak boleh sampai menjatuhkan nomor yang mengirim OTP.

Spintax pada text

Gunakan {opsi1|opsi2|opsi3} supaya setiap penerima mendapat variasi kalimat yang sedikit berbeda — mengurangi risiko pesan terdeteksi sebagai blast otomatis identik.

{Halo|Hai|Selamat pagi} Pak {nama}, {ada promo|tersedia penawaran} khusus hari ini.

Kenapa pesan bisa tertunda

  • Di luar jam kirim nomor (default 08:00–20:00) — pesan dijadwalkan ke jendela berikutnya, bukan ditolak.
  • Kuota harian/per jam tercapai — nomor baru punya kuota lebih kecil (naik bertahap selama masa warmup).
  • Kontak baru (belum pernah membalas) punya kuota tersendiri yang lebih kecil.

Balasan ke kontak yang baru mengirim pesan dalam 24 jam terakhir (lane: "reply-window") tidak terikat jam kirim — langsung diproses kapan saja.

Kode Status HTTP

KodeArti
200Berhasil (permintaan GET, atau POST yang idempotent/tidak menghasilkan entitas baru)
202Pesan diterima — masuk antrian atau dijadwalkan
401API key tidak disertakan, tidak valid, atau nonaktif
403Tidak berhak — nomor bukan milik aplikasi Anda, atau nomor blacklist tidak terkait aplikasi Anda
404Sumber daya tidak ditemukan (pesan, atau session yang disebut)
409Tidak ada nomor yang cocok/tersedia/tersambung saat ini
422Validasi gagal, atau ditolak kebijakan Guard — lihat error
429Terlalu banyak permintaan — lihat Batas Laju
500Kesalahan internal. Respons menyertakan ref — sertakan saat melapor ke admin

Batas Laju (Rate Limit)

CakupanBatas
Seluruh endpoint /api/v1/*120 permintaan / menit / aplikasi
POST /contacts/check30 permintaan / menit / aplikasi (tambahan, lebih ketat)

Respons 429 menyertakan header Retry-After (detik) — tunggu selama itu sebelum mencoba lagi.

Webhook

Kalau aplikasi Anda diberi webhook_url oleh admin, SmartWA akan mengirim POST ke URL itu setiap kali ada kejadian berikut:

EventKapanPayload data
message.inboundPesan WhatsApp masuk ke nomor Anda { session, phone, body, type, is_group, wa_message_id }
message.sentPesan berhasil dikirim { id, phone, wa_message_id, session }
message.statusStatus pesan berubah (delivered/read) { session, wa_message_id, status }
message.failedPesan gagal terkirim setelah percobaan berulang { id, phone, error, session }
session.logged_outNomor ter-logout, perlu scan ulang QR { session }

Bentuk permintaan

Body JSON, dan dua header tambahan:

POST <webhook_url_anda>
X-SmartWA-Event: message.inbound
X-SmartWA-Signature: sha256=<hex>
Content-Type: application/json

{ "event": "message.inbound", "data": { ... }, "ts": 1789200000000 }

Verifikasi tanda tangan

Wajib diverifikasi sebelum memproses — mencegah orang lain mengirim payload palsu ke endpoint Anda. Gunakan webhook_secret yang diberikan admin bersamaan dengan API key.

// Node.js
const crypto = require('crypto');

function isValidSignature(rawBody, signatureHeader, webhookSecret) {
  const expected = 'sha256=' + crypto
    .createHmac('sha256', webhookSecret)
    .update(rawBody)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signatureHeader || '')
  );
}
Hitung HMAC dari raw body (byte mentah sebelum di-parse JSON) — bukan hasil JSON.stringify() ulang, yang bisa berbeda urutan/spasi dan membuat tanda tangan tidak cocok.

Percobaan ulang

Kalau endpoint Anda tidak merespons 2xx, SmartWA mencoba ulang hingga 5 kali dengan jeda 1, 5, 15, 60, lalu 180 menit. Pastikan endpoint merespons cepat (di bawah 10 detik) dan idempoten terhadap pengiriman ganda.

Pertanyaan Umum

Kenapa pesan saya berstatus "scheduled", bukan langsung terkirim?

Lihat Kategori & Kebijakan — biasanya karena di luar jam kirim nomor atau kuota harian/kontak-baru sudah tercapai. Ini bagian dari perlindungan nomor terhadap pemblokiran, bukan kesalahan.

Bisakah saya memilih nomor pengirim secara spesifik?

Ya, isi field session dengan kode nomor (lihat Daftar Nomor). Kosongkan untuk memilih otomatis.

Bagaimana kalau saya perlu kirim ke banyak nomor sekaligus (broadcast)?

Panggil endpoint kirim pesan satu per satu dari sisi aplikasi Anda. SmartWA akan mengatur jeda dan kecepatan pengiriman sungguhan secara internal — jangan mencoba mem-parallelkan pengiriman dari sisi aplikasi Anda, itu tidak akan mempercepat pengiriman dan berisiko terhadap kesehatan nomor.

Apakah nomor saya bisa dipakai aplikasi lain, atau sebaliknya?

Tidak. Setiap nomor secara eksplisit di-assign per aplikasi oleh admin. Aplikasi Anda tidak bisa melihat atau memakai nomor yang bukan haknya, walau menyebut kode nomor yang benar.

API key saya hilang, bagaimana?

API key tidak bisa ditampilkan ulang setelah dibuat. Minta admin memutar ulang (rotate) API key aplikasi Anda dari dashboard — key lama otomatis tidak berlaku begitu key baru dibuat.

SmartWA · WhatsApp Gateway · indosmartmedia