Texa AI in your product
A WhatsApp bot in 15 minutes
Your server already talks to Meta's Cloud API; Texa AI answers your customers through it.
No code at all? Connect your WhatsApp number in Texa Desk (Settings → Inboxes → Add inbox → WhatsApp) and switch Texa AI on for it under Texa AI → Agent and inboxes. This guide is for when your own server already talks to WhatsApp through Meta's Cloud API and you want Texa AI to answer.
What you need
- A WhatsApp Business number on Meta's Cloud API: its phone number id and an access token. Meta must send its webhook to your server.
- A Texa test key (
texa_test_…), from Texa AI → Developers. - Python 3.10+ with
fastapi,uvicornandhttpx.
The whole bot
# bot.py: run with uvicorn bot:app --port 8000
import os
import httpx
from fastapi import FastAPI, Request
TEXA = httpx.Client(base_url="https://api.texadesk.com/v1", timeout=90,
headers={"Authorization": f"Bearer {os.environ['TEXA_API_KEY']}"})
META = httpx.Client(base_url="https://graph.facebook.com/v21.0", timeout=30,
headers={"Authorization": f"Bearer {os.environ['META_TOKEN']}"})
PHONE_ID = os.environ["META_PHONE_NUMBER_ID"]
conversations: dict[str, str] = {} # customer's number -> Texa conversation (use your DB)
app = FastAPI()
def send_whatsapp(to: str, text: str) -> None:
META.post(f"/{PHONE_ID}/messages", json={
"messaging_product": "whatsapp", "to": to, "type": "text", "text": {"body": text}})
def conversation_for(number: str, name: str | None) -> str:
if number not in conversations:
r = TEXA.post("/conversations", headers={"Idempotency-Key": f"wa-open-{number}"},
json={"channel": "whatsapp", "customer_ref": f"wa:{number}",
"first_name": name})
conversations[number] = r.json()["conversation_id"]
return conversations[number]
@app.post("/whatsapp") # Meta's webhook (verify its signature too)
async def incoming(request: Request):
body = await request.json()
for entry in body.get("entry", []):
for change in entry.get("changes", []):
value = change.get("value", {})
names = {c["wa_id"]: c["profile"]["name"] for c in value.get("contacts", [])}
for m in value.get("messages", []):
if m.get("type") != "text":
continue
number = m["from"]
cid = conversation_for(number, names.get(number))
r = TEXA.post(f"/conversations/{cid}/messages",
json={"message_id": m["id"], "text": m["text"]["body"]})
if r.status_code == 409 and r.json()["error"]["code"] == "conversation_closed":
conversations.pop(number) # it ended: start a new one
cid = conversation_for(number, names.get(number))
r = TEXA.post(f"/conversations/{cid}/messages",
json={"message_id": m["id"], "text": m["text"]["body"]})
for event in r.json().get("events", []):
if event["type"] == "reply":
send_whatsapp(number, event["text"])
return {"ok": True}
Run it, point Meta's webhook at https://your-server/whatsapp, and message your number.
Why it is safe to retry
Meta sends a webhook again when your server is slow. Passing Meta's message id as
message_id means Texa answers a repeat with the first answer ("replayed": true) and
never replies twice. Opening with an Idempotency-Key means one number gets one
conversation, however often the first message arrives.
Things WhatsApp decides
- The 24-hour window. You can send a free-form message only within 24 hours of the customer's last one. After that, Meta takes only an approved template. A reply that arrives later (see below) may need one.
- Replies that come later. A voice note or photo is understood before Texa AI answers,
so its reply can come after your request has returned. Subscribe a
webhook to
conversation.replyand send those too. Usereply_idto make sure you send each reply once. - People taking over. When an event is a
handover, Texa AI stops replying. With handover to Texa Desk, your team answers there, and each answer reaches you as theconversation.human_messagewebhook: send itstextto the customer on WhatsApp. See handing over. - Voice notes and photos. Upload the file with
POST /v1/media(with theconversation_id), then send a message whoseattachmentsholds{"media_id": ...}.
Going live
Make a live key, swap it in, and nothing else changes. Everything is counted against your prepaid credits. Watch it in Texa Desk under Texa AI → Credits and billing.