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 #sales or #deals channel is full of real objections, buyer quotes, competitive mentions, and win patterns — all in the words your reps actually use. This job pulls recent messages, sends them to Mave for structured extraction, and produces a messaging playbook: top objections with rebuttals, winning phrases, competitive positioning, and persona-specific talk tracks. Flow: Slack conversations.history (#sales) → Mavera POST /mave/chat → Messaging playbook

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"}

CHANNEL_ID = "C0123SALES"  # Replace with your #sales channel ID
DAYS_BACK = 30

# 1. Fetch channel history
oldest = str(int(time.time()) - DAYS_BACK * 86400)
messages = []
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)
    r.raise_for_status()
    data = r.json()
    if not data.get("ok"):
        print(f"Slack error: {data.get('error')}")
        break
    messages.extend(data.get("messages", []))
    cursor = data.get("response_metadata", {}).get("next_cursor")
    if not cursor:
        break
    time.sleep(1)

# 2. Filter to substantive messages (skip bot messages, reactions-only, etc.)
human_msgs = [m for m in messages if m.get("type") == "message"
              and not m.get("bot_id") and len(m.get("text","")) > 30]
print(f"Fetched {len(messages)} messages, {len(human_msgs)} substantive")

# 3. Build corpus (anonymize user IDs)
corpus = "\n\n".join(
    f"[{time.strftime('%Y-%m-%d', time.localtime(float(m.get('ts',0))))}] "
    f"{m.get('text','')[:500]}"
    for m in sorted(human_msgs, key=lambda x: float(x.get("ts",0)))[-100:]
)

# 4. Mave extraction
playbook = requests.post(f"{MV_BASE}/mave/chat", headers=MV_H, json={
    "message": f"Sales messaging analyst. Analyze {len(human_msgs)} messages from our #sales channel (past {DAYS_BACK} days).\n\n"
        f"MESSAGES:\n{corpus[:8000]}\n\n"
        "Extract and structure:\n\n"
        "1. **TOP 5 OBJECTIONS** — Exact objection, frequency, winning rebuttal\n"
        "2. **WINNING PHRASES** — Language that appears in won deals\n"
        "3. **COMPETITIVE MENTIONS** — Which competitors, in what context, rep response\n"
        "4. **BUYER QUOTES** — Direct quotes from prospects (useful for marketing)\n"
        "5. **PERSONA TALK TRACKS** — If different buyer personas are mentioned, create a mini talk track per persona\n"
        "6. **DEAL VELOCITY PATTERNS** — What accelerates deals? What stalls them?\n\n"
        "Be specific. Quote directly from the messages."
}).json()

print(f"\n{'='*60}\nSALES MESSAGING PLAYBOOK\n{'='*60}")
print(playbook.get("content", "")[:3000])

Example Output

Fetched 847 messages, 312 substantive

SALES MESSAGING PLAYBOOK
============================================================

1. TOP 5 OBJECTIONS:
   a) "We already use [Competitor X]" (23 mentions)
      Rebuttal: "Most of our enterprise clients migrated from [X].
      Here's what they gained: [ROI stat]"
   b) "Need to get IT approval" (18 mentions)
      Rebuttal: "We have a 2-page IT security doc. Can we send
      that to your CTO directly?"
   c) "Budget is locked until Q3" (14 mentions)
      Rebuttal: "We offer quarterly billing with a 90-day pilot."

2. WINNING PHRASES:
   - "This paid for itself in week two"
   - "Our VP of Marketing saw the demo and said 'why aren't we using this?'"
   - "The time-to-value was under 48 hours"

3. COMPETITIVE MENTIONS:
   - Competitor X: 31 mentions, mostly "switching from" context
   - Competitor Y: 12 mentions, "evaluating alongside"

Error Handling

Ensure the bot is invited to the channel. not_in_channel error means you need to /invite @bot. channel_not_found means the ID is wrong — use conversations.list to verify.
conversations.history is Tier 3 (50 req/min). Pagination with 200 messages per page keeps most channels under 5 calls. Add 1s delay between pages.
Channel history doesn’t include threaded replies by default. For important threads, fetch conversations.replies with the thread’s ts.
Sales channels often contain prospect names, emails, and deal amounts. Strip PII before sending to external APIs. The code limits to 500 chars per message to reduce exposure.