How I Deployed a WhatsApp AI Bot on Cloud Run (And Why I'll Never Go Back to Long-Polling)
A first-person breakdown of building and deploying a production WhatsApp AI bot on Google Cloud Run using Green API and Claude — architecture decisions, cold start fixes, and the three things that actually broke in the first week.
The bot running inside my current multi-agent system started as a weekend experiment. By Tuesday it was handling real WhatsApp messages from real people, running on Cloud Run, costing about $4 a month. Here's everything I learned building it — architecture, failure modes, and the specific decisions I'd make differently if I started over today.
Why Cloud Run and Not a VPS
The first version ran on a DigitalOcean droplet. $12/month, always on, straightforward. The problem wasn't cost — it was me. Droplets need babysitting. Certificates expire. Node processes crash at 3 AM. You need to care about uptime in a way that pulls attention away from the thing you actually want to build.
Cloud Run is different. You give it a container, it runs on demand. No server to SSH into. No uptime dashboard to check. The container sleeps between requests and wakes up in under a second when a message arrives. For a WhatsApp bot handling conversational traffic — bursts of activity, then silence — this is almost the perfect compute model.
The other reason: Cloud Run integrates cleanly with Google Secret Manager, Cloud Logging, and Cloud Scheduler. If your architecture already lives in GCP (mine does — I use Firestore for agent memory, Cloud Storage for document ingestion), you get service account auth and IAM for free. No separate secrets infrastructure to maintain.
The Architecture in Plain Language
The bot runs as a single Cloud Run service. Here's the flow when someone sends a message:
1. Green API receives the WhatsApp message via webhook and forwards a POST request to the Cloud Run service URL
2. The service validates the webhook payload, extracts the message text and sender ID
3. It calls the Claude API (Anthropic) with the message, plus any context it pulls from Firestore (conversation history, user state)
4. It sends the reply back through Green API's send-message endpoint
5. The response is logged to Cloud Logging and the conversation state is updated in Firestore
That's it. No queue. No worker thread. No Redis. The whole thing processes in under 3 seconds for most messages, which is fast enough that users don't notice.
Scratch that — there's one case where the queue matters. If a user sends three messages in rapid succession before the first reply lands, you get race conditions on the Firestore read/write for conversation state. I solved this with a simple mutex pattern using a Firestore transaction, not a separate queue service. Three extra lines of code, no new infrastructure.
Why Green API and Not the Official WhatsApp Business API
This question comes up every time I mention the stack. The official WhatsApp Business API (Meta) requires a verified business account, a phone number that can't be your personal number, and a 2-4 week approval process. You also pay per conversation (around $0.005–$0.008 per 24-hour window for user-initiated chats in most tiers).
Green API is an unofficial bridge — it connects to WhatsApp Web under the hood. You use a real phone number (a SIM you own), link it to Green API, and it exposes a clean REST API for sending/receiving messages. The webhook delivery is reliable. The rate limits are generous for personal-scale or small-business use.
The tradeoff is that it's against WhatsApp's ToS for fully commercial use, and Meta can ban the number if it detects automation. In practice, for single-number bots with reasonable message volumes (say, under 200 messages per day), I've had zero issues. The number running my current system has been live for seven months without a strike.
If you're building for a large company or need absolute policy compliance, use the official API. For prototypes, internal tools, or personal agents — Green API is genuinely excellent.
The Container: What's Actually Inside
The service is a Node.js Express app. The Dockerfile is about 15 lines. Here's roughly what it does:
FROM node:20-slim
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
ENV PORT=8080
EXPOSE 8080
CMD ["node", "index.js"]
The image ends up at about 180MB. Cold start time on Cloud Run is around 1.1 seconds for this image, which I measured across 40 cold starts in the first week. That's acceptable for a WhatsApp bot — users are used to a natural typing delay — but it's something to watch.
The critical ENV variables are passed in via Cloud Run's built-in secret manager integration:
ANTHROPIC_API_KEY— the Claude API keyGREEN_API_INSTANCE_ID— the Green API instance identifierGREEN_API_TOKEN— the auth tokenFIRESTORE_PROJECT_ID— GCP project for conversation state
Nothing sensitive lives in the container image or in source control. This is non-negotiable. I've seen people push .env files to public repos — once is enough to burn a Stripe key or an API credential at 2 AM.
The Three Things That Actually Broke
1. Webhook Verification Timing
Cloud Run services default to requiring a minimum of 1 instance to be running if you set min-instances=1, or they can scale to zero. I started with scale-to-zero to save cost. The problem: the first message after a cold period triggered the cold start (1.1 seconds), and Green API's webhook retry logic interpreted the delayed response as a failure. It retried the same message — and my bot replied twice to the first message every time it woke up from cold.
Fix: I set min-instances=1 during business hours using Cloud Scheduler. A cron job sends a keepalive ping at 8 AM and another removes the min-instance setting at 10 PM. Adds maybe $1.50/month. Worth it.
2. Firestore Conversation History Growing Without Bounds
Each conversation writes to a Firestore document. I didn't set a TTL. After two weeks, some documents had 300+ message turns stored. The Claude context window gets expensive fast if you're naively sending the full history with every request.
Fix: I capped the conversation history at the last 20 turns. Anything older gets archived to a Cloud Storage bucket (flat JSON, $0.02/GB/month) and dropped from the Firestore document. The bot still has access to older context via a separate summary field — every 20 turns, I run a Claude call that compresses the prior history into a 3-sentence summary. Cost for that summary call: about $0.0008. Cost of sending 300 turns to Claude: much more.
3. Unhandled Media Messages
Users send images, voice notes, stickers, documents. My first version panicked on anything that wasn't a text message. Not a crash — I'd added a try/catch — but I was silently dropping media messages and sending no reply, which felt broken from the user's side.
Fix: A media type router. Images get a description via Claude's vision endpoint. Voice notes I skip for now and reply with "Voice notes coming soon — send text for now." Documents I extract text from if they're PDFs (using a lightweight parser), otherwise I reply with a file type acknowledgment. This took about four hours to build and handles 95% of real-world message variety.
Scaling and Cost
The current system has been running for seven months. Here's the real cost breakdown for a representative month (April 2026):
- Cloud Run compute: $2.14 (mostly the min-instances keepalive during business hours)
- Firestore reads/writes: $0.83
- Cloud Storage (archive): $0.04
- Claude API (claude-3-5-haiku for most messages, claude-3-5-sonnet for complex queries): $7.22
- Green API subscription: $15/month (flat fee for their service)
Total: ~$25/month for a bot handling roughly 400–600 real messages. About $0.05 per conversation. For anything I'd charge a client, that's negligible.
The Claude cost was higher in the first month ($31) before I implemented the history capping and switched routing logic to send simpler queries to Haiku. Two decisions, one afternoon, 75% cost drop.
Connecting It to the Broader Agent System
The WhatsApp bot isn't a standalone product. It's one of seven agents in my current multi-agent system. The bot acts as the conversational front door — it receives messages, classifies intent, and routes to specialized agents: one for calendar operations, one for content (connected to the content pipeline that runs harelasaf.com), one for research queries, one for financial tracking.
This is what I mean when I describe myself as an AI builder rather than a chatbot developer. The bot is the interface. The system behind it is where the actual work happens. Building the Cloud Run service took maybe eight hours. Building the agent routing layer, the memory architecture, the tool integrations — that's the real project.
The architecture I described in How I Built a Multi-Agent System That Runs Itself is the same system this bot feeds into. The WhatsApp layer is the input surface. Firestore is the shared memory. Cloud Run is the compute. Claude is the intelligence.
What I'd Change If I Started Today
Use Telegram instead for prototypes. Telegram's Bot API is official, free, has better webhook reliability, and doesn't risk account suspension. For anything that needs to reach WhatsApp specifically (and most clients do), move to the official WhatsApp Business API once you're past prototype stage. Green API is excellent for speed but carries ToS risk.
Start with min-instances=1 immediately. The cold-start UX issue is real. The cost difference for a minimal keepalive is not.
Add a message queue on day one. A simple Cloud Tasks queue in front of the handler costs nothing and eliminates the race condition issue entirely. I added it after the fact, which meant refactoring the handler. Four hours I could have saved.
Instrument everything from the start. Cloud Logging is free to use but you have to build the log structure. I wish I'd defined a standard log format (message_id, user_hash, intent_class, latency_ms, model_used, token_count) before writing the first handler. Retrofitting structured logging into a working system is annoying.
Sources
These are technical references, not independent verification of the personal costs, timings, architecture or results reported in this essay. Those historical figures require the underlying records; do not use them as a current budget or benchmark.
- Cloud Run billing settings: instance and request based billing.
- Cloud Run minimum instances: warm instance behavior.
FAQ
What is a WhatsApp AI bot on Cloud Run?
A WhatsApp AI bot on Cloud Run is a containerized application deployed on Google Cloud Run that receives WhatsApp messages via a webhook, processes them with an LLM like Claude, and sends replies automatically. Cloud Run handles scaling and infrastructure; you write the logic. A minimal version runs for under $5/month in compute costs.
Can I use the official WhatsApp Business API instead of Green API?
Yes. The official WhatsApp Business API (Meta) is policy-compliant and appropriate for commercial use. It requires business verification and has per-conversation pricing (~$0.005–$0.008). Green API is an unofficial bridge using a real phone number — faster to prototype with, but against WhatsApp's ToS for commercial automation. Choose based on your compliance requirements.
How much does a WhatsApp Claude bot cost to run on Cloud Run?
In my production system, Cloud Run compute costs about $2/month with min-instances=1 during business hours. Claude API costs depend on message volume and model choice — I pay roughly $7–10/month for 400–600 messages using a mix of Haiku and Sonnet. Green API adds $15/month flat. Total: approximately $25/month.
How do I handle cold starts on Cloud Run for a WhatsApp bot?
Set min-instances=1 during your expected active hours using Cloud Scheduler. A cron job sends a keepalive ping at the start of the day and Cloud Run's min-instances stays at 1, eliminating cold starts. At night, scale back to zero. This costs roughly $1.50/month for a small service and removes the webhook retry issue caused by slow cold-start responses.
What database should I use for WhatsApp bot conversation history?
Firestore works well for conversational state — fast reads, document-per-user structure, real-time updates. The key thing to avoid is unbounded growth: cap stored history at 20–30 turns, archive older messages to Cloud Storage, and maintain a running summary field for long-running conversations. This keeps read costs predictable and Claude context window costs manageable.
How do I store secrets like API keys for a Cloud Run WhatsApp bot?
Use Google Secret Manager with Cloud Run's native secret integration. Reference secrets as environment variables in your Cloud Run service configuration — they're injected at runtime, never stored in your container image or source code. Grant the Cloud Run service account the Secret Manager Secret Accessor role. This is non-negotiable for any production deployment.
Can I connect a WhatsApp bot to a multi-agent system?
Yes. The WhatsApp bot acts as the conversational front door — it receives messages, classifies intent, and routes to specialized agents via internal API calls or shared state. In my system, the bot feeds into seven agents including a calendar agent, content agent, and research agent. Shared memory lives in Firestore; routing logic lives in the bot handler.
What's the difference between a WhatsApp bot and an AI agent on WhatsApp?
A WhatsApp bot follows fixed rules: if input matches pattern X, send response Y. An AI agent on WhatsApp uses an LLM to understand intent, decides which tools to call (calendar, search, document retrieval), and takes multi-step actions before responding. The conversation interface looks the same. The capability difference is significant — agents handle novel inputs that bots would fail on.
Related reading
- How I Use Cloud Run to Keep My AI Agents Running 24/7
A practitioner's walkthrough of why Cloud Run beats a VPS or Lambda for persistent AI agents — with real startup times, idle costs, and auto-scaling numbers from my own setup.
The build log
Follow the work.
New essays and prototypes, in your feed reader. Email subscriptions are paused while I connect a working delivery service. No email is collected here.
Follow via RSS