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

# Competitive Intelligence → Research Brief

### Scenario

Teams often share competitive intel in a dedicated `#competitive-intel` or `#market-intel` channel — competitor screenshots, pricing changes, feature launches, customer switches. This job aggregates these organic observations, enriches them through Mave, and produces a structured research brief that product, marketing, and sales can act on.

**Flow:** Slack `conversations.history` (#competitive-intel) → Mavera `POST /mave/chat` → Structured competitive brief

### 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 = "C0123INTEL"
  DAYS_BACK = 30

  # 1. Fetch intel 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)

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

  # 2. Build enriched corpus (include thread replies for context)
  enriched = []
  for m in sorted(intel, key=lambda x: float(x.get("ts",0)))[-50:]:
      entry = f"[{time.strftime('%Y-%m-%d', time.localtime(float(m.get('ts',0))))}] {m.get('text','')[:500]}"
      if m.get("reply_count", 0) > 0:
          replies = requests.get(f"{SL_BASE}/conversations.replies", headers=SL_H,
              params={"channel": CHANNEL_ID, "ts": m["ts"], "limit": 5}).json()
          if replies.get("ok"):
              for rep in replies.get("messages", [])[1:]:
                  entry += f"\n  → Reply: {rep.get('text','')[:200]}"
          time.sleep(1)
      enriched.append(entry)

  corpus = "\n\n".join(enriched)

  # 3. Mave research brief
  brief = requests.post(f"{MV_BASE}/mave/chat", headers=MV_H, json={
      "message": f"Competitive intelligence analyst. Synthesize {len(intel)} internal observations from our #competitive-intel channel.\n\n"
          f"RAW INTEL:\n{corpus[:8000]}\n\n"
          "Produce a COMPETITIVE RESEARCH BRIEF with:\n\n"
          "1. **Executive Summary** (3 sentences)\n"
          "2. **Competitor Activity Matrix** — What each competitor did, our assessment\n"
          "3. **Pricing Intelligence** — Any pricing changes detected\n"
          "4. **Feature Gap Analysis** — What competitors have that we don't\n"
          "5. **Customer Movement** — Any switching signals (theirs to us, ours to them)\n"
          "6. **Recommended Actions** for Product, Marketing, and Sales\n"
          "7. **Watch List** — Emerging threats to monitor next month\n\n"
          "Format as a brief that can be shared directly with leadership."
  }).json()

  print(f"\n{'='*60}\nCOMPETITIVE RESEARCH BRIEF — {DAYS_BACK}-day window\n{'='*60}")
  print(brief.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 = "C0123INTEL";
  const DAYS_BACK = 30;
  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 intel = messages.filter(m => m.type === "message" && (m.text||"").length > 20);
  console.log(`Intel messages: ${intel.length}`);

  // 2. Enrich with replies
  const enriched = [];
  for (const m of intel.sort((a,b) => parseFloat(a.ts)-parseFloat(b.ts)).slice(-50)) {
    let entry = `[${new Date(parseFloat(m.ts)*1000).toISOString().slice(0,10)}] ${(m.text||"").slice(0,500)}`;
    if ((m.reply_count||0) > 0) {
      const replies = await (await fetch(
        `${SL_BASE}/conversations.replies?channel=${CHANNEL_ID}&ts=${m.ts}&limit=5`, { headers: SL_H })).json();
      if (replies.ok) for (const rep of (replies.messages||[]).slice(1))
        entry += `\n  → Reply: ${(rep.text||"").slice(0,200)}`;
      await new Promise(r => setTimeout(r, 1000));
    }
    enriched.push(entry);
  }

  // 3. Brief
  const brief = await fetch(`${MV_BASE}/mave/chat`, { method: "POST", headers: MV_H,
    body: JSON.stringify({ message: `Competitive analyst. ${intel.length} observations from #competitive-intel.\n\n${enriched.join("\n\n").slice(0,8000)}\n\nBRIEF: Executive Summary, Competitor Activity Matrix, Pricing Intel, Feature Gaps, Customer Movement, Actions (Product/Marketing/Sales), Watch List. Leadership-ready.` }),
  }).then(r => r.json());

  console.log(`\n${"=".repeat(60)}\nCOMPETITIVE RESEARCH BRIEF`);
  console.log((brief.content || "").slice(0, 3000));
  ```
</CodeGroup>

### Example Output

```text theme={"dark"}
Intel messages: 94 (past 30 days)

COMPETITIVE RESEARCH BRIEF — 30-day window
============================================================

EXECUTIVE SUMMARY:
Competitor X launched a free tier targeting our SMB segment. Competitor Y
raised Series C ($45M) and is hiring aggressively in EMEA. Three customer
switch signals detected — two inbound (from Competitor X), one outbound.

COMPETITOR ACTIVITY MATRIX:
| Competitor | Action | Assessment |
|------------|--------|------------|
| X | Free tier launch | Direct threat to our starter plan |
| Y | $45M raise + EMEA expansion | Market expansion, not feature |
| Z | Acquired data provider | Enriches their analytics moat |

RECOMMENDED ACTIONS:
- Product: Ship SSO before Q3 (Competitor X just added it)
- Marketing: Publish "Why Free Isn't Free" comparison content
- Sales: Arm reps with updated battlecards (Competitor X pricing)
```

### Error Handling

<AccordionGroup>
  <Accordion title="Thread depth">`conversations.replies` returns the full thread. For very long threads (50+ replies), paginate with the `cursor` parameter.</Accordion>
  <Accordion title="Attachments and links">Intel often contains screenshots (attachments) and links. The code captures text only. For link-heavy channels, extract URLs from the `blocks` field and fetch their titles.</Accordion>
  <Accordion title="Confidential intel">Competitive intelligence may include sensitive information. Review the Mave output before sharing — remove any data that shouldn't leave internal channels.</Accordion>
</AccordionGroup>
