Skip to content

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

  1. Webhook masuk — gateway POST payload kind: "message", type: "text". Verifikasi signature dulu (tanpa ini siapa pun bisa mengirim payload palsu).

  2. Pemetaan field — tiga field dari pesan masuk dipetakan ke request balasan:

    Dari webhook (InboundEnvelope)Ke POST /v1/messages
    message_idquoted_message_id
    sender_jidquoted_participant dan to
    textquoted_text
  3. BalasPOST /v1/messages dengan Idempotency-Key berbasis message_id masuk, 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: true sebelum membalas, lalu kirim pesannya.

Detail tiap jenis pesan: Kirim Pesan. Detail payload masuk: Webhook.

Dokumentasi WagWay