Integrating the Official WhatsApp Business Cloud API in Indonesia

Photo by Wikimedia Commons contributor (CC0)
No. The Cloud API is hosted by Meta and you can integrate directly through the Meta Developer dashboard and Graph API. A BSP can simplify onboarding, billing in Rupiah, and support, but it is optional. Many Indonesian teams start direct and move to a BSP only when they need local invoicing or higher-level tooling.
Only with a pre-approved message template. Free-form messages are allowed only inside the 24-hour customer service window that opens when the customer messages you. Outside that window, a template is the only way to initiate contact.
Since July 2025 Meta charges per delivered template message, with rates varying by category. Service messages inside an open 24-hour window are free, and utility templates sent inside an open window are also free. Marketing and authentication templates are billed per message, so check the current Indonesia rate card before budgeting.
Those libraries are reverse-engineered clients that violate WhatsApp's Terms of Service. They break whenever WhatsApp updates its web client and frequently get the sender's number permanently banned, which takes your whole notification flow offline. For any production system the official Cloud API is the only reliable choice.
Yes. Meta requires explicit opt-in that specifically names WhatsApp, and this aligns with Indonesia's UU PDP data protection law, which treats a phone number as personal data. Record when and how each customer consented, and honour opt-outs immediately with a stop-list.

Photo by Wikimedia Commons contributor (CC0)
Key Takeaway
For Indonesian customer notifications, use Meta's official WhatsApp Business Cloud API, not unofficial libraries. Register pre-approved message templates, open a free 24-hour service window when a customer replies, receive inbound messages via webhooks, and budget for per-message pricing that varies by category (marketing, utility, authentication, service).
In Indonesia, WhatsApp is not just a chat app — it is the default channel for everything from order confirmations to payment reminders. On a recent project for an Indonesian client, I needed the backend to fire reliable notifications the moment an order shipped or an invoice fell due. The temptation is always to grab a scraper library that automates WhatsApp Web. I strongly advise against it, and this article walks through the official path I took instead.
The official option is the WhatsApp Business Platform Cloud API, hosted by Meta. There is no server to run for WhatsApp itself — you call one HTTP endpoint, and Meta handles delivery, encryption, and account health. That single decision removes an entire class of production incidents that unofficial tools create.
Popular Node and Python libraries that drive WhatsApp Web through a headless browser look convenient because they are free and skip the approval process. But they are reverse-engineered clients that violate WhatsApp's Terms of Service. In practice they break every time WhatsApp updates its web client, and — worse — they routinely get the sender's number banned. For a business notification pipeline, a banned number means silent, total outage. Here is how the two approaches compare.
| Aspect | Official Cloud API | Unofficial library |
|---|---|---|
| Reliability | Meta-hosted, stable versioned endpoints | Reverse-engineered WhatsApp Web, breaks on updates |
| Account safety | Sanctioned by Meta, no ban risk | High risk of the number getting banned |
| Compliance | Template review and opt-in enforced | No guardrails, real legal exposure |
| Scale | Official rate limits, thousands per day | Fragile single session, easily flagged |
| Support | Meta docs plus BSP support | Community only, no recourse when it breaks |
A banned WhatsApp number is often permanent and takes your entire notification flow down with zero warning. No amount of saved cost from an unofficial library is worth that operational risk on a production system.
Any message you send to a customer who has not messaged you first must use a pre-approved template. You create the template once via the Graph API, Meta reviews it (usually within minutes, up to 24 hours), and only then can you send it. Templates support placeholder variables that you fill at send time, so one approved template covers thousands of personalised messages. Pick the right category — utility for transactional updates, marketing for promotions, authentication for one-time codes — because the category drives both approval rules and billing.
// Create a UTILITY template (Bahasa Indonesia) via the Graph API
// POST https://graph.facebook.com/v21.0/<WABA_ID>/message_templates
// Authorization: Bearer <SYSTEM_USER_TOKEN>
{
"name": "order_shipped_id",
"language": "id",
"category": "UTILITY",
"components": [
{
"type": "BODY",
"text": "Halo {{1}}, pesanan {{2}} Anda sudah dikirim dan diperkirakan tiba {{3}}."
}
]
}Write templates in Bahasa Indonesia from the start and give each one a locale suffix in its name (for example the id suffix). Approval is per language, and mixing English and Indonesian in one template body slows review and reads unnaturally to Indonesian customers.
The moment a customer sends you a message, a 24-hour customer service window opens. While it is open you can reply with free-form messages — no template required — and each fresh customer message resets the timer to another 24 hours. Once the window closes, the only way to reach that customer again is a template message. This distinction is the heart of the platform: templates for business-initiated contact, free-form service messages for conversations the customer started.
All outbound messages hit a single endpoint. You need your phone number ID from the Meta dashboard, a permanent access token, and the recipient in E.164 format — for Indonesia that means the 62 country code with no leading zero and no plus sign, so a number starting with 0812 becomes 62812. The body parameters fill the template's placeholders in order.
// POST https://graph.facebook.com/v21.0/<PHONE_NUMBER_ID>/messages
// Authorization: Bearer <ACCESS_TOKEN>
{
"messaging_product": "whatsapp",
"to": "6281234567890",
"type": "template",
"template": {
"name": "order_shipped_id",
"language": { "code": "id" },
"components": [
{
"type": "body",
"parameters": [
{ "type": "text", "text": "Budi" },
{ "type": "text", "text": "INV-2026-0817" },
{ "type": "text", "text": "besok sore" }
]
}
]
}
}To react to customer replies — and to know that a service window just opened — you subscribe to webhooks. Meta first calls your endpoint with a GET request to verify it against a token you set, then delivers every inbound message and delivery-status update as a POST. Always respond with a fast HTTP 200, otherwise Meta retries and you get duplicate deliveries. Do the heavy processing on a queue, not inline in the webhook handler.
// Express webhook: verification (GET) + inbound handling (POST)
app.get("/webhook", (req, res) => {
const mode = req.query["hub.mode"];
const token = req.query["hub.verify_token"];
const challenge = req.query["hub.challenge"];
if (mode === "subscribe" && token === process.env.VERIFY_TOKEN) {
return res.status(200).send(challenge);
}
return res.sendStatus(403);
});
app.post("/webhook", (req, res) => {
res.sendStatus(200); // ack immediately, then process
const value = req.body.entry?.[0]?.changes?.[0]?.value;
const message = value?.messages?.[0];
if (message) {
// Customer messaged us -> 24-hour service window is now open
enqueue({ from: message.from, text: message.text?.body });
}
});Since 1 July 2025, Meta moved from the old conversation-based model to per-message pricing: you are charged each time a template message is delivered, and the rate depends on the template category and the recipient's country code. Crucially, service messages you send inside an open 24-hour window are free, and as of that same change, utility templates delivered inside an open window are also free. For Indonesian numbers, utility and authentication messages are billed at a modest per-message rate — verify the current Indonesia rate card before you budget, since Meta adjusts it periodically.
| Category | Typical use | Billed? |
|---|---|---|
| Marketing | Promos, newsletters, cart reminders | Charged per delivered message |
| Utility | Order updates, receipts, invoice reminders | Free inside an open 24h window, otherwise charged |
| Authentication | OTP and login verification codes | Charged (Authentication-International rate in Indonesia) |
| Service | Your replies to a customer inside the 24h window | Free |
Design flows so transactional updates land while a window is open. If a customer just placed an order in-chat, sending the shipping update as a utility message inside that window costs nothing — real savings at Indonesian volumes.
Meta requires explicit opt-in before you message anyone. The customer must actively agree to receive WhatsApp messages from your business, the opt-in must name WhatsApp specifically, and you must record when and how consent was captured. This is not just a Meta rule — it aligns with Indonesia's personal data protection law (UU PDP), which treats a phone number as personal data requiring a lawful basis. Store the consent trail; you will want it if a customer disputes or if Meta audits your quality rating.
Put together, this is a pipeline I trust in production: approved templates for outbound alerts, a webhook that flips customers into a free service window, category-aware sending to control cost, and a consent record that keeps you compliant. It takes more setup than a scraper library, but it is the only version that survives contact with real Indonesian traffic.