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

# Campaign Event Analysis → Messaging Strategy

> Pull Customer.io campaign metrics, identify winning message patterns, and generate new variations using Mave Agent and Mavera Generate

### Scenario

Your Customer.io campaigns generate open, click, and conversion metrics across email, push, and in-app channels. But you never synthesize which message *types* actually perform — is it urgency-driven subject lines? Personalized value props? Social proof? This job pulls campaign metrics from the App API, identifies the highest-performing message patterns, then feeds those patterns to Mave Agent for analysis and Mavera Generate to produce new variations that extend the winning formula.

**Flow:** Customer.io App API `/v1/campaigns/{id}/metrics` → Rank campaigns by conversion → Extract winning copy patterns → Mave `POST /api/v1/mave/chat` (pattern analysis) → `POST /api/v1/generations` (new variations)

### Architecture

```mermaid theme={"dark"}
flowchart LR
A["GET /v1/campaigns/{id}/metrics"] --> B["Rank by CTR/Conversion"] --> C["Extract top-performing copy"] --> D["Mave Agent: Analyze patterns"] --> E["Generate: New variations"]
```

### Code

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

  CIO_APP = os.environ["CIO_APP_KEY"]
  MV = os.environ["MAVERA_API_KEY"]
  APP_BASE = "https://api.customer.io/v1"
  MB = "https://app.mavera.io/api/v1"
  APP_H = {"Authorization": f"Bearer {CIO_APP}"}
  MV_H = {"Authorization": f"Bearer {MV}", "Content-Type": "application/json"}

  # 1. List campaigns
  campaigns = requests.get(f"{APP_BASE}/campaigns",
      headers=APP_H).json().get("campaigns", [])

  # 2. Pull metrics for each active campaign
  ranked = []
  for camp in campaigns:
      if camp.get("state") != "active":
          continue
      cid = camp["id"]
      metrics = requests.get(f"{APP_BASE}/campaigns/{cid}/metrics",
          headers=APP_H, params={"period": "days", "steps": 30})
      if metrics.status_code == 429:
          time.sleep(1)
          metrics = requests.get(f"{APP_BASE}/campaigns/{cid}/metrics",
              headers=APP_H, params={"period": "days", "steps": 30})
      metrics.raise_for_status()
      data = metrics.json()

      series = data.get("series", {})
      sent = sum(series.get("sent", [0]))
      clicked = sum(series.get("clicked", [0]))
      converted = sum(series.get("converted", [0]))
      ctr = clicked / max(sent, 1)

      ranked.append({
          "id": cid,
          "name": camp.get("name", "Untitled"),
          "type": camp.get("type", "email"),
          "sent": sent, "clicked": clicked, "converted": converted,
          "ctr": ctr,
      })
      time.sleep(0.12)

  ranked.sort(key=lambda x: -x["ctr"])
  top = ranked[:5]
  bottom = ranked[-3:] if len(ranked) >= 8 else []

  # 3. Pull message content for top campaigns
  top_copy = []
  for camp in top:
      actions = requests.get(f"{APP_BASE}/campaigns/{camp['id']}/actions",
          headers=APP_H).json().get("actions", [])
      for act in actions[:2]:
          body = act.get("body", "")[:500]
          subject = act.get("subject", act.get("name", ""))
          if subject or body:
              top_copy.append(
                  f"Campaign: {camp['name']} (CTR: {camp['ctr']:.1%})\n"
                  f"Subject: {subject}\n{body}"
              )

  # 4. Mave pattern analysis
  analysis = requests.post(f"{MB}/mave/chat", headers=MV_H, json={
      "message": f"""Analyze these top-performing Customer.io campaign messages.
  Identify: 1) Common structural patterns 2) Tone and voice traits
  3) Subject line formulas 4) CTA patterns 5) What the bottom performers lack

  TOP PERFORMERS:
  {chr(10).join(top_copy[:8])}

  BOTTOM PERFORMERS (for contrast):
  {chr(10).join(f"- {c['name']} (CTR: {c['ctr']:.1%})" for c in bottom)}"""
  }).json()

  print("=== Pattern Analysis ===")
  print(analysis.get("content", "")[:1500])

  # 5. Generate new variations
  gen = requests.post(f"{MB}/generations", headers=MV_H, json={
      "prompt": (
          f"Based on these winning patterns from our email campaigns, "
          f"generate 5 new email subject line + 100-word body variations. "
          f"Topic: product feature announcement. "
          f"Patterns to follow: {analysis.get('content', '')[:800]}"
      ),
      "count": 5,
  }).json()

  print("\n=== New Variations ===")
  for i, g in enumerate(gen.get("results", [gen]), 1):
      print(f"\nVariant {i}:\n{g.get('content', g.get('text', ''))[:300]}")
  ```

  ```javascript JavaScript theme={"dark"}
  const CIO_APP = process.env.CIO_APP_KEY;
  const MV = process.env.MAVERA_API_KEY;
  const APP_BASE = "https://api.customer.io/v1";
  const MB = "https://app.mavera.io/api/v1";
  const appH = { Authorization: `Bearer ${CIO_APP}` };
  const mvH = { Authorization: `Bearer ${MV}`, "Content-Type": "application/json" };

  // 1. List campaigns
  const { campaigns } = await fetch(`${APP_BASE}/campaigns`,
    { headers: appH }).then(r => r.json());

  // 2. Pull metrics
  const ranked = [];
  for (const camp of campaigns) {
    if (camp.state !== "active") continue;
    let res = await fetch(
      `${APP_BASE}/campaigns/${camp.id}/metrics?period=days&steps=30`,
      { headers: appH });
    if (res.status === 429) {
      await new Promise(r => setTimeout(r, 1000));
      res = await fetch(
        `${APP_BASE}/campaigns/${camp.id}/metrics?period=days&steps=30`,
        { headers: appH });
    }
    const data = await res.json();
    const series = data.series || {};
    const sum = (a) => (a || [0]).reduce((s, v) => s + v, 0);
    const sent = sum(series.sent), clicked = sum(series.clicked),
          converted = sum(series.converted);
    ranked.push({
      id: camp.id, name: camp.name || "Untitled", type: camp.type || "email",
      sent, clicked, converted, ctr: clicked / Math.max(sent, 1),
    });
    await new Promise(r => setTimeout(r, 120));
  }

  ranked.sort((a, b) => b.ctr - a.ctr);
  const top = ranked.slice(0, 5);
  const bottom = ranked.length >= 8 ? ranked.slice(-3) : [];

  // 3. Pull message content for top campaigns
  const topCopy = [];
  for (const camp of top) {
    const { actions } = await fetch(`${APP_BASE}/campaigns/${camp.id}/actions`,
      { headers: appH }).then(r => r.json());
    for (const act of (actions || []).slice(0, 2)) {
      const subject = act.subject || act.name || "";
      const body = (act.body || "").slice(0, 500);
      if (subject || body)
        topCopy.push(`Campaign: ${camp.name} (CTR: ${(camp.ctr * 100).toFixed(1)}%)\nSubject: ${subject}\n${body}`);
    }
  }

  // 4. Mave pattern analysis
  const analysis = await fetch(`${MB}/mave/chat`, {
    method: "POST", headers: mvH,
    body: JSON.stringify({
      message: `Analyze these top-performing Customer.io campaign messages.
  Identify: 1) Structural patterns 2) Tone traits 3) Subject line formulas 4) CTA patterns 5) What bottom performers lack

  TOP PERFORMERS:
  ${topCopy.slice(0, 8).join("\n\n")}

  BOTTOM PERFORMERS:
  ${bottom.map(c => `- ${c.name} (CTR: ${(c.ctr * 100).toFixed(1)}%)`).join("\n")}`,
    }),
  }).then(r => r.json());

  console.log("=== Pattern Analysis ===");
  console.log((analysis.content || "").slice(0, 1500));

  // 5. Generate variations
  const gen = await fetch(`${MB}/generations`, {
    method: "POST", headers: mvH,
    body: JSON.stringify({
      prompt: `Based on winning patterns from our emails, generate 5 subject + 100-word body variations. Topic: product feature announcement. Patterns: ${(analysis.content || "").slice(0, 800)}`,
      count: 5,
    }),
  }).then(r => r.json());

  console.log("\n=== New Variations ===");
  (gen.results || [gen]).forEach((g, i) =>
    console.log(`\nVariant ${i + 1}:\n${(g.content || g.text || "").slice(0, 300)}`)
  );
  ```
</CodeGroup>

### Example Output

```text theme={"dark"}
=== Pattern Analysis ===
## Winning Patterns (5 campaigns, avg CTR 12.4%)

1. **Subject Line Formula:** [Number] + [Benefit] + [Timeframe]
   - "3 ways to cut onboarding time this week"
   - "Your Q2 metrics — 2 quick wins inside"

2. **Tone:** Direct, peer-to-peer, avoids marketing jargon.
   Uses "you/your" 3x more than bottom performers.

3. **CTA Pattern:** Single action, specific timeframe.
   Winners: "Reply with your top priority" (8.2% reply rate)
   Losers: "Learn more" (0.3% CTR)

4. **Structure:** <100 words, one idea per email, PS line with social proof.

5. **Bottom performers lack:** Personalization, specificity, urgency framing.

=== New Variations ===
Variant 1:
Subject: Your onboarding is 40% slower than peers — here's the fix
We analyzed 200 teams like yours. The gap? Manual steps between...

Variant 2:
Subject: 2 changes. 15 minutes. Measurable pipeline lift.
Last month, [Similar Co] made two adjustments to their workflow...
```

### Error Handling

<AccordionGroup>
  <Accordion title="App API rate limit (10 req/sec)">The App API is strict at 10 req/sec. Code includes 120ms delay between requests. For workspaces with 50+ campaigns, batch in groups of 8 with 1s pauses between batches.</Accordion>
  <Accordion title="Empty metric series">New campaigns may return empty `series` arrays. The code defaults to `[0]` to avoid division errors. Filter out campaigns with fewer than 100 sends for meaningful CTR.</Accordion>
  <Accordion title="Campaign actions structure">The `/actions` endpoint returns message variants. Multi-variant campaigns have multiple actions. The code limits to 2 per campaign to stay within rate limits.</Accordion>
</AccordionGroup>

***

## What's Next

<CardGroup cols={2}>
  <Card title="Customer.io Integration" icon="message" href="/integrations/customer-io">
    Back to Customer.io integration overview
  </Card>

  <Card title="Customer Attribute Personas" icon="user" href="/integrations/customer-io/attribute-personas">
    Build attribute-clustered personas from Customer.io segments
  </Card>

  <Card title="Mave Agent" icon="brain" href="/api-reference/mave">
    Full reference for POST /api/v1/mave/chat
  </Card>

  <Card title="Generate API" icon="wand-magic-sparkles" href="/api-reference/generate">
    Full reference for POST /api/v1/generations
  </Card>
</CardGroup>
