Oleh Amy Baker
Dipublikasikan pada 25 Jul 2026
Cara Mengirim Pesan Template WhatsApp Menggunakan API
Sedang membangun sistem notifikasi WhatsApp? Panduan ini membahas ketiga cara mengirim pesan template melalui API Chakra, dua pola penggantian variabel yang berbeda, penanganan error 131049 dan 131026, pelacakan pengiriman melalui webhook, serta pengiriman massal dengan batas laju.
Jika Anda membangun sistem notifikasi di WhatsApp — pembaruan pesanan, pengingat janji temu, OTP, alur untuk menjangkau kembali pelanggan — pada akhirnya Anda perlu mengirim pesan template secara terprogram. Chakra menyediakan tiga cara berbeda untuk melakukannya, masing-masing sesuai untuk pola integrasi yang berbeda. Panduan ini membahas ketiganya: struktur permintaan, kapan menggunakan masing-masing cara, penggantian variabel, serta penanganan dua error yang paling sering Anda temui di lingkungan produksi — 131049 dan 131026.
Anggaplah Anda sudah memahami "apa itu template WhatsApp" — Anda memiliki WABA yang disetujui, template yang disetujui, dan kunci API. Jika Anda perlu menyiapkannya, bagian kunci API Chakra adalah tempat Anda membuat token bearer.
Autentikasi Singkat
Setiap permintaan memerlukan token bearer yang dibuat dari kunci API dengan cakupan peran/hak istimewa yang sesuai:
curl --request GET \ --url https://api.chakrahq.com/v1/ext/config \ --header 'Authorization: Bearer <your_access_token>'Batasi cakupan kunci hanya pada hak istimewa minimum, terutama untuk apa pun yang menyentuh kode sisi klien. Perlu diketahui juga sejak awal: Chakra menerapkan batas laju 200 panggilan API/menit — perhitungkan ini dalam setiap perulangan pengiriman massal, jangan sampai baru mengetahuinya lewat error 429 di lingkungan produksi.
Tiga Cara Mengirim Pesan Template
| Metode | Pola Endpoint | Cocok Untuk |
|---|---|---|
| Berdasarkan Proses | .../process/{procedureShortId}/{primaryKeyOrId}/send-template-message | Pengiriman yang dikaitkan dengan data CRM — prospek, tiket, atau kasus yang sudah ada di Chakra |
| Berdasarkan Nomor Telepon | .../phoneNumber/{phoneNumber}/send-template-message | Pengiriman sederhana dan mandiri saat Anda sudah memiliki nomor penerima dan tidak memerlukan data yang ditautkan |
| Format API Meta | .../api/{whatsappApiVersion}/{whatsappPhoneNumberId}/messages | Kontrol penuh dengan payload Meta Cloud API yang persis — berguna jika Anda memindahkan kode integrasi Meta yang sudah ada atau memerlukan jenis pesan yang tidak diabstraksikan oleh endpoint sederhana Chakra |
Ketiganya pada akhirnya menggunakan infrastruktur WhatsApp yang sama — perbedaannya terletak pada bentuk payload dan konteks yang disertakan Chakra saat mengirim.
Metode 1: Kirim Berdasarkan Proses
Gunakan metode ini saat pesan terkait dengan data yang sudah tersimpan di platform Chakra — prospek, tiket, kunjungan lapangan, atau jenis proses kustom lainnya. Metode ini tepat jika Anda ingin pesan keluar otomatis dicatat pada data tersebut, tanpa perlu langkah terpisah untuk mengaitkannya.
Endpoint:
POST https://api.chakrahq.com/v1/ext/plugin/whatsapp/{pluginId}/process/{procedureShortId}/{primaryKeyOrId}/send-template-messagepluginId— UUID koneksi WhatsApp Anda, yang dapat disalin dari halaman detail plugin di layar penyiapanprocedureShortId— jenis data (lead,ticket,case, atau shortId proses kustom)primaryKeyOrId— ID atau kunci utama data tertentu
Contoh curl:
curl --location --request POST \ 'https://api.chakrahq.com/v1/ext/plugin/whatsapp/d83e1d23-50b8-4d87-8f92-842a0ac516f6/process/lead/8842/send-template-message' \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data-raw '{ "whatsappPhoneNumberId": "775966265503012", "templateName": "order_confirmation", "mapping": [ { "schemaPropertyName": "1", "schemaPropertyValue": "John" }, { "schemaPropertyName": "2", "schemaPropertyValue": "ORD-4521" } ] }'JavaScript (fetch):
async function sendTemplateForLead(leadId, templateName, variables) { const response = await fetch( `https://api.chakrahq.com/v1/ext/plugin/whatsapp/${PLUGIN_ID}/process/lead/${leadId}/send-template-message`, { method: "POST", headers: { "Authorization": `Bearer ${API_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ whatsappPhoneNumberId: PHONE_NUMBER_ID, templateName, mapping: variables.map((value, i) => ({ schemaPropertyName: String(i + 1), schemaPropertyValue: value, })), }), } ); if (!response.ok) { const error = await response.json(); throw new Error(`Template send failed: ${JSON.stringify(error)}`); } return response.json();}Metode 2: Kirim Berdasarkan Nomor Telepon
Payload-nya secara fungsional sama dengan Metode 1, tetapi Anda mengirim langsung ke nomor telepon, bukan ke data proses yang ditautkan. Gunakan metode ini jika keterkaitan dengan data tidak relevan — misalnya, pengiriman yang dipicu webhook dari sistem eksternal (Shopify, WooCommerce, backend Anda sendiri) yang tidak memerlukan prospek Chakra terkait.
Endpoint:
POST https://api.chakrahq.com/v1/ext/plugin/whatsapp/{pluginId}/phoneNumber/{phoneNumber}/send-template-messageContoh curl:
curl --location --request POST \ 'https://api.chakrahq.com/v1/ext/plugin/whatsapp/d83e1d23-50b8-4d87-8f92-842a0ac516f6/phoneNumber/919901258433/send-template-message' \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data-raw '{ "whatsappPhoneNumberId": "775966265503012", "templateName": "shipping_update", "mapping": [ { "schemaPropertyName": "1", "schemaPropertyValue": "Priya" } ], "imageUrl": "https://your-cdn.com/shipping-banner.png" }'JavaScript (fetch):
async function sendTemplateByPhone(phoneNumber, templateName, mapping, imageUrl) { const body = { whatsappPhoneNumberId: PHONE_NUMBER_ID, templateName, mapping }; if (imageUrl) body.imageUrl = imageUrl; const response = await fetch( `https://api.chakrahq.com/v1/ext/plugin/whatsapp/${PLUGIN_ID}/phoneNumber/${phoneNumber}/send-template-message`, { method: "POST", headers: { "Authorization": `Bearer ${API_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify(body), } ); return handleWhatsAppResponse(response); // shared error handler, see below}Perhatikan kolom imageUrl — jika template Anda memiliki komponen gambar header, masukkan URL yang dapat diakses publik di sini alih-alih mengunggah media secara terpisah, kecuali jika Anda menggunakan endpoint format Meta, yang mengharuskan ID media.
Metode 3: Format API Meta (Penerusan Mentah)
Endpoint ini hanyalah pembungkus tipis — Chakra meneruskan payload Anda langsung ke Cloud API milik Meta, menggunakan skema Meta sendiri. Gunakan metode ini jika Anda sudah memiliki logika payload format Meta (yang dipindahkan dari integrasi Cloud API langsung sebelumnya atau dibuat berdasarkan dokumentasi Meta), memerlukan jenis pesan atau komponen yang tidak didukung endpoint sederhana Chakra, atau menginginkan kesesuaian byte demi byte dengan kontrak permintaan/respons Meta untuk pencatatan Anda sendiri.
Endpoint:
POST https://api.chakrahq.com/v1/ext/plugin/whatsapp/{pluginId}/api/{whatsappApiVersion}/{whatsappPhoneNumberId}/messagesContoh curl:
curl --location --request POST \ 'https://api.chakrahq.com/v1/ext/plugin/whatsapp/d83e1d23-50b8-4d87-8f92-842a0ac516f6/api/v19.0/775966265503012/messages' \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data-raw '{ "messaging_product": "whatsapp", "recipient_type": "individual", "to": "919901258433", "type": "template", "template": { "name": "order_confirmation", "language": { "policy": "deterministic", "code": "en_US" }, "components": [ { "type": "body", "parameters": ["John", "ORD-4521"] } ] } }'JavaScript (fetch):
async function sendTemplateMetaFormat(to, templateName, langCode, bodyParams) { const response = await fetch( `https://api.chakrahq.com/v1/ext/plugin/whatsapp/${PLUGIN_ID}/api/v19.0/${PHONE_NUMBER_ID}/messages`, { method: "POST", headers: { "Authorization": `Bearer ${API_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ messaging_product: "whatsapp", recipient_type: "individual", to, type: "template", template: { name: templateName, language: { policy: "deterministic", code: langCode }, components: [{ type: "body", parameters: bodyParams }], }, }), } ); return handleWhatsAppResponse(response);}Endpoint yang sama juga menerima payload native Meta untuk pesan sesi (pesan bebas format) — teks, gambar, daftar interaktif — karena endpoint ini meneruskan langsung ke API /messages yang mendasarinya, bukan hanya untuk template.
Dua Pola Penggantian Variabel yang Berbeda
Bagian ini sering membingungkan saat beralih metode, jadi penting untuk menjelaskannya secara rinci:
Metode 1 & 2 (abstraksi Chakra) menggunakan larik mapping yang berisi pasangan { schemaPropertyName, schemaPropertyValue } . schemaPropertyName adalah posisi variabel template dalam bentuk string ("1", "2", ...), sedangkan schemaPropertyValue adalah nilai penggantinya. Ini adalah skema sederhana milik Chakra yang tidak bergantung pada struktur komponen mentah Meta.
Metode 3 (format Meta) menggunakan larik native Meta, yaitu components . Setiap komponen (header, body, button) memiliki larik parameters sendiri, dan variabel diisi secara berurutan berdasarkan posisinya. Jika template Anda memiliki beberapa variabel di bagian isi pesan, parameters: ["John", "ORD-4521"] akan mengisi {{1}} dan {{2}} secara berurutan sesuai urutan dalam larik — tidak ada kolom indeks eksplisit, jadi urutannya harus benar.
Jika Anda membangun sistem yang mungkin perlu beralih endpoint nanti (misalnya, mulai dengan sederhana lalu membutuhkan fitur Meta mentah), sebaiknya tulis fungsi adaptor internal untuk mengubah daftar variabel Anda ke bentuk yang diharapkan endpoint yang digunakan, alih-alih menggandakan logika pemetaan variabel di seluruh basis kode Anda.
Menangani Error yang Benar-Benar Akan Anda Temui
Error 131049 — Keterlibatan ekosistem / tidak terkirim. Ini bukan error permintaan yang tidak valid; sistem WhatsApp menolak mengirim pesan karena kemungkinan penerima tidak menginginkannya (interaksi terbaru rendah, tingkat pemblokiran tinggi, atau secara umum mencapai batas pemasaran per pengguna di berbagai bisnis). Jangan langsung mencoba kembali pengiriman yang sama — percobaan ulang langsung hanya akan mengulangi kegagalan dan dapat memperburuk keadaan bagi kontak tersebut. Catat error ini, kecualikan kontak dari siklus pengiriman berikutnya, lalu coba lagi setelah jeda (24 jam adalah jeda minimum yang wajar) menggunakan template Utility jika konteksnya memungkinkan.
Error 131026 — Penerima tidak menggunakan WhatsApp / tidak dapat dijangkau. Nomor tersebut tidak valid di WhatsApp, belum menyetujui ketentuan yang berlaku, atau tidak dapat menerima jenis pesan ini. Ini masalah kualitas data, bukan masalah sementara — mencoba lagi tidak akan memperbaikinya. Tandai kontak untuk pembersihan daftar daripada memasukkannya kembali ke antrean.
Penangan error bersama untuk ketiga metode dapat terlihat seperti ini:
async function handleWhatsAppResponse(response) { if (response.ok) return response.json(); const errorBody = await response.json(); const errorCode = errorBody?.error?.code || errorBody?._data?.error?.code; switch (errorCode) { case 131049: // Don't retry now. Queue for a delayed retry, exclude from current campaign. await flagForDelayedRetry(errorBody, { minDelayHours: 24 }); break; case 131026: // Bad number — don't retry, flag for cleanup. await flagInvalidNumber(errorBody); break; default: // Log and surface unexpected errors for manual review. console.error("Unhandled WhatsApp send error:", errorBody); } throw new Error(`WhatsApp send failed (${errorCode}): ${JSON.stringify(errorBody)}`);}Menerapkan klasifikasi seperti ini sejak awal pada alur pengiriman akan mencegah loop percobaan ulang terus-menerus mengirim ke nomor yang sama dan gagal tanpa disadari. Ini salah satu cara tercepat untuk merusak Peringkat Kualitas Anda.
Melacak Status Pengiriman Setelah Pesan Dikirim
Respons 200 dari salah satu dari ketiga endpoint hanya mengonfirmasi bahwa pesan diterima untuk dikirim — bukan bahwa pesan telah terkirim atau dibaca. Untuk mengetahuinya, Anda perlu menerima webhook peristiwa masukdari Chakra, yang mengirimkan pembaruan status (sent, delivered, read, failed) saat status tersebut terjadi, yang dicocokkan dengan whatsappMessageId atau externalId yang dikembalikan dalam respons pengiriman awal Anda.
Contoh handler webhook sederhana:
app.post("/webhooks/whatsapp", (req, res) => { const event = req.body; if (event.type === "message_status") { const { messageId, status, errorCode } = event.data; // status: sent | delivered | read | failed updateMessageRecord(messageId, { status, errorCode }); if (status === "failed" && errorCode) { handleWhatsAppResponse({ ok: false, json: async () => ({ error: { code: errorCode } }) }); } } res.sendStatus(200); // acknowledge quickly; don't block on downstream processing});Menyimpan whatsappMessageId bersama catatan Anda sendiri (ID pesanan, ID prospek, atau apa pun yang memicu pengiriman) saat pesan dikirim akan membuat webhook ini benar-benar berguna — jika tidak, Anda harus mencocokkan peristiwa status dengan konteksnya setelah kejadian.
Mengirim dalam Volume Besar: Mematuhi Batas Laju
Dengan batas 200 panggilan/menit, loop sederhana untuk mengirim ke beberapa ribu penerima akan cepat terkena error 429. Pola antrean dan pembatasan laju yang sederhana dapat mengatasinya tanpa rekayasa berlebihan:
async function sendBatch(recipients, templateName, buildMapping) { const RATE_LIMIT = 200; // calls per minute const DELAY_MS = Math.ceil(60000 / RATE_LIMIT); for (const recipient of recipients) { try { await sendTemplateByPhone(recipient.phone, templateName, buildMapping(recipient)); } catch (err) { console.error(`Send failed for ${recipient.phone}:`, err.message); // classification already handled inside handleWhatsAppResponse } await new Promise((resolve) => setTimeout(resolve, DELAY_MS)); }}Untuk pengiriman lebih dari beberapa ribu pesan, antrean tugas yang tepat (BullMQ, SQS, atau sejenisnya) dengan jeda bertahap untuk percobaan ulang layak digunakan dibanding loop dalam proses — antrean ini tetap berjalan jika proses dimulai ulang di tengah pengiriman, tidak seperti loop for sederhana.
Mengapa Membangun Ini di Chakra
Berikut beberapa hal yang perlu diketahui saat Anda mempertimbangkan tempat untuk membangun integrasi ini:
- Dokumentasi lengkap — cuplikan kode, contoh permintaan/respons, dan panduan webhook untuk setiap endpoint, sehingga Anda tidak perlu menebak-nebak cara kerjanya melalui coba-coba
- Infrastruktur kelas enterprise — uptime 99.9% dengan logika percobaan ulang dan penanganan batas laju bawaan di sisi Chakra. Ini penting saat Anda mengirim dalam volume besar dan tidak bisa membiarkan pesan hilang tanpa disadari
- Wrapper yang sederhana dan transparan — endpoint berformat Meta benar-benar hanya meneruskan permintaan, sehingga dokumentasi Meta tetap berlaku sepenuhnya; Anda tidak perlu mempelajari lapisan abstraksi khusus untuk setiap kebutuhan
- Harga transparan tanpa markup — Anda membayar tarif standar Meta untuk pesan template, tanpa margin tersembunyi
- Webhook real-time — peristiwa masuk dan perubahan status pengiriman tersedia saat terjadi, sehingga sistem Anda dapat merespons kegagalan (seperti kasus 131049/131026 di atas) tanpa polling
Jika Anda membangun sistem notifikasi yang perlu berkembang melampaui beberapa pengiriman uji coba, sebaiknya bangun sistem tersebut langsung dengan API Chakra daripada merangkai sendiri panggilan Meta Cloud API tingkat rendah — logika percobaan ulang, penanganan batas laju, dan pengiriman yang terhubung dengan CRM sudah tersedia.
Dapatkan kunci API Anda dan mulai mengirim — daftar, hubungkan WABA Anda, dan Anda dapat mengirim pesan template pertama dalam hitungan menit.