Lewati ke konten

Official Meta Business Partner

WhatsApp API buat kirim Notifikasi Pesanan

Langsung dari aplikasimu. Kalau pelanggan membalas, tim CS lanjut dari inbox WatZap.

Animasi: kode mengirim pesan WhatsApp, pelanggan membaca lalu membalasnya.

Node.js · @watzapid/sdk
// npm install @watzapid/sdk
import { WatZap } from '@watzapid/sdk'

const wz = new WatZap()

const pesan = await wz.whatsapp
  .messages.sendText({
    to: '081234567890',
    text: 'Pesanan #INV-2091 dikirim',
  })
Terminal
npm install @watzapid/sdk

Pelanggan balas notifikasi, tim CS yang lanjutin

Notifikasi dari aplikasimu dan balasan tim CS tercatat di satu percakapan di inbox WatZap.

  1. Dikirim aplikasimu lewat API

    Pesannya langsung masuk ke percakapan pelanggan itu.

  2. Dibalas tim CS dari inbox WatZap

    Balasan keluar dari nomor yang sama, jadi pelanggan tetap di satu chat.

Mau sambungkan apa ke aplikasimu?

Konfirmasi pesanan

Begitu order masuk, pelanggan dapat pesan template berisi nomor pesanan dan tombol lacak.

sendTemplate
await wz.whatsapp.messages
  .sendTemplate({
    to: '081234567890',
    template: 'order_confirm',
    language: 'id',
    variables: {
      customer_name: 'Budi',
    },
  })

Kode OTP

Kode login atau verifikasi dikirim lewat template autentikasi WhatsApp.

sendTemplate · otpCode
await wz.whatsapp.messages
  .sendTemplate({
    to: '081234567890',
    template: 'login_otp',
    otpCode: '123456',
  })

Balasan ke servermu

Setiap pesan masuk diteruskan ke URL webhook-mu sebagai JSON.

webhook · Express
// URL-nya didaftarkan lewat
// wz.whatsapp.webhook(wabaId)
//   .set(url)
app.post('/watzap/masuk',
  (req, res) => {
    const body = req.body
    if (isWabaWebhook(body)) {
      console.log(
        body.data.message_text,
      )
    }
    res.sendStatus(200)
  })

Sinkron kontak

Daftar pelanggan di databasemu disinkronkan jadi list WatZap. Yang hilang dari daftar ditandai nonaktif, tidak dihapus.

lists.subscribers.sync
await wz.lists.subscribers.sync(
  listId,
  pelangganDariDatabase,
  { markMissingInactive: true },
)

Nomor WhatsApp biasa juga bisa dipakai, termasuk kirim ke grup. Lihat caranya

Timeout nggak bikin pesan terkirim dua kali

SDK resmi @watzapid/sdk untuk Node.js 18 ke atas, tanpa dependensi, dengan tipe TypeScript.

Node.js · try / catch
import {
  WatZap,
  WatZapError,
} from '@watzapid/sdk'

const wz = new WatZap()

try {
  await wz.whatsapp.messages
    .sendText({
      to: '+62 812-3456-7890',
      text: 'Pesanan dikirim',
    })
} catch (err) {
  if (!(err instanceof WatZapError))
    throw err
  if (err.mayHaveBeenProcessed) {
    // cek status, jangan kirim ulang
  }
}
  1. sendText

    Tidak diulang otomatis kalau koneksi putus di tengah kirim, jadi pelanggan tidak dapat pesan dobel.

  2. '+62 812-3456-7890'

    Format 08…, 62…, dan +62… dinormalkan sendiri.

  3. WatZapError

    Semua error turunan satu kelas, lengkap dengan kode dan status HTTP.

  4. mayHaveBeenProcessed

    True kalau pesan mungkin sudah sampai. Cek statusnya dulu, jangan langsung kirim ulang.

Pakai ChatGPT, Claude, atau Cursor? Salin prompt berisi daftar method SDK yang benar-benar ada.

Lihat isi prompt
Prompt · @watzapid/sdk 0.2.0
You are helping me integrate WatZap into my app with the official SDK @watzapid/sdk (TypeScript/JavaScript, Node.js 18+). Use only the methods and options listed here. Do not invent others.

SETUP
- Install: npm install @watzapid/sdk
- ESM: import { WatZap } from '@watzapid/sdk'   CommonJS: const { WatZap } = require('@watzapid/sdk')
- const wz = new WatZap()  // reads the API key from the WATZAP_API_KEY environment variable
- Server-side only. Never put the API key in frontend code or in the repository.
- List methods return a PagedList: `await` it for one page ({ items, page, perPage, total, hasNextPage }), or `for await (const item of ...)` for every item.

WHATSAPP: WABA (official). Sends return Promise<{ id, raw }>; id is the Meta message ID (wamid).
- wz.whatsapp.messages.sendText({ to, text })
- wz.whatsapp.messages.sendTemplate({ to, template, language?, variables?, header?, buttons?, otpCode? })
  - variables: an object for NAMED templates, an array for POSITIONAL templates ({{1}}, {{2}})
  - header: { url, filename? }; buttons: { urlSuffix?: string | { [buttonIndex]: string }, couponCode?: string }
- wz.whatsapp.messages.sendImage|sendDocument|sendVideo({ to, url, caption? }); sendAudio({ to, url }); react({ to, messageId, emoji })
- wz.whatsapp.messages.sendButtons({ to, body, buttons: [{ id, title }] (1-3, title max 20 bytes), header?, footer? })
- wz.whatsapp.messages.sendList({ to, body, buttonText, sections: [{ title?, rows: [{ id, title, description? }] }] })
- wz.whatsapp.messages.sendLocation({ to, latitude, longitude, name?, address? }); sendContacts({ to, contacts }); sendRaw(payload)
- wz.whatsapp.messages.get(id) / status(id) / list({ status?, phone?, page?, perPage? }) / stats()
- wz.whatsapp.templates.list({ wabaId?, status?, search? }) / get(templateId | { name, wabaId, language? }) / status(ref) / create({ name, category, language, components }) / delete(ref) / sync()
- wz.whatsapp.webhook(wabaId).set(url) / get() / unset()
- Text and media only reach users who messaged the business in the last 24 hours. Otherwise use sendTemplate.
- Do not use wz.whatsapp.broadcasts to send campaigns: the server does not process broadcasts created through the API yet.

WHATSAPP: UNOFFICIAL. numberKey is the 16-character key of a linked number. Sends return { message, senderNumber, raw } with no message ID.
- const device = wz.unofficial.device(numberKey)
- device.messages.sendText({ to, text }) / sendImage({ to, url, caption? }) / sendFile({ to, url })
- device.groups.list() / device.groups.sendText({ groupId, text }) / sendImage({ groupId, url, caption? }) / sendFile({ groupId, url })
- device.validateNumber(phone); device.webhook.set(url) / get() / unset()
- wz.unofficial.conversations.list() / get(id) / messages(id) / markRead(id) / archive(id, archived?)

SHARED
- wz.senders.list() returns every WABA and Unofficial number with its numberKey / wabaId and status
- wz.contacts.list({ listId? }) / get({ contactId } | { phone } | { externalId }) / upsert({ listId, phone?, externalId?, name?, source?, status? }) / delete(contactId) / optIn({ phone }) / optOut({ phone })
  - upsert overwrites fields you leave out with empty values: always send every field you want to keep
  - opt-out is only recorded: check it yourself before sending
- wz.lists.list() / get(listId) / create({ name, customFields? }) / update(listId, {...}) / delete(listId)
- wz.lists.subscribers.list(listId, { channel? }) / get(listId, id) / upsert(listId, { phone?, email?, externalId?, name?, customFields? }) / delete(listId, id) / bulkUpsert(listId, items) / sync(listId, items, { markMissingInactive? })
- wz.media.uploadUrl({ url }) / list() / get(id) / download(id) / delete(id)
- wz.automations.bots() / rules() / n8nConfig() / n8nLogs() / n8nStats()

PHONE NUMBERS: 08xxx, 62xxx and +62xxx are all accepted and normalized. Use a leading + for non-Indonesian numbers.

ERRORS
- Every error extends WatZapError and has name, message, code, httpStatus, response, mayHaveBeenProcessed; RateLimitError also has retryAfterSeconds.
- Classes: ValidationError (bad input, checked by the SDK or the server), AuthenticationError, PermissionDeniedError, NotFoundError, ConflictError, InsufficientCreditsError, UnprocessableError, RateLimitError, ProviderError, InternalServerError, APIConnectionError, APITimeoutError, APIUserAbortError.
- The SDK already retries whenever it is safe. Do not wrap WhatsApp sends in your own retry loop.
- If err.mayHaveBeenProcessed is true, the message may already have been delivered: check wz.whatsapp.messages.status(id) or log it; do not resend automatically.

Now write the code for this: <DESCRIBE WHAT YOU WANT TO BUILD>

Yang perlu kamu siapkan

  1. Buat akun WatZap

    Satu akun untuk dashboard dan API.

  2. Sambungkan nomor WhatsApp Business

    Tim tetap bisa membalas dari aplikasi.

  3. Ambil API key dari dashboard

    Simpan di server, jangan di kode frontend.

  4. Pasang SDK

    Terminal
    npm install @watzapid/sdk

Tanya Jawab

F.A.Q - Pertanyaan Umum

Yang paling sering ditanyakan soal WhatsApp API

  • Bedanya WhatsApp API resmi dan nomor WhatsApp biasa apa?

    WhatsApp API resmi (WABA) memakai nomor yang terdaftar di Meta. Status pesannya bisa dipantau sampai dibaca, dan pesan ke pelanggan yang belum chat dikirim lewat template yang disetujui Meta.

    Nomor WhatsApp biasa juga bisa dipakai lewat API, termasuk kirim ke grup, tapi status terkirimnya tidak bisa dilacak.

  • Bisa kirim pesan ke pelanggan yang belum pernah chat?

    Bisa, pakai pesan template yang sudah disetujui Meta. Pesan teks dan media biasa hanya sampai ke pelanggan yang chat kamu dalam 24 jam terakhir.

  • Bisa kirim gambar, dokumen, atau tombol?

    Bisa. Selain teks, API bisa mengirim gambar, dokumen, video, audio, lokasi, dan kontak, juga pesan bertombol (sampai 3 tombol) dan pesan berisi daftar pilihan.

  • Balasan pelanggan masuk ke mana?

    Ke inbox WatZap, tempat tim CS bisa lanjut membalas, dan ke webhook di sistemmu kalau kamu pasang URL-nya. Pesan yang kamu kirim lewat API juga tercatat di percakapan yang sama.

  • Harus pakai Node.js?

    Nggak. SDK-nya untuk TypeScript dan JavaScript, tapi bahasa lain bisa langsung memanggil REST API-nya. Caranya ada di dokumentasi API.

  • Nggak bisa koding, tetap bisa pakai WatZap?

    Nggak apa-apa. Situsnya WordPress? Pasang plugin resmi WatZap. Toko online juga bisa disambungkan ke WatZap tanpa koding lewat integrasi siap pakai seperti OrderHero, Scalev, dan WooCommerce.

Masih ada yang mau ditanyakan? Chat tim WatZap