Tutorial step-by-step bikin Google Sheet jadi panel broadcast WA via Apps Script + API broadcast/create Alat WA.
Tutorial ini akan menunjukkan cara mengubah Google Sheet biasa menjadi panel broadcast WhatsApp. Tim sales/marketing tidak perlu masuk ke dashboard Alat WA - cukup isi data di Sheet, klik tombol, broadcast langsung jalan. Plus bisa pantau status real-time per baris (Sent/Delivered/Read).
Satu Google Sheet dengan kemampuan:
Buka sheets.new. Header minimal yang dibutuhkan: phone dan message. Kolom status dan broadcast_id opsional - akan auto-isi.
| A: phone | B: message | C: status | D: broadcast_id |
|---|---|---|---|
| 08123456789 | Halo Budi, ada promo Lebaran 30%! Cek toko kami ya kak. | ||
| 08987654321 | Hai Sari, lagi diskon 30% nih buat Lebaran. Stok terbatas. |
Penting: Pesan di kolom message harus sudah final - persis seperti yang akan diterima customer. Tidak ada placeholder yang akan diproses script. Kalau mau personalisasi pakai formula Sheet:
="Halo "&C2&", ada promo Lebaran 30%!"
Kalau kamu upload file .xlsx ke Drive, file akan terbuka dengan badge .XLSX di sebelah judul - menu Extensions/Ekstensi disembunyikan! Klik File -> Save as Google Sheets dulu untuk konversi ke format native. Setelah jadi Google Sheets, badge hilang dan menu Ekstensi muncul.
Code.gs// ============== KONFIGURASI ==============
const ALATWA_URL = 'https://api.alatwa.com/broadcast/create';
const STATUS_URL = 'https://api.alatwa.com/broadcast/status/';
const API_KEY = 'PASTE_API_KEY_DISINI'; // dari Profile -> API Key
const DEVICE = 'YOUR_DEVICE_ID'; // Device ID dari menu Device (BUKAN nomor WA)
const TITLE = 'Broadcast dari Sheet';
const INTERVAL_FROM = 5; // jeda min antar pesan (detik)
const INTERVAL_TO = 15; // jeda max antar pesan (detik)
const SHEET_NAME = 'Sheet1'; // ganti kalau sheet kamu nama lain
const COL_PHONE = 1;
const COL_MESSAGE = 2;
const COL_STATUS = 3;
const COL_BC_ID = 4;
// ============== MENU ==============
function onOpen() {
SpreadsheetApp.getUi()
.createMenu('Alat WA')
.addItem('Kirim Broadcast', 'kirimBroadcast')
.addItem('Refresh Status Per Baris', 'refreshStatusPerBaris')
.addItem('Cek Status Ringkasan', 'cekStatusTerakhir')
.addToUi();
}
function ui_(msg) { SpreadsheetApp.getUi().alert(msg); }
// ============== KIRIM BROADCAST ==============
function kirimBroadcast() {
const sh = SpreadsheetApp.getActive().getSheetByName(SHEET_NAME);
const lastRow = sh.getLastRow();
if (lastRow < 2) { ui_('Sheet kosong.'); return; }
const data = sh.getRange(2, 1, lastRow - 1, 2).getValues();
const receiver = [];
const rowIndices = [];
data.forEach((row, i) => {
if (!row[0] || !row[1]) return;
receiver.push({ phone: String(row[0]).trim(), message: String(row[1]) });
rowIndices.push(i + 2);
});
if (!receiver.length) { ui_('Tidak ada baris valid.'); return; }
const payload = {
device: DEVICE,
title: TITLE + ' - ' + new Date().toLocaleString('id-ID'),
date_execute: 'now',
interval_from: INTERVAL_FROM,
interval_to: INTERVAL_TO,
type: 'text',
generate_receiver: 'yes',
receiver: receiver,
};
const res = UrlFetchApp.fetch(ALATWA_URL, {
method: 'post',
contentType: 'application/json',
headers: { 'Authorization': API_KEY },
payload: JSON.stringify(payload),
muteHttpExceptions: true,
});
const json = JSON.parse(res.getContentText() || '{}');
if (json.status === 'ok') {
rowIndices.forEach(idx => {
sh.getRange(idx, COL_STATUS).setValue('Scheduled');
sh.getRange(idx, COL_BC_ID).setValue(json.id);
});
PropertiesService.getDocumentProperties().setProperty('LAST_BC_ID', json.id);
ui_('SUKSES! Broadcast dibuat.
ID: ' + json.id + '
Total: ' + receiver.length + ' nomor
Klik "Refresh Status Per Baris" beberapa detik kemudian untuk update status real-time.');
} else {
ui_('GAGAL: ' + (json.message || 'unknown'));
}
}
// ============== REFRESH STATUS PER BARIS ==============
function refreshStatusPerBaris() {
const sh = SpreadsheetApp.getActive().getSheetByName(SHEET_NAME);
const lastRow = sh.getLastRow();
if (lastRow < 2) { ui_('Sheet kosong.'); return; }
const data = sh.getRange(2, 1, lastRow - 1, 4).getValues();
const ids = [...new Set(data.map(r => String(r[3] || '').trim()).filter(Boolean))];
if (!ids.length) { ui_('Belum ada broadcast_id di kolom D.'); return; }
const phoneStatus = {};
ids.forEach(bcId => {
const res = UrlFetchApp.fetch(STATUS_URL + bcId, {
method: 'get',
headers: { 'Authorization': API_KEY },
muteHttpExceptions: true,
});
const json = JSON.parse(res.getContentText() || '{}');
if (json.status !== 'ok' || !Array.isArray(json.data)) return;
json.data.forEach(rec => {
let p = String(rec.receiver || '').replace(/D/g, '');
if (p.indexOf('0') === 0) p = '62' + p.substring(1);
phoneStatus[p] = rec.status;
});
});
let updated = 0;
data.forEach((row, i) => {
let phone = String(row[0] || '').replace(/D/g, '');
if (phone.indexOf('0') === 0) phone = '62' + phone.substring(1);
if (phone && phoneStatus[phone]) {
sh.getRange(i + 2, COL_STATUS).setValue(phoneStatus[phone]);
updated++;
}
});
ui_('Status di-update di ' + updated + ' baris.
Legenda:
- Scheduled: antri
- Sent: keluar dari engine
- Delivered: sampai HP (centang abu)
- Read: dibaca (centang biru)
- Failed: gagal');
}
// ============== CEK STATUS RINGKASAN ==============
function cekStatusTerakhir() {
const id = PropertiesService.getDocumentProperties().getProperty('LAST_BC_ID');
if (!id) { ui_('Belum pernah kirim broadcast.'); return; }
const res = UrlFetchApp.fetch(STATUS_URL + id, {
method: 'get',
headers: { 'Authorization': API_KEY },
muteHttpExceptions: true,
});
const json = JSON.parse(res.getContentText() || '{}');
if (json.status !== 'ok' || !json.summary) { ui_('Status tidak tersedia.'); return; }
const s = json.summary;
ui_(
'Status Broadcast Terakhir
' +
'ID: ' + id + '
' +
'Total : ' + (s.total || 0) + '
' +
'Pending : ' + (s.scheduled || 0) + '
' +
'Sent : ' + (s.sent || 0) + '
' +
'Delivered: ' + (s.delivered || 0) + '
' +
'Read : ' + (s.read || 0) + '
' +
'Failed : ' + (s.failed || 0)
);
}
API_KEY -> paste API Key dari step 1DEVICE -> Device ID dari menu DeviceScheduled, kolom D terisi broadcast_idSent -> Delivered -> Read sesuai progress di HP penerimaAPI /broadcast/status/{id} return data per-recipient lengkap dengan status terkini:
{
"status": "ok",
"id": "9a2c8b7d3e1f5a6c8d9e2f1b3a4c5d6e",
"summary": {
"total": "100", "scheduled": "25", "sent": "75",
"delivered": "70", "read": "50", "failed": "5", "stop": "0"
},
"data": [
{
"message_id": "BAE52XXXXXXXX",
"receiver": "6281234567891",
"status": "Read",
"date_sent": "2024-05-28 14:15:53"
}
]
}
Script refreshStatusPerBaris baca array data, match by receiver phone, lalu update kolom status di Sheet.
| Status | Arti |
|---|---|
| Scheduled | Antri di queue, belum dikirim |
| Sent | Engine sudah kirim ke server WhatsApp (1 centang abu) |
| Delivered | Sampai HP penerima (2 centang abu) |
| Read | Sudah dibaca (2 centang biru) |
| Failed | Gagal kirim (nomor invalid / blokir / dll) |
| Stop | Dihentikan manual dari dashboard |
Karena setiap baris di Sheet adalah pesan independen, bebas variasikan kalimat per baris. Atau pakai spintax di message - server otomatis pilih random:
{Halo|Hai|Hi} kak, {ada promo|lagi diskon|sale} Lebaran 30%!
Apps Script -> Triggers (icon jam) -> Add Trigger
refreshStatusPerBarisTrigger: function kirimBroadcast, time-driven Daily / Weekly.
Tambah kolom helper name di kolom E. Di kolom message pakai formula:
="Halo "&E2&", ada promo Lebaran 30% untuk semua produk!"
| Masalah | Solusi |
|---|---|
| Menu Extensions tidak muncul | File masih mode .XLSX. File -> Save as Google Sheets dulu. |
Unauthorized | API Key salah - cek Profile -> API Key |
Unknown device | Device ID salah / disconnect - cek menu Device, scan ulang QR |
| Pesan tidak masuk meski OK | Nomor invalid - script sudah auto-format dengan generate_receiver: yes |
| Apps Script timeout | List >2000 - pecah jadi beberapa batch |
| Customer komplain spam | Naikkan INTERVAL_TO ke 30-60 detik |
Terakhir diperbarui: 25 Apr 2026

Rasakan sensasi produktivitas yang lebih baik dengan menggunakan Alat WA, Anda tidak perlu membalas secara manual satu persatu karena sudah dihandle oleh robot. Anda cukup follow up customer yang membutuhkan hal spesifik yang tidak bisa dilayani oleh robot.
Anda tidak perlu menyiapkan server, anda hanya perlu berlangganan dan bisa langsung digunakan, cukup scan qrcode whatsapp maka whatsapp anda akan terhubung dengan sistem Alat WA