A sweet shop in Kanpur was taking Diwali orders on a personal WhatsApp number — one cousin typing replies, losing track of who ordered what, and hand-writing bills with the wrong GST. Over one weekend in August 2025 we built a WhatsApp bot that takes the order in Hindi, confirms quantities, shows a GST-correct bill, and drops every order into a Google Sheet the counter watches. WhatsApp Cloud API for messaging, OpenAI for understanding the Hindi, a tiny GST calculator for the bill. Here's the full build — including the festive-load gotcha that nearly broke it.
The 60-Word Answer
Use the WhatsApp Cloud API to receive and send messages via webhook. Pass each incoming Hindi message to OpenAI with a system prompt and an order-taking function, so the model extracts items and quantities from natural language. Run the items through a small GST calculator (most packaged mithai is 5%), reply with an itemised bill, and log confirmed orders to a Google Sheet. The whole thing is one webhook handler.
Why a Bot, Not Just a Person on WhatsApp
A person on WhatsApp works until festive rush, when 200 messages arrive in an hour and orders get lost between "2 kg kaju katli" and "wait make it 1.5." A bot reads every message, never loses an order, and produces a consistent, GST-correct bill — which matters because hand-written festive bills are where small shops get GST wrong and where buyers who want input credit walk away. The bot doesn't replace the shop's warmth; it replaces the dropped orders and the wrong arithmetic during the three weeks a year that decide the shop's revenue.
What You'll Need
- A Meta Business account and a WhatsApp Cloud API app (free to set up in Meta for Developers)
- A WhatsApp Business phone number and a permanent access token
- An OpenAI API key
- Node.js 20+ and a public HTTPS endpoint for the webhook
- A Google Sheet plus a service-account key for the Sheets API
- The shop's menu with prices and the correct GST rate per item (confirm with their CA)
Step 1: Receive WhatsApp Messages via Webhook
The Cloud API calls your webhook on every incoming message. You verify the webhook once, then handle message events.
// webhook.js
import express from "express";
const app = express();
app.use(express.json()); // Meta verifies your webhook once with a GET
app.get("/webhook", (req, res) => {
const VERIFY = process.env.WA_VERIFY_TOKEN;
if (req.query["hub.verify_token"] === VERIFY) {
return res.send(req.query["hub.challenge"]);
}
res.sendStatus(403);
});
// Incoming messages arrive as POST
app.post("/webhook", async (req, res) => {
res.sendStatus(200); // ACK immediately — process async
const msg = req.body?.entry?.[0]?.changes?.[0]?.value?.messages?.[0];
if (!msg || msg.type !== "text") return;
await handleMessage(msg.from, msg.text.body);
});
200 to Meta immediately, then do the slow work (OpenAI, Sheets) asynchronously. If your webhook takes too long to respond, Meta retries — and you process the same order twice. This is the festive-load gotcha in miniature; more on it below.Step 2: Understand the Order With OpenAI
A function-calling prompt turns messy Hindi into structured items. The model extracts; it does not price.
// understand.js
import OpenAI from "openai";
const openai = new OpenAI(); const MENU = "kaju katli, soan papdi, motichoor ladoo, gulab jamun, barfi";
const tools = [{
type: "function",
function: {
name: "record_order_items",
description: "Record the sweets the customer wants, with quantity and unit.",
parameters: {
type: "object",
properties: {
items: {
type: "array",
items: {
type: "object",
properties: {
name: { type: "string", description: one of: ${MENU} },
qty: { type: "number" },
unit: { type: "string", enum: ["kg", "box", "piece"] }
},
required: ["name", "qty", "unit"]
}
}
},
required: ["items"]
}
}
}];
export async function understand(history) {
const system = You take sweet-shop orders on WhatsApp for a Kanpur mithai shop.
Customers write in Hindi or Hinglish. Menu: ${MENU}.
Extract items, quantity and unit. Ask a short clarifying question if unclear.
Reply warmly in the customer's language. Do NOT calculate prices — that is done separately.;
return openai.chat.completions.create({
model: "gpt-4o-mini",
messages: [{ role: "system", content: system }, ...history],
tools, tool_choice: "auto", temperature: 0.3
});
}
Step 3: The GST-Correct Bill (Code, Not the Model)
This is the part you must keep out of the LLM. Tax math goes in a small, testable function. Most packaged Indian sweets fall under 5% GST, split as 2.5% CGST + 2.5% SGST for an intra-state sale — but always confirm rates with the shop's CA, since some items differ.
// gst.js
const PRICES = { // per unit, in ₹
"kaju katli": { kg: 900 },
"soan papdi": { box: 250, kg: 500 },
"motichoor ladoo": { kg: 400 },
"gulab jamun": { box: 200 },
"barfi": { kg: 450 }
};
const GST_RATE = 0.05; // 5% on packaged mithai (confirm per item) export function buildBill(items) {
const lines = items.map(it => {
const unitPrice = PRICES[it.name]?.[it.unit] ?? 0;
const amount = unitPrice * it.qty;
return { ...it, unitPrice, amount };
});
const subtotal = lines.reduce((s, l) => s + l.amount, 0);
const gst = +(subtotal * GST_RATE).toFixed(2);
const cgst = +(gst / 2).toFixed(2);
const sgst = +(gst / 2).toFixed(2);
const total = +(subtotal + gst).toFixed(2);
return { lines, subtotal, cgst, sgst, total };
}
buildBill against the shop's CA-approved examples.Step 4: Reply, Confirm, and Log to the Sheet
// handle.js
import { understand } from "./understand.js";
import { buildBill } from "./gst.js"; export async function handleMessage(from, text) {
const history = loadHistory(from);
history.push({ role: "user", content: text });
const res = await understand(history);
const m = res.choices[0].message;
if (m.tool_calls) {
const { items } = JSON.parse(m.tool_calls[0].function.arguments);
const bill = buildBill(items);
const reply =
bill.lines.map(l => ${l.qty}${l.unit} ${l.name} — ₹${l.amount}).join("\n")
+ \nSubtotal: ₹${bill.subtotal}
+ \nCGST 2.5%: ₹${bill.cgst} SGST 2.5%: ₹${bill.sgst}
+ \nTotal: ₹${bill.total}\n\nConfirm karein? (haan/nahi);
await sendWhatsApp(from, reply);
saveDraftOrder(from, bill);
} else if (/haan|yes|confirm|ha\b/i.test(text)) {
const bill = getDraftOrder(from);
await appendToSheet(from, bill); // log confirmed order
await sendWhatsApp(from, "Order confirm! Aapka order taiyaar ho raha hai. Dhanyavaad 🙏");
} else {
await sendWhatsApp(from, m.content); // a clarifying question
}
}
Step 5: Verify Before Diwali
The Festive-Load Gotcha
200 instantly, process asynchronously, and dedupe on Meta's message id (store seen IDs for a few minutes). We hit exactly this on a 3x order spike; the fix was a 5-line dedupe and an instant ACK. Test it before the festival, not during.When NOT to Build This
Real Example: The Kanpur Mithai Shop
We shipped the bot above on a weekend. It took orders in Hindi and Hinglish, produced GST-correct bills the shop's CA had pre-approved, and logged confirmed orders to a Sheet the counter watched. The festive-load dedupe fix earned its keep on day one of the rush, when traffic tripled and the naive version would have double-booked. The cousin went back to packing sweets instead of typing. We later generalised this into a full WhatsApp + OpenAI order bot writeup and paired it with a Tally sync that closes the books daily.
The same WhatsApp Cloud API foundation runs under our GSTR-3B reminder workflow for a CA firm, and the Hindi-understanding piece reuses the patterns behind TalkDrill, our in-house English-speaking app. Our AI automation team ships these vernacular commerce bots for Indian retailers regularly.
Frequently Asked Questions
Is the WhatsApp Cloud API free?
The Cloud API itself is free to set up, and service conversations (where the customer messages you first) are free within a rolling window under Meta's current pricing. Business-initiated template messages are charged. For an order bot where customers message first, most conversations cost nothing — but check Meta's latest pricing, which changes.Can the WhatsApp bot understand orders written in Hindi?
Yes. Passing the message to OpenAI with a function-calling prompt lets the model extract items, quantities, and units from natural Hindi or Hinglish — including corrections like "make it 1.5 kg instead." The model handles the language; a separate code function handles the pricing and GST.What GST rate applies to sweets and mithai?
Most packaged Indian sweets (mithai) attract 5% GST, split as 2.5% CGST and 2.5% SGST for an intra-state sale. Some items and packaging can differ, so always confirm the exact rate per product with the shop's chartered accountant before wiring it into the billing code.Should the AI calculate the GST bill?
No. Keep all tax arithmetic in a small, testable code function, never in the language model. Models can be confidently wrong at multi-step math, and an incorrect tax figure on a real bill is a compliance issue. The model extracts the order; deterministic code computes the money.How do I stop duplicate orders during a festive rush?
Acknowledge Meta's webhook with a 200 response instantly, process the order asynchronously, and dedupe on the message ID (store seen IDs for a few minutes). Meta retries the webhook if you respond slowly under load, which is what causes the same order to land in your log twice.How long does it take to build a WhatsApp order bot?
A focused engineer can ship a working v1 over a weekend: a few hours for the webhook and WhatsApp send/receive, a few for the OpenAI extraction and the GST calculator with tests, and a few for the Google Sheet logging and a dedupe pass. Menu setup with the shop takes additional coordination time.Want a WhatsApp order bot live on your number before the next festival?
We build Hindi and vernacular WhatsApp order bots for Indian retailers — natural-language ordering, GST-correct bills, and a simple order log your staff already trust — in 7–10 working days. Suitable for sweet shops, bakeries, and small retailers with a fixed menu. We test the festive-load path before you go live.
Book a 20-min CallRelated reading
As Hrishikesh, our CTO, puts it: let the AI read the Hindi, let the code do the maths, and let the shopkeeper pack the sweets. Everyone does what they're good at.
