Webhooks & Automation October 2026

Build a WhatsApp Bot in Python: Meta Cloud API Webhook Handler (Zero Servers)

A production guide to building a WhatsApp Business bot using Python and the Meta Cloud API. Learn how to verify webhooks, filter delivery statuses, and reply automatically without managing a server.

L
LiteLambda Team
9 min read

WhatsApp is the world's most dominant messaging platform, with over 2.5 billion active users. For businesses across India, Southeast Asia, Latin America, and Europe, WhatsApp is the primary channel for customer support, order updates, lead qualification, and transaction alerts.

To automate two-way customer communication, Meta provides the WhatsApp Business Cloud API. Whenever a customer messages your WhatsApp Business Number, Meta dispatches an HTTPS POST request to your registered webhook URL.

However, building a production WhatsApp webhook handler involves several specific architectural requirements:
1. The Meta Verification Handshake: Meta requires a GET verification challenge (hub.challenge) before activating the webhook.
2. Filtering Status Receipts vs. Real Messages: Meta uses the same webhook endpoint to send delivery receipts (sent, delivered, read). If your code doesn't filter these out, your bot will attempt to reply to its own delivery reports in an infinite loop.
3. The 5-Second Response Deadline: Meta expects an HTTP 200 within 5 seconds. If your code takes longer (e.g. querying a large database or calling an LLM), Meta retries the webhook and creates duplicate replies.

Here is how to build, verify, and host an enterprise WhatsApp Bot in Python without running or maintaining a dedicated server.


How the WhatsApp Cloud API Webhook Flow Works

User Sends WhatsApp Message
             │
             ▼
Meta WhatsApp Cloud Infrastructure
             │
     POST to Webhook URL (Contains messages[] & statuses[])
             │
             ▼
   LiteLambda Python Webhook
   ┌───────────────────────────────────────────────────────┐
   │ 1. Filter out status events (sent / delivered / read) │
   │ 2. Extract Customer Phone ('from') & Text Body        │
   │ 3. Check Idempotency Key ('wamid...')                 │
   │ 4. Acknowledge HTTP 200 to Meta immediately           │
   │ 5. Call Graph API to send WhatsApp reply to customer  │
   └───────────────────────────────────────────────────────┘

The Complete Python WhatsApp Webhook Handler

The following production script handles:
- Verification GET requests (hub.challenge)
- Incoming user messages (ignoring status receipts)
- Message deduplication (wamid...)
- Sending outbound replies via the Meta Cloud API

import os
import json
import requests

def handler(event, context):
    """
    Production WhatsApp Business Cloud API Webhook.
    Handles verification challenges (GET) and incoming messages (POST).
    """
    http_method = event.get("method", "GET").upper()
    query_params = event.get("query_params") or {}

    # ── 1. Meta Webhook Verification Handshake (GET) ─────────────────────────
    if http_method == "GET":
        mode = query_params.get("hub.mode")
        token = query_params.get("hub.verify_token")
        challenge = query_params.get("hub.challenge")

        verify_token = os.environ.get("WHATSAPP_VERIFY_TOKEN", "wa_bot_secret_token_2026")

        if mode == "subscribe" and token == verify_token:
            print("✅ WhatsApp webhook verified successfully.")
            return {
                "status": 200,
                "body": challenge,
                "headers": {"Content-Type": "text/plain"}
            }
        else:
            print("❌ WhatsApp verify token mismatch.")
            return {"status": 403, "body": "Verification token mismatch"}

    # ── 2. Handle Incoming WhatsApp Events (POST) ────────────────────────────
    payload = event.get("body_json") or {}

    entries = payload.get("entry", [])
    for entry in entries:
        changes = entry.get("changes", [])
        for change in changes:
            value = change.get("value", {})

            # Filter out status receipts (sent, delivered, read)
            # We ONLY want to respond to actual incoming customer messages
            if "statuses" in value:
                status_info = value["statuses"][0]
                status_name = status_info.get("status")
                # e.g. "delivered", "read"
                continue

            messages = value.get("messages", [])
            if not messages:
                continue

            msg = messages[0]
            msg_id = msg.get("id") # Unique WhatsApp Message ID: "wamid.HBg..."
            sender_phone = msg.get("from") # Customer's phone number e.g. "919876543210"
            msg_type = msg.get("type")

            # Deduplication: Prevent replying twice if Meta retries
            idempotency_key = f"wa_msg_{msg_id}"
            if hasattr(context, "kv"):
                if context.kv.get(idempotency_key):
                    print(f"Duplicate WhatsApp message {msg_id}. Skipping.")
                    return {"status": 200, "received": True}
                context.kv.set(idempotency_key, "1", ttl=86400) # 24 hours

            # Handle Text Messages
            if msg_type == "text":
                user_text = msg.get("text", {}).get("body", "").strip()
                print(f"📩 Incoming WhatsApp from {sender_phone}: {user_text}")

                # Generate bot reply
                reply_message = generate_bot_reply(user_text, sender_phone)

                # Send response back to customer
                send_whatsapp_message(sender_phone, reply_message)

            elif msg_type == "interactive":
                # Handle button clicks and list selections
                interactive = msg.get("interactive", {})
                button_reply = interactive.get("button_reply", {}).get("title")
                send_whatsapp_message(sender_phone, f"You selected: {button_reply}")

    # Meta requires a fast 200 OK
    return {"status": 200, "received": True}


def generate_bot_reply(text: str, phone: str) -> str:
    """
    Define your bot's business logic, FAQ router, or AI agent integration.
    """
    text_lower = text.lower()

    if "pricing" in text_lower or "plans" in text_lower:
        return (
            "💼 *LiteLambda Pricing:*\n\n"
            "• Starter: $4.99/mo (Includes 100k executions)\n"
            "• Pro: $19.99/mo (Unlimited cron + webhooks)\n\n"
            "Sign up here: https://litelambda.in/pricing/"
        )
    elif "order" in text_lower or "track" in text_lower:
        return f"🔍 Tracking order for phone: *{phone}*. Your latest package is currently out for delivery!"

    elif "hi" in text_lower or "hello" in text_lower:
        return (
            "👋 Welcome! I am an automated assistant running serverlessly on LiteLambda.\n\n"
            "Reply with *pricing*, *order*, or ask any question to get started!"
        )

    return f"Thanks for reaching out! We received your message: '{text}'. An agent will respond shortly."


def send_whatsapp_message(recipient_phone: str, text: str):
    """
    Sends an outbound WhatsApp text message via Meta Graph API.
    """
    phone_number_id = os.environ.get("WHATSAPP_PHONE_NUMBER_ID")
    access_token = os.environ.get("WHATSAPP_ACCESS_TOKEN")

    if not phone_number_id or not access_token:
        print("Missing WhatsApp credentials. Cannot send outbound message.")
        return

    url = f"https://graph.facebook.com/v21.0/{phone_number_id}/messages"
    headers = {
        "Authorization": f"Bearer {access_token}",
        "Content-Type": "application/json"
    }
    payload = {
        "messaging_product": "whatsapp",
        "to": recipient_phone,
        "type": "text",
        "text": {"body": text}
    }

    try:
        res = requests.post(url, json=payload, headers=headers, timeout=8)
        res.raise_for_status()
        print(f"✅ Reply sent to {recipient_phone}")
    except requests.RequestException as e:
        print(f"Failed to send WhatsApp message: {e}")

Why Traditional Servers Fail for WhatsApp Bots

If you deploy a WhatsApp bot on a shared VPS or Heroku dyno:
1. Memory leaks in Python chat libraries crash the background process unnoticed.
2. SSL certificate renewal failures break Meta's strict HTTPS requirement.
3. During promotion spikes, synchronous requests queue up and exceed Meta's 5-second timeout, triggering message floods.

On LiteLambda, your WhatsApp webhook is completely serverless. It handles incoming messages in parallel micro-sandboxes, maintains built-in message deduplication, and provides full execution history.

Deploy your WhatsApp Bot on LiteLambda in 60s →


Step-by-Step Meta Setup Guide

Step 1: Deploy to LiteLambda

Save the code as whatsapp_handler.py. Deploy it in seconds:

pip install litelambda-cli
litelambda login

litelambda deploy whatsapp-bot whatsapp_handler.py \
  --type webhook \
  --env WHATSAPP_VERIFY_TOKEN="wa_bot_secret_token_2026" \
  --env WHATSAPP_PHONE_NUMBER_ID="109876543210123" \
  --env WHATSAPP_ACCESS_TOKEN="EAAxxxxxxx..."

LiteLambda gives you your live HTTPS endpoint URL:

https://run.litelambda.in/w/whatsapp-bot/live_sec_872c01d

Step 2: Configure Webhook in Meta App Dashboard

  1. Navigate to the Meta Developer Portal and open your Business App.
  2. In the left sidebar, click WhatsApp → Configuration.
  3. Under Webhook, click Edit.
  4. In Callback URL, paste your LiteLambda HTTPS endpoint URL.
  5. In Verify Token, enter the exact string you set in WHATSAPP_VERIFY_TOKEN (e.g. wa_bot_secret_token_2026).
  6. Click Verify and Save. Meta immediately performs the handshake GET request.
  7. Under Webhook Fields, click Manage and subscribe to messages.

Step 3: Test on WhatsApp

From your personal phone, send a WhatsApp message to your Meta Test Phone Number (or your official registered Business Number).

Your LiteLambda function will trigger instantly, parse the text, acknowledge the webhook, and send the automated reply back to your phone within 1 to 2 seconds.


Comparison: Hosting WhatsApp Bots

Metric LiteLambda VPS (Droplet / EC2) Heroku / Render
Monthly Cost $4.99/mo $10–$20/mo $7–$25/mo
Idle Resource Consumption Zero 100% (24/7 idle RAM/CPU) 100%
Meta 5s Timeout Protection Built-in async & micro-latency Vulnerable to request queueing Vulnerable to sleep cycles
Deduplication (wamid) Built-in KV store Requires Redis container Requires Redis addon
Maintenance None (pure code) Linux updates, Nginx, Certbot Dyno management

Summary

The WhatsApp Business Cloud API provides direct access to customer communication. By running your bot logic on LiteLambda's serverless webhook infrastructure, you eliminate server maintenance, prevent duplicate replies, and ensure your business never misses an inbound customer conversation.

Deploy your WhatsApp Bot on LiteLambda →

Skip the infrastructure setup.

Run this exact code in our secure, isolated Docker sandbox. It takes 10 seconds to deploy.

Deploy this script in 60s →

No DevOps required.