> ## 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.

# SMS Conversation Analysis

### 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/Conversations` → `GET /v1/Conversations/{sid}/Messages` → Mavera `POST /mave/chat` → Conversation intelligence

### Code

<CodeGroup>
  ```python Python theme={"dark"}
  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])
  ```

  ```javascript JavaScript theme={"dark"}
  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_CONV = "https://conversations.twilio.com/v1";
  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. List conversations
  const conversations = (await (await fetch(
    `${TW_CONV}/Conversations?PageSize=50`, { headers: TW_H })).json()).conversations || [];
  console.log(`Found ${conversations.length} conversations`);

  // 2. Fetch messages
  const allThreads = [];
  for (const conv of conversations.slice(0, 30)) {
    const sid = conv.sid || "";
    const mr = await fetch(`${TW_CONV}/Conversations/${sid}/Messages?PageSize=50&Order=asc`,
      { headers: TW_H });
    if (!mr.ok) continue;
    const msgs = (await mr.json()).messages || [];
    const threadMsgs = msgs.map(m => ({
      author: m.author || "unknown",
      body: (m.body || "").slice(0, 300),
      date: (m.date_created || "").slice(0, 16),
    }));
    if (threadMsgs.length)
      allThreads.push({ label: conv.friendly_name || conv.unique_name || sid,
        messages: threadMsgs, msg_count: threadMsgs.length });
    await new Promise(r => setTimeout(r, 100));
  }

  const totalMsgs = allThreads.reduce((s, t) => s + t.msg_count, 0);
  console.log(`Threads: ${allThreads.length}, Total messages: ${totalMsgs}`);

  // 3. Corpus
  let corpus = "";
  for (const t of allThreads.slice(0, 20)) {
    corpus += `\n--- THREAD: ${t.label} (${t.msg_count} msgs) ---\n`;
    for (const m of t.messages) corpus += `[${m.date}] ${m.author}: ${m.body}\n`;
  }

  // 4. Analysis
  const analysis = await fetch(`${MV_BASE}/mave/chat`, { method: "POST", headers: MV_H,
    body: JSON.stringify({ message: `Conversation analyst. ${allThreads.length} SMS threads (${totalMsgs} messages).\n\n${corpus.slice(0,8000)}\n\n1. Response Patterns\n2. Sentiment Analysis\n3. Drop-off Points\n4. Top Intents\n5. Winning Messages\n6. Problem Messages\n7. Optimization Recommendations (5)\n\nInclude quotes.` }),
  }).then(r => r.json());

  console.log(`\n${"=".repeat(60)}\nSMS CONVERSATION INTELLIGENCE`);
  console.log((analysis.content || "").slice(0, 3000));
  ```
</CodeGroup>

### Example Output

```text theme={"dark"}
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

<AccordionGroup>
  <Accordion title="Conversations vs Messages API">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.</Accordion>
  <Accordion title="Rate limits">Conversations API: 20 req/sec. The code fetches 30 conversations × 1 message request = 30 calls. At 100ms delay, \~3 seconds total. Well within limits.</Accordion>
  <Accordion title="Message encoding">SMS has 160-char segments. Long messages are concatenated by carriers. Twilio stores the full message body. No truncation handling needed.</Accordion>
</AccordionGroup>
