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

# Flow Performance → Content Refresh

> Pull Klaviyo flow metric aggregates to identify underperforming emails, then use Mavera Generate to rewrite content with your brand voice

### Scenario

Your Klaviyo flows (welcome series, abandoned cart, post-purchase) are underperforming — open rates dropped, click rates are flat, revenue per email is declining. You pull metric aggregates to identify which flows and specific messages are lagging, then use Mavera's Generate to rewrite the underperformers with your brand voice. The result is refreshed flow content informed by actual performance data.

### Architecture

```mermaid theme={"dark"}
flowchart LR
A["POST /api/metric-aggregates"] --> B["Identify underperforming flows"] --> C["POST /api/v1/generations"] --> D["Rewritten flow content"]
```

### Code

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

  KL_KEY = os.environ["KLAVIYO_API_KEY"]
  MV = os.environ["MAVERA_API_KEY"]
  MB = "https://app.mavera.io/api/v1"
  MH = {"Authorization": f"Bearer {MV}", "Content-Type": "application/json"}
  KH = {
      "Authorization": f"Klaviyo-API-Key {KL_KEY}",
      "Content-Type": "application/json",
      "revision": "2024-10-15",
  }

  r = requests.get(f"https://a.klaviyo.com/api/flows", headers=KH, params={
      "fields[flow]": "name,status,trigger_type",
  })
  if r.status_code == 429:
      time.sleep(int(r.headers.get("Retry-After", 10)))
      r = requests.get(f"https://a.klaviyo.com/api/flows", headers=KH, params={
          "fields[flow]": "name,status,trigger_type",
      })
  r.raise_for_status()
  flows = r.json().get("data", [])

  print(f"Found {len(flows)} flows\n")

  flow_metrics = []
  for flow in flows[:10]:
      flow_id = flow["id"]
      flow_name = flow["attributes"]["name"]
      flow_status = flow["attributes"].get("status", "unknown")

      metrics_r = requests.post(
          f"https://a.klaviyo.com/api/metric-aggregates",
          headers=KH,
          json={
              "data": {
                  "type": "metric-aggregate",
                  "attributes": {
                      "metric_id": "open",
                      "measurements": ["count", "unique"],
                      "interval": "month",
                      "page_size": 1,
                      "filter": [f"equals(flow_id,'{flow_id}')"],
                      "timeframe": {"key": "last_30_days"},
                  },
              },
          },
      )

      open_data = {}
      if metrics_r.status_code == 200:
          agg = metrics_r.json().get("data", {}).get("attributes", {})
          open_data = {"opens": agg.get("data", [{}])[0] if agg.get("data") else {}}

      flow_metrics.append({
          "id": flow_id,
          "name": flow_name,
          "status": flow_status,
          "metrics": open_data,
      })
      time.sleep(0.5)

  BENCHMARK = {"open_rate": 0.25, "click_rate": 0.035}

  UNDERPERFORMERS = [
      {"name": "Welcome Series", "open_rate": 0.18, "click_rate": 0.02, "current_subject": "Welcome to our family!", "current_preview": "Here's what to expect..."},
      {"name": "Abandoned Cart", "open_rate": 0.15, "click_rate": 0.025, "current_subject": "You forgot something!", "current_preview": "Complete your purchase"},
      {"name": "Post-Purchase", "open_rate": 0.22, "click_rate": 0.018, "current_subject": "Thanks for your order", "current_preview": "Your order details inside"},
  ]

  BRAND_VOICE_ID = os.environ.get("BRAND_VOICE_ID", "")

  print("--- Flow Content Refresh ---\n")
  for flow in UNDERPERFORMERS:
      prompt = (
          f"Rewrite the email content for our '{flow['name']}' flow. "
          f"Current performance is below benchmark:\n"
          f"  Open rate: {flow['open_rate']:.0%} (benchmark: {BENCHMARK['open_rate']:.0%})\n"
          f"  Click rate: {flow['click_rate']:.0%} (benchmark: {BENCHMARK['click_rate']:.0%})\n\n"
          f"Current subject: \"{flow['current_subject']}\"\n"
          f"Current preview: \"{flow['current_preview']}\"\n\n"
          f"Generate:\n"
          f"1. 3 subject line variants (each under 50 chars)\n"
          f"2. Preview text for each (under 90 chars)\n"
          f"3. Email body rewrite (150-200 words) that:\n"
          f"   - Hooks in the first line\n"
          f"   - Addresses the reader's emotional state at this flow stage\n"
          f"   - Has a clear, compelling CTA\n"
          f"4. Explain what's wrong with the current version"
      )

      payload = {"prompt": prompt}
      if BRAND_VOICE_ID:
          payload["brand_voice_id"] = BRAND_VOICE_ID

      gen = requests.post(f"{MB}/generations", headers=MH, json=payload).json()

      print(f"{'='*50}")
      print(f"Flow: {flow['name']}")
      print(f"Current: {flow['open_rate']:.0%} open / {flow['click_rate']:.0%} click")
      print(f"{'='*50}")
      print(gen.get("output", gen.get("content", ""))[:700])
      print()
      time.sleep(0.5)
  ```

  ```javascript JavaScript theme={"dark"}
  const KL_KEY = process.env.KLAVIYO_API_KEY;
  const MV = process.env.MAVERA_API_KEY;
  const MB = "https://app.mavera.io/api/v1";
  const MH = { Authorization: `Bearer ${MV}`, "Content-Type": "application/json" };
  const KH = { Authorization: `Klaviyo-API-Key ${KL_KEY}`, "Content-Type": "application/json", revision: "2024-10-15" };
  const BRAND_VOICE_ID = process.env.BRAND_VOICE_ID || "";

  const flowsRes = await fetch("https://a.klaviyo.com/api/flows?fields[flow]=name,status,trigger_type", { headers: KH }).then((r) => r.json());
  console.log(`Found ${(flowsRes.data || []).length} flows\n`);

  const UNDERPERFORMERS = [
    { name: "Welcome Series", openRate: 0.18, clickRate: 0.02, subject: "Welcome to our family!", preview: "Here's what to expect..." },
    { name: "Abandoned Cart", openRate: 0.15, clickRate: 0.025, subject: "You forgot something!", preview: "Complete your purchase" },
    { name: "Post-Purchase", openRate: 0.22, clickRate: 0.018, subject: "Thanks for your order", preview: "Your order details inside" },
  ];

  const BENCHMARK = { openRate: 0.25, clickRate: 0.035 };

  for (const flow of UNDERPERFORMERS) {
    const payload = {
      prompt: `Rewrite '${flow.name}' flow email.\nCurrent: ${(flow.openRate * 100).toFixed(0)}% open (benchmark: ${(BENCHMARK.openRate * 100).toFixed(0)}%), ${(flow.clickRate * 100).toFixed(0)}% click (benchmark: ${(BENCHMARK.clickRate * 100).toFixed(0)}%).\nSubject: "${flow.subject}" | Preview: "${flow.preview}"\n\nGenerate: 1) 3 subject variants (<50 chars) 2) Preview for each 3) Body rewrite (150-200 words) 4) What's wrong with current version.`,
    };
    if (BRAND_VOICE_ID) payload.brand_voice_id = BRAND_VOICE_ID;

    const gen = await fetch(`${MB}/generations`, { method: "POST", headers: MH,
      body: JSON.stringify(payload) }).then((r) => r.json());

    console.log(`${"=".repeat(50)}`);
    console.log(`${flow.name}: ${(flow.openRate * 100).toFixed(0)}% open / ${(flow.clickRate * 100).toFixed(0)}% click`);
    console.log((gen.output || gen.content || "").slice(0, 700));
    console.log();
    await new Promise((r) => setTimeout(r, 500));
  }
  ```
</CodeGroup>

### Example Output

```text theme={"dark"}
==================================================
Flow: Welcome Series
Current: 18% open / 2% click
==================================================
**What's wrong:** "Welcome to our family!" is generic and overpromises
emotional connection with a brand they just met. The preview "Here's
what to expect" is passive — it should create anticipation.

**Subject Variants:**
1. "Your first 3 minutes: a quick win inside" (42 chars)
2. "Here's the shortcut nobody told you about" (43 chars)
3. "You're in — here's what to do first" (36 chars)

**Preview for #1:** Skip the tour. Do this one thing and you'll see
why 12,000 people switched.

**Body Rewrite:**
You just signed up. Congrats — but let's skip the fluff.

Most new users spend their first session clicking around aimlessly.
You're not going to do that. Here's the one thing that separates
people who love this tool from people who forget about it:

**Set up your first [Core Feature] in the next 3 minutes.**

It takes 4 clicks. When you're done, you'll have [Specific Outcome]
ready to share with your team.

→ [Set Up Now — 3 Minutes]

P.S. If you'd rather explore on your own, here's a 90-second video
that covers everything: [Watch]
```

### Error Handling

<AccordionGroup>
  <Accordion title="Metric aggregates API structure">Klaviyo's metric aggregates endpoint uses a JSON:API format with nested `data.attributes`. The `filter` array uses Klaviyo's filter syntax. Test queries in Klaviyo's API playground first.</Accordion>
  <Accordion title="Flow-level vs. message-level metrics">The metric aggregates can be filtered by `flow_id` (all messages in a flow) or by `flow_message_id` (specific message). For granular rewrites, query per message.</Accordion>
  <Accordion title="Brand voice alignment">For best results, create a Mavera Brand Voice from your top-performing Klaviyo campaigns before using Generate. This ensures rewrites match your established tone.</Accordion>
</AccordionGroup>

***

## What's Next

<CardGroup cols={2}>
  <Card title="Klaviyo Integration" icon="envelope" href="/integrations/klaviyo">
    Back to Klaviyo integration overview
  </Card>

  <Card title="Predictive Persona Enrichment" icon="chart-line" href="/integrations/klaviyo/predictive-persona-enrichment">
    CLV-tier personas with churn profiles
  </Card>

  <Card title="Segment Overlap Analysis" icon="circle-nodes" href="/integrations/klaviyo/segment-overlap-analysis">
    Simplified segmentation recommendations
  </Card>

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