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
Code
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])
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));
Example Output
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 |