Channel API — Dokumentasi Integrasi Bot WhatsApp
API ini untuk menghubungkan bot WhatsApp eksternal ke data toko (produk, order, delivery) dan mencatat status koneksi WhatsApp Channel. Kazean tidak menjalankan engine WhatsApp — bot pihak ketiga yang mengelola koneksi WA, lalu melaporkan statusnya ke sini.
1. Autentikasi
Setiap request (kecuali webhook) memakai scoped API key yang dibuat di halaman /developer:
Authorization: Bearer kz_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Key ini bukan key admin — hanya boleh mengakses scope yang dipilih saat dibuat, dan hanya melihat data milik akun yang membuatnya. Simpan di sisi bot (server), jangan pernah ditempel di kode frontend/browser.
Scope yang tersedia
| Scope | Kegunaan |
|---|---|
products.read | Baca katalog produk aktif |
orders.read | Baca order milik akun ini |
orders.update | Set status pengiriman sisi bot (bukan status pembayaran) |
delivery.read | Baca detail hasil pembelian (delivery) sebuah order |
channel.read | Baca daftar & status channel |
channel.event | Kirim event channel (connected/disconnected/dst) |
stock.read | Cek stok produk & riwayat perubahannya |
stock.write | Restock produk (hanya produk yang diizinkan admin untuk key ini) |
panel.read | Baca status & sisa masa aktif panel milik akun ini |
panel.trigger | Perpanjang panel & memesan panel baru (provisioning) milik akun ini |
Key tidak pernah diberi izin admin. Kalau butuh scope tambahan, buat key baru dengan scope yang sesuai — jangan minta semua scope kalau bot cuma butuh sebagian.
2. Channel
Channel didaftarkan lewat halaman /developer (bukan lewat API) —
hasilnya channel_id dan webhookSecret
(tampil sekali, simpan di sisi bot).
GET /api/channel
Scope: channel.read. Daftar semua channel milik akun ini.
GET /api/channel/:channel_id
Scope: channel.read. Detail satu channel.
GET /api/channel/:channel_id/status
Scope: channel.read. Cek cepat status channel saja.
{ "success": true, "data": { "channel_id": "...", "status": "ACTIVE" } }
Status channel
| Status | Arti |
|---|---|
| ACTIVE | Tersambung, boleh dipakai |
| INACTIVE | Terputus sementara (mis. setelah channel.disconnected) |
| DISABLED | Dinonaktifkan manual oleh pemilik — request event ditolak |
| REVOKED | Dicabut permanen oleh admin — request event ditolak |
3. Mengirim Event Channel
Ada dua jalur yang setara secara fungsi — pakai salah satu sesuai kebutuhan bot:
A. POST /api/channel/:channel_id/event (pakai API key)
Scope: channel.event.
{
"event_id": "evt_unik_123",
"event_type": "channel.connected",
"payload": { "any": "data" }
}
B. POST /api/webhook/channel (pakai signature, bukan API key)
Body sama seperti di atas ditambah channel_id, tapi autentikasi
pakai header signature HMAC-SHA256 dari webhookSecret channel:
X-Kazean-Signature: <hex HMAC-SHA256 dari raw body pakai webhookSecret>
Body:
{
"channel_id": "ch_xxx",
"event_id": "evt_unik_123",
"event_type": "channel.connected",
"payload": { "any": "data" }
}
Contoh lengkap: bangun body, buat signature, lalu kirim webhook-nya (Node.js 18+, pakai fetch bawaan — tinggal salin & sesuaikan baseUrl/webhookSecret):
const crypto = require("crypto");
const baseUrl = "https://kazean.biz.id";
const channelId = "ch_xxx"; // channel_id dari /developer
const webhookSecret = "..."; // webhookSecret dari /developer, simpan aman di server bot
async function sendChannelEvent(eventType, payload) {
const body = JSON.stringify({
channel_id: channelId,
event_id: crypto.randomUUID(), // wajib unik per event, dipakai buat idempotency
event_type: eventType,
payload,
});
const signature = crypto.createHmac("sha256", webhookSecret).update(body).digest("hex");
const res = await fetch(`${baseUrl}/api/webhook/channel`, {
method: "POST",
headers: { "Content-Type": "application/json", "X-Kazean-Signature": signature },
body,
});
if (!res.ok) throw new Error(`Webhook gagal: ${res.status} ${await res.text()}`);
return res.json();
}
module.exports = { sendChannelEvent };
// Contoh pakai:
// sendChannelEvent("channel.connected", { number: "6281234567890" });
import crypto from "crypto";
const baseUrl = "https://kazean.biz.id";
const channelId = "ch_xxx"; // channel_id dari /developer
const webhookSecret = "..."; // webhookSecret dari /developer, simpan aman di server bot
export async function sendChannelEvent(eventType, payload) {
const body = JSON.stringify({
channel_id: channelId,
event_id: crypto.randomUUID(), // wajib unik per event, dipakai buat idempotency
event_type: eventType,
payload,
});
const signature = crypto.createHmac("sha256", webhookSecret).update(body).digest("hex");
const res = await fetch(`${baseUrl}/api/webhook/channel`, {
method: "POST",
headers: { "Content-Type": "application/json", "X-Kazean-Signature": signature },
body,
});
if (!res.ok) throw new Error(`Webhook gagal: ${res.status} ${await res.text()}`);
return res.json();
}
// Contoh pakai:
// await sendChannelEvent("channel.connected", { number: "6281234567890" });
Pilih format sesuai package.json bot kamu — kalau ada
"type": "module" (atau file .mjs) pakai tab
ESM, kalau tidak (default Node.js / .cjs) pakai tab CommonJS. Jangan
dicampur (import + module.exports di file
yang sama akan error).
Tipe event yang dikenali
| event_type | Efek di Kazean |
|---|---|
| channel.connected | Channel → ACTIVE |
| channel.disconnected | Channel → INACTIVE |
| channel.message | Dicatat saja (tidak mengubah status) |
| channel.updated | Dicatat saja |
| channel.error | Dicatat saja |
Idempotency: setiap event wajib punya event_id unik.
Event dengan event_id yang sama pada channel yang sama hanya
diproses sekali — kirim ulang saat retry itu aman, tidak akan dobel.
4. Data Toko
GET /api/integrations/products
Scope: products.read. Katalog produk aktif (tanpa data stok rahasia/link download).
GET /api/integrations/orders
Scope: orders.read. Semua order milik akun pemilik key ini.
GET /api/integrations/delivery/:orderId
Scope: delivery.read. Detail hasil pembelian (akun/lisensi/link/dll) sebuah order — order harus milik akun ini.
PUT /api/integrations/orders/:id/status
Scope: orders.update. Terbatas — hanya menandai
status pengiriman pesan di sisi bot, bukan status pembayaran/order:
{ "botDeliveryStatus": "sent", "note": "opsional" }
Nilai botDeliveryStatus: sent, failed, atau acknowledged.
Status pembayaran/order inti (pending/paid/completed) selalu berasal dari webhook payment
gateway di backend, tidak bisa diubah lewat API ini — ini untuk mencegah manipulasi stok/order.
5. Stock Engine
Endpoint ini untuk bot yang perlu tahu/mengelola stok produk tertentu, mis. bot reseller yang otomatis upload stok baru begitu supplier mengirim kode.
GET /api/integrations/stock/:productId
Scope: stock.read.
{ "success": true, "data": {
"productId": "...", "name": "...", "active": true,
"unlimitedStock": false, "hasStockPool": true,
"stock": 12, "inStock": true
} }
GET /api/integrations/stock/:productId/history
Scope: stock.read. Query page,
limit (default 20, maks 100). Riwayat sale,
restock, manual_adjustment,
restore untuk produk ini.
POST /api/integrations/stock/:productId/restock
Scope: stock.write. Terbatas — hanya berhasil kalau
admin sudah mengisi meta.apiRestockCustomerId produk ini dengan id
akun pemilik key (diset admin lewat form edit produk di /manage),
supaya sembarang pembeli tidak bisa menyuntik stok ke katalog toko.
{ "items": "kode1\nkode2\nkode3" }
// atau
{ "items": ["kode1", "kode2", "kode3"] }
6. Panel / Pterodactyl Provisioning
Endpoint ini cuma menyentuh panel milik akun pemilik key sendiri — tidak bisa dipakai untuk memesankan atau melihat panel customer lain.
GET /api/integrations/panel
Scope: panel.read. Semua panel (order kategori panel) milik akun ini.
GET /api/integrations/panel/:orderId
Scope: panel.read. Detail satu panel — panelStatus
(active/suspended/missing),
expiresAt, renewPrice, renewDays.
POST /api/integrations/panel/:orderId/renew
Scope: panel.trigger. Perpanjang panel, dibayar dari saldo akun
pemilik key — sama seperti tombol Perpanjang di halaman Riwayat. Gagal dengan pesan jelas kalau
saldo tidak cukup, panel ditangguhkan admin, atau server sudah tidak ada di panel.
POST /api/integrations/panel/order
Scope: panel.trigger. Memesan produk kategori panel baru (memicu
provisioning otomatis ke Pterodactyl), dibayar dari saldo akun pemilik key:
{ "productId": "...", "panelUsername": "opsional", "panelPassword": "opsional" }
Provisioning berjalan async lewat antrian — cek statusnya lewat
GET /api/integrations/panel/:orderId atau
GET /api/integrations/orders.
7. Rate Limit
Endpoint ber-API-key: 60 request/menit per key. Webhook: 120 request/menit per
channel_id. Response 429 kalau terlampaui.
8. Error Umum
| Status | Arti |
|---|---|
| 401 | API key tidak ada/salah, atau signature webhook tidak valid |
| 403 | Key/channel tidak aktif, scope tidak cukup, atau channel bukan milik key ini |
| 404 | Channel/order/produk tidak ditemukan |
| 409 | Konflik (mis. stok baru saja habis, username panel sudah dipakai) |
| 429 | Rate limit terlampaui |
| 502 | Gagal menghubungi Pterodactyl saat provisioning/renew panel |