Lesson 2 / 7 · 12 min
Connecting to the WhatsApp Cloud API
Meta app, test number, webhook verification, receiving a message, sending a reply. Checkpoint: a working echo bot.
Step 1: the Meta side
At developers.facebook.com create an app, add the WhatsApp product, and open API Setup. Meta gives you a free test phone number; copy two values into .env: the temporary access token (WHATSAPP_TOKEN) and the Phone number ID (PHONE_NUMBER_ID). Add your own WhatsApp number as a verified recipient, the test number can message up to five.
Step 2: webhook verification (server.js)
import "dotenv/config";
import express from "express";
const app = express();
app.use(express.json());
// Meta calls this ONCE when you save the webhook URL.
// Echo the challenge back if the verify token matches.
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 === process.env.VERIFY_TOKEN) {
return res.status(200).send(challenge);
}
res.sendStatus(403);
});
app.listen(process.env.PORT, () =>
console.log("bot listening on port " + process.env.PORT),
);Step 3: receive messages
// server.js (below the GET handler)
app.post("/webhook", (req, res) => {
res.sendStatus(200); // ack immediately, always
const value = req.body?.entry?.[0]?.changes?.[0]?.value;
const msg = value?.messages?.[0];
if (!msg || msg.type !== "text") return;
console.log("from " + msg.from + ": " + msg.text.body);
});Step 4: send a reply (wa.js)
export async function sendText(to, body) {
const url =
"https://graph.facebook.com/v21.0/" +
process.env.PHONE_NUMBER_ID +
"/messages";
const res = await fetch(url, {
method: "POST",
headers: {
Authorization: "Bearer " + process.env.WHATSAPP_TOKEN,
"Content-Type": "application/json",
},
body: JSON.stringify({
messaging_product: "whatsapp",
to,
text: { body },
}),
});
if (!res.ok) console.error("send failed:", await res.text());
}Step 5: tunnel and wire it up
# terminal 1
node server.js
# terminal 2
ngrok http 3000
# copy the https URL, e.g. https://abc123.ngrok-free.app
# In the Meta console -> WhatsApp -> Configuration:
# Callback URL: https://abc123.ngrok-free.app/webhook
# Verify token: the VERIFY_TOKEN from your .env
# Subscribe to the "messages" fieldCheckpoint: import sendText into server.js and inside the POST handler call sendText(msg.from, "Anda menulis: " + msg.text.body). Message the test number from your phone. If the bot echoes back, the entire pipe works: WhatsApp to Meta to ngrok to your code and back. Everything after this is logic.
What you take away
- The test number gives you the real official API free, to 5 recipients
- GET /webhook answers Meta's one-time verification challenge
- POST /webhook acks fast, then parses entry/changes/value/messages
- sendText() wraps the Graph API send; the echo bot proves the whole pipe
Curated sources
Hand-picked references for going deeper, reviewed by our team.