What you need
- A Meta developer account and a Meta Business portfolio (the setup flow creates one if needed).
- A phone with WhatsApp to receive test messages.
curlor any HTTP client. No server is needed to send; you only need one to receive (webhooks).
Background on how WhatsApp compares with other platforms: Telegram vs Discord vs WhatsApp. The full architecture is in our WhatsApp Cloud API guide.
1. Create the app and open API Setup
- In developers.facebook.com/apps, create an app and choose the WhatsApp use case (the flow is labeled around connecting with customers through WhatsApp; Meta renames these screens from time to time).
- Open WhatsApp > API Setup. You get: - a test business phone number and its Phone number ID (not the phone number itself), - a WhatsApp Business Account ID, - a temporary access token that expires after a short time (about a day at the time of writing).
- Under "To", add your own number as a recipient and confirm the code WhatsApp sends you. The test number can only message a small allow-list of verified recipients.
2. Send the hello_world template
Business-initiated conversations must start with an approved template. Every new account has the sample hello_world template:
export WA_TOKEN="EAAG..." # temporary token from API Setup
export PHONE_ID="1234567890" # Phone number ID, not the number
export TO="15551234567" # recipient in international format, digits only
curl -s "https://graph.facebook.com/v23.0/$PHONE_ID/messages" \
-H "Authorization: Bearer $WA_TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"messaging_product\": \"whatsapp\",
\"to\": \"$TO\",
\"type\": \"template\",
\"template\": { \"name\": \"hello_world\", \"language\": { \"code\": \"en_US\" } }
}"
v23.0 is the Graph API version shown in Meta's examples at the time of writing; use the version your dashboard shows. A success response contains a messages[0].id starting with wamid.. That ID is how you match later status webhooks (sent, delivered, read).
3. Reply, then send free-form text
When the recipient replies to your template, a 24-hour customer service window opens. Inside it you can send normal messages:
curl -s "https://graph.facebook.com/v23.0/$PHONE_ID/messages" \
-H "Authorization: Bearer $WA_TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"messaging_product\": \"whatsapp\",
\"recipient_type\": \"individual\",
\"to\": \"$TO\",
\"type\": \"text\",
\"text\": { \"body\": \"Hi! This came from the Cloud API.\" }
}"
After 24 hours without a message from the user, you are back to templates only. Buttons and lists can be designed in our WhatsApp message builder.
4. Replace the temporary token
The temporary token is fine for a first test, not for a bot. Create a system user in Business Settings, assign it your app and WhatsApp account, and generate a token with the whatsapp_business_messaging and whatsapp_business_management permissions (Meta's getting-started guide also lists business_management). Store it like any secret: see securing bot tokens.
5. Receive messages with a webhook
To read replies you need an HTTPS endpoint that:
- Answers the verification GET: if
hub.modeissubscribeandhub.verify_tokenequals the value you chose in the dashboard, returnhub.challengeas plain text. - Accepts POST notifications and checks the
X-Hub-Signature-256header, an HMAC-SHA256 of the raw body keyed with your app secret. - Returns 200 quickly.
# FastAPI sketch
import hmac, hashlib, os
from fastapi import FastAPI, Request, Response
app = FastAPI()
@app.get("/webhook")
def verify(request: Request):
q = request.query_params
if q.get("hub.mode") == "subscribe" and q.get("hub.verify_token") == os.environ["WA_VERIFY"]:
return Response(q.get("hub.challenge"), media_type="text/plain")
return Response(status_code=403)
@app.post("/webhook")
async def receive(request: Request):
raw = await request.body()
sig = "sha256=" + hmac.new(os.environ["WA_APP_SECRET"].encode(), raw, hashlib.sha256).hexdigest()
if not hmac.compare_digest(sig, request.headers.get("X-Hub-Signature-256", "")):
return Response(status_code=401)
# enqueue the payload and return immediately
return Response(status_code=200)
Then subscribe the app to the messages field in the WhatsApp webhook settings. Our signature generator helps you test the HMAC check locally. Hosting options: deploying a WhatsApp bot.
Errors you will probably see
| Code | Meaning | Fix |
|---|---|---|
131030 |
Recipient phone number not in allowed list | Add and verify the number under API Setup (test numbers only). |
131047 |
Re-engagement message: more than 24 hours since the user's last message | Send an approved template instead of free text. |
132001 |
Template does not exist | Check the template name and language code exactly. |
190 |
Access token expired or invalid | Generate a new token; move to a system user token. |
100 |
Invalid parameter | Usually a malformed to number or JSON field; read error.message. |
Costs
Meta charges per delivered template message, with rates that depend on the category and the recipient's country, and service replies inside the customer service window have been free under the current model. Prices change, so check Meta's official pricing page; our WhatsApp cost calculator helps you estimate volumes.
Frequently asked questions
Can I use my personal WhatsApp number?
A number registered on the Cloud API cannot be used in the regular WhatsApp or WhatsApp Business app at the same time. Use a dedicated number for production.
Why did my template message return success but never arrive?
The API accepted it, but delivery failed later. Check the status webhook for that wamid; failures arrive there with an error code.
Do I need a verified business to start?
Not for testing with the test number. Higher messaging limits and some features require business verification, as described in Meta's documentation.
Sources
- Meta for Developers, WhatsApp Cloud API get started: developers.facebook.com/docs/whatsapp/cloud-api/get-started
- Meta for Developers, Messages reference: developers.facebook.com/docs/whatsapp/cloud-api/reference/messages
- Meta for Developers, error codes: developers.facebook.com/docs/whatsapp/cloud-api/support/error-codes
- Meta for Developers, webhooks: developers.facebook.com/docs/whatsapp/cloud-api/guides/set-up-webhooks
- Meta for Developers, pricing: developers.facebook.com/docs/whatsapp/pricing