Claude x WhatsApp Agent
- By Praveen Jha · Updated 2026-10-23 · 15 min read · Developer & MCP tools
WHATSAPP BUSINESS PLATFORM • CLAUDE • PYTHON
Build a production WhatsApp AI agent for a business - instant replies in the brand's tone, tap-through menus, call booking, file delivery and lead capture - on the official WhatsApp Business Platform (Cloud API).
The one idea that controls cost and speed: menus and buttons are scripted - free and instant. Claude is called only when a customer types a free-form message. Design every journey so most taps never touch the AI.
Who this is for
A developer building this for a business owner, using Claude Code to write most of the code. You do not need to be a WhatsApp specialist - you need Python basics, a Meta account, and care with tokens, webhooks and the 24-hour messaging window.
How to use this guide
- Work through Phases 0-1 to get a talking bot on a free test number.
- Add Phase 2 scripted features and Phase 3 Claude tools.
- Test locally (Phase 4), then harden and launch (Phase 5).
- Start the actual build with the starter brief on the last pages.
1. 1 What you will build
A business WhatsApp assistant that behaves like a good front desk, inside the chat customers already use:
Instant replies in brand tone - greetings, prices, timings, FAQs.
Tap menus - lists and buttons instead of typing, wherever possible.
Booking calls - offer slots, hold one briefly, confirm it.
File delivery - send a brochure, price list, image or PDF on request.
- Lead capture - collect name, need and contact details into a record the owner can act on, without making the chat feel like a form.
Scripted (no AI cost)
Welcome, menus, buttons, forms, slot lists, file sends, confirmations. Deterministic, testable, instant.
Claude (paid, per typed message) Open questions, mixed language, follow-ups, and choosing a real action through tools. Only here does quality of wording matter most.
2 Architecture at a glance
Customer on WhatsApp
↓
Meta Cloud API - sends each message to your webhook (HTTPS POST)
↓
Python bot (Flask, bot.py ) - verifies, dedupes, routes
- → known tap / keyword → scripted flow
- → free-typed text → Claude API (with business knowledge + tools)
↓
Reply via Cloud API - text, list, buttons, image or PDF
Keep the bot as one small, readable service at first. Split files by job (flows, cards, knowledge) rather than adding frameworks, queues or a database before you need them.
3 What you need before you start
| Item | Notes |
|---|---|
| Meta / Facebook account + business portfolio | Free. This owns the Meta app, the WhatsApp account and, later, business verification. |
| WhatsApp test number | Free from Meta. Can message up to 5 recipient numbers you add - enough for development and demos. |
| Claude API key + small credit | From the Anthropic Console. Start with about $5 credit and leave auto-reload off until production spend is understood. |
| Claude Code | Your coding partner for the build. The starter brief (Section 14) is its first message. |
| Python 3.11+ | With pip and git available. Flask is the web layer. |
| cloudflared | Exposes your local bot over HTTPS so Meta's webhook can reach it during testing. |
| Hosting (production only) | An always-on host with persistent disk, or a VPS. A sleeping laptop is not production. |
4 Phase 0 - Accounts and keys
- Create the Meta app and add the WhatsApp use case / product.
- Note the two IDs from the WhatsApp setup screen: the Phone Number ID (which number is sending) and the WABA ID - WhatsApp Business Account ID (which business account it belongs to). Mixing these up is a common source of errors.
- Copy the temporary access token. It lasts roughly 24 hours - fine for testing, never for production.
- Add your own phone as a recipient of the test number, and verify it if prompted.
- Create the Anthropic API key in the Console and add the starting credit.
- Check your tools:
Copy-ready text
python --version # 3.11 or newer
pip --version
git --versionRule for every secret: keys and tokens go straight into your local .env file - never into chat, screenshots, commits or this guide. If a secret leaks, revoke and re-issue it.
5 Phase 1 - The core bot
Project layout
Copy-ready text
bot.py Flask app: webhook, routing, sending
flows.py Scripted journeys (menus, forms, booking)
cards.py Builders for lists, buttons, tickets, images
knowledge.md Everything Claude may say about the business
.env Secrets and config (never committed)
data/ chats.json and other local state
assets/ Banner, brochures, PDFs, QR and ticket inputsThe webhook must do these jobs, in order
- Verify (GET): answer Meta's challenge only when the verify token matches your own WHATSAPP_VERIFY_TOKEN.
- Check the signature (POST): validate the request against the app secret before trusting its contents.
- Dedupe: Meta can retry deliveries. Store processed message IDs and ignore repeats, or customers get double replies.
- Acknowledge fast, then work: return 200 promptly and handle the reply without blocking the webhook.
- Show presence: mark the message read and show typing while Claude is thinking - it makes a 2-3 second wait feel normal.
- Route: interactive replies (list/button IDs) go to scripted flows; plain text goes to Claude unless a flow is mid-step and expecting typed input.
- Log with tags: log route, phone (masked in shared logs), latency and outcome so later bugs are diagnosable.
The Claude brain
Model comes from CLAUDE_MODEL in .env - never hard-code it.
- System prompt is built from knowledge.md: services, prices, FAQs, tone, and what never to promise.
- Prompt caching: cache the stable system/knowledge block so repeated conversations do not re-pay full price for it.
- Low effort / short answers: WhatsApp replies should be brief. Long essays kill the chat experience and raise cost.
- Refusals: if Claude declines or the request is out of scope, reply with a safe fallback and offer human handover - never forward a raw refusal.
- Hidden lead capture: when a typed chat reveals name, need or budget, extract and save it quietly; do not interrogate the customer.
Memory, robustness and tests (Phase 1, continued)
Per-customer memory
- Keep conversation history per phone number in data/chats.json (or a small database later). One customer's context must never leak into another's.
- Do not persist thinking blocks. Store only user/assistant text and tool results you actually need. Saved thinking blocks are the root of a nasty recurring error (below).
- Prefix context: prepend the current date/time to the model call so "tomorrow", "this evening" and slot questions resolve correctly.
- Self-heal on the thinking-signature 400: if the API rejects a stored history with a thinking signature error, drop the offending history for that chat, start that customer fresh, and log it - do not crash the bot for everyone.
Test before you connect WhatsApp
| Test What it proves |
|---|
| Terminal chat Talk to the Claude brain from your console - no Meta, no tunnel. Fix tone and knowledge here, where iteration is fast. |
| Persona tests Run scripted pretend customers: price-hunter, Hinglish speaker, confused first-timer, someone asking out-of-scope questions. Save these as rerunnable tests. |
| Flow tests Every button/list ID lands in the right flow; every flow can exit or hand over. |
| Definition of done for Phase 1:a test recipient can say "hi", tap through a menu, type one open question, and get a correct, in-tone answer - with no duplicate replies. |
6 Phase 2 - Premium features, zero AI cost
Everything here is scripted - free, instant and reliable:
- Welcome banner + interactive list: a branded image on first contact, then a list menu - maximum 10 rows per list.
- Reply buttons: quick actions under a message - maximum 3 buttons. Design within the limit instead of fighting it.
- Lead / brief form flow: step-by-step questions (one per message) that end in a saved, structured lead.
- Booking flow: show slots → customer picks → hold the slot for about 10 minutes → payment step → ticket. In demos, any UPI QR must be clearly labelled as a demo.
- File delivery: upload media once via the Media API, then send by media ID - faster and more reliable than re-uploading.
- Waitlist: if slots fill, capture the lead instead of dead-ending the chat.
- Human handover: a clear "talk to a person" path that pauses the bot for that chat and alerts the owner.
- Local image generation: tickets, banners and QR images with Pillow + qrcode - no paid image API.
7 Phase 3 - Stand out: Claude takes real actions
Scripted menus break the moment a customer writes naturally: _"Kal shaam ko call ho sakta hai?"_ ("Can we do a call tomorrow evening?") should work just as well as the English version. That is what Claude tools are for.
The key tool: offer_call_slots
- Define a tool such as offer_call_slots with inputs like date and part_of_day (morning / afternoon / evening).
- Claude reads the typed request - in Hinglish or English - and calls the tool with normalised values. Your code, not the model, looks up real availability and renders the slot list.
- The customer taps a slot and the normal scripted booking flow takes over. AI interprets; code executes.
The tool loop, safely
- Send the message + tool definitions to Claude.
- If Claude requests a tool, run it in your code, return the result, and call Claude again for the final wording.
- Cap the loop (a few iterations), validate every tool argument, and never let a tool do payments, deletions or messages to third parties without a deterministic check in code.
- Context follow-ups: keep enough history that "the later one" or "usi din" ("that same day") resolves to the slots just offered.
Optional upgrades, in value order
| Upgrade | Value |
|---|---|
| Returning-customer memory | Greet by context ("reorder?", "your last booking") using saved profile data - with consent and an easy opt-out. |
| Voice-note understanding | Transcribe voice notes and feed the text into the same brain. Many customers prefer speaking. |
| Image understanding | Let customers send a photo (a product, a document, a problem) and have Claude describe / route it. |
| Daily owner summary | One scheduled message to the owner: new leads, bookings, handovers and unanswered questions. |
8 Phase 4 - Local testing, end to end
- Start the bot locally (for example, the Flask app's serve command) and confirm the health / webhook route responds.
- Open the tunnel with cloudflared, forcing QUIC if the default protocol misbehaves on your network:
Copy-ready text
cloudflared tunnel --url http://localhost:PORT --protocol quic- Configure the webhook in Meta: paste the tunnel's HTTPS callback URL, set the verify token to exactly your WHATSAPP_VERIFY_TOKEN, and complete verification.
- Subscribe to the messages field on the webhook - without this, Meta verifies you but sends nothing.
- Subscribe the app to the WABA - the step everyone misses:
Copy-ready text
POST /{WABA_ID}/subscribed_apps
Authorization: Bearer <access token>- Send "hi" from an added test recipient. Expect: read ticks, the welcome, and the menu.
- Then test in this order: every menu tap, one form, one booking, one file, one typed Hinglish question, one handover.
Grey ticks and silence? Work the troubleshooting table (Section 13) in order - WABA subscription, token, webhook field, tunnel - before changing bot code.
9 Phase 5 - Production
- Permanent token: create a Meta System User, give it only the WhatsApp assets it needs, and generate a token that does not expire. Replace the 24-hour token everywhere.
- Real number: register the business's own number, set the display name, and complete business verification early - it can take time.
- Templates: outside the 24-hour window you may only open with approved template messages. Submit utility templates (confirmations, reminders) before you need them.
- Hosting: Render or Railway (always-on plan, persistent disk for data/ ) or a VPS running gunicorn with 1 worker if state lives in local files / memory - multiple workers with file state will disagree with each other.
- Calendar: connect Google Calendar so offered slots are real and confirmed bookings create events.
- Payments: integrate Razorpay (or the chosen provider) and confirm a booking only on the signed payment webhook - never because the customer says "paid".
Reliability and the go-live checklist
- Retries: retry transient API failures with backoff; never retry by re-sending a customer message blindly - dedupe first.
- Per-user rate limit: cap messages per customer per minute so one loop or one abusive user cannot burn your Claude credit.
Backups: back up data/ (chats, leads, bookings) on a schedule, and test a restore once.
- Spend alerts: set Anthropic and hosting alerts so a runaway loop is a notification, not a bill shock.
- Health check: an uptime check on the webhook URL; alert the owner if it fails.
Go-live checklist
- System User (permanent) token in production .env; temporary token removed
- Real number live, display name approved, business verification done
- Utility templates approved for outside-24h messages
- Always-on hosting, persistent disk, HTTPS webhook re-pointed from tunnel to production
- App re-subscribed to the WABA on the production setup
- Payment confirmation only via signed provider webhook; calendar events verified
- Rate limits, backups, spend alerts and handover path tested with a real phone
10 Environment variables (.env)
Names only below - values are placeholders in your own file. Commit a .env.example with names and dummy values; never commit the real .env.
Copy-ready text
WHATSAPP_TOKEN= # Meta access token
WHATSAPP_PHONE_NUMBER_ID= # sender number ID (not the number itself)
WHATSAPP_VERIFY_TOKEN= # a password YOU invent for webhook verify
WHATSAPP_APP_SECRET= # for webhook signature checks
ANTHROPIC_API_KEY= # Claude API key
CLAUDE_MODEL= # model ID - change here, nowhere else
PORT= # local / host port for the bot
GRAPH_VERSION= # Meta Graph API version, e.g. vXX.0Add .env to .gitignore on day one, before the first commit.
11 What it costs
Only free-typed messages cost AI money. Figures are approximate planning numbers - verify current pricing before quoting a client.
Claude cost per 100 typed messages (approx.)
| Model tier | Per 100 typed messages | Best for |
|---|---|---|
| Opus (most capable) | Highest - roughly $1.50-2.50 / Rs 130-210 | Complex reasoning, premium clients |
| Sonnet (balanced) | Mid - roughly $0.50-0.90 / Rs 45-75 | Default choice for most businesses |
| Haiku (fastest, cheapest) | Lowest - roughly $0.15-0.30 / Rs 13-25 | High volume, simple Q&A |
Actual cost depends on message length, history size and caching.
Everything else
| Item | Cost |
|---|---|
| Scripted actions (menus, buttons, | Free - no AI call |
| forms, files) | |
| Replies inside the 24-hour window | Free on the Cloud API |
| Utility templates (outside 24h) | A few paise per message in India |
| Marketing templates (India) | Roughly Rs 0.8-0.9 per message |
| Hosting | Rs 0-600 / month depending on platform and tier |
| Payment gateway (Razorpay) | Roughly 2% + GST per transaction |
Changing the model: edit CLAUDE_MODEL in .env, restart the bot, and re-run the persona tests. No code change. Step down a tier to cut cost; step up only if test answers actually improve.
12 Compliance - the non-negotiables
- Official API only. No unofficial gateways, no automation of the personal WhatsApp app. Bans are real and numbers are hard to recover.
- Business-scoped, not a general chatbot. Open-ended general assistants breach platform policy.
- Disclose the AI and keep a human path available. Customers message first - reply inside the 24-hour window; outside it, only approved templates.
- Human handover must work - complaints, sensitive cases and repeated failures go to a person.
- Restricted categories decline and are routed per policy, not improvised around. Payments are confirmed only by the provider's signed webhook. Privacy: collect minimum lead data, store it securely, and honour deletion requests.
13 Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Messages sent, only grey ticks, bot silent | App not subscribed to the WABA, or messages field not subscribed | POST to /{WABA_ID}/subscribed_apps; check webhook subscriptions |
| 401 errors from the API | Temporary token expired (~24h) | Re-copy the token for testing; use a System User token in production |
| Webhook verification fails | Verify token mismatch, or wrong callback URL | Make Meta's token character-identical to WHATSAPP_VERIFY_TOKEN; use the current tunnel URL |
| Tunnel errors / | Default tunnel protocol blocked on | Re-run cloudflared with --protocol quic; |
| unreachable webhook | the network | restart after sleep/reconnect |
| Certificate errors (pip / downloads) | Antivirus or network intercepting TLS | Fix the certificate store / AV setting properly; never disable verification permanently |
| 400 error mentioning thinking / signature | Saved chat history contains thinking blocks | Stop persisting them; clear that chat's history (self-heal) and restart the conversation |
| "Recipient not allowed" on test number | Test numbers can only message 5 added recipients | Add the number in Meta's setup screen, or go live with a real number |
| Message rejected outside business hours | Outside the 24-hour window | Use an approved utility/marketing template to reopen the conversation |
| Bot stops overnight | The laptop running it slept | For real use, move to always-on hosting; tunnels and local bots die with the machine |
| Menu or button does nothing / is rejected | Interactive limits exceeded | Max 10 list rows, max 3 reply buttons - redesign the step within the limit |
14 Start the build: the starter brief
The companion starter brief is a ready-made first message for Claude Code. Used well, it does most of the project setup for you:
- Create an empty project folder and open Claude Code in it.
- Open the starter brief and complete its "About my business" section - do not skip fields and hope the bot guesses:
| Fill in | Why it matters |
|---|---|
| Business name and one-line description | Sets identity, greetings and the system prompt |
| Services and prices | Becomes knowledge.md- the only facts Claude may quote |
| FAQs with approved answers | Stops improvisation on timings, areas served, policies |
| Handover rules | Which topics or phrases immediately go to a human |
| Files to deliver | Brochures / PDFs / images the bot should send, and when |
| Brand colours | Used on the banner, ticket and generated images |
- Paste the whole brief as the first message in Claude Code and let it scaffold the project (layout in Section 5).
- Then work phase by phase in this guide, testing as you go - do not ask for the whole production system in one prompt.
15 Deliverables - what "done" looks like
The hosted bot - live on the real business number, on always-on hosting, with the permanent token.
- A README - how it is structured, how to run it locally, how to deploy, where secrets live (names, not values).
- An owner's manual - in plain language: how to read leads and bookings, use handover, update prices/FAQs in knowledge.md, and what to do if the bot is silent.
- Rerunnable tests - the terminal-chat and persona tests from Phase 1, kept in the repo so any future change can be checked in minutes.
Final check before handover: pick up a phone that is not a test recipient, message the business like a stranger, book a call, pay (test mode), and hand over to a human. If all four work, you are done.
By Praveen Jha | praveenjha.com. Pricing, limits and platform rules change; verify current figures in the Meta and Anthropic consoles before quoting or launching.