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

Code

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]}")

Example Output

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

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.
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.
The /actions endpoint returns message variants. Multi-variant campaigns have multiple actions. The code limits to 2 per campaign to stay within rate limits.

What’s Next

Customer.io Integration

Back to Customer.io integration overview

Customer Attribute Personas

Build attribute-clustered personas from Customer.io segments

Mave Agent

Full reference for POST /api/v1/mave/chat

Generate API

Full reference for POST /api/v1/generations