whatsapp-ai-chatbot-aws — architecture designer for WhatsApp AI bots on AWS
Turn a one-line idea ("AI bot on WhatsApp for my business") into a reviewed architecture: entry path, async pipeline, agent layer, memory, knowledge base, tools, ops, and cost. Output is a document the user can build from, not a lecture.
Default to talking and drawing. Write code only when the user explicitly asks to scaffold (then use CDK, see step 6).
Step 1 — Ask five questions, once
Ask only what changes the design. Skip any the user already answered.
- Channel ownership — already have a Meta Business app / phone number, or starting fresh?
- Knowledge size — what must the bot know? Rough token count: menu/FAQ (small) vs catalog/manuals/PDFs (large).
- Actions — what must it do? Orders, bookings, payments, CRM writes, ticket creation. Each is a tool.
- Volume and region — messages/day, user country (decides AWS region and Meta conversation pricing).
- Media — voice notes, images, documents in scope?
If the user says "just design it", assume: fresh channel, small KB, 2-3 tools, <1,000 messages/day, India/ap-south-1, text + buttons only. State the assumptions.
Step 2 — Pick the entry path
| AWS End User Messaging Social | Meta Cloud API direct | |
|---|---|---|
| Webhook code | none, AWS manages | you own API Gateway + Lambda + signature check |
| Inbound lands on | SNS topic | your HTTPS endpoint |
| Outbound | SendWhatsAppMessage API |
Graph API /messages |
| Cost | Meta fee + small AWS per-message fee | Meta fee only |
| Pick when | starting fresh on AWS | multi-cloud, existing Meta app, need every Meta feature day one |
Default: End User Messaging Social. All current AWS reference samples use it.
Step 3 — Draw the pipeline
Always async. Never call the model inside the webhook / SNS handler.
WhatsApp user
-> End User Messaging Social (or API Gateway + ingest Lambda)
-> SNS topic (filter: messages only; statuses go elsewhere)
-> SQS FIFO + DLQ (MessageGroupId = phone, dedupe on wamid)
-> Worker Lambda (agent loop)
|- DynamoDB session, cart/state, dedupe (TTL)
|- Bedrock Claude Haiku default, Sonnet on demand
|- Bedrock Knowledge Base static shared docs (only if large KB)
|- Tools user's APIs / DB for live + private data
-> SendWhatsAppMessage text, buttons, list picker
Render this as a mermaid flowchart TD in the output doc. Keep one diagram per
concern: pipeline, worker fan-out, KB ingestion, proactive/outbound.
Why each piece (put this reasoning in the doc):
- Queue: Meta expects 2xx in 3 s and retries with backoff for up to 7 days. LLM takes 2-10 s. Ack first, think later.
- FIFO by phone: "2 biryani" then "make it 3" must not race.
- Dedupe: retries guarantee duplicates. Conditional put on
wamid, 7-day TTL. - Status split: delivered/read events are ~3x message volume; filter at the SNS subscription so the worker never sees them.
Step 4 — Decide memory, KB, tools
Session memory — DynamoDB, PK = phone. Last 10 turns, 24 h TTL (matches
Meta's service window). Explicit state field when there is a flow
(IDLE, AWAITING_ADDRESS, AWAITING_PAYMENT, HANDED_OFF).
Knowledge — apply the split rule and write it down:
| Data | Where | How |
|---|---|---|
| Small static (< ~50-100k tokens: menu, FAQ, hours, policies) | system prompt | load from S3/DynamoDB at cold start, Bedrock prompt caching, refresh on S3 event |
| Large static (catalog, manuals, many PDFs) | Bedrock Knowledge Base | S3 source, S3 Vectors (cheap) or OpenSearch Serverless (fast, ~$350/mo floor), sync on upload |
| Dynamic or per-user (orders, balance, booking slots) | tool call at runtime | never embed live data |
Tools — one per action from step 1. Rule: model proposes, code validates and
executes. Prices, totals, availability come from the tool, never from the model.
Always include a handoff_to_human tool: sets HANDED_OFF in session, mutes bot,
notifies staff.
Model routing — cheap intent check first (Haiku, tiny prompt). FAQ hit = canned or KB answer. Only open-ended or multi-step goes to the full agent. Sonnet only when reasoning is needed. Never Opus for chat.
Agent runtime — plain Bedrock Converse API with tool use is enough for most bots. Reach for Strands Agents SDK (on Lambda or Bedrock AgentCore Runtime) when there are multiple specialist agents or cross-channel memory (same user on WhatsApp + Instagram via AgentCore Memory).
Step 5 — Side paths and ops
Include in the doc, each one line:
- Media: pull from Meta to S3 once, reference the key. Voice: Transcribe streaming for short clips, batch job + callback for long. Send "got it, processing" first when work exceeds ~5 s.
- Proactive messages: EventBridge Scheduler or domain event -> send Lambda. Outside the 24 h window only approved templates are allowed.
- Payments: send provider link; provider webhook hits its own API Gateway route, updates state, triggers confirmation message.
- Rate limits: new numbers start at 250 unique users / 24 h. Queue outbound, never drop. Per-user outbound cap 3-5 replies/min protects quality rating.
- Secrets: Secrets Manager for Meta token, app secret, tool API keys. Never env vars, never logs.
- Observability: CloudWatch + X-Ray end to end; log tokens and tool calls per turn; alarm on DLQ depth > 0.
- Guardrails: Bedrock Guardrails for PII and off-topic; content filter on every outbound message.
- Deploy: one CDK app. Region same as End User Messaging setup.
Step 6 — Estimate cost and hand over
Give a one-table estimate from the volume in step 1. Reference points people report at ~1,000 messages/day: total $30-50/month on AWS, of which Bedrock $20-30, Lambda $5-10. Haiku ~$0.001-0.005/turn, Sonnet ~$0.01-0.03/turn. Meta conversation fees are separate and vary by country.
Deliver:
docs/architecture/whatsapp-ai-bot.md(or the path the user names) with the diagrams, component table, decisions + why, checklist from references/production-checklist.md.- Offer next steps: scaffold CDK stack, write the spec (
specs-skillsif the repo is spec-driven), or deep-dive one component.
Only scaffold code if asked. When asked: CDK (TypeScript or Python), one stack, constructs in this order — messaging channel/SNS, SQS FIFO + DLQ, DynamoDB table, worker Lambda with Bedrock + DynamoDB + SQS IAM, Secrets Manager refs, CloudWatch alarms. No application logic beyond a working echo until the spec exists.
Anti-patterns to call out if you see them
- Sync LLM call inside the webhook handler (number one cause of lost messages).
- Unofficial WhatsApp libraries / browser automation — ban within days.
- Model computes prices or totals.
- Generic ChatGPT-sounding system prompt — users block, quality rating tanks.
- Static KB with no refresh path.
- No DLQ, no dedupe, tokens in environment variables.
Detailed reference and sources: references/architecture.md.