What you'll build
This is Part 1 of a series where you build a WhatsApp bot for a small food business called Quick Bites. Customers send a WhatsApp message to place orders, browse the menu, and receive a Paystack payment link — no app download required.
By the end of the series you'll have:
- A WhatsApp menu bot that handles multi-step conversations
- A product catalogue flow with add-to-cart interaction
- Paystack payment link generation sent directly in WhatsApp
- A Cloudflare Worker deployment for zero-config global hosting
By the end of Part 1 you'll have a running webhook that receives WhatsApp messages and sends back a reply.
Prerequisites
- Node.js 20+ installed
- VS Code installed
- BlocWeave installed
- A free Twilio account — no credit card needed for sandbox
- ngrok for local testing
- A WhatsApp account on your phone (any number)
Join the Twilio WhatsApp sandbox
- In your Twilio console, go to Messaging → Try it out → Send a WhatsApp message
- You'll see a sandbox number (e.g.
+1 415 523 8886) and a join code (e.g.join bronze-sunset) - On your phone, send the join code to that number on WhatsApp — you'll get a confirmation
- Your phone is now connected to the sandbox
Sandbox limitations: Each person who wants to test the bot must send the join code first. This is a sandbox restriction — production WhatsApp Business numbers don't require it. For a real launch, apply for a WhatsApp Business account through Twilio's console.
Create the project
mkdir quickbites-bot && cd quickbites-bot
npm init -y
npm install express dotenv twilio
npm install -D typescript ts-node @types/node @types/express nodemon
Create tsconfig.json:
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"outDir": "dist",
"rootDir": "src",
"strict": true,
"esModuleInterop": true
}
}
Create .env:
TWILIO_ACCOUNT_SID=ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
TWILIO_AUTH_TOKEN=your_auth_token
TWILIO_WHATSAPP_NUMBER=whatsapp:+14155238886
PORT=3000
Find your Account SID and Auth Token in the Twilio console dashboard.
Tell BlocWeave about the project
Create BLOCWEAVE_PLAN.md:
# Quick Bites WhatsApp Bot
## Stack
- Node.js 20 + Express + TypeScript
- Twilio SDK for WhatsApp messaging
- Session state in a Map (later: KV store on Cloudflare)
- Deploying to Cloudflare Workers in Part 4
## Conventions
- All source in src/
- Webhook handler in src/bot/handler.ts
- Menu data in src/bot/menu.ts
- Session management in src/bot/session.ts
- Twilio sends POST with body fields: Body (message text), From (sender's WhatsApp number), To (your sandbox number)
- Reply by calling twilio.messages.create({ from: To, to: From, body: replyText })
Build the webhook
Create the WhatsApp webhook server for the Quick Bites bot. 1. src/index.ts — Express server on PORT. POST /webhook route. GET /health. Start listening. 2. src/bot/handler.ts — export webhookHandler(req, res): - Parse Twilio POST body fields: Body (trim and lowercase), From, To - Initialise a Twilio client using TWILIO_ACCOUNT_SID and TWILIO_AUTH_TOKEN - Reply by calling client.messages.create({ from: To, to: From, body: replyText }) - For now: if the incoming message is "hi" or "hello" or "menu", reply with "Welcome to Quick Bites! 🍔\n\nReply with:\n1. View menu\n2. My order\n0. Help" - For anything else: reply with "Send menu to see what we have, or hi to start." - Respond to Twilio with 200 immediately (before awaiting the send) to avoid Twilio timeout retries - Use res.sendStatus(200) right after parsing — fire the Twilio send as a Promise without awaiting
Start ngrok and connect Twilio
Start the dev server:
npx ts-node src/index.ts
In a second terminal, start ngrok:
ngrok http 3000
Copy the https:// URL. In Twilio console, go to Messaging → Sandbox for WhatsApp → Sandbox settings and set:
- WHEN A MESSAGE COMES IN:
https://your-ngrok-url.ngrok-free.app/webhook - METHOD: POST
Save settings.
Test it
On your phone, send hi to the Twilio sandbox number on WhatsApp. You should see the welcome menu reply within a few seconds.
Try sending menu — same reply. Try sending hello there — you should get the fallback message.
Twilio retries: If your webhook takes more than 15 seconds to respond, Twilio retries the request, which can cause duplicate messages. Always send a 200 response immediately after parsing — before awaiting external calls. The fire-and-forget pattern for sending the reply is intentional.
Project structure after Part 1
src/
bot/
handler.ts ← webhook handler
index.ts
BLOCWEAVE_PLAN.md
.env
package.json
What's next
In Part 2 we build the full menu flow — a numbered catalogue the customer navigates by replying with numbers, with session state tracking their position in the conversation.
BlocWeave