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

# Support Channel → Persona Pain Points

### Scenario

Your `#customer-support` or `#help` channel captures raw customer frustrations in real time. This job pulls support messages, sends them to Mave for categorization by persona type (enterprise admin, SMB user, developer, end user), then produces a structured pain point map — which personas hurt most, what they complain about, and how to address each in marketing copy.

**Flow:** Slack `conversations.history` (#support) → Mavera `POST /mave/chat` (categorize) → Pain point matrix by persona

### Code

<CodeGroup>
  ```python Python theme={"dark"}
  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 = "C0123SUPPORT"  # Replace with your #support channel ID
  DAYS_BACK = 14

  # 1. Fetch support channel
  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)
      data = r.json()
      if not data.get("ok"):
          break
      messages.extend(data.get("messages", []))
      cursor = data.get("response_metadata", {}).get("next_cursor")
      if not cursor:
          break
      time.sleep(1)

  tickets = [m for m in messages if m.get("type") == "message" and len(m.get("text","")) > 20]
  print(f"Support input: {len(tickets)} (past {DAYS_BACK} days)")

  # 2. Build corpus
  corpus = "\n\n".join(
      f"[msg-{i}] {m.get('text','')[:400]}"
      for i, m in enumerate(tickets[-80:])
  )

  # 3. Mave categorization
  analysis = requests.post(f"{MV_BASE}/mave/chat", headers=MV_H, json={
      "message": f"Customer support analyst. Categorize these {len(tickets)} support messages by PERSONA TYPE and PAIN POINT.\n\n"
          f"MESSAGES:\n{corpus[:8000]}\n\n"
          "PERSONA TYPES to identify:\n"
          "- Enterprise Admin (manages team accounts, SSO, billing)\n"
          "- SMB User (small team, price-sensitive, self-serve)\n"
          "- Developer (API, integrations, technical issues)\n"
          "- End User (uses the product daily, UX issues)\n\n"
          "For EACH persona:\n"
          "1. Pain points (ranked by frequency)\n"
          "2. Emotional intensity (1-10)\n"
          "3. Representative quotes (exact words from messages)\n"
          "4. Suggested marketing response (how to address in copy)\n"
          "5. Feature request signal (what product change would help)\n\n"
          "End with a PRIORITY MATRIX: persona × pain point × urgency."
  }).json()

  print(f"\n{'='*60}\nSUPPORT PAIN POINTS BY PERSONA\n{'='*60}")
  print(analysis.get("content", "")[:3000])
  ```

  ```javascript JavaScript theme={"dark"}
  const SL_TOKEN = process.env.SLACK_BOT_TOKEN;
  const SL_BASE = "https://slack.com/api";
  const SL_H = { Authorization: `Bearer ${SL_TOKEN}` };
  const MV = process.env.MAVERA_API_KEY;
  const MV_BASE = "https://app.mavera.io/api/v1";
  const MV_H = { Authorization: `Bearer ${MV}`, "Content-Type": "application/json" };

  const CHANNEL_ID = "C0123SUPPORT";
  const DAYS_BACK = 14;
  const oldest = String(Math.floor(Date.now() / 1000) - DAYS_BACK * 86400);

  // 1. Fetch
  const messages = [];
  let cursor;
  do {
    const params = new URLSearchParams({ channel: CHANNEL_ID, limit: "200", oldest });
    if (cursor) params.set("cursor", cursor);
    const data = await (await fetch(`${SL_BASE}/conversations.history?${params}`, { headers: SL_H })).json();
    if (!data.ok) break;
    messages.push(...(data.messages || []));
    cursor = data.response_metadata?.next_cursor;
    await new Promise(r => setTimeout(r, 1000));
  } while (cursor);

  const tickets = messages.filter(m => m.type === "message" && (m.text||"").length > 20);
  console.log(`Support messages: ${tickets.length}`);

  // 2. Corpus
  const corpus = tickets.slice(-80).map((m, i) => `[msg-${i}] ${(m.text||"").slice(0,400)}`).join("\n\n");

  // 3. Categorize
  const analysis = await fetch(`${MV_BASE}/mave/chat`, { method: "POST", headers: MV_H,
    body: JSON.stringify({ message: `Categorize ${tickets.length} support messages by persona.\n\n${corpus.slice(0,8000)}\n\nPersonas: Enterprise Admin, SMB User, Developer, End User.\n\nPer persona: pain points (ranked), intensity (1-10), quotes, marketing response, feature signal.\n\nPRIORITY MATRIX: persona × pain × urgency.` }),
  }).then(r => r.json());

  console.log(`\n${"=".repeat(60)}\nSUPPORT PAIN POINTS BY PERSONA`);
  console.log((analysis.content || "").slice(0, 3000));
  ```
</CodeGroup>

### Example Output

```text theme={"dark"}
Support messages: 203 (past 14 days)

SUPPORT PAIN POINTS BY PERSONA
============================================================

## Enterprise Admin (34% of messages, intensity: 8/10)
1. SSO configuration failures (12 mentions)
   "We've been trying to set up SAML for two days. Our IT team is
   losing patience."
   → Marketing: Lead with "SSO in 15 minutes" in enterprise collateral
2. Billing confusion with seat-based pricing (9 mentions)
   → Marketing: Transparent pricing page with calculator

## Developer (28% of messages, intensity: 7/10)
1. API rate limit hitting without warning (8 mentions)
   "Getting 429s with no X-RateLimit headers. Can't build retry logic."
   → Marketing: "Developer-first API" only works if DX is actually good

PRIORITY MATRIX:
| Persona | Pain | Urgency | Marketing Action |
|---------|------|---------|-----------------|
| Enterprise Admin | SSO | 9/10 | Case study: "SSO in 15 min" |
| Developer | Rate limits | 8/10 | API docs overhaul |
| End User | Mobile UX | 7/10 | "Mobile-first" feature launch |
| SMB User | Pricing | 6/10 | Pricing page redesign |
```

### Error Handling

<AccordionGroup>
  <Accordion title="Private channels">Bot tokens need `groups:history` scope for private channels. User tokens access any channel the user is in.</Accordion>
  <Accordion title="High volume channels">Support channels can have 1000+ messages/day. Adjust `DAYS_BACK` to keep corpus manageable. Focus on messages with thread replies (indicates complex issues).</Accordion>
  <Accordion title="PII stripping">Support messages contain customer names, account IDs, and emails. Consider regex-stripping before sending to Mavera: replace emails with `[EMAIL]`, IDs with `[ID]`.</Accordion>
</AccordionGroup>
