WhaBuzz is a WhatsApp Cloud API gateway for businesses and platforms in India. You send Meta's own request bodies and receive signed webhooks with 24 hours of retries and replay. Meta bills your messages directly at Meta's rate; WhaBuzz charges ₹500 a month for the first number, ₹250 for each after, excluding GST, and nothing per message.
If you have read Meta's Cloud API docs, you already know our send format. Take Meta's message object, add from with the phone number ID you connected, and POST it to /v1/messages. What comes back is Meta's response, wamid and all.
Around that we added what Meta leaves to you. Test and live keys. Tenants, so one account can hold many businesses. An Idempotency-Key header, so a retried POST does not send twice. A batch call that takes 10,000 messages.
Each response also carries Server-Timing: wb;dur=…, meta;dur=…, so a slow send tells you whose milliseconds they were.
# What a delivery looks like. Point it at your handler to test it.curl http://localhost:3000/whatsapp \-H"Content-Type: application/json" \-H"X-WhaBuzz-Signature: t=1792141200,v1=5f2b9c…e9" \-H"X-WhaBuzz-Event-Id: evt_01JAB3K9X2S8C4M7Q1V5R6T0YZ" \-H"X-WhaBuzz-Event-Type: message.inbound" \-H"X-WhaBuzz-Delivery-Id: dlv_01JAB3Y3B6D9F2H5K8N1Q4T7W0" \-H"X-WhaBuzz-Attempt: 1" \-d '{"id": "evt_01JAB3K9X2S8C4M7Q1V5R6T0YZ","type": "message.inbound","phone_number_id": "1013622581841046","wa_id": "16505551234","dedupe_key": "msg:wamid.HBgLMTY1MDU1NTEyMzQVAgASGBQzQUY5QTdGNkU0RTFGMkI3QzFBMDEAAA=","data": {"message": {"type": "text","text": { "body": "Hi, is the 7am batch open tomorrow?" } } },"raw_path": "entry[0].changes[0].value.messages[0]" }'
import crypto from"node:crypto";import express from"express";const app = express();app.post("/whatsapp", express.raw({ type: "*/*" }), (req, res) => {const raw = req.body.toString("utf8");const sig = req.get("X-WhaBuzz-Signature") ?? "";if (!verify(raw, sig, process.env.WHABUZZ_SECRET)) return res.sendStatus(401);const event = JSON.parse(raw);if (event.type === "webhook.verify") return res.json({ challenge: event.challenge }); res.sendStatus(200); // answer within 3 seconds, then do the work jobs.add(event); // your queue; dedupe on event.dedupe_key});function verify(raw, header, secret) {const parts = header.split(",").map((p) => p.split(/=(.*)/s));const t = Number(parts.find(([k]) => k === "t")?.[1]);if (!t || Math.abs(Date.now() / 1000 - t) > 300) returnfalse;const want = crypto.createHmac("sha256", secret).update(`${t}.${raw}`).digest();return parts.some(([k, v]) => {if (k !== "v1") returnfalse;const got = Buffer.from(v, "hex");return got.length === want.length && crypto.timingSafeEqual(got, want); });}
import hashlibimport hmacimport osimport timefrom flask import Flask, requestapp = Flask(__name__)SECRET = os.environ["WHABUZZ_SECRET"].encode()@app.post("/whatsapp")def whatsapp(): raw = request.get_data()ifnot verify(raw, request.headers.get("X-WhaBuzz-Signature", "")):return"", 401 event = request.get_json()if event["type"] == "webhook.verify":return {"challenge": event["challenge"]} jobs.put(event) # your queue; dedupe on event["dedupe_key"]return"", 200# answer within 3 secondsdef verify(raw: bytes, header: str) -> bool: parts = [p.split("=", 1) for p in header.split(",") if"="in p] t = next((int(v) for k, v in parts if k == "t"), 0)if abs(time.time() - t) > 300:returnFalse want = hmac.new(SECRET, f"{t}.".encode() + raw, hashlib.sha256).hexdigest()returnany(hmac.compare_digest(v, want) for k, v in parts if k == "v1")
Answer 2xx inside 3 seconds and do the work after. A retry or a replay of the same event carries the same dedupe_key, so you can skip what you have already handled. Webhooks guide
02 · Webhooks
What you get, down to the header format
These are the numbers in our code today. The docs carry the same figures, with code.
Updated for 2026 · last revised
specv1
Retry ladder
The first attempt waits 3 seconds for a 2xx. After that we retry at 30 s, 1 m, 2 m, 5 m, 10 m, 20 m, 40 m and 1 h, then hourly until 24 hours have passed. Retries wait 10 seconds each. Redirects are not followed.
Dead deliveries
After 24 hours a delivery is marked dead. Replay one with POST /v1/webhook-deliveries/{id}/replay, or up to 10,000 at once by filter on the endpoint.
Signature
HMAC-SHA256 of {t}.{raw body}, keyed with your whsec_ secret. Reject anything more than 300 seconds from your clock.
Both secrets sign during the overlap, so the header carries two v1 values. The overlap is 24 hours unless you set 0 to 168.
Meta's errors
Passed through with Meta's HTTP status and "source": "meta". 131047 means more than 24 hours have passed since the recipient last replied, so you need a template, and 131009 means a parameter value is not valid, according to Meta's error codes reference. Our own errors say "source": "whabuzz", for example number_not_connected.
Retention
Message bodies are kept 90 days by default. Set 30, 180 or 365 with PATCH /v1/tenant. Replay works inside that window. Media is streamed through and never stored.
Where it runs
Cloudflare's Asia-Pacific region, for Workers and for data (D1, Durable Objects, R2).
Limits
10 webhook endpoints per tenant. 10,000 messages per batch call. Idempotency keys are held for 24 hours.
How to use WhaBuzz from Claude Code, Cursor or any MCP client
One endpoint, https://api.whabuzz.com/mcp, and the same API key you use on /v1. Every tool is a thin call into /v1, so scopes, number binding and send gates apply unchanged.
A tenant key lists 12 tools, among them send_message, list_events, list_templates and get_wallet. An owner key lists 18.
Prices exclude GST. Sign-up is open to Indian businesses: confirm your email and a WhatsApp code to your mobile.
Meta already sets the price of each message it delivers. WhaBuzz charges a flat fee per connected number, so your message bill stays what Meta charges and nothing more.
When should I send a template instead of free text?
When the 24-hour customer service window has closed. The window starts when a user messages you and resets each time they message again, according to Meta's pricing page. Outside it, Meta rejects a free-form message with error 131047 and WhaBuzz returns it to you as is, with "source": "meta". We never pre-check the window, so the rule you hit is always Meta's current one.
What does WhaBuzz charge on top of Meta's rate?
Nothing per message. Meta charges per delivered message, at a rate set by the message category and the recipient's country calling code, according to Meta's pricing page. Meta bills the business's own Meta account. WhaBuzz charges for connected numbers only: ₹500 a month for the first, ₹250 for each after, plus GST, drawn a day at a time from a prepaid wallet.
What happens to a webhook my server misses?
WhaBuzz retries on the ladder above for 24 hours. After that the delivery is marked dead and you can replay it for as long as the event is retained, 90 days by default. Replays carry the same dedupe_key as the original.
Can I move a number from WATI, Plivo or another BSP?
Yes, if the WhatsApp Business Account is in your business's name. Connect it to WhaBuzz, pull your approved templates across with POST /v1/templates/sync, then switch the webhook subscription. Expect a short gap where inbound messages reach neither provider. The migration guide has the steps, including what to do if the provider owns the account.