How to Automate Business WhatsApp with the Cloud API
The 24-hour window, approved templates and what Meta requires: what decides a WhatsApp project before you write the first line.

1. The two routes, and why the choice decides everything else
There are two ways to run WhatsApp in a business, and confusing them is the mistake that costs weeks. The **WhatsApp Business app** is free, installs on a phone and exists for answering by hand. It has quick replies and labels, but no API: it does not connect to a CRM, does not receive webhooks, and automates nothing meaningful. Meta’s **WhatsApp Cloud API** is the route to automation. It is an HTTP API hosted by Meta, with no server in between. You can receive messages by webhook, reply programmatically, and integrate with whatever you like. The practical difference: on the app the number lives on a phone; on the Cloud API the number belongs to a WhatsApp Business account and **stops working in the app**. There is no middle ground, and the migration is not undone with a click. Decide this before writing a line of code. If the number you want to automate is the one your team uses on their phones, you need a different number.
2. The 24-hour window: the rule that stops everyone
This is the part almost no tutorial explains, and the one that decides what you can build. When someone messages you, a **24-hour window** opens during which you can reply in free-form text — whatever you like, as often as you like. Once 24 hours pass with no new message from the customer, the window closes. With the window closed, **you may only send templates approved in advance by Meta**. No free text. And approving a template takes time: you submit the wording, Meta reviews it, and it can be rejected for reasons ranging from aggressive promotion to formatting. What this means for design: - A bot that answers whoever writes first is simple — it is always inside the window - A system that starts conversations needs approved templates for each kind of message - Reminders, confirmations and follow-ups have to be templates, thought through in advance Design the flow around this rule from the start. Discovering it after building forces a rewrite.
3. What Meta requires before it lets you send
The account does not open in five minutes. You need: **A Meta Business account** with the business identified. For higher sending limits and the verified badge, Meta asks for company documentation — incorporation, address, proof of activity. A sole trader can start, but on lower limits. **A phone number** not active in the WhatsApp app, able to receive an SMS or call for the confirmation code. **A public HTTPS endpoint** for the webhook, with a valid certificate. Meta will not deliver to addresses without TLS, nor to IPs. **An approved message template**, if you intend to start conversations. **Sending limits** start low and rise with the quality of your conversations — if enough people block or report you, they fall. Do not buy lists or message people who did not ask: besides being illegal in the EU without consent, it destroys the quality rating and can cost you the number.
4. Receiving messages: the webhook
Meta validates your endpoint with a `GET` request carrying a challenge. You must return the value of `hub.challenge` as plain text, and only if `hub.verify_token` matches what you configured. Getting this wrong is the most common reason a subscription never activates. After validation, messages arrive by `POST`. Two details bite: Meta **retries** deliveries when it does not get a fast `200`, so acknowledge immediately and process afterwards; and the payload is nested under `entry[].changes[].value.messages[]`, not at the root.
import os
from fastapi import FastAPI, Request, Response, BackgroundTasks
app = FastAPI()
VERIFY_TOKEN = os.environ["WA_VERIFY_TOKEN"]
@app.get("/webhook")
async def verificar(request: Request):
p = request.query_params
if p.get("hub.mode") == "subscribe" and p.get("hub.verify_token") == VERIFY_TOKEN:
# Texto simples, não JSON: a Meta compara byte a byte.
return Response(content=p.get("hub.challenge"), media_type="text/plain")
return Response(status_code=403)
@app.post("/webhook")
async def receber(request: Request, tarefas: BackgroundTasks):
body = await request.json()
for entry in body.get("entry", []):
for change in entry.get("changes", []):
for msg in change["value"].get("messages", []):
tarefas.add_task(tratar, msg["from"], msg.get("text", {}).get("body", ""))
# Confirmar já: a Meta repete a entrega se demorares.
return Response(status_code=200)5. Replying through the Cloud API
Sending is a `POST` to your number’s messages endpoint, authenticated with a token. Inside the 24-hour window you send free text; outside it, templates only. Keep the `phone_number_id` and the token in environment variables. The token grants sending on behalf of your business — treat it like any other credential.
import os, httpx
PHONE_ID = os.environ["WA_PHONE_NUMBER_ID"]
TOKEN = os.environ["WA_TOKEN"]
BASE = f"https://graph.facebook.com/v21.0/{PHONE_ID}/messages"
HEADERS = {"Authorization": f"Bearer {TOKEN}"}
async def responder(para: str, texto: str):
"""Texto livre. Só funciona dentro da janela de 24 horas."""
async with httpx.AsyncClient(timeout=15) as c:
r = await c.post(BASE, headers=HEADERS, json={
"messaging_product": "whatsapp",
"to": para,
"type": "text",
"text": {"body": texto},
})
r.raise_for_status()
async def enviar_modelo(para: str, nome: str, variaveis: list[str]):
"""Fora da janela, só modelos previamente aprovados pela Meta."""
async with httpx.AsyncClient(timeout=15) as c:
r = await c.post(BASE, headers=HEADERS, json={
"messaging_product": "whatsapp",
"to": para,
"type": "template",
"template": {
"name": nome,
"language": {"code": "pt_PT"},
"components": [{
"type": "body",
"parameters": [{"type": "text", "text": v} for v in variaveis],
}],
},
})
r.raise_for_status()6. Keeping conversation state
WhatsApp gives you no sessions. Each message arrives on its own, identified only by phone number. If your flow has more than one step, state is your problem. The minimum that works is a table keyed by number holding the current step, whatever has been collected so far, and the time of the last message — that last one tells you whether the 24-hour window is still open. Two traps. First: the same person can send several messages in a row, and deliveries can arrive out of order; handle state carefully under concurrency. Second: conversations stall halfway. Set a time after which state expires, or you accumulate abandoned flows forever.
7. Adding a language model
This is where most projects go wrong: they wire a model straight into WhatsApp and let it answer everything. What works is narrower. Use the model to **classify** intent, **extract** data from free text — dates, names, references — and **rephrase** answers you already have. Keep business logic in code. Three rules that avoid the usual trouble: **Constrain the scope in the prompt and check the output.** A model that invents prices or commits to deadlines creates an obligation to a customer on your behalf. **Plan the handover to a human.** When the model does not know, or the person asks, route it onward. A bot with no way through to anyone costs more customers than it converts. **Do not forward sensitive data without thinking.** If the conversation contains personal data, you are sending it to the model provider. That has GDPR implications and belongs in your privacy policy.
8. Building from scratch or using a platform
Platforms sit between you and the Cloud API — ManyChat, Twilio, 360dialog and others. They make sense in some cases and not others. **They pay off** when whoever maintains the flow does not write code, when you need a visual builder the team can touch, or when you want to start today without dealing with a server and a webhook. **They do not** when the logic is specific to your business, when volume makes the subscription cost more than running it yourself, or when you need integrations the platform lacks. There is a less visible cost: you become tied to their data model. Migrating flows between platforms, or to your own code, usually means rewriting from scratch. In between, automation tools such as n8n or Make connect to the Cloud API without locking you into a closed builder — and n8n you can host yourself.
9. Consent and GDPR
WhatsApp is a personal channel, and the law treats it as one. **You need consent** to message someone who did not contact you first. That consent has to be specific to this channel — holding a customer’s number because of an invoice does not authorise commercial messages. **Keep the evidence** of when and how it was given. If a supervisory authority asks, the answer is a record, not an assertion. **Make opting out easy.** A clear instruction to stop, that works first time, and that you honour. **Say it is a bot.** The EU AI Act requires transparency when someone is interacting with an AI system. One sentence in the first message covers it, and costs no conversion — people work out it is automated anyway. Your privacy policy should state what you collect through this channel, how long you keep it, and which providers you share it with.
10. Costs, and when it is not worth it
Meta charges for Cloud API usage, and the pricing model has already changed more than once — per conversation, per message, with different categories depending on who starts. I am not quoting figures because they age fast: check the official table on the day you decide, and confirm which one applies to your country. On top sits the cost of what you build around it: server, maintenance, and the language model if you use one. **When it is not worth it:** if you get a handful of messages a day, a person answering in the app is cheaper and answers better. Automation starts to pay when repetitive volume is steady, when you need to answer outside hours, or when the same question arrives dozens of times a week. Before building, spend a week counting how many messages you get and how many are genuinely repeats. If most are specific, automating does not save you time — it moves it.
Frequently Asked Questions
Can I automate the number I already use in the WhatsApp Business app?
You can migrate it to the Cloud API, but it then stops working in the app — there is no simultaneous use. If your team answers from a phone on that number, use a different one to automate.
How long does message template approval take?
It varies, and it can be rejected. Submit templates ahead of time rather than the day before you need them, and avoid aggressive promotional wording, which is the most common reason for rejection.
Does the webhook have to run on my own server?
No. Any public HTTPS endpoint with a valid certificate works — a serverless function will do. What Meta will not accept is plain HTTP or an IP address.
Do I need a registered company to use the Cloud API?
To get started and test, no. For higher sending limits and the verified badge, Meta asks for business documentation. A sole trader can operate, on lower limits.
What happens if my quality rating drops?
Sending limits fall, and in persistent cases the number can be restricted. The usual cause is messaging people who did not ask. Explicit consent and an easy way out protect the rating better than any trick.