Scenario
WhatsApp Business messages carry a different conversational register than SMS — longer messages, richer context, and more emotional language. This job pulls WhatsApp messages from Twilio’s Messages API, creates personas representing different customer segments, then runs each persona through Mavera Speak to generate insights about how each segment perceives your brand based on real WhatsApp interactions. Flow: TwilioGET /Messages.json (filter WhatsApp) → Cluster by theme → Mavera POST /personas → POST /speak → Persona insight report
Code
import os, requests, time
TW_SID = os.environ["TWILIO_ACCOUNT_SID"]
TW_TOKEN = os.environ["TWILIO_AUTH_TOKEN"]
TW_AUTH = (TW_SID, TW_TOKEN)
TW_BASE = f"https://api.twilio.com/2010-04-01/Accounts/{TW_SID}"
MV = os.environ["MAVERA_API_KEY"]
MV_BASE = "https://app.mavera.io/api/v1"
MV_H = {"Authorization": f"Bearer {MV}", "Content-Type": "application/json"}
# 1. Fetch WhatsApp messages
r = requests.get(f"{TW_BASE}/Messages.json", auth=TW_AUTH, params={
"PageSize": 200, "To": "whatsapp:+14155238886",
})
r.raise_for_status()
all_msgs = r.json().get("messages", [])
wa_inbound = [m for m in all_msgs if m.get("direction") == "inbound"
and "whatsapp:" in m.get("from", "")]
wa_outbound = [m for m in all_msgs if m.get("direction") == "outbound-api"]
print(f"WhatsApp input: {len(wa_inbound)} inbound, {len(wa_outbound)} outbound")
# 2. Build conversation corpus
inbound_corpus = "\n".join(
f"- [{m.get('date_sent','')[:10]}] {m.get('body','')[:300]}"
for m in wa_inbound[:80]
)
# 3. Theme extraction via Mave
themes = requests.post(f"{MV_BASE}/mave/chat", headers=MV_H, json={
"message": f"Analyze {len(wa_inbound)} inbound WhatsApp business messages.\n\n"
f"MESSAGES:\n{inbound_corpus[:6000]}\n\n"
"Extract:\n"
"1. Top 5 conversation themes (with counts)\n"
"2. Common customer language patterns (phrases, tone, formality)\n"
"3. Sentiment distribution (positive/negative/neutral %)\n"
"4. Time-of-day patterns (if dates suggest)\n"
"5. Unmet needs (what customers ask for that we don't provide)"
}).json()
themes_text = themes.get("content", "")
print(f"\nThemes:\n{themes_text[:1000]}")
# 4. Create customer personas based on message patterns
CUSTOMER_TYPES = [
{"name": "Urgent Buyer", "desc": "Sends short, direct messages. Wants immediate answers. Price-focused. Uses WhatsApp because it's faster than email. Expects quick responses."},
{"name": "Relationship Builder", "desc": "Sends longer messages with context. Asks follow-up questions. Wants to feel valued. Uses emojis and greetings. Loyal when treated well."},
{"name": "Comparison Shopper", "desc": "Asks detailed product questions. Mentions competitors. Wants specs, pricing breakdowns, and guarantees. Analytical. Makes decisions slowly."},
]
persona_ids = []
for ct in CUSTOMER_TYPES:
p = requests.post(f"{MV_BASE}/personas", headers=MV_H, json={
"name": f"WhatsApp: {ct['name']}",
"description": ct["desc"],
}).json()
persona_ids.append({"id": p["id"], "name": ct["name"]})
time.sleep(0.3)
# 5. Speak sessions for persona insights
for persona in persona_ids:
speak = requests.post(f"{MV_BASE}/speak", headers=MV_H, json={
"persona_id": persona["id"],
"input": [
{"role": "system", "content": f"You are a {persona['name']} who communicates with businesses via WhatsApp. Based on these real customer conversation themes, share your perspective."},
{"role": "user", "content": f"Here's what customers are saying on WhatsApp:\n\n{themes_text[:1500]}\n\nAs a {persona['name']}, what resonates with you? What would make you buy? What turns you off?"},
{"role": "user", "content": "How should businesses message you on WhatsApp to earn your trust and get a response?"},
{"role": "user", "content": "What's the one thing most businesses get wrong when messaging you on WhatsApp?"},
],
"mode": "interview",
}).json()
print(f"\n{'='*60}\nPERSONA INSIGHT: {persona['name']}\n{'='*60}")
for ex in speak.get("exchanges", speak.get("messages", [])):
if ex.get("role") == "user":
print(f"\n Q: {ex['content'][:100]}")
elif ex.get("role") == "assistant":
print(f" A: {ex['content'][:400]}")
time.sleep(0.3)
# 6. Generate optimized WhatsApp templates
templates = requests.post(f"{MV_BASE}/generations", headers=MV_H, json={
"prompt": f"Based on these WhatsApp persona insights:\n\n{themes_text[:2000]}\n\n"
"Generate 5 optimized WhatsApp Business message templates:\n\n"
"1. Welcome / First Contact\n"
"2. Product Inquiry Response\n"
"3. Price Quote\n"
"4. Follow-up After No Response\n"
"5. Post-Purchase Check-in\n\n"
"Each template: max 160 chars for first message, conversational tone, "
"include a clear next step. Avoid sounding automated. Use WhatsApp formatting "
"(bold: *text*, italic: _text_).",
}).json()
print(f"\n{'='*60}\nOPTIMIZED WHATSAPP TEMPLATES\n{'='*60}")
print(templates.get("output", templates.get("content", ""))[:1500])
const TW_SID = process.env.TWILIO_ACCOUNT_SID;
const TW_TOKEN = process.env.TWILIO_AUTH_TOKEN;
const TW_CREDS = btoa(`${TW_SID}:${TW_TOKEN}`);
const TW_BASE = `https://api.twilio.com/2010-04-01/Accounts/${TW_SID}`;
const TW_H = { Authorization: `Basic ${TW_CREDS}` };
const MV = process.env.MAVERA_API_KEY;
const MV_BASE = "https://app.mavera.io/api/v1";
const MV_H = { Authorization: `Bearer ${MV}`, "Content-Type": "application/json" };
// 1. WhatsApp messages
const allMsgs = (await (await fetch(
`${TW_BASE}/Messages.json?PageSize=200&To=${encodeURIComponent("whatsapp:+14155238886")}`,
{ headers: TW_H })).json()).messages || [];
const waInbound = allMsgs.filter(m => m.direction === "inbound" && (m.from||"").includes("whatsapp:"));
const waOutbound = allMsgs.filter(m => m.direction === "outbound-api");
console.log(`WhatsApp: ${waInbound.length} inbound, ${waOutbound.length} outbound`);
// 2. Corpus
const inboundCorpus = waInbound.slice(0, 80)
.map(m => `- [${(m.date_sent||"").slice(0,10)}] ${(m.body||"").slice(0,300)}`).join("\n");
// 3. Themes
const themes = await fetch(`${MV_BASE}/mave/chat`, { method: "POST", headers: MV_H,
body: JSON.stringify({ message: `Analyze ${waInbound.length} WhatsApp messages.\n\n${inboundCorpus.slice(0,6000)}\n\nExtract: 5 themes, language patterns, sentiment %, time patterns, unmet needs.` }),
}).then(r => r.json());
const themesText = themes.content || "";
// 4. Personas
const TYPES = [
{ name: "Urgent Buyer", desc: "Short messages, immediate answers, price-focused." },
{ name: "Relationship Builder", desc: "Long messages, follow-ups, emojis, loyalty." },
{ name: "Comparison Shopper", desc: "Detailed questions, competitors, specs, slow decision." },
];
const personaIds = [];
for (const ct of TYPES) {
const p = await fetch(`${MV_BASE}/personas`, { method: "POST", headers: MV_H,
body: JSON.stringify({ name: `WhatsApp: ${ct.name}`, description: ct.desc }),
}).then(r => r.json());
personaIds.push({ id: p.id, name: ct.name });
await new Promise(r => setTimeout(r, 300));
}
// 5. Speak
for (const persona of personaIds) {
const speak = await fetch(`${MV_BASE}/speak`, { method: "POST", headers: MV_H,
body: JSON.stringify({ persona_id: persona.id, mode: "interview",
input: [
{ role: "system", content: `You are a ${persona.name} on WhatsApp.` },
{ role: "user", content: `Themes:\n${themesText.slice(0,1500)}\n\nWhat resonates? What makes you buy? What turns you off?` },
{ role: "user", content: "How should businesses message you on WhatsApp?" },
{ role: "user", content: "What do businesses get wrong on WhatsApp?" },
] }),
}).then(r => r.json());
console.log(`\n${"=".repeat(60)}\nPERSONA: ${persona.name}`);
for (const ex of speak.exchanges || speak.messages || []) {
if (ex.role === "user") console.log(`\n Q: ${ex.content.slice(0,100)}`);
else if (ex.role === "assistant") console.log(` A: ${ex.content.slice(0,400)}`);
}
}
// 6. Templates
const templates = await fetch(`${MV_BASE}/generations`, { method: "POST", headers: MV_H,
body: JSON.stringify({ prompt: `WhatsApp templates from insights:\n\n${themesText.slice(0,2000)}\n\n5 templates: Welcome, Inquiry Response, Quote, Follow-up, Post-Purchase.\nMax 160 chars first msg. Conversational. WhatsApp formatting (*bold*, _italic_).` }),
}).then(r => r.json());
console.log(`\n${"=".repeat(60)}\nWHATSAPP TEMPLATES`);
console.log((templates.output || templates.content || "").slice(0, 1500));
Example Output
WhatsApp messages: 142 inbound, 89 outbound
Themes:
1. Product Availability (38 msgs) — "Do you have X in stock?"
2. Pricing Inquiries (29 msgs) — "How much for X?"
3. Order Status (24 msgs) — "Where's my order?"
4. Returns/Exchanges (18 msgs) — "Can I return this?"
5. Custom Requests (12 msgs) — "Can you make X in size Y?"
PERSONA INSIGHT: Urgent Buyer
============================================================
Q: What resonates? What makes you buy?
A: Speed. If you reply in under 5 minutes, I'm 80% likely to buy.
After 30 minutes, I've already messaged your competitor. Don't
send me a catalog — answer my specific question. "Yes, we have
it, here's the price, want me to reserve it?" That's the perfect
WhatsApp message.
Q: What do businesses get wrong?
A: Automated responses that don't answer my question. "Thank you for
your message, a representative will be with you shortly" — that's
email energy. This is WhatsApp. Be human or be fast. Ideally both.
OPTIMIZED WHATSAPP TEMPLATES
============================================================
1. WELCOME: Hey! 👋 Thanks for reaching out. I'm [Name] — what
can I help you find today?
2. INQUIRY: Great question! *[Product]* is available in [options].
Want me to send photos or pricing?
3. QUOTE: Here's the breakdown:
*[Product]*: $XX
*Shipping*: Free over $50
Ready to order? I can set it up right now.
Error Handling
WhatsApp number format
WhatsApp number format
WhatsApp numbers use
whatsapp:+{E.164} format. Filter messages by To or From containing whatsapp: prefix. The Twilio Sandbox uses whatsapp:+14155238886.24-hour window
24-hour window
WhatsApp Business API requires customers to message first (or opt in). You can only respond within 24 hours of the last customer message. After that, you need pre-approved templates.
Media messages
Media messages
WhatsApp messages may include images, documents, and voice notes. The Messages API returns media URLs via
MediaUrl fields. This code processes text only — extend with media download for richer analysis.GDPR / consent
GDPR / consent
WhatsApp messages are personal communications. Ensure you have consent before sending message content to external APIs. Anonymize phone numbers and PII. Consider processing aggregated themes rather than raw messages.