
Kalau kamu sudah pegang WhatsApp API untuk toko online atau SaaS, cepat atau lambat kamu ketemu satu pertanyaan: kapan boleh balas pelanggan dengan teks biasa, dan kapan API memaksa memakai template yang disetujui Meta. Jawabannya ada di jendela 24 jam WhatsApp API, yaitu tenggang 24 jam yang terbuka setiap kali pelanggan mengirim pesan masuk ke nomor bisnismu. Selama jendela itu terbuka kamu bisa membalas dengan pesan bebas lewat POST /api/v1/public/messages/send. Begitu 24 jam lewat tanpa pesan baru dari pelanggan, gateway menolak pesan bebas dan mengembalikan galat 24-hour messaging window expired. Use template message., jadi satu-satunya cara menghubungi lagi adalah template yang sudah disetujui Meta.
Aturan ini kami tegakkan di WhatsApp API api.co.id, dan gateway melacak batasnya untuk setiap pelanggan. Ambil satu percakapan toko online: pembeli bertanya status pesanan, CS membalas bebas hari itu, lalu keesokan harinya resi berubah saat jendela sudah tutup dan kamu harus beralih ke template.
Apa itu jendela 24 jam WhatsApp API
Jendela 24 jam adalah aturan dari Meta yang kami tegakkan di sisi gateway. Meta hanya mengizinkan bisnis mengirim pesan bebas ke pelanggan selama 24 jam sejak pelanggan terakhir menghubungi. Lewat dari itu, isi yang kamu kirim harus berupa template yang sudah direview dan disetujui. Kamu bisa membaca dasarnya di dokumentasi resmi Meta soal customer service window.
Di gateway kami, batas itu disimpan sebagai nilai windowExpiresAt per pelanggan. Jadi kamu tidak perlu menghitung sendiri kapan jendela tutup; kamu tinggal menanyakannya ke gateway sebelum mengirim.
Dari mana 24 jam dihitung dan kapan reset
Jam dihitung dari pesan masuk terakhir pelanggan. Saat pelanggan mengirim pesan, gateway membuka jendela dengan rumus windowExpiresAt = lastInboundMessageAt + 24 jam dan menandai isWindowActive = true. Balasan yang kamu kirim tidak menggeser timer; yang menggeser hanya pesan masuk baru dari pelanggan.
Contohnya pukul 10:00 pelanggan tanya “Pesanan saya sampai mana?”. Jendela terbuka dan windowExpiresAt jatuh pada 10:00 keesokan harinya. Kalau pelanggan membalas lagi pukul 15:00, timer di-reset ke 24 jam penuh dari pukul 15:00 itu. Setiap pesan masuk baru memberi kamu 24 jam segar.
Tiga status jendela: active, expired, never_messaged
Gateway merangkum keadaan pelanggan ke dalam tiga status. active berarti ada pesan masuk kurang dari 24 jam lalu, jadi kamu masih boleh balas bebas. expired berarti pesan masuk terakhir sudah lewat 24 jam, dan pesan bebas akan ditolak. never_messaged berarti pelanggan belum pernah mengirim pesan; di sini helper gateway memberi alasan Customer has never sent a message. Only template messages allowed., jadi pelanggan baru hanya bisa kamu hubungi dengan template.
Apa yang boleh dikirim selama jendela terbuka
Selama status active, kamu bebas mengirim teks biasa tanpa menunggu approval template. Pakai message_type: "text" dengan isi di field text, panjang maksimal 1024 karakter. Ini yang membuat inbox tim bisa menjawab pertanyaan seperti chat biasa, dan juga yang dipakai balasan otomatis saat pelanggan sedang aktif. Tim yang membalas dari satu inbox omnichannel bekerja di dalam jendela yang sama.
curl -X POST https://use.api.co.id/api/v1/public/messages/send \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"phone_number": "6281234567890",
"channel": "whatsapp",
"message_type": "text",
"whatsapp_phone_number_id": "1067xxxxx",
"text": "Halo, pesanan INV-12345 sedang disiapkan ya."
}'
Pastikan isi text tidak melebihi 1024 karakter; skema di gateway akan menolak yang lebih panjang.
Kapan wajib template dan error yang muncul
Begitu 24 jam lewat tanpa pesan baru, jendela dinonaktifkan di sisi gateway. Kalau kamu nekat mengirim pesan bebas setelah itu, gateway menolaknya dengan payload berikut.
{
"message": "24-hour messaging window expired. Use template message.",
"windowStatus": {
"isActive": false,
"expiredAt": "2026-10-10T10:00:00Z",
"lastInboundAt": "2026-10-09T10:00:00Z"
}
}
Kamu memakai blok windowStatus untuk mengambil keputusan di kode: begitu isActive bernilai false, jangan coba kirim teks lagi, langsung alihkan ke template. Nilai lastInboundAt berguna kalau kamu ingin menampilkan ke CS kapan pelanggan terakhir menghubungi.
Supaya alur lengkapnya kelihatan, begini perjalanan satu percakapan: pesan masuk membuka jendela, ada zona 24 jam tempat kamu boleh balas bebas, lalu titik expiry, lalu zona tempat hanya template yang lolos, dan timer kembali ke nol saat pelanggan mengirim pesan berikutnya.

Mengecek sisa waktu lewat endpoint window-status
Daripada menebak, tanyakan dulu ke gateway sebelum mengirim. Kamu bisa membaca sisa waktu jendela seorang pelanggan lewat GET /api/v1/public/customers/{id}/window-status.
curl https://use.api.co.id/api/v1/public/customers/cus_8f2a/window-status \
-H "Authorization: Bearer YOUR_API_KEY"
{
"success": true,
"data": {
"status": "active",
"windowExpiresAt": "2026-10-10T10:00:00Z",
"remainingTime": { "hours": 5, "minutes": 12, "seconds": 0, "totalSeconds": 18720 },
"remainingTimeFormatted": "5h 12m"
}
}
Nilai remainingTimeFormatted bisa dipakai untuk badge di inbox CS (“sisa 5j 12m”), sedangkan status dan totalSeconds yang kamu pakai di backend untuk memutuskan apakah pesan dikirim sebagai teks atau template. Cek ini menyelamatkan kamu dari kirim yang pasti gagal.
Membuat, mengajukan, dan mengirim template
Template dibuat dalam dua langkah. Pertama kamu membuatnya dengan POST /api/v1/public/templates yang mendarat dengan status PENDING, lalu kamu ajukan ke Meta lewat POST /api/v1/public/templates/{id}/submit. Review Meta berjalan dari beberapa menit sampai 24 jam, dan statusnya berubah jadi APPROVED atau REJECTED. Begini bentuk pembuatannya untuk notifikasi pesanan.
curl -X POST https://use.api.co.id/api/v1/public/templates \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"template_name": "order_update",
"category": "UTILITY",
"language": "id",
"body": "Halo {{1}}, pesanan {{2}} Anda sedang diproses.",
"variables": ["Budi", "INV-12345"],
"whatsapp_phone_number_id": "1067xxxxx"
}'
Kategori menentukan jenis template: MARKETING untuk promosi, UTILITY untuk pemberitahuan transaksi seperti notifikasi transaksi otomatis, dan AUTHENTICATION untuk OTP. Khusus AUTHENTICATION, badan pesannya digenerate otomatis dan hanya punya tombol OTP tanpa footer, jadi kamu tidak menulis isinya sendiri saat ingin kirim OTP WhatsApp.
Setelah template APPROVED, ambil namanya lewat GET /api/v1/public/templates?status=APPROVED, lalu kirim dengan message_type: "template". Kirim ini lolos walau jendela sedang tutup.
curl -X POST https://use.api.co.id/api/v1/public/messages/send \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"phone_number": "6281234567890",
"channel": "whatsapp",
"message_type": "template",
"whatsapp_phone_number_id": "1067xxxxx",
"template": {
"name": "order_update",
"language": { "code": "id" },
"components": [
{ "type": "body", "parameters": [
{ "type": "text", "text": "Budi" },
{ "type": "text", "text": "INV-12345" }
] }
]
}
}'
Siapkan template yang sering kamu pakai jauh sebelum kamu membutuhkannya, karena approval bisa makan waktu sampai 24 jam. Untuk kirim ke banyak nomor sekaligus dengan template, kamu beralih ke blast WhatsApp resmi.
Apakah pesan dalam jendela kena biaya
Pesan bebas yang kamu kirim selama jendela 24 jam masih terbuka tidak ditagih Meta sampai 1000 pesan per bulan, dan selebihnya akan ditagihkan dengan tarif services, selengkapnya bisa cek disini https://whatsappbusiness.com/products/platform-pricing/. Di luar jendela, satu-satunya jalan adalah template, dan di situlah penagihan berbasis template berlaku. Jadi selain lebih cepat, membalas selama pelanggan masih aktif juga menekan biaya, karena balasan di dalam jendela gratis dari sisi Meta hingga 1000 pesan per bulan.
Menerapkannya di kode tanpa mengirim pesan yang pasti gagal
Pola yang kami sarankan: cek status dulu, baru pilih jalur kirim. Backend memanggil window-status, lalu bercabang berdasarkan status.
const { data } = await getWindowStatus(customerId);
if (data.status === "active") {
// pelanggan masih dalam jendela, balas bebas
await sendMessage({ message_type: "text", text: balasan });
} else {
// expired atau never_messaged, pakai template APPROVED
await sendMessage({ message_type: "template", template: orderUpdate });
}
Dengan cek ini, toko online di kasus kita jalan mulus. Pembeli bertanya pukul 10:00 dan CS membalas bebas hari itu. Keesokan hari pukul 11:00 resi berubah, pembeli sudah diam lebih dari 24 jam, backend melihat status: expired, lalu otomatis mengirim template UTILITY order_update alih-alih teks yang pasti ditolak. Saat pembeli membalas template, jendela terbuka lagi, timer reset, dan sistem boleh kembali berkirim bebas untuk menuntaskan percakapan.
Pertanyaan yang sering muncul
Apakah aturan jendela 24 jam berlaku untuk broadcast?
Ya. Broadcast lewat POST /api/v1/public/broadcast/send ke nomor yang di luar jendela wajib memakai template yang sudah APPROVED, sama seperti kirim satuan. Kamu tidak bisa mengirimim pesan bebas ke daftar pelanggan yang sudah lama diam.
Kenapa indikator sedang mengetik juga ditolak setelah jendela tutup?
Karena indikator mengetik ikut terikat jendela yang sama. Setelah 24 jam lewat, percobaan mengirimnya ditolak dengan 24-hour messaging window expired. Cannot send typing indicator.. Jadi kalau indikator tidak muncul, itu tanda jendela pelanggan sudah expired dan kamu memang harus beralih ke template.






