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

Transcribe customer calls with diarization and per-utterance sentiment. Separate customer from agent speech and mine the customer’s exact language — how they describe problems, what words they use for pain points — to build a messaging playbook in the customer’s own voice. Flow: Deepgram POST /v1/listen?model=nova-3&diarize=true&sentiment=true&utterances=true → speaker-separated transcript with sentiment → Mavera POST /mave/chat → Messaging playbook

Code

import os, requests, time

DG = os.environ["DEEPGRAM_API_KEY"]
MV = os.environ["MAVERA_API_KEY"]
MV_BASE = "https://app.mavera.io/api/v1"
MV_H = {"Authorization": f"Bearer {MV}", "Content-Type": "application/json"}

CALLS = ["calls/discovery-acme.wav", "calls/support-globex.wav",
    "calls/onboarding-initech.wav", "calls/renewal-umbrella.wav", "calls/churn-soylent.wav"]
params = {"model": "nova-3", "smart_format": "true", "punctuate": "true",
    "diarize": "true", "sentiment": "true", "utterances": "true", "language": "en"}
all_cust = []
for call_file in CALLS:
    with open(call_file, "rb") as f:
        resp = requests.post("https://api.deepgram.com/v1/listen", params=params,
            headers={"Authorization": f"Token {DG}", "Content-Type": "audio/wav"},
            data=f, timeout=120)
    resp.raise_for_status()
    r = resp.json()
    utts = r.get("results",{}).get("utterances",[])
    sents = r.get("results",{}).get("sentiments",{}).get("segments",[])
    sent_map = {f"{s.get('start',0):.1f}": s.get("sentiment","neutral") for s in sents}
    spk_wc = {}
    for u in utts:
        sp = u.get("speaker",0); spk_wc[sp] = spk_wc.get(sp,0) + len(u.get("transcript","").split())
    cust_sp = max(spk_wc, key=spk_wc.get) if spk_wc else 0
    cust_utts = [{"text": u["transcript"],
        "sentiment": sent_map.get(f"{u.get('start',0):.1f}", "neutral"),
        "time": f"{int(u.get('start',0)//60)}:{int(u.get('start',0)%60):02d}"}
        for u in utts if u.get("speaker") == cust_sp]
    all_cust.append({"file": os.path.basename(call_file), "utts": cust_utts})
    print(f"{os.path.basename(call_file)}{len(utts)} total, {len(cust_utts)} customer")
    time.sleep(2)

corpus = ""
for c in all_cust:
    corpus += f"\n### {c['file']}\n"
    for u in c["utts"][:15]:
        corpus += f"  [{u['time']}] ({u['sentiment']}) \"{u['text'][:250]}\"\n"

time.sleep(1)
playbook = requests.post(f"{MV_BASE}/mave/chat", headers=MV_H, json={
    "message": f"Voice-of-customer analyst. {len(CALLS)} calls (customer only).\n\n"
        f"{corpus[:10000]}\n\nProduce:\n"
        "1. **PAIN POINT VOCABULARY** — Exact phrases for problems\n"
        "2. **PRODUCT LANGUAGE** — How customers describe your product vs. you\n"
        "3. **EMOTIONAL TRIGGERS** — Words with negative sentiment\n"
        "4. **SUCCESS LANGUAGE** — How customers describe wins\n"
        "5. **OBJECTION PATTERNS** — Recurring concerns\n"
        "6. **MESSAGING PLAYBOOK** — 10 copy lines using customer vocabulary\n"
        "7. **WORDS TO STEAL** — Customer phrases better than your copy\n"
}).json()
print(playbook.get("content", "")[:4000])

Example Output

discovery-acme.wav — 142 total, 68 customer | churn-soylent.wav — 121 total, 63 customer

PAIN POINT VOCABULARY
  "We're flying blind on what's working" (3 calls)
  "I spend half my Monday pulling reports nobody reads" (2 calls)

PRODUCT LANGUAGE
  They say: "the dashboard thing" → You say: "analytics platform"
  They say: "when it tells me what to do" → You say: "AI recommendations"

MESSAGING PLAYBOOK
  1. "Stop flying blind. See what's working in real time."
  2. "Your Monday shouldn't start with report pulling."

WORDS TO STEAL
  "flying blind" > "lack visibility" (use in hero copy)

Error Handling

Deepgram assigns IDs (0, 1, 2…) without labeling customer vs. agent. The code assumes the speaker with the most words is the customer. For inbound support calls, flip the logic or detect based on a known agent greeting.
Sentiment is segment-level (5-15 seconds), not per-word. Values are positive, neutral, or negative — no numeric scores.
Processing 5+ calls sequentially takes time. Parallelize with asyncio or Promise.all, limiting to 5 concurrent requests.