Forward Genix LogoForward Genix
WhatsApp Business API Integration Guide for Malaysian Developers

WhatsApp Business API Integration Guide for Malaysian Developers

A practical, code-focused guide to integrating WhatsApp Cloud API for Malaysian businesses — from Meta Business Verification with SSM to webhook handlers and template messages.

Js Yau

Js Yau

Founder & Lead Developer

14 min read
177 views

# WhatsApp Business API Integration Guide for Malaysian Developers

WhatsApp has over 24 million users in Malaysia. 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 officially deprecated the on-premise API in October 2025. The Cloud API is now the only path forward for new integrations.

FeatureCloud APIOn-Premise (Deprecated)
HostingMeta-managedSelf-hosted
Setup timeMinutesDays to weeks
ScalingAutomaticManual infrastructure
CostFree API accessServer costs + licensing
MaintenanceZeroOngoing ops burden
Media storage30 days on Meta serversYour 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 downtime to worry about.

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) — this is the primary document Meta accepts for Malaysian business verification
  • Business phone number (must not already be registered on WhatsApp)
  • Business website with matching domain

Verification Process

  1. Go to Meta Business Settings and click "Start Verification"
  2. Enter your business details exactly as they appear on your SSM certificate
  3. Upload your SSM cert (PDF or clear photo)
  4. Meta will review within 1-5 business days (typically 2-3 days for Malaysian businesses)
  5. 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 certificate is expired — make sure your annual return is up to date
  • Phone number is already associated with another WhatsApp account
Once verified, you unlock higher messaging limits (1,000 → 10,000 → 100,000 messages per day).

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

  1. Your client clicks "Connect WhatsApp" in your app
  2. A Meta popup guides them through creating or connecting a WABA
  3. On completion, you receive an access token and WABA ID
  4. Your client owns the WABA — you get API access as a 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
  • Instant setup: Client goes from zero to sending messages in under 5 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 — 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 fileRes.buffer();
}

Conversation-Based Pricing

Meta charges per 24-hour conversation window, not per message. Understanding the pricing model is critical before you build.

CategoryWho OpensCost (Malaysia)Example
MarketingYou (business)~RM 0.36 / conversationPromotions, announcements
UtilityYou (business)~RM 0.20 / conversationOrder updates, receipts
AuthenticationYou (business)~RM 0.13 / conversationOTP codes
ServiceCustomer initiatesFree (first 1,000/month)Customer replies to you

Free Tier

Every WABA gets 1,000 free service conversations per month. This means if your customers message you first, the first 1,000 conversations cost nothing. For most Malaysian SMEs, this covers a significant portion of their volume.

Key Pricing Rules

  • A conversation window lasts 24 hours from the first message in that category
  • Multiple messages within the same window do not incur additional charges
  • If a customer messages you and you reply within 24 hours, that is a service conversation (free tier eligible)
  • If you initiate contact, you must use an approved template message, and it counts as marketing/utility/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

  • Templates are reviewed by Meta within 24-48 hours
  • Use clear, specific language — vague templates get rejected
  • Do not include URLs in the body text unless using a URL button component
  • Avoid words like "free", "win", "prize" — these trigger spam filters
  • 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 costs money). Build your system to track conversation windows and warn agents before they expire.

Rate Limits

  • New WABAs start at 250 business-initiated conversations per day
  • After phone number verification and quality rating, this increases to 1,000 → 10,000 → 100,000
  • API rate limit: 80 messages per second per phone number
  • Webhook delivery: Meta retries failed webhooks for up to 7 days

Phone Number Rules

  • One phone number per WABA — you cannot register the same number on two accounts
  • If the number is currently on WhatsApp (personal or Business app), you must delete that account first
  • Porting from Business app to API is supported but requires a 2-step migration
  • Malaysian numbers (+60) work without issues — both mobile and landline with SMS verification

Quality Rating

Meta monitors your message quality. If too many users block or report you, your quality rating drops from Green → Yellow → Red. A red rating restricts your messaging limits. Monitor your quality rating in WhatsApp Manager regularly.

Production Example: ForwardChat

Building a production WhatsApp integration from scratch takes 3-6 months when you account for edge cases: webhook reliability, media handling, multi-device support, template management, and conversation routing.

ForwardChat is an example of a production-grade WhatsApp platform built for the Malaysian market. It handles Embedded Signup onboarding, multi-agent chat routing, and automated responses — 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 leveraging 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.

The combination of WhatsApp's reach in Malaysia with AI-powered automation is where the highest ROI lies for most businesses.

Summary

The WhatsApp Cloud API is mature, well-documented, and free to use (you only pay for conversations). For Malaysian developers:

  1. Start with Cloud API — on-premise is deprecated
  2. Get your SSM cert ready for Meta Business Verification
  3. Use Embedded Signup if building multi-tenant SaaS
  4. Respect the 24-hour window and plan your template strategy
  5. Monitor quality rating to maintain messaging limits
  6. Start with the free tier — 1,000 service conversations per month covers most SMEs

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 has built multiple WhatsApp-integrated platforms for Malaysian and international clients. Get in touch to discuss your integration requirements.

Js Yau
Written By

Js Yau

Founder & Lead Developer

Founder & Lead Developer with 10+ years experience building enterprise software solutions. Has delivered projects for RHB Bank, Fortune 500 insurance companies, and 50+ Malaysian SMEs. Specialized in React, Next.js, Node.js, and AI integration.

Let's Build Together

Ready to Transform Your Business?

Get expert guidance on implementing the strategies discussed in this article. Book a free consultation with our digital transformation specialists.