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

# Community Questions → FAQ Generation

### Scenario

Your community asks the same questions repeatedly across `#help`, `#general`, and `#support`. This job mines question messages (identified by `?` marks and question patterns), clusters them by topic via Mave, then generates polished FAQ entries with your brand voice — ready for docs, help centers, or pinned messages.

**Flow:** Discord `GET /channels/{id}/messages` → Filter questions → Mavera `POST /mave/chat` (cluster) → `POST /generations` (FAQ entries)

### Code

<CodeGroup>
  ```python Python theme={"dark"}
  import os, requests, time, re

  DC_TOKEN = os.environ["DISCORD_BOT_TOKEN"]
  DC_BASE = "https://discord.com/api/v10"
  DC_H = {"Authorization": f"Bot {DC_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"}

  HELP_CHANNELS = ["CHANNEL_ID_1", "CHANNEL_ID_2"]
  QUESTION_PATTERNS = re.compile(r"\?|how do|how can|is there|can i|does anyone|why does|where is|what is", re.I)

  # 1. Collect questions from channels
  all_questions = []
  for channel_id in HELP_CHANNELS:
      after = "0"
      while True:
          r = requests.get(f"{DC_BASE}/channels/{channel_id}/messages", headers=DC_H,
              params={"limit": 100, "after": after})
          if r.status_code == 429:
              time.sleep(r.json().get("retry_after", 1))
              continue
          if not r.ok:
              break
          batch = r.json()
          if not batch:
              break
          for m in batch:
              content = m.get("content", "")
              if (not m.get("author",{}).get("bot",False)
                  and QUESTION_PATTERNS.search(content)
                  and 15 < len(content) < 500):
                  all_questions.append(content)
          after = batch[0]["id"]
          if len(all_questions) >= 200:
              break
          time.sleep(0.5)

  print(f"Collected {len(all_questions)} questions")

  # 2. Cluster via Mave
  clustering = requests.post(f"{MV_BASE}/mave/chat", headers=MV_H, json={
      "message": f"FAQ analyst. Cluster these {len(all_questions)} community questions into 10-15 FAQ topics.\n\n"
          + "\n".join(f"- {q[:200]}" for q in all_questions[:100])
          + "\n\nFor each cluster:\n- Topic name (clear, concise)\n- Frequency (how many questions)\n- Representative question (best phrased example)\n- Key details needed in the answer\n\nRank by frequency."
  }).json()
  clusters_text = clustering.get("content", "")
  print(f"\nClusters:\n{clusters_text[:1500]}")

  # 3. Generate FAQ entries
  faq = requests.post(f"{MV_BASE}/generations", headers=MV_H, json={
      "prompt": f"Generate a complete FAQ page from these clustered community questions.\n\n"
          f"CLUSTERS:\n{clusters_text[:4000]}\n\n"
          "For each FAQ entry:\n"
          "- Question (phrased clearly, search-friendly)\n"
          "- Answer (2-4 sentences, helpful, specific)\n"
          "- Related: link suggestions to other FAQ entries\n\n"
          "Tone: friendly, direct, knowledgeable. Use 'you' not 'the user'.\n"
          "Format as markdown with ## headers per question.",
  }).json()

  print(f"\n{'='*60}\nGENERATED FAQ ({len(all_questions)} questions → clustered)\n{'='*60}")
  print(faq.get("output", faq.get("content", ""))[:2500])
  ```

  ```javascript JavaScript theme={"dark"}
  const DC_TOKEN = process.env.DISCORD_BOT_TOKEN;
  const DC_BASE = "https://discord.com/api/v10";
  const DC_H = { Authorization: `Bot ${DC_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 HELP_CHANNELS = ["CHANNEL_ID_1", "CHANNEL_ID_2"];
  const QUESTION_RE = /\?|how do|how can|is there|can i|does anyone|why does|where is|what is/i;

  // 1. Collect questions
  const allQuestions = [];
  for (const channelId of HELP_CHANNELS) {
    let after = "0";
    while (allQuestions.length < 200) {
      const r = await fetch(`${DC_BASE}/channels/${channelId}/messages?limit=100&after=${after}`, { headers: DC_H });
      if (r.status === 429) { await new Promise(res => setTimeout(res, (await r.json()).retry_after * 1000 || 1000)); continue; }
      const batch = await r.json();
      if (!batch.length) break;
      for (const m of batch) {
        const c = m.content || "";
        if (!m.author?.bot && QUESTION_RE.test(c) && c.length > 15 && c.length < 500)
          allQuestions.push(c);
      }
      after = batch[0].id;
      await new Promise(r => setTimeout(r, 500));
    }
  }
  console.log(`Collected ${allQuestions.length} questions`);

  // 2. Cluster
  const clustering = await fetch(`${MV_BASE}/mave/chat`, { method: "POST", headers: MV_H,
    body: JSON.stringify({ message: `Cluster ${allQuestions.length} questions into 10-15 FAQ topics.\n\n${allQuestions.slice(0,100).map(q=>`- ${q.slice(0,200)}`).join("\n")}\n\nPer cluster: topic, frequency, best question, answer details. Rank by frequency.` }),
  }).then(r => r.json());
  const clustersText = clustering.content || "";
  console.log(`Clusters:\n${clustersText.slice(0, 1500)}`);

  // 3. FAQ
  const faq = await fetch(`${MV_BASE}/generations`, { method: "POST", headers: MV_H,
    body: JSON.stringify({ prompt: `FAQ page from clusters.\n\n${clustersText.slice(0,4000)}\n\nPer entry: clear question, answer (2-4 sentences), related links.\n\nTone: friendly, direct. Use 'you'. Markdown ## headers.` }),
  }).then(r => r.json());

  console.log(`\n${"=".repeat(60)}\nGENERATED FAQ`);
  console.log((faq.output || faq.content || "").slice(0, 2500));
  ```
</CodeGroup>

### Example Output

```text theme={"dark"}
Collected 187 questions

Clusters:
1. Pricing & Plans (32 questions) — "What's the difference between free and pro?"
2. API Rate Limits (24 questions) — "How do I increase my API rate limit?"
3. Mobile App Issues (21 questions) — "Why does the app crash on Android?"
4. SSO / Team Setup (18 questions) — "How do I set up SSO for my org?"
5. Integrations (15 questions) — "Can I connect this to Zapier?"

GENERATED FAQ
============================================================

## What's the difference between the Free and Pro plans?

Free gives you up to 5 team members, 1,000 API calls/day, and access
to core features. Pro unlocks unlimited team members, 50,000 API
calls/day, SSO, custom dashboards, and priority support. You can
compare plans side-by-side on our [pricing page](/pricing).

**Related:** How do I upgrade? | What happens when I hit the API limit?

## How do I increase my API rate limit?

Your rate limit depends on your plan — Free is 100 req/min, Pro is
1,000 req/min. If you need more, contact sales for an Enterprise plan
with custom limits. In the meantime, implement client-side caching
and exponential backoff to maximize your current allocation.
```

### Error Handling

<AccordionGroup>
  <Accordion title="Question detection">The regex pattern catches most questions but may miss imperative-style questions ("Tell me how to..."). Add patterns for your community's question style.</Accordion>
  <Accordion title="Noise filtering">Community channels contain memes, off-topic chat, and reactions. The length filter (15-500 chars) removes most noise. For cleaner results, only process messages from specific roles.</Accordion>
  <Accordion title="FAQ staleness">Regenerate monthly. Pin the latest FAQ in your Discord channels and update help center docs via webhook.</Accordion>
</AccordionGroup>
