Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.mavera.io/llms.txt

Use this file to discover all available pages before exploring further.

Scenario

Your sales, support, or appointment-reminder SMS flows generate thousands of two-way conversations. This job pulls conversations from Twilio’s Conversations API, aggregates message threads, and sends them to Mave for structured analysis — response rates, sentiment patterns, drop-off points, and messaging optimization recommendations. Flow: Twilio GET /v1/ConversationsGET /v1/Conversations/{sid}/Messages → Mavera POST /mave/chat → Conversation intelligence

Code

import os, requests, time
from datetime import datetime, timedelta

TW_SID = os.environ["TWILIO_ACCOUNT_SID"]
TW_TOKEN = os.environ["TWILIO_AUTH_TOKEN"]
TW_AUTH = (TW_SID, TW_TOKEN)
TW_CONV = "https://conversations.twilio.com/v1"
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. List recent conversations
r = requests.get(f"{TW_CONV}/Conversations", auth=TW_AUTH,
    params={"PageSize": 50})
r.raise_for_status()
conversations = r.json().get("conversations", [])
print(f"Found {len(conversations)} conversations")

# 2. Fetch messages for each conversation
all_threads = []
for conv in conversations[:30]:
    sid = conv.get("sid", "")
    label = conv.get("friendly_name", conv.get("unique_name", sid))

    mr = requests.get(f"{TW_CONV}/Conversations/{sid}/Messages", auth=TW_AUTH,
        params={"PageSize": 50, "Order": "asc"})
    if not mr.ok:
        continue
    msgs = mr.json().get("messages", [])

    thread_msgs = []
    for m in msgs:
        thread_msgs.append({
            "author": m.get("author", "unknown"),
            "body": m.get("body", "")[:300],
            "date": m.get("date_created", "")[:16],
        })

    if thread_msgs:
        all_threads.append({"label": label, "input": thread_msgs,
                           "msg_count": len(thread_msgs)})
    time.sleep(0.1)

print(f"Threads with input: {len(all_threads)}, "
      f"Total input: {sum(t['msg_count'] for t in all_threads)}")

# 3. Build corpus
corpus = []
for t in all_threads[:20]:
    corpus.append(f"\n--- THREAD: {t['label']} ({t['msg_count']} messages) ---")
    for m in t["messages"]:
        corpus.append(f"[{m['date']}] {m['author']}: {m['body']}")
corpus_text = "\n".join(corpus)

# 4. Mave analysis
analysis = requests.post(f"{MV_BASE}/mave/chat", headers=MV_H, json={
    "message": f"Conversation intelligence analyst. Analyze {len(all_threads)} SMS conversation threads ({sum(t['msg_count'] for t in all_threads)} total messages).\n\n"
        f"{corpus_text[:8000]}\n\n"
        "Produce a CONVERSATION INTELLIGENCE REPORT:\n\n"
        "1. **Response Patterns** — Average messages per thread, response time distribution, who initiates\n"
        "2. **Sentiment Analysis** — Overall tone, sentiment shifts within threads, frustration signals\n"
        "3. **Drop-off Points** — Where conversations end prematurely, common last messages before silence\n"
        "4. **Top Intents** — What customers are asking about (categories with counts)\n"
        "5. **Winning Messages** — Our messages that get the best responses (highest engagement)\n"
        "6. **Problem Messages** — Our messages that cause confusion, no-reply, or negative reactions\n"
        "7. **Optimization Recommendations** — 5 specific changes to improve conversion/satisfaction\n\n"
        "Include representative quotes."
}).json()

print(f"\n{'='*60}\nSMS CONVERSATION INTELLIGENCE\n{'='*60}")
print(analysis.get("content", "")[:3000])

Example Output

Found 50 conversations
Threads with messages: 28, Total messages: 247

SMS CONVERSATION INTELLIGENCE
============================================================

1. RESPONSE PATTERNS:
   - Avg messages per thread: 8.8
   - Customer response rate: 72% (first reply), drops to 41% by msg 5
   - Business initiates 85% of threads

2. SENTIMENT ANALYSIS:
   - Overall: Neutral-positive (0.34/1.0)
   - Frustration spikes at appointment rescheduling messages
   - Positive peak: confirmation messages with specific times

3. DROP-OFF POINTS:
   - 28% drop after "Reply YES to confirm" — too many options confuse
   - 19% drop after pricing messages — sticker shock
   - Last message before silence: "I'll think about it" (14 instances)

5. WINNING MESSAGES:
   "Your appointment is confirmed for Tuesday at 2pm with Dr. Smith.
   Reply CHANGE to reschedule." → 94% response rate

6. PROBLEM MESSAGES:
   "Please select from the following options: 1) Schedule 2) Cancel
   3) Reschedule 4) Speak to agent 5) More info" → 31% drop-off

7. RECOMMENDATIONS:
   a) Reduce options per message from 5 to 2 (binary choices)
   b) Include specific time/date in every confirmation
   c) Replace "Reply YES" with conversational language

Error Handling

The Conversations API (conversations.twilio.com) manages multi-party threads. The older Messages API (api.twilio.com) stores individual SMS records. Use Conversations for thread-level analysis, Messages for volume metrics.
Conversations API: 20 req/sec. The code fetches 30 conversations × 1 message request = 30 calls. At 100ms delay, ~3 seconds total. Well within limits.
SMS has 160-char segments. Long messages are concatenated by carriers. Twilio stores the full message body. No truncation handling needed.