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

# Sprint Retro Notes → Messaging Insights

### Scenario

Your product/marketing team runs sprint retrospectives in Asana — each retro is a task or project with discussion items captured as subtasks and comments. Over time, retros reveal messaging gold: what customers love, where positioning falls flat, which features generate excitement. This job aggregates retro content and feeds it to Mave for messaging pattern extraction.

**Flow:** Asana `GET /tasks` (retro tasks) → `GET /tasks/{id}/subtasks` + `GET /tasks/{id}/stories` → Aggregate → Mavera `POST /api/v1/mave/chat` → Messaging insights

### Code

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

  ASANA = os.environ["ASANA_PAT"]
  MV = os.environ["MAVERA_API_KEY"]
  AB = "https://app.asana.com/api/1.0"
  MB = "https://app.mavera.io/api/v1"
  AH = {"Authorization": f"Bearer {ASANA}"}
  MH = {"Authorization": f"Bearer {MV}", "Content-Type": "application/json"}

  RETRO_PROJECT_GID = "1234567890123456"

  # 1. Fetch retro tasks (each retro is a task in the project)
  retro_tasks = requests.get(f"{AB}/projects/{RETRO_PROJECT_GID}/tasks", headers=AH,
      params={
          "opt_fields": "name,notes,completed_at,created_at",
          "limit": 20,
      }).json().get("data", [])

  print(f"Found {len(retro_tasks)} retro entries")

  # 2. For each retro, get subtasks (discussion items) and comments
  retro_corpus = []
  for retro in retro_tasks[:12]:
      entry = f"## {retro['name']}\n{retro.get('notes', '')[:1000]}\n"

      # Subtasks (retro items)
      subtasks = requests.get(f"{AB}/tasks/{retro['gid']}/subtasks", headers=AH,
          params={"opt_fields": "name,notes,completed", "limit": 50}).json().get("data", [])
      if subtasks:
          entry += "\nDiscussion items:\n"
          for st in subtasks:
              entry += f"- {'[done]' if st.get('completed') else '[open]'} {st['name']}"
              if st.get("notes"):
                  entry += f": {st['notes'][:200]}"
              entry += "\n"

      # Comments (stories of type "comment")
      stories = requests.get(f"{AB}/tasks/{retro['gid']}/stories", headers=AH,
          params={"opt_fields": "text,type,created_by.name", "limit": 30}).json().get("data", [])
      comments = [s for s in stories if s.get("type") == "comment" and s.get("text")]
      if comments:
          entry += "\nComments:\n"
          for c in comments[:10]:
              author = c.get("created_by", {}).get("name", "Unknown")
              entry += f"- [{author}] {c['text'][:300]}\n"

      retro_corpus.append(entry)
      time.sleep(0.5)

  corpus = "\n\n---\n\n".join(retro_corpus)
  print(f"Aggregated {len(retro_corpus)} retros ({len(corpus)} chars)")

  # 3. Mave messaging insights
  insights = requests.post(f"{MB}/mave/chat", headers=MH, json={
      "message": (
          f"Messaging strategist. Analyze {len(retro_corpus)} sprint retrospectives "
          f"from a product/marketing team.\n\n"
          f"RETRO NOTES:\n{corpus[:10000]}\n\n"
          "Extract MESSAGING INSIGHTS:\n\n"
          "1. **Customer Language Patterns** — Phrases and framing the team uses when "
          "discussing customer feedback. These are messaging gold.\n"
          "2. **Positioning Gaps** — Where did messaging fall flat? What confused customers?\n"
          "3. **Feature Excitement Signals** — Which features/updates generated the most "
          "positive discussion? These should be amplified in marketing.\n"
          "4. **Competitive Positioning Clues** — Any competitor mentions and how the team "
          "frames the comparison.\n"
          "5. **Objection Patterns** — Recurring customer pushback themes.\n"
          "6. **Messaging Recommendations** — 5 specific messaging changes based on the data.\n"
          "7. **Tagline Candidates** — 3-5 tagline options derived from the language patterns.\n\n"
          "Quote directly from the retro notes. Attribution matters."
      ),
  }).json()

  print(f"\n{'='*60}\nMESSAGING INSIGHTS FROM RETROS\n{'='*60}")
  print(insights.get("content", "")[:3000])
  ```

  ```javascript JavaScript theme={"dark"}
  const ASANA = process.env.ASANA_PAT;
  const MV = process.env.MAVERA_API_KEY;
  const AB = "https://app.asana.com/api/1.0";
  const MB = "https://app.mavera.io/api/v1";
  const AH = { Authorization: `Bearer ${ASANA}` };
  const MH = { Authorization: `Bearer ${MV}`, "Content-Type": "application/json" };

  const RETRO_PROJECT_GID = "1234567890123456";

  // 1. Fetch retro tasks
  const retroTasks = await fetch(
    `${AB}/projects/${RETRO_PROJECT_GID}/tasks?` +
    new URLSearchParams({ opt_fields: "name,notes,completed_at", limit: "20" }),
    { headers: AH },
  ).then(r => r.json()).then(d => d.data || []);

  console.log(`Found ${retroTasks.length} retro entries`);

  // 2. Enrich with subtasks + comments
  const retroCorpus = [];
  for (const retro of retroTasks.slice(0, 12)) {
    let entry = `## ${retro.name}\n${(retro.notes || "").slice(0, 1000)}\n`;

    const subtasks = await fetch(
      `${AB}/tasks/${retro.gid}/subtasks?opt_fields=name,notes,completed&limit=50`,
      { headers: AH },
    ).then(r => r.json()).then(d => d.data || []);

    if (subtasks.length) {
      entry += "\nDiscussion items:\n";
      subtasks.forEach(st => {
        entry += `- [${st.completed ? "done" : "open"}] ${st.name}`;
        if (st.notes) entry += `: ${st.notes.slice(0, 200)}`;
        entry += "\n";
      });
    }

    const stories = await fetch(
      `${AB}/tasks/${retro.gid}/stories?opt_fields=text,type,created_by.name&limit=30`,
      { headers: AH },
    ).then(r => r.json()).then(d => d.data || []);

    const comments = stories.filter(s => s.type === "comment" && s.text);
    if (comments.length) {
      entry += "\nComments:\n";
      comments.slice(0, 10).forEach(c => {
        entry += `- [${c.created_by?.name || "Unknown"}] ${c.text.slice(0, 300)}\n`;
      });
    }

    retroCorpus.push(entry);
    await new Promise(r => setTimeout(r, 500));
  }

  const corpus = retroCorpus.join("\n\n---\n\n");
  console.log(`Aggregated ${retroCorpus.length} retros (${corpus.length} chars)`);

  // 3. Insights
  const insights = await fetch(`${MB}/mave/chat`, { method: "POST", headers: MH,
    body: JSON.stringify({
      message: `Messaging strategist. ${retroCorpus.length} sprint retros.\n\n${corpus.slice(0, 10000)}\n\nExtract: 1) Customer language patterns 2) Positioning gaps 3) Feature excitement signals 4) Competitive clues 5) Objection patterns 6) 5 messaging recommendations 7) 3-5 tagline candidates. Quote from notes.`,
    }),
  }).then(r => r.json());

  console.log(`\n${"=".repeat(60)}\nMESSAGING INSIGHTS FROM RETROS`);
  console.log((insights.content || "").slice(0, 3000));
  ```
</CodeGroup>

### Example Output

```text theme={"dark"}
Found 12 retro entries
Aggregated 12 retros (14832 chars)

MESSAGING INSIGHTS FROM RETROS
============================================================

## 1. Customer Language Patterns
- Customers consistently say "I just want it to work" (appeared in
  5/12 retros) → Simplicity messaging should lead
- "We don't have time to learn another tool" (4 retros) → Zero
  learning curve positioning
- "Show me the ROI before I commit" (3 retros) → ROI calculator
  should be above-the-fold

## 2. Positioning Gaps
- Sprint 8 retro: "Customers confused by 'AI-powered' — they think
  it means less control, not more" → Reframe AI as "AI-assisted"
- Sprint 10: "Enterprise prospects think we're too small for them"
  → Need social proof from similar-sized companies

## 3. Feature Excitement
- Real-time dashboards: "Biggest wow moment in every demo" (7 retros)
- API webhooks: "Developers light up when they see this"
- Custom reports: "This is where deals accelerate"

## 6. Messaging Recommendations
1. Replace "AI-powered" with "AI-assisted" in all materials
2. Lead demos with real-time dashboards (proven wow factor)
3. Add ROI calculator to pricing page
4. Create "Zero Learning Curve" campaign pillar
5. Build enterprise case study program (3 logos by Q3)

## 7. Tagline Candidates
- "It just works. You just grow."
- "Less learning. More doing."
- "From data to decisions in minutes, not meetings."
```

### Error Handling

<AccordionGroup>
  <Accordion title="Empty retro tasks">Some retros may be placeholder tasks with no notes or subtasks. The code processes them but generates thin analysis. Filter by `notes` length or subtask count for higher-quality input.</Accordion>
  <Accordion title="Story types">Asana stories include system events (assignment, status changes) and comments. The code filters to `type: "comment"` only. System stories add noise to the corpus.</Accordion>
  <Accordion title="Rate limit on stories endpoint">The stories endpoint counts against the global rate limit. With 12 retros × 2 calls each (subtasks + stories) = 24 calls. The 500ms delay keeps this well within limits.</Accordion>
  <Accordion title="Historical retros">Old retros may reference obsolete features or resolved issues. Consider limiting to the last 3-6 months of retros for relevant messaging insights.</Accordion>
</AccordionGroup>
