Skip to main content
RapidDev - Software Development Agency
flutterflow-integrationsFlutterFlow API Call

Whatsapp Business API

Connect FlutterFlow to WhatsApp Business API by routing all message sends through a Firebase Cloud Function that holds your permanent System User access token. FlutterFlow calls the function URL with the recipient number and template params — the Cloud Function calls Meta's WhatsApp Cloud API. Inbound replies arrive as webhooks to that same function and are written to Firestore for the app to display.

What you'll learn

  • How the WhatsApp 24-hour customer service window and template policy affects what you can send
  • Why the System User access token must live in a Cloud Function, not in FlutterFlow
  • How to build a Cloud Function that sends approved template messages via the WhatsApp Cloud API
  • How to set up a webhook Cloud Function to receive inbound WhatsApp replies
  • How to display a WhatsApp conversation thread in a FlutterFlow app using Firestore
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Advanced15 min read90 minutesCommunicationLast updated July 2026RapidDev Engineering Team
TL;DR

Connect FlutterFlow to WhatsApp Business API by routing all message sends through a Firebase Cloud Function that holds your permanent System User access token. FlutterFlow calls the function URL with the recipient number and template params — the Cloud Function calls Meta's WhatsApp Cloud API. Inbound replies arrive as webhooks to that same function and are written to Firestore for the app to display.

Quick facts about this guide
FactValue
ToolWhatsApp Business API
CategoryCommunication
MethodFlutterFlow API Call
DifficultyAdvanced
Time required90 minutes
Last updatedJuly 2026

Add WhatsApp Messaging to Your FlutterFlow App via Meta's Cloud API

WhatsApp Business API is the most powerful messaging channel available for businesses — but it comes with the most compliance requirements. Unlike Telegram or SMS, WhatsApp has a strict two-tier messaging policy. Inside the 24-hour customer service window (opened when a user messages you first), you can send any free-form text. Outside that window, or for business-initiated conversations, you must use pre-approved message templates — structured messages created in Meta Business Manager and approved by Meta before you can send them. Getting this policy wrong is the single biggest trip-up for FlutterFlow developers: the API returns error 131047 for messages sent outside the customer service window without a template.

Authentication uses a permanent System User access token generated in Meta Business Manager. This is not a short-lived user token — it's a permanent credential scoped to your WhatsApp Business Account. Because it's permanent and powerful, it must never appear in a FlutterFlow API Call header or anywhere in the compiled app bundle. The entire send flow goes through a Firebase Cloud Function: FlutterFlow sends the message parameters to the function, the function calls Meta's Cloud API (`https://graph.facebook.com/v19.0/{phone-number-id}/messages`) with the token server-side, and the result is returned to the FlutterFlow app.

For inbound messages — when a WhatsApp user replies to your bot or template — Meta delivers updates as webhooks to a server endpoint. FlutterFlow apps cannot receive webhooks (they run on devices, not servers), so a second Cloud Function registers as the webhook receiver, verifies the Meta challenge handshake, and writes incoming messages to a Firestore collection. Your FlutterFlow app reads that collection in real time using a Backend Query or Firestore listener, giving you a functional WhatsApp inbox inside the app.

Integration method

FlutterFlow API Call

WhatsApp Business API authentication uses a permanent System User access token — a powerful, long-lived credential that must never appear in a FlutterFlow API Call or compiled app. All sends go through a Firebase Cloud Function that holds the token server-side. FlutterFlow POSTs the message parameters to the function URL. Inbound messages and status callbacks arrive as webhooks to a second Cloud Function that writes them to Firestore, where a FlutterFlow ListView can read and display them in real time.

Prerequisites

  • A Meta Business Manager account with a verified WhatsApp Business Account (WABA)
  • A WhatsApp Business phone number registered in Meta Business Manager and a Phone Number ID
  • At least one approved message template in Meta Business Manager (Templates → Create Template → submit for Meta approval)
  • A Firebase project on the Blaze plan for Cloud Functions with outbound network access
  • A FlutterFlow project with Firestore configured for storing conversation data

Step-by-step guide

1

Set up your WhatsApp Business Account and get the Phone Number ID and access token

Log in to Meta Business Manager at business.facebook.com. If you don't have a WhatsApp Business Account (WABA), go to Business Settings → Accounts → WhatsApp Accounts and add one. You'll need to verify a phone number — this can be a real business number or a temporary test number from Meta's developer sandbox. Once your WABA is set up, go to Meta Developers (developers.facebook.com), find your app, and navigate to WhatsApp → API Setup. Here you'll see your Test Phone Number (for development) and the Phone Number ID — copy it. For production, you'll see your verified business phone number's ID instead. To get a permanent access token: go to Business Settings → System Users → Create a System User, assign it a role (Admin), and then generate a token with the `whatsapp_business_messaging` and `whatsapp_business_management` permissions. Copy this token — this is the long-lived credential that goes into your Cloud Function's environment config. For testing purposes, Meta also provides a temporary 24-hour access token in the API Setup page — don't use this for production. Your test sandbox pre-approves a WhatsApp number to send to: add up to 5 test phone numbers in the 'To' section of the API Setup sandbox.

Pro tip: The Phone Number ID is different from your phone number. It's a Meta-assigned numeric ID (like 1234567890123) that identifies your specific WhatsApp number within Meta's system. Use this ID in the API URL path, not the actual phone number digits.

Expected result: You have the Phone Number ID, a permanent System User access token, and at least one test recipient phone number in the Meta sandbox. A test template send from the API Setup page works.

2

Get at least one message template approved in Meta Business Manager

WhatsApp's policy restricts business-initiated messages to pre-approved templates outside the 24-hour customer service window. Go to Meta Business Manager → Accounts → WhatsApp Accounts → your account → Manage → Message Templates. Click Create Template. Choose a category (Marketing, Utility, or Authentication), select a language, and give it a name (lowercase, underscores only, e.g., `order_confirmation`). Build the template body using double curly braces for variables: 'Your order #{{1}} has been confirmed. Delivery expected: {{2}}.' Add a header (optional) and footer (optional). Submit for review. Meta's review typically takes 24-72 hours for standard templates. During development, use the pre-approved 'hello_world' template that is available in every new WhatsApp Business Account — it sends 'Hello World! This is an example message from Fable.' with no variable substitution. Once approved, your custom template appears with status 'Approved' and you can send it. The template name and language code you'll use in the API body are shown on this page — copy them.

Pro tip: Template approval is faster for Utility and Authentication categories than Marketing. If your message is an order confirmation, shipping update, or OTP, choose Utility — these are approved faster and cost less per conversation.

Expected result: At least one template with status 'Approved' in Meta Business Manager, or the hello_world template available for testing.

3

Deploy a Firebase Cloud Function that sends WhatsApp messages

In the Firebase console for your project, navigate to Functions and click Get Started (or write the function in your local project and deploy via CLI). The function below accepts a JSON body with `to` (phone number WITHOUT leading +, per WhatsApp Cloud API convention), `templateName`, `languageCode`, and an array of `components` for template variable substitution. It stores the System User access token and Phone Number ID as environment configuration variables — never hard-coded. The function calls the WhatsApp Cloud API at `https://graph.facebook.com/v19.0/{PHONE_NUMBER_ID}/messages` with a Bearer Authorization header. CORS headers are included for FlutterFlow web builds. An important WhatsApp-specific convention: the `to` phone number uses E.164 format BUT without the leading + sign (e.g., `14155551234` not `+14155551234`). This differs from Twilio's convention. Sending with a leading `+` causes delivery failures. After deployment, set the environment config with the Firebase CLI commands shown in the tip, then copy the deployed function URL.

index.js
1// Firebase Cloud Function: functions/index.js
2const functions = require('firebase-functions');
3const https = require('https');
4
5exports.sendWhatsApp = functions.https.onRequest(async (req, res) => {
6 res.set('Access-Control-Allow-Origin', '*');
7 res.set('Access-Control-Allow-Methods', 'POST, OPTIONS');
8 res.set('Access-Control-Allow-Headers', 'Content-Type');
9
10 if (req.method === 'OPTIONS') return res.status(204).send('');
11
12 const ACCESS_TOKEN = functions.config().whatsapp.token;
13 const PHONE_NUMBER_ID = functions.config().whatsapp.phone_number_id;
14
15 const { to, templateName, languageCode, components } = req.body;
16
17 if (!to || !templateName || !languageCode) {
18 return res.status(400).json({ error: 'to, templateName, and languageCode are required' });
19 }
20
21 const payload = JSON.stringify({
22 messaging_product: 'whatsapp',
23 to: to, // E.164 WITHOUT leading +
24 type: 'template',
25 template: {
26 name: templateName,
27 language: { code: languageCode },
28 components: components || []
29 }
30 });
31
32 const options = {
33 hostname: 'graph.facebook.com',
34 path: `/v19.0/${PHONE_NUMBER_ID}/messages`,
35 method: 'POST',
36 headers: {
37 'Authorization': `Bearer ${ACCESS_TOKEN}`,
38 'Content-Type': 'application/json',
39 'Content-Length': Buffer.byteLength(payload)
40 }
41 };
42
43 return new Promise((resolve) => {
44 const request = https.request(options, (response) => {
45 let data = '';
46 response.on('data', chunk => data += chunk);
47 response.on('end', () => {
48 try {
49 resolve(res.status(response.statusCode).json(JSON.parse(data)));
50 } catch (e) {
51 resolve(res.status(200).json({ raw: data }));
52 }
53 });
54 });
55 request.on('error', e => resolve(res.status(500).json({ error: e.message })));
56 request.write(payload);
57 request.end();
58 });
59});

Pro tip: Set config before deploying: `firebase functions:config:set whatsapp.token="EAA..." whatsapp.phone_number_id="12345678901234"`. Never commit these values to git.

Expected result: The Cloud Function deploys and its URL appears in the Firebase console. Testing it with a POST (action=send, to=your test number, templateName=hello_world, languageCode=en_US) sends a WhatsApp message to your sandbox test number.

4

Add the FlutterFlow API Call pointing to the Cloud Function

In your FlutterFlow project, click API Calls in the left navigation panel. Click + Add and choose Create API Group. Name it 'WhatsAppProxy'. Set the Base URL to your Cloud Function's base URL (e.g., `https://us-central1-your-project-id.cloudfunctions.net`). Leave Authentication as None — the Cloud Function handles all Meta authentication server-side. Click + Add API Call inside the group. Name it 'SendTemplate'. Set the method to POST. Set the API URL to `/sendWhatsApp`. In the Body tab, set body type to JSON. Add Variables: `to` (String), `templateName` (String), `languageCode` (String, default `en_US`). For template components (variable substitution), you can hardcode them for simple templates or add a `components` variable as a JSON string. In the Response & Test tab, enter test values: use a phone number from your Meta sandbox test recipients (without the + prefix), `templateName` = `hello_world`, `languageCode` = `en_US`. Click Test API Call — a 200 response with a `messages[0].id` in the response means success. Wire the Send action from a button in your app: bind `to` to the recipient's phone number field, `templateName` to a Dropdown or hardcoded value, and call the action. In the action chain, check for a successful response (JSON Path `$.messages[0].id` is non-empty) and show a 'Message sent via WhatsApp' Snackbar.

Pro tip: Store the recipient's WhatsApp number in Firestore WITHOUT the leading + — this is what the Cloud Function expects. If users enter their number with a + in the app's phone number field, strip it with a Custom Function before storing.

Expected result: The SendTemplate API Call test returns a 200 with a messages array containing the sent message ID. The WhatsApp message appears on the target test device.

5

Set up a webhook Cloud Function for inbound messages and display them in FlutterFlow

To receive inbound WhatsApp messages (user replies, contact initiations), you need a webhook endpoint. FlutterFlow itself cannot be a webhook receiver since it runs on devices. The flow is: Meta sends webhook events to a Cloud Function URL → the function writes messages to Firestore → the FlutterFlow app reads Firestore in real time. Deploy a second Cloud Function (code below) that handles Meta's two-step webhook setup: GET requests for the initial verification challenge (returning `hub.challenge`), and POST requests for actual messages. After deploying, go to Meta Developers → your app → WhatsApp → Configuration → Webhooks. Set the Callback URL to your function's URL and the Verify Token to any secret string you choose (store it in your function's config). Click Verify and Save. Meta sends a GET to your function with the challenge; the function must return `hub.challenge` to confirm ownership. Once verified, subscribe to the `messages` webhook field. Now in FlutterFlow, add a Backend Query to a ListView on the conversations page, binding it to the `whatsapp_messages` Firestore collection ordered by `timestamp` descending. Each document contains `from` (the sender's phone number), `text`, `timestamp`, and `message_id`. Display these in a ChatBubble-style layout to create a WhatsApp inbox view.

index.js
1// Firebase Cloud Function: webhook handler
2const admin = require('firebase-admin');
3admin.initializeApp();
4
5exports.whatsappWebhook = functions.https.onRequest(async (req, res) => {
6 // Webhook verification (GET)
7 if (req.method === 'GET') {
8 const VERIFY_TOKEN = functions.config().whatsapp.verify_token;
9 const mode = req.query['hub.mode'];
10 const token = req.query['hub.verify_token'];
11 const challenge = req.query['hub.challenge'];
12
13 if (mode === 'subscribe' && token === VERIFY_TOKEN) {
14 return res.status(200).send(challenge);
15 }
16 return res.status(403).send('Forbidden');
17 }
18
19 // Incoming message (POST)
20 if (req.method === 'POST') {
21 const body = req.body;
22 const entry = body.entry?.[0];
23 const changes = entry?.changes?.[0];
24 const value = changes?.value;
25
26 if (value?.messages) {
27 const message = value.messages[0];
28 const contact = value.contacts?.[0];
29
30 await admin.firestore().collection('whatsapp_messages').add({
31 from: message.from,
32 name: contact?.profile?.name || '',
33 text: message.text?.body || '',
34 type: message.type,
35 message_id: message.id,
36 timestamp: admin.firestore.FieldValue.serverTimestamp()
37 });
38 }
39
40 return res.status(200).send('EVENT_RECEIVED');
41 }
42});

Pro tip: Always return HTTP 200 to Meta's webhook, even if your processing fails. If Meta receives a non-200 response repeatedly, it disables the webhook and stops sending events. Process messages asynchronously if needed — return 200 first, then write to Firestore.

Expected result: The webhook function is verified in Meta's Developers portal and subscribed to the messages field. Sending a WhatsApp message to your business number creates a document in the `whatsapp_messages` Firestore collection within seconds, and it appears in the FlutterFlow ListView.

Common use cases

E-commerce app that sends WhatsApp order confirmation templates

When a customer places an order in the FlutterFlow app, an action chain calls the Cloud Function with the customer's WhatsApp number and template parameters (order ID, total, delivery date). Meta sends the pre-approved order confirmation template to the customer's WhatsApp. The customer replies to track their order, and those replies appear in a Firestore-backed inbox in the FlutterFlow admin app.

FlutterFlow Prompt

After a successful Stripe payment in the checkout flow, call the Cloud Function sendWhatsAppTemplate with the customer's phone number and order details. Show a 'WhatsApp confirmation sent' toast on success.

Copy this prompt to try it in FlutterFlow

Appointment booking app with WhatsApp reminders

A Cloud Function (triggered by a Firestore write or a scheduled function) sends a WhatsApp reminder template to the patient or client 24 hours before their appointment. The FlutterFlow app shows the booking details and allows the admin to manually trigger a reminder send via the same Cloud Function.

FlutterFlow Prompt

Add a 'Send Reminder' button to the booking detail page. When tapped, call the Cloud Function sendWhatsAppTemplate with the booking's WhatsApp number and the appointment_reminder template name and parameters.

Copy this prompt to try it in FlutterFlow

Customer support app with a WhatsApp inbox powered by Firestore

A FlutterFlow admin app shows a list of ongoing WhatsApp conversations, each backed by a Firestore document. Inbound messages arrive via a webhook Cloud Function that writes them to the `whatsapp_conversations` collection. An agent selects a conversation, reads the message history from Firestore, and replies using a free-form send (if inside the 24-hour window) or sends a template with a re-engagement prompt.

FlutterFlow Prompt

On the inbox page, add a ListView bound to the whatsapp_conversations Firestore collection sorted by last_message_at. Each item shows the contact name, last message, and unread count. Tapping opens the conversation thread.

Copy this prompt to try it in FlutterFlow

Troubleshooting

WhatsApp Cloud API returns error code 131047 with message 'Re-engagement message'

Cause: You tried to send a free-form text message to a user outside the 24-hour customer service window. WhatsApp only allows free-form messages within 24 hours of the last user-initiated message.

Solution: Use a pre-approved re-engagement template with the template type set to Marketing or Utility. Design a template in Meta Business Manager specifically for re-contacting users who haven't written in over 24 hours. The template can include a call-to-action button that invites them to reply and reopen the service window.

The Cloud Function returns 400 from the WhatsApp API with 'Parameter phone_number_id is not a valid WhatsApp phone number ID'

Cause: The Phone Number ID in the Cloud Function config is incorrect — it may be the actual phone number digits rather than Meta's internal numeric ID for the phone number.

Solution: In Meta Developers → WhatsApp → API Setup, look for the Phone Number ID field (it's a long numeric string, not the actual phone number). Copy that value and set it in your function config with `firebase functions:config:set whatsapp.phone_number_id="YOUR_NUMERIC_ID"`.

WhatsApp message fails to deliver with 'The `to` field does not represent a valid WhatsApp user'

Cause: The recipient phone number has a leading `+` sign, which the WhatsApp Cloud API does not accept in the `to` field.

Solution: Strip the leading `+` from the phone number before sending. In the Cloud Function, add a line: `const cleanTo = to.startsWith('+') ? to.slice(1) : to;` and use `cleanTo` in the API payload. This is different from Twilio's convention where E.164 WITH the `+` is required.

Meta webhook verification fails with 403 even though the Verify Token looks correct

Cause: The Verify Token in the Meta Developers portal does not exactly match the one in the Cloud Function's config, including case sensitivity and whitespace.

Solution: Verify the token string character-by-character. Set it again with `firebase functions:config:set whatsapp.verify_token="your-exact-token"` and redeploy. Also ensure the Cloud Function is deployed and publicly accessible before clicking Verify in Meta's portal.

Best practices

  • Never place the System User access token in a FlutterFlow API Call header or App Values — it is a permanent credential that grants full WhatsApp Business API access and must only exist in the Cloud Function's environment configuration or Firebase Secret Manager.
  • Design and get templates approved in Meta Business Manager before building the FlutterFlow UI — template approval takes 24-72 hours and blocks the integration until complete.
  • Store recipient phone numbers without the leading `+` in Firestore (WhatsApp Cloud API convention) — strip the `+` when the user enters their number in the app's phone field.
  • Always return HTTP 200 from your webhook Cloud Function to Meta, even if internal processing fails — non-200 responses cause Meta to retry and eventually disable the webhook subscription.
  • For conversation management, store each WhatsApp conversation as a separate Firestore document keyed by the contact's phone number — this makes it easy to query the message thread for a specific contact in the FlutterFlow inbox view.
  • Use the Utility or Authentication template category for order confirmations and OTP codes — these are approved faster and cost less per conversation than Marketing templates.
  • Monitor your WhatsApp Business Account's messaging tier in Meta Business Manager — new accounts start at 1,000 business-initiated conversations per 24 hours and can increase the limit by maintaining a good quality rating.

Alternatives

Frequently asked questions

Do I need a business phone number to use WhatsApp Business API, or can I use a personal number?

You need a phone number specifically registered for WhatsApp Business API — this cannot be a number already registered to a personal or regular WhatsApp Business app account. Most developers use a new number (a virtual number works) or migrate an existing business number from the WhatsApp Business App. The number does not need to be able to receive actual calls or SMS — Meta uses it as an identifier.

What is the 24-hour customer service window and how does it affect my app?

WhatsApp's customer service window opens when a user sends your business a message and stays open for 24 hours. Within that window, you can send any text, media, or interactive message. After 24 hours, you can only send pre-approved templates. For most app use-cases (order confirmations, reminders, alerts), you'll always be using templates since the business initiates the conversation — design your templates accordingly and get them approved before launching.

Can I send images, documents, or buttons via WhatsApp from FlutterFlow?

Yes, via message templates. WhatsApp templates support header components (image, video, document), body text with variables, footer text, and call-to-action or quick-reply buttons. For the media header, you pass a media ID or URL in the template's component. This is all handled in the Cloud Function's payload — update the `components` array in the request body to include the media type and parameters your template expects.

How much does WhatsApp Business API cost?

Meta charges per conversation (not per message), with pricing varying by category (marketing, utility, authentication, service) and the recipient's country. Some service conversations may be free (within the customer service window). Pricing changes periodically — always check the current rates in Meta's Business Help Center or pricing calculator. In general, expect per-conversation costs ranging from a few cents to a few dozen cents depending on category and country.

Can I use the WhatsApp Cloud API without Meta Business verification?

You can use the sandbox (test mode) without business verification — it supports sending to up to 5 manually added test numbers. For production use (sending to real customers who haven't opted in to the test sandbox), your WhatsApp Business Account must complete Meta's business verification. This requires uploading business documents and can take several days.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Integrations are where projects stall

We wire FlutterFlow integrations like this one every week — auth, webhooks, edge cases included. Fixed-price builds from $13K, delivered in 6–10 weeks.

Talk to an integration engineer

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.