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

Retained users behave differently from churned users — they use different features, visit at different times, and engage with different content. You pull cohort data from Mixpanel comparing retained vs. churned user behaviors, then send the behavioral differences to Mave with the instruction to research strategies for converting churning behaviors into retained behaviors.

Architecture

Code

import os, requests, time

MP_SA = os.environ["MIXPANEL_SERVICE_ACCOUNT"]
MP_SECRET = os.environ["MIXPANEL_SECRET"]
MP_PROJECT = os.environ["MIXPANEL_PROJECT_ID"]
MV = os.environ["MAVERA_API_KEY"]
mp_auth = (MP_SA, MP_SECRET)

def query_engage_segment(filter_expr, props):
    r = requests.post("https://mixpanel.com/api/query/engage", auth=mp_auth,
        json={
            "project_id": MP_PROJECT,
            "filter_by_cohort": filter_expr,
            "output_properties": props,
            "page": 0,
        })
    if r.status_code == 429: time.sleep(60)
    r.raise_for_status()
    return r.json().get("results", [])

PROFILE_PROPS = [
    "plan", "role", "company_size", "signup_source",
    "total_events", "sessions_count", "features_used",
    "days_active_last_30", "last_seen",
]

retained = query_engage_segment(
    '(properties["days_active_last_30"]) >= 10', PROFILE_PROPS
)
time.sleep(2)
churned = query_engage_segment(
    '(properties["days_active_last_30"]) <= 1 and (properties["sessions_count"]) >= 5', PROFILE_PROPS
)

def summarize(users, label):
    if not users:
        return f"{label}: No users found"
    props_list = [u.get("$properties", {}) for u in users]
    avg_events = sum(int(p.get("total_events", 0) or 0) for p in props_list) / len(props_list)
    avg_sessions = sum(int(p.get("sessions_count", 0) or 0) for p in props_list) / len(props_list)
    avg_days = sum(int(p.get("days_active_last_30", 0) or 0) for p in props_list) / len(props_list)

    from collections import Counter
    plans = Counter(p.get("plan") for p in props_list if p.get("plan")).most_common(3)
    roles = Counter(p.get("role") for p in props_list if p.get("role")).most_common(5)
    sources = Counter(p.get("signup_source") for p in props_list if p.get("signup_source")).most_common(3)
    features = Counter(
        f for p in props_list
        for f in (p.get("features_used", "").split(",") if isinstance(p.get("features_used"), str) else [])
        if f.strip()
    ).most_common(10)

    return (
        f"**{label}** ({len(users)} users)\n"
        f"  Avg events: {avg_events:.0f} | Avg sessions: {avg_sessions:.0f} | Avg days active (30d): {avg_days:.1f}\n"
        f"  Plans: {', '.join(f'{p[0]} ({p[1]})' for p in plans)}\n"
        f"  Roles: {', '.join(f'{r[0]} ({r[1]})' for r in roles)}\n"
        f"  Sources: {', '.join(f'{s[0]} ({s[1]})' for s in sources)}\n"
        f"  Top features: {', '.join(f'{f[0]} ({f[1]})' for f in features)}"
    )

retained_summary = summarize(retained, "Retained (10+ days active/30d)")
churned_summary = summarize(churned, "Churned (≤1 day active, had 5+ sessions)")

mave = requests.post(
    "https://app.mavera.io/api/v1/mave/chat",
    headers={"Authorization": f"Bearer {MV}", "Content-Type": "application/json"},
    json={"message": f"""Research strategies to convert churning user behaviors into retained user behaviors.

RETAINED USERS:
{retained_summary}

CHURNED USERS:
{churned_summary}

Analyze:
1. Key behavioral differences between retained and churned
2. Which features correlate with retention? Which are missing from churned?
3. Content strategies to drive feature adoption among at-risk users
4. Onboarding improvements to increase early activation
5. Re-engagement campaign ideas for users showing churn signals
6. Messaging recommendations: what to say at each stage of the retention curve"""},
).json()

print("--- Cohort Content Strategy ---")
print(mave.get("content", "")[:3000])

Example Output

--- Cohort Content Strategy ---

## Key Behavioral Differences
| Metric | Retained | Churned |
|--------|----------|---------|
| Avg events | 842 | 67 |
| Days active (30d) | 18.4 | 0.6 |
| Top features | Dashboard, API, Reports, Alerts | Dashboard only |
| Plans | Pro (61%), Enterprise (24%) | Free (78%), Starter (18%) |

## Feature-Retention Correlation
Users who activate **3+ features in the first 7 days** retain at 4.2x the
rate of those who use only the dashboard. The "Aha features" are:
1. **API integration** — 89% retention when activated in week 1
2. **Alerts/notifications** — 72% retention
3. **Custom reports** — 68% retention

## Content Strategies for At-Risk Users
1. **Day 3 email:** "You've set up your dashboard — here's what power users
   do next" → tutorial link to API integration
2. **Day 7 in-app prompt:** "Set up your first alert in 2 minutes" → guided flow
3. **Day 14 re-engagement:** "Your data is waiting — 3 insights you're missing"

## Messaging by Stage
- **Day 1-3:** "Quick win" focus — show immediate value
- **Day 4-7:** "Level up" — introduce second and third features
- **Day 8-14:** "Team value" — show collaboration features
- **Day 15-30:** "ROI proof" — send usage stats and time-saved metrics

Error Handling

Mixpanel’s Engage API uses a specific filter expression syntax. Test filters in the Mixpanel UI’s Explore section before using them in API calls. Common mistake: using == instead of >= for numeric comparisons.
The code defines “retained” as 10+ days active in 30d and “churned” as ≤1 day active with 5+ historical sessions. Adjust thresholds to match your product’s engagement model.
Two Engage queries back-to-back count against the 60/hour limit. The 2s delay between queries is a courtesy; the actual constraint is 60/hour total.

What’s Next

Mixpanel Integration

Back to Mixpanel integration overview

Funnel Drop-off → Focus Group

Investigate funnel abandonment with focus groups

Event-Based Persona Enrichment

Enrich personas with raw event patterns

Mave Agent

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