In this article
WhatsApp is the messaging app most Malaysians use every day. If you are building a SaaS product, CRM, or customer engagement tool for the Malaysian market, WhatsApp Business API integration is not optional. It is expected.
This guide covers everything you need to integrate the WhatsApp Cloud API into your product, with specific notes for Malaysian businesses.
Cloud API vs On-Premise API
Meta fully retired (sunset) the On-Premises API on 23 October 2025. The Cloud API is now the only path for any integration.
| Feature | Cloud API | On-Premise (Retired) |
|---|---|---|
| Hosting | Meta-managed | Self-hosted |
| Setup time | Minutes | Days to weeks |
| Scaling | Automatic | Manual infrastructure |
| Cost | Free API access | Server costs + licensing |
| Maintenance | Handled by Meta | Ongoing ops burden |
| Media storage | 30 days on Meta servers | Your own storage |
The Cloud API is hosted entirely by Meta. You send HTTP requests to graph.facebook.com, and Meta handles delivery, encryption, and infrastructure. There is no server to manage, no Docker container to run, and no WhatsApp client to patch.
For Malaysian developers building products today, Cloud API is the only sensible choice.
Meta Business Verification for Malaysian Businesses
Before you can send messages to customers, your Meta Business Account must be verified. For Malaysian businesses, this means:
Required Documents
- SSM Certificate (Sijil Pendaftaran Perniagaan / Suruhanjaya Syarikat Malaysia), the primary document Meta accepts for Malaysian business verification
- A business phone number Meta can reach for verification
- Business website with matching domain
Verification Process
- Go to Meta Business Settings and click "Start Verification"
- Enter your business details exactly as they appear on your SSM certificate
- Upload your SSM cert (PDF or clear photo)
- Meta reviews the submission, which usually takes a few business days
- You may receive a verification code via phone or email
Common Rejection Reasons
- Business name does not match SSM certificate exactly (watch for Sdn Bhd vs SDN BHD)
- Website domain does not match the business name
- SSM registration has lapsed (sole proprietors and partnerships must renew theirs; companies should keep their SSM filings current)
- Documents or contact details that do not match what Meta can check
Once verified, your business can move up to higher messaging limits more quickly.
Embedded Signup: The SaaS-Friendly Approach
If you are building a SaaS platform where your clients need their own WhatsApp Business Account (WABA), Embedded Signup is the correct approach.
How It Works
- Your client clicks "Connect WhatsApp" in your app
- A Meta popup guides them through creating or connecting a WABA
- On completion, you receive an access token and WABA ID
- Your client owns the WABA, and you get API access as a Tech Provider or Solution Partner
This is how production platforms like ForwardChat handle multi-tenant WhatsApp integration. Each client connects their own number, owns their data, and your platform orchestrates the messaging layer.
Why Embedded Signup Wins for SaaS
- Client ownership: The client owns their WABA and phone number, not you
- No manual onboarding: Eliminates back-and-forth CSV uploads and support tickets
- Fast setup: Client goes from zero to sending messages in minutes
- Compliance: Each client handles their own Meta Business Verification
Implementation
Add the Meta SDK to your frontend:
// Load Facebook SDK
window.fbAsyncInit = function () {
FB.init({
appId: 'YOUR_APP_ID',
autoLogAppEvents: true,
xfbml: true,
version: 'v21.0'
});
};
// Launch Embedded Signup
function launchWhatsAppSignup() {
FB.login(
function (response) {
if (response.authResponse) {
const accessToken = response.authResponse.accessToken;
// Send this token to your backend
// Exchange for a permanent System User token
console.log('Access token:', accessToken);
}
},
{
config_id: 'YOUR_CONFIG_ID',
response_type: 'code',
override_default_response_type: true,
extras: {
setup: {
// Pre-fill business info if available
}
}
}
);
}
Webhook Setup: Receiving Messages
Your webhook endpoint handles two things: verification challenges from Meta, and incoming message payloads.
Express.js Webhook Handler
import express from 'express';
const app = express();
app.use(express.json());
const VERIFY_TOKEN = process.env.WHATSAPP_VERIFY_TOKEN;
// Verification challenge (GET)
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 === VERIFY_TOKEN) {
console.log('Webhook verified');
return res.status(200).send(challenge);
}
return res.sendStatus(403);
});
// Incoming messages (POST)
app.post('/webhook', (req, res) => {
const body = req.body;
if (body.object !== 'whatsapp_business_account') {
return res.sendStatus(404);
}
// Always respond 200 quickly, then process async
res.sendStatus(200);
for (const entry of body.entry) {
for (const change of entry.changes) {
if (change.field !== 'messages') continue;
const messages = change.value.messages || [];
const contacts = change.value.contacts || [];
for (const message of messages) {
handleIncomingMessage(message, contacts, change.value.metadata);
}
}
}
});
function handleIncomingMessage(message: any, contacts: any[], metadata: any) {
const from = message.from; // sender phone number
const phoneNumberId = metadata.phone_number_id;
switch (message.type) {
case 'text':
console.log(\`Text from \${from}: \${message.text.body}\`);
break;
case 'image':
case 'document':
case 'video':
console.log(\`Media from \${from}: \${message[message.type].id}\`);
// Download media via GET /v21.0/{media-id}
break;
case 'interactive':
// Button replies, list replies
console.log(\`Interactive from \${from}\`);
break;
default:
console.log(\`Unhandled type: \${message.type}\`);
}
}
Sending Replies
async function sendTextMessage(
phoneNumberId: string,
to: string,
text: string,
accessToken: string
) {
const response = await fetch(
\`https://graph.facebook.com/v21.0/\${phoneNumberId}/messages\`,
{
method: 'POST',
headers: {
'Authorization': \`Bearer \${accessToken}\`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
messaging_product: 'whatsapp',
recipient_type: 'individual',
to,
type: 'text',
text: { preview_url: false, body: text }
})
}
);
return response.json();
}
Handling Media Downloads
async function downloadMedia(mediaId: string, accessToken: string) {
// Step 1: Get media URL
const metaRes = await fetch(
\`https://graph.facebook.com/v21.0/\${mediaId}\`,
{ headers: { Authorization: \`Bearer \${accessToken}\` } }
);
const { url } = await metaRes.json();
// Step 2: Download the actual file
const fileRes = await fetch(url, {
headers: { Authorization: \`Bearer \${accessToken}\` }
});
return Buffer.from(await fileRes.arrayBuffer());
}
Per-Message Pricing
Since 1 July 2025, Meta charges per delivered template message, not per 24-hour conversation. The old conversation-based model is deprecated. Understanding the pricing model is critical before you build.
| Category | Who Opens | Cost (Malaysia, roughly) | Example |
|---|---|---|---|
| Marketing | You (business) | ~RM 0.35 / message | Promotions, announcements |
| Utility | You (business) | ~RM 0.06 / message (free inside an open service window) | Order updates, receipts |
| Authentication | You (business) | ~RM 0.06 / message | OTP codes |
| Service | Customer initiates | Free (non-template replies inside the 24-hour window) | Customer replies to you |
Rates change from time to time and Meta now offers billing in MYR, so check Meta's current rate card before you set your own prices.
What Is Free
When a customer messages you first, your free-form replies inside the 24-hour customer service window cost nothing, and so do utility templates sent inside that window. For many Malaysian SMEs, most of their volume is customers messaging first, so the bill stays small.
Key Pricing Rules
- The customer service window opens when a customer messages you and closes 24 hours after their last message
- Each delivered template message is charged on its own, by category and the recipient's country code
- If a customer messages you and you reply with free-form text within 24 hours, that reply is free
- If you initiate contact, you must use an approved template message, and it is charged as marketing, utility or authentication
Template Messages
You cannot send the first message to a customer using free-form text. You must use a pre-approved template.
Creating Templates
Templates are created in the WhatsApp Manager or via API:
async function createTemplate(wabaId: string, accessToken: string) {
const response = await fetch(
\`https://graph.facebook.com/v21.0/\${wabaId}/message_templates\`,
{
method: 'POST',
headers: {
Authorization: \`Bearer \${accessToken}\`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: 'order_confirmation',
language: 'en',
category: 'UTILITY',
components: [
{
type: 'BODY',
text: 'Hi {{1}}, your order #{{2}} has been confirmed. Estimated delivery: {{3}}.',
example: {
body_text: [['Ahmad', '12345', '2 March 2026']]
}
}
]
})
}
);
return response.json();
}
Approval Tips
- Meta usually reviews templates quickly, often within minutes and sometimes up to a day or two
- Use clear, specific language, because vague templates get rejected
- Do not include URLs in the body text unless using a URL button component
- Keep promotional wording ("free", "win", "prize") out of utility templates, or Meta may reject or recategorise them as marketing
- Include example values for all variables
- Malay language templates are supported: set
language: 'ms'
Common Pitfalls
The 24-Hour Window
Once a customer messages you, you have 24 hours to reply with free-form text. After that window closes, you must use a template message (which is usually charged). Build your system to track conversation windows and warn agents before they expire.
Rate Limits
- New business portfolios start at a messaging limit of 250 business-initiated messages per day
- Since October 2025 the limit is set per business portfolio and shared by all its numbers; it rises in steps as you verify the business and keep message quality up
- API throughput: 80 messages per second per phone number by default
- Webhook delivery: Meta retries failed webhooks for up to 7 days
Phone Number Rules
- One WABA per phone number: you cannot register the same number on two accounts
- A number on a personal WhatsApp account must be deleted from that account first
- A number already on the WhatsApp Business app can use coexistence, which keeps the app working alongside the Cloud API and can sync recent chat history
- Malaysian numbers (+60) work, both mobile and landline (landlines verify by voice call)
Quality Rating
Meta monitors your message quality. If too many users block or report you, your quality rating drops from green to yellow to red. Low quality can hold back increases to your messaging limit and, in bad cases, lead to restrictions. Monitor your quality rating in WhatsApp Manager regularly.
Production Example: ForwardChat
Building a production WhatsApp integration from scratch usually takes months once you account for edge cases: webhook reliability, media handling, multi-device support, template management, and conversation routing.
ForwardChat is our own WhatsApp AI product, built for the Malaysian market and handling about 70,000 customer messages a month. It handles Embedded Signup onboarding, hand-off to human staff, and automated replies, all running on the Cloud API infrastructure described in this guide.
If you are building a SaaS product, consider whether building the WhatsApp layer yourself is the best use of your engineering time, or whether using an existing platform makes more sense.
Integration with AI
WhatsApp API pairs well with AI services for automated customer support. You can pipe incoming messages through an LLM to generate contextual replies, classify intent, or escalate to human agents. If you are exploring this path, our AI development services cover ChatGPT and custom LLM integration for production workloads.
For many Malaysian businesses, WhatsApp's reach combined with AI-powered automation is where the best return lies.
Summary
The WhatsApp Cloud API is mature, well-documented, and free to use (you only pay for delivered template messages). For Malaysian developers:
- Start with Cloud API: on-premise is retired
- Get your SSM cert ready for Meta Business Verification
- Use Embedded Signup if building multi-tenant SaaS
- Respect the 24-hour window and plan your template strategy
- Monitor quality rating to maintain messaging limits
- Use the free service window: replies to customers who message first cost nothing
Need WhatsApp API Integration for Your Product?
Building WhatsApp into your product is straightforward for basic use cases, but production-grade integration with multi-tenant support, AI automation, and reliable webhook processing requires significant engineering investment.
Forward Genix builds and runs ForwardChat on the WhatsApp Cloud API. Get in touch to discuss your integration requirements.


