WhatsApp AI Agent: A Complete Guide using Gemini
The Technical Architecture
The data flow is straightforward but requires a reliable bridge between Meta's infrastructure and Google's model.
- WhatsApp Cloud API (Meta): This handles the inbound messages via webhooks and outbound replies via the Graph API.
- Flask Server: A lightweight Python middleman that receives the webhook POST requests and triggers the AI.
- Gemini API: The reasoning engine that processes the user's text and generates a response.
To get this running, you need a Meta for Developers account, a Google AI Studio API key, and ngrok to tunnel your local environment to a public URL so Meta can actually hit your webhook.
Deployment Step-by-Step
1. Environment Configuration
First, get your API keys from Google AI Studio and set up your Meta Business app. Once you have your Phone Number ID and temporary access token, initialize your project:
mkdir whatsapp-gemini-agent && cd whatsapp-gemini-agent
python -m venv .venv && source .venv/bin/activate
pip install flask google-genai requests python-dotenvStore your credentials in a .env file to keep them out of version control:
GEMINI_API_KEY=your_gemini_key
WHATSAPP_TOKEN=your_whatsapp_access_token
WHATSAPP_PHONE_NUMBER_ID=your_phone_number_id
VERIFY_TOKEN=pick_any_random_string2. Building the Webhook Server
The core of the agent is a Flask app. Meta requires a GET endpoint for the initial verification handshake and a POST endpoint to receive actual messages.
import os
import requests
from flask import Flask, request
from google import genai
from dotenv import load_dotenv
load_dotenv()
GEMINI_API_KEY = os.environ["GEMINI_API_KEY"]
WHATSAPP_TOKEN = os.environ["WHATSAPP_TOKEN"]
PHONE_NUMBER_ID = os.environ["WHATSAPP_PHONE_NUMBER_ID"]
VERIFY_TOKEN = os.environ["VERIFY_TOKEN"]
app = Flask(__name__)
client = genai.Client(api_key=GEMINI_API_KEY)
SYSTEM_PROMPT = (
"You are a friendly, concise WhatsApp assistant. "
"Keep replies short and clear — this is a chat app, not email."
)
def ask_gemini(user_text: str) -> str:
response = client.models.generate_content(
model="gemini-2.0-flash",
contents=f"{SYSTEM_PROMPT}\n\nUser: {user_text}"
)
return response.text
@app.route("/webhook", methods=["GET"])
def verify():
# Meta verification logic
if request.args.get("hub.verify_token") == VERIFY_TOKEN:
return request.args.get("hub.challenge")
return "Verification failed", 403
@app.route("/webhook", methods=["POST"])
def webhook():
data = request.get_json()
try:
# Extract message text from WhatsApp JSON structure
message = data["entry"][0]["changes"][0]["value"]["messages"][0]["text"]["body"]
sender = data["entry"][0]["changes"][0]["value"]["messages"][0]["from"]
ai_response = ask_gemini(message)
# Send reply back to WhatsApp
requests.post(
f"https://graph.facebook.com/v17.0/{PHONE_NUMBER_ID}/messages",
headers={"Authorization": f"Bearer {WHATSAPP_TOKEN}"},
json={"messaging_product": "whatsapp", "to": sender, "type": "text", "text": {"body": ai_response}}
)
except Exception as e:
print(f"Error: {e}")
return "EVENT_RECEIVED", 200
if __name__ == "__main__":
app.run(port=5000)Real-World Implementation Notes
When rolling this out to my colleagues, the biggest hurdle wasn't the code—it was the prompt engineering. Standard LLM responses are too wordy for WhatsApp. I had to iterate on the SYSTEM_PROMPT several times to ensure the agent didn't send "walls of text" that users would just ignore.
For those looking for a more robust AI workflow, I recommend moving from temporary Meta tokens to a Permanent System User token via the Meta Business Suite, otherwise, your bot will die every 24 hours. Using Gemini as a copilot during this process significantly cut down the time spent debugging the nested JSON structure of Meta's webhooks.