Skip to main content

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.

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

Code

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)

Example Output

==================================================
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

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.
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.
For best results, create a Mavera Brand Voice from your top-performing Klaviyo campaigns before using Generate. This ensures rewrites match your established tone.

What’s Next

Klaviyo Integration

Back to Klaviyo integration overview

Predictive Persona Enrichment

CLV-tier personas with churn profiles

Segment Overlap Analysis

Simplified segmentation recommendations

Generate API

Full reference for POST /api/v1/generations