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

> Transcribe Close CRM call recordings and feed them into Mave Agent for structured meeting analysis — decisions, objections, next steps, and coaching insights

## Scenario

Close stores call recordings with metadata (duration, direction, user). You want to **transcribe recordings and feed them into Mave Agent** for structured meeting analysis: decisions made, objections raised, next steps promised, and coaching insights.

## Architecture

```mermaid theme={"dark"}
flowchart LR
    A[Close Call Recordings] --> B[Whisper transcription] --> C["POST /api/v1/mave/chat"] --> D[Meeting analysis report]
```

## Code

<CodeGroup>
  ```python Python theme={"dark"}
  import os, requests
  from requests.auth import HTTPBasicAuth
  from openai import OpenAI

  CLOSE_KEY = os.environ["CLOSE_API_KEY"]
  CLOSE_AUTH = HTTPBasicAuth(CLOSE_KEY, "")
  MAVERA_KEY = os.environ["MAVERA_API_KEY"]

  def close_get(path, params=None):
      r = requests.get(f"https://api.close.com/api/v1{path}", auth=CLOSE_AUTH, params=params or {})
      r.raise_for_status()
      return r.json()

  # 1. Pull calls with recordings
  calls = [c for c in close_get("/activity/call/", {"_limit": 50}).get("data", []) if c.get("recording_url")]
  if not calls:
      print("No calls with recordings found.")
      exit()

  call = calls[0]

  # 2. Download and transcribe via Whisper
  audio = requests.get(call["recording_url"], auth=CLOSE_AUTH)
  audio.raise_for_status()
  audio_path = "/tmp/close_call.mp3"
  with open(audio_path, "wb") as f:
      f.write(audio.content)

  whisper = OpenAI(api_key=os.environ.get("OPENAI_API_KEY", ""))
  with open(audio_path, "rb") as af:
      transcription = whisper.audio.transcriptions.create(model="whisper-1", file=af, response_format="text")
  transcript = transcription if isinstance(transcription, str) else transcription.text

  # 3. Feed to Mave Agent
  mave = requests.post(
      "https://app.mavera.io/api/v1/mave/chat",
      headers={"Authorization": f"Bearer {MAVERA_KEY}"},
      json={"message": (
          f"Analyze this sales call transcript. Extract:\n"
          f"1. Key decisions made\n2. Objections raised\n"
          f"3. Next steps and commitments\n4. Coaching insights for the rep\n"
          f"5. Call quality score (1-10)\n\n"
          f"Duration: {call.get('duration', 'unknown')}s | Direction: {call.get('direction', 'unknown')}\n\n"
          f"Transcript:\n{transcript[:8000]}"
      )},
  )
  mave.raise_for_status()
  print(f"Call Analysis for {call.get('phone', 'unknown')}")
  print(f"Duration: {call.get('duration', 0)}s\n")
  print(mave.json().get("content", "")[:1000])
  ```

  ```javascript JavaScript theme={"dark"}
  const OpenAI = require("openai");
  const fs = require("fs");
  const path = require("path");

  const CLOSE_KEY = process.env.CLOSE_API_KEY;
  const CLOSE_AUTH = "Basic " + Buffer.from(`${CLOSE_KEY}:`).toString("base64");
  const MAVERA_KEY = process.env.MAVERA_API_KEY;

  async function closeGet(endpoint, params = {}) {
    const url = new URL(`https://api.close.com/api/v1${endpoint}`);
    for (const [k, v] of Object.entries(params)) url.searchParams.set(k, v);
    const res = await fetch(url, { headers: { Authorization: CLOSE_AUTH } });
    if (!res.ok) throw new Error(`Close ${res.status}`);
    return res.json();
  }

  // 1. Pull calls with recordings
  const calls = ((await closeGet("/activity/call/", { _limit: 50 })).data || []).filter((c) => c.recording_url);
  if (!calls.length) { console.log("No recordings found."); process.exit(0); }
  const call = calls[0];

  // 2. Download and transcribe
  const audioRes = await fetch(call.recording_url, { headers: { Authorization: CLOSE_AUTH } });
  const audioPath = path.join("/tmp", "close_call.mp3");
  fs.writeFileSync(audioPath, Buffer.from(await audioRes.arrayBuffer()));

  const whisper = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
  const transcription = await whisper.audio.transcriptions.create({
    model: "whisper-1", file: fs.createReadStream(audioPath), response_format: "text",
  });
  const transcript = typeof transcription === "string" ? transcription : transcription.text;

  // 3. Mave Agent analysis
  const maveRes = await fetch("https://app.mavera.io/api/v1/mave/chat", {
    method: "POST",
    headers: { Authorization: `Bearer ${MAVERA_KEY}`, "Content-Type": "application/json" },
    body: JSON.stringify({ message:
      `Analyze this sales call. Extract:\n1. Decisions\n2. Objections\n` +
      `3. Next steps\n4. Coaching insights\n5. Quality score (1-10)\n\n` +
      `Duration: ${call.duration || "?"}s\n\nTranscript:\n${transcript.slice(0, 8000)}` }),
  });
  const analysis = await maveRes.json();
  console.log(`Call Analysis for ${call.phone || "unknown"} | ${call.duration || 0}s\n`);
  console.log((analysis.content || "").slice(0, 1000));
  ```
</CodeGroup>

## Example Output

```text theme={"dark"}
Call Analysis for +1-555-0192 | 1847s

**Decisions:** Prospect agreed to technical deep-dive (next Thursday).
Budget deferred to Q2 planning.

**Objections:** (1) "Locked into 2-year contract" — handled well.
(2) "No bandwidth for implementation" — offered onboarding but
didn't quantify time savings.

**Next Steps:** Send case study by EOD Friday. Schedule 30-min demo
with CTO + 2 engineers. Research competitor contract clause.

**Coaching:** Great discovery first 10 min. Missed ROI quantification.
Talk-to-listen 60/40; aim 40/60. **Quality: 7/10**
```

## Error Handling

| Error                       | Cause                         | Fix                                      |
| --------------------------- | ----------------------------- | ---------------------------------------- |
| `recording_url` returns 403 | Recording expired             | Check Close recording retention settings |
| Whisper timeout             | File > 25 MB                  | Split audio or use Deepgram streaming    |
| Garbled transcript          | Poor audio, multiple speakers | Use Deepgram with diarization            |
| Mave truncation             | Transcript too long           | Send first 8,000 chars or pre-summarize  |
