Build a WhatsApp auto-reply bot in Python — Flask webhook tutorial
An auto-reply bot is the "hello world" of WhatsApp automation: someone messages your number, your code decides what to answer, and the reply arrives in their chat within seconds. This tutorial builds one in Python with Flask — around 60 lines, no Meta Business account, no template approval.
What you need
- A TextMeFlow account with your WhatsApp number linked via QR (getting started guide)
- Your API key and webhook secret from the portal
- Python 3.10+ with
flaskandrequestsinstalled
Step 1 — receive incoming messages
TextMeFlow delivers every incoming message to your webhook URL as JSON, signed with HMAC-SHA256 so you can verify it genuinely came from us. The skeleton:
import hashlib
import hmac
import os
import requests
from flask import Flask, request, abort
app = Flask(__name__)
WEBHOOK_SECRET = os.environ["TMF_WEBHOOK_SECRET"]
API_KEY = os.environ["TMF_API_KEY"]
API_URL = "https://api.textmeflow.eu/v1/messages"
@app.post("/webhook")
def webhook():
received = request.headers.get("X-TextMeFlow-Signature", "")
expected = "sha256=" + hmac.new(
WEBHOOK_SECRET.encode(), request.get_data(), hashlib.sha256
).hexdigest()
if not hmac.compare_digest(received, expected):
abort(401)
event = request.get_json()
sender = event["from"]
text = event.get("message", "").strip().lower()
reply = make_reply(text)
if reply:
send_message(sender, reply)
return {"ok": True}
The signature lives in the X-TextMeFlow-Signature header as sha256=<hex> — an HMAC-SHA256 digest of the raw request body. Always compute it over request.get_data() (the raw bytes, not re-encoded JSON) and compare with hmac.compare_digest (constant-time) before trusting the payload. The payload is TextMeBot-compatible (type, from, from_name, to, message, file); full details, including retry behaviour and idempotency, are in our webhook signature guide.
Step 2 — decide what to answer
Start with simple keyword routing. It's unglamorous and it works:
def make_reply(text: str) -> str | None:
if text in ("hi", "hello", "hallo"):
return "Hi! Reply OPENING for our hours, or PRICE for rates."
if "opening" in text:
return "We're open Mon-Fri 9:00-18:00, Sat 10:00-16:00."
if "price" in text:
return "A standard service is €95 incl. VAT. Reply BOOK to schedule."
return None # unknown input: stay silent, a human will follow up
Returning None for unrecognised input is deliberate. A bot that answers everything badly is worse than one that answers three things well and leaves the rest to a person.
Step 3 — send the reply
Sending is one POST request:
def send_message(to: str, text: str) -> None:
resp = requests.post(
API_URL,
headers={"Authorization": f"Bearer {API_KEY}"},
json={"to": to, "text": text},
timeout=10,
)
resp.raise_for_status()
Note that replies count against your plan's monthly quota and the per-number rate limits (1 msg/sec) — for an auto-responder answering real customers, you'll stay far below both.
Step 4 — run it
export TMF_API_KEY=... TMF_WEBHOOK_SECRET=...
flask run --port 8000
Expose the port with a tunnel during development (ngrok or similar), set the tunnel URL as your webhook endpoint in the TextMeFlow portal, and message your own number. You should see the reply arrive within a second or two.
Two rules that keep your bot out of trouble
Respect STOP. TextMeFlow handles STOP keywords automatically at the API level — an opted-out recipient is blocked before your code even runs — but don't design flows that badger people into replying.
Never loop. If your bot can trigger on its own outgoing messages (group chats are a classic case), guard against replying to yourself. One boolean check saves you a very embarrassing infinite conversation.
Where to go from here
Swap the keyword table for an LLM call, connect a booking system, or forward unmatched messages to a human inbox — the webhook-in, POST-out structure stays identical. The free plan (50 messages/month, forever) is plenty for building and testing. Create your free account and have the bot answering in an afternoon.
Zelf WhatsApp-berichten versturen via API?
Gratis voor altijd tot 50 berichten/maand. QR scannen en binnen 5 minuten verstuur je je eerste bericht.
Gratis voor altijd