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

# Call Recording → Meeting Analysis

### Scenario

Call recordings from sales calls, support calls, and consultations contain the richest customer language — tone, objections, questions, and commitments. This job fetches recordings, transcribes them (via Twilio's built-in transcription or an external service), then runs the transcript through Mave for meeting-style analysis: key topics, action items, sentiment flow, and follow-up recommendations.

**Flow:** Twilio `GET /Recordings` → Fetch transcription → Mavera `POST /mave/chat` → Meeting-style analysis report

### Code

<CodeGroup>
  ```python Python theme={"dark"}
  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. List recent recordings
  r = requests.get(f"{TW_BASE}/Recordings.json", auth=TW_AUTH,
      params={"PageSize": 10})
  r.raise_for_status()
  recordings = r.json().get("recordings", [])
  print(f"Found {len(recordings)} recordings")

  # 2. Fetch transcriptions
  analyzed = []
  for rec in recordings[:5]:
      rec_sid = rec.get("sid", "")
      duration = int(rec.get("duration", 0))
      date = rec.get("date_created", "")[:10]

      tr = requests.get(f"{TW_BASE}/Recordings/{rec_sid}/Transcriptions.json", auth=TW_AUTH)
      if not tr.ok or not tr.json().get("transcriptions"):
          print(f"  {rec_sid}: No transcription available (duration: {duration}s)")
          continue

      transcriptions = tr.json()["transcriptions"]
      transcript_text = ""
      for t in transcriptions:
          t_sid = t.get("sid", "")
          t_detail = requests.get(f"{TW_BASE}/Transcriptions/{t_sid}.json", auth=TW_AUTH)
          if t_detail.ok:
              transcript_text += t_detail.json().get("transcription_text", "")
      
      if transcript_text:
          analyzed.append({
              "sid": rec_sid, "duration": duration, "date": date,
              "transcript": transcript_text[:3000],
          })
          print(f"  {rec_sid}: {duration}s, {len(transcript_text):,} chars transcribed")
      time.sleep(0.5)

  if not analyzed:
      print("No transcriptions found. Ensure recording transcription is enabled in Twilio.")

  # 3. Analyze each call via Mave
  for call in analyzed:
      analysis = requests.post(f"{MV_BASE}/mave/chat", headers=MV_H, json={
          "message": f"Meeting analyst. Analyze this {call['duration']}s call recording transcript from {call['date']}.\n\n"
              f"TRANSCRIPT:\n{call['transcript']}\n\n"
              "Produce a CALL ANALYSIS REPORT:\n\n"
              "1. **Call Summary** — 2-3 sentence overview\n"
              "2. **Key Topics Discussed** — Bulleted list with time estimates\n"
              "3. **Customer Sentiment Flow** — How did sentiment change through the call?\n"
              "4. **Questions Asked** — By the customer (signals interest/concern areas)\n"
              "5. **Objections Raised** — With how they were handled\n"
              "6. **Commitments Made** — By both parties\n"
              "7. **Action Items** — What needs to happen next\n"
              "8. **Call Score** — Rate the call quality (1-10) with reasoning\n"
              "9. **Coaching Notes** — 2-3 suggestions for the rep\n\n"
              "Be specific. Reference transcript content."
      }).json()

      print(f"\n{'='*60}\nCALL ANALYSIS: {call['sid']} ({call['duration']}s, {call['date']})\n{'='*60}")
      print(analysis.get("content", "")[:2500])
  ```

  ```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_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. Recordings
  const recordings = (await (await fetch(
    `${TW_BASE}/Recordings.json?PageSize=10`, { headers: TW_H })).json()).recordings || [];
  console.log(`Found ${recordings.length} recordings`);

  // 2. Transcriptions
  const analyzed = [];
  for (const rec of recordings.slice(0, 5)) {
    const recSid = rec.sid || "";
    const duration = parseInt(rec.duration || 0, 10);
    const date = (rec.date_created || "").slice(0, 10);

    const tr = await fetch(`${TW_BASE}/Recordings/${recSid}/Transcriptions.json`, { headers: TW_H });
    if (!tr.ok) continue;
    const transcriptions = (await tr.json()).transcriptions || [];
    if (!transcriptions.length) { console.log(`  ${recSid}: no transcription`); continue; }

    let transcriptText = "";
    for (const t of transcriptions) {
      const detail = await (await fetch(`${TW_BASE}/Transcriptions/${t.sid}.json`, { headers: TW_H })).json();
      transcriptText += detail.transcription_text || "";
    }
    if (transcriptText) {
      analyzed.push({ sid: recSid, duration, date, transcript: transcriptText.slice(0, 3000) });
      console.log(`  ${recSid}: ${duration}s, ${transcriptText.length.toLocaleString()} chars`);
    }
    await new Promise(r => setTimeout(r, 500));
  }

  // 3. Analyze
  for (const call of analyzed) {
    const analysis = await fetch(`${MV_BASE}/mave/chat`, { method: "POST", headers: MV_H,
      body: JSON.stringify({ message: `Meeting analyst. ${call.duration}s call from ${call.date}.\n\nTRANSCRIPT:\n${call.transcript}\n\n1. Summary\n2. Key Topics\n3. Sentiment Flow\n4. Customer Questions\n5. Objections\n6. Commitments\n7. Action Items\n8. Call Score (1-10)\n9. Coaching Notes\n\nReference transcript.` }),
    }).then(r => r.json());

    console.log(`\n${"=".repeat(60)}\nCALL ANALYSIS: ${call.sid} (${call.duration}s, ${call.date})`);
    console.log((analysis.content || "").slice(0, 2500));
  }
  ```
</CodeGroup>

### Example Output

```text theme={"dark"}
Found 10 recordings
  RE_abc123: 347s, 4,231 chars transcribed
  RE_def456: 892s, 11,456 chars transcribed

CALL ANALYSIS: RE_abc123 (347s, 2026-03-12)
============================================================

1. SUMMARY: Sales discovery call with mid-market prospect. Customer
   is evaluating 3 vendors. Main concerns: pricing transparency and
   implementation timeline. Call ended with agreement to schedule demo.

2. KEY TOPICS:
   • Current pain points with existing tool (0:00-2:30)
   • Feature comparison vs Competitor X (2:30-4:00)
   • Pricing discussion (4:00-5:00) — customer pushed back on per-seat
   • Implementation timeline (5:00-5:30)

3. SENTIMENT FLOW:
   Opened positive (curious) → Dipped at pricing ("that's more than
   we expected") → Recovered after ROI framing → Ended cautiously positive

5. OBJECTIONS:
   a) "Per-seat pricing doesn't work for us — we have seasonal staff"
      → Rep offered annual plan with flexible seats. Partially resolved.
   b) "Your competitor does this for free"
      → Rep: "Free tier doesn't include SSO or audit logs." Effective.

8. CALL SCORE: 7/10
   Strong discovery questions. Pricing objection handling was adequate
   but could be sharper — missed opportunity to quantify ROI.

9. COACHING:
   • Prepare ROI calculator before pricing discussions
   • Ask "What does your budget process look like?" before quoting
   • Mirror customer's exact language when restating pain points
```

### Error Handling

<AccordionGroup>
  <Accordion title="Transcription availability">Twilio's built-in transcription is English-only and may not be enabled by default. Enable via TwiML `<Record transcribe="true">`. For higher quality, use AssemblyAI or Deepgram on the recording audio URL.</Accordion>
  <Accordion title="Recording access">Recordings are stored for the retention period configured in your Twilio Console (default: indefinitely). Access the audio at `https://api.twilio.com/2010-04-01/Accounts/{sid}/Recordings/{RecordingSid}.mp3`.</Accordion>
  <Accordion title="Long calls">Calls over 15 minutes produce large transcripts. The code limits to 3,000 chars. For full analysis of long calls, split the transcript into segments and analyze each separately, then summarize.</Accordion>
  <Accordion title="Speaker diarization">Twilio transcription doesn't separate speakers. For speaker-attributed analysis, use a service like AssemblyAI with speaker labels, then format as `Agent: ... / Customer: ...` before sending to Mave.</Accordion>
</AccordionGroup>
