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

Your #wins and #losses channels (or #deal-updates) capture the language of victory and defeat. This job combines both, feeds them to Mavera’s Brand Voice endpoint, and extracts two complementary voices: a “winning voice” (confidence, proof, momentum) and a “recovery voice” (empathy, resilience, learning). Both are useful for marketing — wins for case studies and sales enablement, losses for retention and re-engagement campaigns. Flow: Slack conversations.history (#wins + #losses) → Concatenate → Mavera POST /brand-voices → Two brand voices + test generation

Code

import os, requests, time

SL_TOKEN = os.environ["SLACK_BOT_TOKEN"]
SL_BASE = "https://slack.com/api"
SL_H = {"Authorization": f"Bearer {SL_TOKEN}"}
MV = os.environ["MAVERA_API_KEY"]
MV_BASE = "https://app.mavera.io/api/v1"
MV_H = {"Authorization": f"Bearer {MV}", "Content-Type": "application/json"}

WINS_CHANNEL = "C0123WINS"
LOSSES_CHANNEL = "C0123LOSSES"
DAYS_BACK = 90

def fetch_channel(channel_id):
    oldest = str(int(time.time()) - DAYS_BACK * 86400)
    msgs = []
    cursor = None
    while True:
        params = {"channel": channel_id, "limit": 200, "oldest": oldest}
        if cursor:
            params["cursor"] = cursor
        r = requests.get(f"{SL_BASE}/conversations.history", headers=SL_H, params=params)
        data = r.json()
        if not data.get("ok"):
            break
        msgs.extend(data.get("messages", []))
        cursor = data.get("response_metadata", {}).get("next_cursor")
        if not cursor:
            break
        time.sleep(1)
    return [m for m in msgs if m.get("type") == "message"
            and not m.get("bot_id") and len(m.get("text","")) > 20]

wins = fetch_channel(WINS_CHANNEL)
losses = fetch_channel(LOSSES_CHANNEL)
print(f"Wins: {len(wins)} messages | Losses: {len(losses)} messages")

# 2. Build corpora
win_text = "\n\n---\n\n".join(m.get("text","")[:500] for m in wins[-50:])
loss_text = "\n\n---\n\n".join(m.get("text","")[:500] for m in losses[-50:])

# 3. Create two brand voices
win_voice = requests.post(f"{MV_BASE}/brand-voices", headers=MV_H, json={
    "name": "Win Voice: Sales Victories",
    "extracted_content": win_text,
    "description": f"Voice from {len(wins)} deal-win announcements. Captures celebration, proof language, competitive positioning, and momentum.",
}).json()
print(f"Win Voice: {win_voice.get('id','')}{win_voice.get('traits', win_voice.get('voice_summary',''))}")

loss_voice = requests.post(f"{MV_BASE}/brand-voices", headers=MV_H, json={
    "name": "Recovery Voice: Deal Losses",
    "extracted_content": loss_text,
    "description": f"Voice from {len(losses)} deal-loss notes. Captures empathy, learning, resilience, and honest reflection.",
}).json()
print(f"Loss Voice: {loss_voice.get('id','')}{loss_voice.get('traits', loss_voice.get('voice_summary',''))}")

# 4. Test generation with each voice
for voice, label, prompt in [
    (win_voice, "WIN", "Write a case study opening paragraph (100 words) celebrating a customer's success with our platform. Confident, proof-heavy, momentum-driven."),
    (loss_voice, "RECOVERY", "Write a re-engagement email (100 words) to a churned customer. Empathetic, honest, learning-focused. Invite them back with a new feature."),
]:
    gen = requests.post(f"{MV_BASE}/generations", headers=MV_H, json={
        "brand_voice_id": voice["id"],
        "prompt": prompt,
    }).json()
    print(f"\n[{label} VOICE TEST]\n{gen.get('output', gen.get('content',''))[:400]}")

Example Output

Wins: 156 messages | Losses: 43 messages
Win Voice: bv_slack_wins_8j3k — Confident, celebratory, data-backed.
  Uses "crushed it," "landed," and specific revenue figures.
Loss Voice: bv_slack_loss_2m4n — Reflective, honest, growth-oriented.
  Uses "learned," "next time," and process language.

[WIN VOICE TEST]
When Acme Corp needed to cut campaign launch time from 6 weeks to 6
days, they didn't hedge — they went all in on our platform. Within 30
days, their team shipped 14 campaigns. Pipeline grew 340%. Their VP of
Marketing called it "the best tool decision we've made in five years."

[RECOVERY VOICE TEST]
We noticed you haven't logged in since February, and honestly, we've
been thinking about why. Since you left, we shipped the dashboard
customization you asked about — and rebuilt our mobile experience from
scratch. We'd love 15 minutes to show you what changed.

Error Handling

Win channels often have 3-5x more messages than loss channels. The code takes the last 50 from each to balance the voice extraction.
Wins often get cross-posted. Deduplicate by matching the first 100 chars of message text before building the corpus.
Win/loss messages may contain deal sizes, customer names, and competitive details. The Brand Voice endpoint extracts patterns, not specifics — but review the voice traits to confirm no PII leaked through.