Dokumentasi Wazent
Wazent adalah gateway WhatsApp Business API (Model A — Anda memakai akun WABA milik sendiri). Dua arah komunikasi: (1) kirim pesan lewat REST API, dan (2) terima pesan masuk lewat webhook yang Wazent teruskan ke sistem Anda.
A. Kirim Pesan (API)
A1. Base URL
Semua permintaan dikirim ke alamat berikut:
https://wazent-api.wazent-roby.workers.devA2. Autentikasi
Sertakan API key Anda di header Authorization. Buat & kelola key di halaman API Keys (perlu login).
Authorization: Bearer WAZENT_API_KEY_ANDAA3. Kirim teks
POST /v1/messages/send — body (JSON):
{
"to": "6281234567890",
"type": "text",
"message": "Halo dari Wazent!"
}type opsional untuk teks (default text). Tambahkan "preview_url": true untuk menampilkan pratinjau link. Contoh dengan curl:
curl -X POST https://wazent-api.wazent-roby.workers.dev/v1/messages/send \
-H "Authorization: Bearer WAZENT_API_KEY_ANDA" \
-H "Content-Type: application/json" \
-d '{"to":"6281234567890","type":"text","message":"Halo dari Wazent!"}'A4. Kirim media
Tipe image, video, audio, dan document dikirim lewat url (https, publik) — Meta yang mengambil file dari URL tersebut.
{
"to": "6281234567890",
"type": "image",
"url": "https://contoh.com/promo.jpg",
"caption": "Promo bulan ini!"
}{
"to": "6281234567890",
"type": "document",
"url": "https://contoh.com/invoice.pdf",
"filename": "invoice-1234.pdf",
"caption": "Faktur Anda"
}{
"to": "6281234567890",
"type": "audio",
"url": "https://contoh.com/voice.ogg"
}caption— opsional untukimage/video/document(maks 1024 karakter). Tidak berlaku untukaudio.filename— opsional, hanya untukdocument.
A5. Kirim template
Pesan template selalu boleh dikirim — termasuk untuk memulai percakapan (lihat Bagian C). Pakai nama template yang sudah disetujui Meta.
{
"to": "6281234567890",
"type": "template",
"template": {
"name": "order_update",
"language": "id",
"components": [
{ "type": "body", "parameters": [ { "type": "text", "text": "1234" } ] }
]
}
}language = kode bahasa Meta (mis. id, en_US). components opsional (untuk parameter), diteruskan apa adanya ke Meta.
A6. Respons sukses
{
"status": "sent",
"to": "6281234567890",
"type": "image",
"message_id": "wamid.HBgN..."
}A7. Kode error
| HTTP | error | Arti |
|---|---|---|
| 401 | unauthorized | API key tidak ada / tidak valid / sudah di-revoke. |
| 402 | subscription_inactive | Langganan tidak aktif. |
| 402 | subscription_expired | Langganan sudah kedaluwarsa. |
| 400 | no_whatsapp | Belum ada WhatsApp yang terhubung. |
| 400 | bad_request | Body bukan objek JSON, atau JSON rusak. |
| 400 | unsupported_type | "type" di luar text/image/video/audio/document/template. |
| 400 | invalid_to | Nomor tujuan salah (harus digit 8–15, format internasional). |
| 400 | invalid_message | Teks kosong atau lebih dari 4096 karakter. |
| 400 | invalid_url | 'url' media bukan https valid / terlalu panjang. |
| 400 | invalid_caption | Caption >1024 karakter, atau caption pada audio. |
| 400 | invalid_filename | 'filename' pada tipe non-document / terlalu panjang. |
| 400 | invalid_template | 'template.name'/'language' salah, atau 'components' bukan array. |
| 409 | window_closed | Window 24 jam tutup — kirim pakai template (lihat Bagian C). |
| 429 | rate_limited | Terlalu banyak permintaan; tunggu sesuai header Retry-After. |
| 502 | send_failed | Meta menolak pengiriman (lihat pesan detail). |
A8. Batas permintaan (rate limit)
- Per API key: maksimal 600 permintaan / 60 detik.
- Per IP hanya saat autentikasi gagal: 5 / 10 detik (anti brute-force — pemakaian normal tidak menyentuh batas ini).
- Jika melebihi batas: 429
rate_limited+ headerRetry-After(detik untuk menunggu).
A9. Cek status layanan
GET /health — untuk memastikan layanan hidup.
{ "ok": true, "service": "wazent-api" }B. Terima Pesan (Webhook — Model A)
B1. Atur tujuan webhook
Di halaman Webhook dashboard: isi URL webhook (https) dan Generate signing secret (whsec_… — salin & simpan). Setiap pesan masuk dari pelanggan akan diteruskan (POST) ke URL itu, ditandatangani agar bisa Anda verifikasi.
B2. Format payload
Wazent mengirim POST dengan body JSON berikut:
{
"event": "message",
"phone_number_id": "1191635700691087",
"conversation_id": "0e6f3b2a-....-uuid",
"from": "6281234567890",
"message": {
"id": "wamid.HBgN...",
"type": "text",
"content": "Halo, saya mau tanya",
"raw": { "...": "objek message mentah dari Meta (fidelity penuh)" }
},
"window": { "open": true, "expires_at": "2026-06-18T12:00:00.000Z" },
"timestamp": "2026-06-18T11:00:00.000Z"
}| Field | Arti |
|---|---|
phone_number_id | ID nomor WABA bisnis (tujuan pesan masuk). |
conversation_id | ID percakapan di Wazent (stabil per pelanggan). |
from | Nomor pelanggan pengirim. |
message.content | Isi teks bila ada; null untuk tipe non-teks. |
message.raw | Objek message asli dari Meta (untuk media, lokasi, dll). |
window | Status window 24 jam saat pesan diterima (lihat Bagian C). |
B3. Verifikasi tanda tangan
Tiap request membawa header X-Wazent-Signature: sha256=<hex> = HMAC-SHA256 atas raw body (string mentah, sebelum di-parse) memakai webhook_secret Anda. Hitung ulang lalu bandingkan secara timing-safe. ⚠️ Wajib pakai raw body — bukan hasil serialisasi ulang JSON.
Node.js (Express):
const crypto = require("crypto");
// Simpan raw body:
// app.use(express.json({ verify: (req, _res, buf) => { req.rawBody = buf; } }));
function verifyWazent(rawBody, header, secret) {
const expected =
"sha256=" + crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
const a = Buffer.from(header || "");
const b = Buffer.from(expected);
return a.length === b.length && crypto.timingSafeEqual(a, b);
}
// app.post("/webhook", (req, res) => {
// if (!verifyWazent(req.rawBody, req.get("X-Wazent-Signature"), SECRET))
// return res.sendStatus(401);
// res.sendStatus(200); // balas cepat, proses async
// });PHP:
<?php
$raw = file_get_contents('php://input');
$header = $_SERVER['HTTP_X_WAZENT_SIGNATURE'] ?? '';
$expected = 'sha256=' . hash_hmac('sha256', $raw, $secret);
if (hash_equals($expected, $header)) {
http_response_code(200); // sah — balas cepat
} else {
http_response_code(401);
}B4. Praktik baik
- Balas 2xx secepatnya; proses berat lakukan asinkron.
- Tangani kemungkinan kiriman ganda secara idempoten lewat
message.id(wamid).
C. Window 24 Jam & Template
C1. Aturan Meta
Anda hanya boleh mengirim pesan bebas (text/media) dalam 24 jam sejak pelanggan terakhir membalas. Setiap balasan pelanggan me-reset jendela 24 jam.
C2. Kapan boleh apa
- Window terbuka → pesan bebas (text/media) boleh.
- Window tutup, atau pelanggan belum pernah chat → pesan bebas ditolak; wajib template.
- Template selalu boleh (termasuk memulai percakapan).
C3. Saat window tutup
Wazent memeriksa window sebelum meneruskan ke Meta, jadi Anda menerima 409 window_closed yang jelas — bukan error mentah Meta (131047).
{
"error": "window_closed",
"message": "Window 24 jam sudah tutup. Pelanggan harus membalas dulu, atau kirim pakai pesan template (type: \"template\").",
"window": { "open": false, "expires_at": null }
}expires_at = waktu kedaluwarsa window bila ada percakapan; null bila pelanggan belum pernah chat.
C4. Solusi
Kirim template yang sudah disetujui Meta (lihat A5). Template tidak terpengaruh window dan bisa dipakai kapan saja.