← Back to dashboard
whatsapp-business-apicommsfresh

WhatsApp Business API Integration Guide

What is the WhatsApp Business API?

The real model

Meta Cloud API or Twilio BSP — templates + 24h sessions + interactive buttons.

Two paths: Meta's *Cloud API* (direct, cheaper at scale, more setup overhead) or *Twilio* (faster onboarding, slightly higher per-message). Templates carry pre-approved content with variable slots (`{{1}}`, `{{2}}`); sessions are free-form text/media within 24h of the user's last inbound. Interactive message types: list pickers, reply buttons, call-to-action buttons (link/phone). Webhooks deliver inbound messages + delivery status — verify the `X-Hub-Signature-256` header on every call. For codeAmani, WhatsApp is the channel of choice for any East-African user-facing notification flow — far higher engagement than SMS or email.

Five WhatsApp primitives

Templates for outbound. 24h windows for free-form. Webhooks for everything inbound.

Text
██╗    ██╗██╗  ██╗ █████╗ ████████╗███████╗ █████╗ ██████╗ ██████╗
██║    ██║██║  ██║██╔══██╗╚══██╔══╝██╔════╝██╔══██╗██╔══██╗██╔══██╗
██║ █╗ ██║███████║███████║   ██║   ███████╗███████║██████╔╝██████╔╝
██║███╗██║██╔══██║██╔══██║   ██║   ╚════██║██╔══██║██╔═══╝ ██╔═══╝
╚███╔███╔╝██║  ██║██║  ██║   ██║   ███████║██║  ██║██║     ██║
 ╚══╝╚══╝ ╚═╝  ╚═╝╚═╝  ╚═╝   ╚═╝   ╚══════╝╚═╝  ╚═╝╚═╝     ╚═╝

██████╗ ██╗   ██╗███████╗██╗███╗   ██╗███████╗███████╗███████╗     █████╗ ██████╗ ██╗
██╔══██╗██║   ██║██╔════╝██║████╗  ██║██╔════╝██╔════╝██╔════╝    ██╔══██╗██╔══██╗██║
██████╔╝██║   ██║███████╗██║██╔██╗ ██║█████╗  ███████╗███████╗    ███████║██████╔╝██║
██╔══██╗██║   ██║╚════██║██║██║╚██╗██║██╔══╝  ╚════██║╚════██║    ██╔══██║██╔═══╝ ██║
██████╔╝╚██████╔╝███████║██║██║ ╚████║███████╗███████║███████║    ██║  ██║██║     ██║
╚═════╝  ╚═════╝ ╚══════╝╚═╝╚═╝  ╚═══╝╚══════╝╚══════╝╚══════╝    ╚═╝  ╚═╝╚═╝     ╚═╝

WhatsApp Business API Integration Guide

Focus: Reach Kenyan/East-African users on their dominant chat app via two paths — Meta Cloud API (direct, lowest cost, full control) or Twilio (a wrapper that unifies WhatsApp with SMS/Voice and smooths webhooks). The operational crux for both is the 24-hour session window + pre-approved template messages.

Overview

Here's the big picture at a glance — two routes to the same WhatsApp user, with the 24-hour window deciding session vs template:

WhatsApp Business has no single SDK — it's a platform with two common integration routes:

  • Meta Cloud API — pure REST against the Graph API. Lowest per-message cost and full control; you manage access tokens, template submission, and webhook signature verification yourself.
  • Twilio — the twilio helper library (Node/Python) wraps WhatsApp behind the same messages.create interface as SMS, with whatsapp: prefixes and built-in webhook tooling. Faster to ship; priced above Meta's raw rates.

Key rule (both paths): you may send free-form session messages only within 24 hours of the user's last inbound message. Outside that window you must send a pre-approved template message (templates are submitted to Meta and categorized: marketing, utility, authentication).

Official Documentation


Path A — Meta Cloud API (REST)

You've got this — here's the full Cloud API round trip, from opening a conversation with a template to handling the user's reply inside the 24h window:

Get a Phone Number ID + System User Access Token from the Meta dashboard, then POST to the Graph API:

Bash
# Free-form text (only valid inside the 24h window)
curl 'https://graph.facebook.com/v23.0/<PHONE_NUMBER_ID>/messages' \
  -H 'Authorization: Bearer <ACCESS_TOKEN>' -H 'Content-Type: application/json' \
  -d '{
    "messaging_product": "whatsapp",
    "recipient_type": "individual",
    "to": "254712345678",
    "type": "text",
    "text": { "body": "Hello!" }
  }'
Bash
# Template message (required to OPEN a conversation / outside the 24h window)
curl 'https://graph.facebook.com/v23.0/<PHONE_NUMBER_ID>/messages' \
  -H 'Authorization: Bearer <ACCESS_TOKEN>' -H 'Content-Type: application/json' \
  -d '{
    "messaging_product": "whatsapp",
    "to": "254712345678",
    "type": "template",
    "template": { "name": "order_confirmation", "language": { "code": "en" } }
  }'

Verify inbound webhooks with the X-Hub-Signature-256 header (HMAC-SHA256 of the raw body using your app secret) before processing.

Creating & getting templates approved

The order_confirmation template above doesn't exist until you submit it to Meta and it's approved. Every template carries a categoryUTILITY (transactional: receipts, OTP-free order updates), MARKETING (promos, re-engagement), or AUTHENTICATION (one-time passcodes) — and Meta prices and reviews them by category. After you submit, the template enters PENDING, then moves to APPROVED or REJECTED (with a rejection reason); only an APPROVED template can be sent. Templates are created against your WhatsApp Business Account (WABA) ID, not the Phone Number ID used to send.

Bash
# Create a UTILITY template (submitted for approval -> PENDING)
curl 'https://graph.facebook.com/v23.0/<WABA_ID>/message_templates' \
  -H 'Authorization: Bearer <ACCESS_TOKEN>' -H 'Content-Type: application/json' \
  -d '{
    "name": "order_confirmation",
    "language": "en",
    "category": "UTILITY",
    "components": [
      {
        "type": "BODY",
        "text": "Hi {{1}}, your order {{2}} is confirmed. Asante!",
        "example": { "body_text": [["Amani", "KE-1024"]] }
      },
      { "type": "FOOTER", "text": "codeAmani Labs" }
    ]
  }'

The {{1}}/{{2}} positional placeholders require an example so reviewers can see real values; when you later send, you fill them via the template's parameters. Check status before sending — filter by name on the WABA endpoint:

Bash
# Confirm APPROVED before sending outside the 24h window
curl 'https://graph.facebook.com/v23.0/<WABA_ID>/message_templates?name=order_confirmation' \
  -H 'Authorization: Bearer <ACCESS_TOKEN>'
# -> data[].status: PENDING | APPROVED | REJECTED

Gotcha: outside the 24-hour window you can send only APPROVED templates — a free-form text or a PENDING/REJECTED template will be dropped. Categorize honestly: MARKETING templates cost more than UTILITY and a marketing-flavored message submitted as UTILITY will be re-categorized or rejected by Meta, breaking your send flow. (Twilio users: the same Meta-approved templates apply, referenced by contentSid instead of name.)

Path B — Twilio

Bash
npm install twilio        # or: pip install twilio
JavaScript
const client = require("twilio")(
  process.env.TWILIO_ACCOUNT_SID,
  process.env.TWILIO_AUTH_TOKEN,
);

await client.messages.create({
  from: "whatsapp:+14155238886",     // Twilio WhatsApp sandbox / your approved sender
  to: "whatsapp:+254712345678",      // note the whatsapp: prefix + E.164 with +
  body: "Hello from codeAmani",      // session message; use contentSid for templates
});

Twilio signs inbound webhooks with X-Twilio-Signature — validate it with the SDK's validateRequest helper.

codeAmani notes

  • Highest-reach channel in Kenya: WhatsApp is the default chat app — pair it with M-Pesa for conversational commerce (browse/order over WhatsApp, charge via Daraja STK Push, confirm via a utility template). Complements the SMS/USSD reach of africas-talking.
  • Phone format trap: WhatsApp uses E.164 with + (+254…); Twilio additionally needs the whatsapp: prefix. The repo's Daraja rule is 254… (no +) — normalize per-API. See MPESA_PATTERNS.md.
  • Pick a path: default to Meta Cloud API for cost + control once volume justifies the webhook/template work; reach for Twilio to ship fast or to keep one vendor across SMS/Voice/WhatsApp.
  • Security: access tokens / auth tokens are server-side only (.env.local / Vercel env, or move them into Infisical). Always verify webhook signatures (X-Hub-Signature-256 for Meta, X-Twilio-Signature for Twilio) before acting on a payload.
  • Templates need approval: budget lead time — marketing/utility/auth templates are reviewed by Meta before they can send.