Appearance
Quickstart: Bot Dua Arah
Contoh utuh end-to-end: terima pesan masuk lewat webhook → balas sambil mengutip pesan itu. Inilah pola inti hampir semua bot WhatsApp.
Alurnya:
Pesan masuk → webhook Anda → ambil message_id + sender_jid → POST /v1/messages (reply)Prasyarat
- Sudah pairing satu nomor (lihat Sesi WhatsApp) — punya
session_id - Sudah daftar webhook dan simpan
secret(lihat Webhook) - API key
wag_xxxxxxxxxxxx
Contoh Lengkap (Node.js)
Server kecil yang otomatis membalas setiap pesan teks masuk dengan kutipan.
js
import crypto from 'crypto'
import express from 'express'
const app = express()
app.use('/webhook/wa', express.raw({ type: 'application/json' }))
const API_BASE = 'https://api.example.com'
const API_KEY = process.env.WAG_API_KEY // wag_xxx
const SESSION_ID = process.env.WAG_SESSION_ID // [email protected]
const SECRET = process.env.WEBHOOK_SECRET // whsec_xxx
// Verifikasi signature — wajib sebelum memproses payload (lihat panduan Webhook)
function verify(secret, rawBody, header) {
const parts = Object.fromEntries(header.split(',').map(p => p.split('=')))
if (!parts.t || !parts.v1) return false
if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return false
const expected = crypto.createHmac('sha256', secret)
.update(`${parts.t}.${rawBody}`).digest('hex')
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1))
}
// Kirim balasan yang mengutip pesan masuk
async function replyQuote(inbound, text) {
await fetch(`${API_BASE}/v1/messages`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
// Key unik per pesan masuk — retry aman, tidak duplikat
'Idempotency-Key': `reply-${inbound.message_id}`,
},
body: JSON.stringify({
session_id: SESSION_ID,
to: inbound.sender_jid,
text,
// Tiga field inilah yang membuat balasan mengutip pesan asal
quoted_message_id: inbound.message_id,
quoted_participant: inbound.sender_jid,
quoted_text: inbound.text,
}),
})
}
app.post('/webhook/wa', async (req, res) => {
const sig = req.headers['x-webhook-signature'] ?? ''
if (!verify(SECRET, req.body, sig)) {
return res.status(401).json({ error: 'Signature tidak valid' })
}
// Balas 200 dulu agar gateway tidak retry, proses di background
res.json({ ok: true })
const payload = JSON.parse(req.body)
// Hanya tangani pesan teks masuk; abaikan receipt, typing, media
if (payload.kind === 'message' && payload.type === 'text') {
await replyQuote(payload, `Anda bilang: "${payload.text}"`)
}
})
app.listen(3000)Yang Terjadi di Tiap Langkah
Webhook masuk — gateway POST payload
kind: "message",type: "text". Verifikasi signature dulu (tanpa ini siapa pun bisa mengirim payload palsu).Pemetaan field — tiga field dari pesan masuk dipetakan ke request balasan:
Dari webhook ( InboundEnvelope)Ke POST /v1/messagesmessage_idquoted_message_idsender_jidquoted_participantdantotextquoted_textBalas —
POST /v1/messagesdenganIdempotency-Keyberbasismessage_idmasuk, jadi retry tidak menghasilkan balasan ganda.
Grup
Untuk chat grup (is_group: true), kirim balasan ke chat_jid (bukan sender_jid), tapi quoted_participant tetap sender_jid pengirim asli.
Variasi
- Reaksi, bukan balasan teks — beri 👍 ke pesan masuk:json
{ "session_id": "...", "to": "<sender_jid>", "type": "reaction", "reacted_message_id": "<message_id masuk>", "emoji": "👍" } - Terasa natural — kirim typing indicator
typing: truesebelum membalas, lalu kirim pesannya.
Detail tiap jenis pesan: Kirim Pesan. Detail payload masuk: Webhook.