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

# Event-Based Persona Enrichment

> Export raw Mixpanel events, classify power-user vs. casual usage patterns, and update Mavera personas with behavioral data

### Scenario

Your user profiles tell you *who* people are. Your event stream tells you *what they do*. You export raw events from Mixpanel, identify power-user vs. casual usage patterns (feature breadth, session frequency, action depth), and update existing Mavera personas with behavioral data. The result is personas that reflect both demographics and real product behavior.

### Architecture

```mermaid theme={"dark"}
flowchart LR
    A["Mixpanel GET /api/2.0/export (raw events)"] --> B[Aggregate events per user] --> C["Classify: power-user vs regular vs casual"] --> D["PATCH /api/v1/personas/{id}"]
```

### Code

<CodeGroup>
  ```python Python theme={"dark"}
  import os, requests, time, json
  from collections import defaultdict
  from datetime import datetime, timedelta

  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"]
  MB = "https://app.mavera.io/api/v1"
  MH = {"Authorization": f"Bearer {MV}", "Content-Type": "application/json"}

  today = datetime.now()
  from_date = (today - timedelta(days=30)).strftime("%Y-%m-%d")
  to_date = today.strftime("%Y-%m-%d")

  r = requests.get(
      "https://data.mixpanel.com/api/2.0/export",
      auth=(MP_SA, MP_SECRET),
      params={"project_id": MP_PROJECT, "from_date": from_date, "to_date": to_date},
      stream=True,
  )
  r.raise_for_status()

  user_events = defaultdict(lambda: {"events": [], "features": set(), "days": set(), "count": 0})

  for line in r.iter_lines():
      if not line:
          continue
      event = json.loads(line)
      props = event.get("properties", {})
      uid = props.get("distinct_id")
      if not uid:
          continue

      user_events[uid]["events"].append(event.get("event", ""))
      user_events[uid]["features"].add(event.get("event", ""))
      user_events[uid]["count"] += 1
      ts = props.get("time")
      if ts:
          user_events[uid]["days"].add(datetime.fromtimestamp(ts).strftime("%Y-%m-%d"))

  print(f"Processed events for {len(user_events)} users")

  patterns = {"power_user": [], "regular": [], "casual": []}
  for uid, data in user_events.items():
      feature_count = len(data["features"])
      event_count = data["count"]
      active_days = len(data["days"])

      profile = {
          "uid": uid, "events": event_count, "features": feature_count,
          "active_days": active_days, "top_events": data["events"][:5],
          "feature_list": list(data["features"])[:10],
      }

      if feature_count >= 5 and active_days >= 15:
          patterns["power_user"].append(profile)
      elif feature_count >= 2 and active_days >= 5:
          patterns["regular"].append(profile)
      else:
          patterns["casual"].append(profile)

  existing = requests.get(f"{MB}/personas", headers=MH).json()
  mp_personas = {
      p["name"]: p for p in (existing if isinstance(existing, list) else [])
      if "Mixpanel" in p.get("name", "")
  }

  for pattern_name, users in patterns.items():
      if not users:
          continue
      label = pattern_name.replace("_", " ").title()
      persona_name = f"Mixpanel: {label}"

      avg_events = sum(u["events"] for u in users) / len(users)
      avg_features = sum(u["features"] for u in users) / len(users)
      avg_days = sum(u["active_days"] for u in users) / len(users)

      from collections import Counter
      all_features = Counter(f for u in users for f in u["feature_list"])
      top_features = [f for f, _ in all_features.most_common(8)]

      payload = {
          "name": persona_name,
          "description": (
              f"{label} segment enriched with event data (30d). "
              f"N={len(users)}. Avg events: {avg_events:.0f}, "
              f"features used: {avg_features:.1f}, active days: {avg_days:.1f}. "
              f"Top features: {', '.join(top_features[:5])}."
          ),
          "psychographic": {
              "usage_pattern": pattern_name,
              "avg_events_30d": avg_events,
              "avg_features_used": avg_features,
              "avg_active_days": avg_days,
              "top_features": top_features,
          },
      }

      if persona_name in mp_personas:
          pid = mp_personas[persona_name]["id"]
          requests.patch(f"{MB}/personas/{pid}", headers=MH, json=payload).raise_for_status()
          print(f"Updated: {persona_name} ({pid}) — {len(users)} users")
      else:
          p = requests.post(f"{MB}/personas", headers=MH, json=payload).json()
          print(f"Created: {persona_name} ({p['id']}) — {len(users)} users")
      time.sleep(0.3)
  ```

  ```javascript JavaScript theme={"dark"}
  const MP_SA = process.env.MIXPANEL_SERVICE_ACCOUNT;
  const MP_SECRET = process.env.MIXPANEL_SECRET;
  const MP_PROJECT = process.env.MIXPANEL_PROJECT_ID;
  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 mpAuth = "Basic " + Buffer.from(`${MP_SA}:${MP_SECRET}`).toString("base64");

  const today = new Date();
  const from = new Date(today - 30 * 86400000).toISOString().slice(0, 10);
  const to = today.toISOString().slice(0, 10);

  const exportRes = await fetch(
    `https://data.mixpanel.com/api/2.0/export?project_id=${MP_PROJECT}&from_date=${from}&to_date=${to}`,
    { headers: { Authorization: mpAuth } }
  ).then((r) => r.text());

  const userEvents = {};
  for (const line of exportRes.split("\n").filter(Boolean)) {
    const event = JSON.parse(line);
    const uid = event.properties?.distinct_id;
    if (!uid) continue;
    userEvents[uid] ??= { events: [], features: new Set(), days: new Set(), count: 0 };
    userEvents[uid].events.push(event.event || "");
    userEvents[uid].features.add(event.event || "");
    userEvents[uid].count++;
    if (event.properties?.time) {
      userEvents[uid].days.add(new Date(event.properties.time * 1000).toISOString().slice(0, 10));
    }
  }

  console.log(`Processed events for ${Object.keys(userEvents).length} users`);

  const patterns = { power_user: [], regular: [], casual: [] };
  for (const [uid, data] of Object.entries(userEvents)) {
    const fc = data.features.size, ad = data.days.size;
    const profile = { uid, events: data.count, features: fc, activeDays: ad, featureList: [...data.features].slice(0, 10) };
    if (fc >= 5 && ad >= 15) patterns.power_user.push(profile);
    else if (fc >= 2 && ad >= 5) patterns.regular.push(profile);
    else patterns.casual.push(profile);
  }

  const existing = await fetch(`${MB}/personas`, { headers: MH }).then((r) => r.json());
  const mpPersonas = Object.fromEntries(
    (Array.isArray(existing) ? existing : []).filter((p) => (p.name || "").includes("Mixpanel")).map((p) => [p.name, p])
  );

  for (const [patternName, users] of Object.entries(patterns)) {
    if (!users.length) continue;
    const label = patternName.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
    const name = `Mixpanel: ${label}`;
    const avgE = users.reduce((s, u) => s + u.events, 0) / users.length;
    const avgF = users.reduce((s, u) => s + u.features, 0) / users.length;
    const avgD = users.reduce((s, u) => s + u.activeDays, 0) / users.length;

    const featureCount = {};
    users.forEach((u) => u.featureList.forEach((f) => { featureCount[f] = (featureCount[f] || 0) + 1; }));
    const topFeatures = Object.entries(featureCount).sort(([, a], [, b]) => b - a).slice(0, 8).map(([f]) => f);

    const payload = {
      name, description: `${label} (${users.length} users, 30d). Avg events: ${avgE.toFixed(0)}, features: ${avgF.toFixed(1)}, days: ${avgD.toFixed(1)}. Top: ${topFeatures.slice(0, 5).join(", ")}.`,
      psychographic: { usage_pattern: patternName, avg_events_30d: avgE, top_features: topFeatures },
    };

    if (mpPersonas[name]) {
      await fetch(`${MB}/personas/${mpPersonas[name].id}`, { method: "PATCH", headers: MH, body: JSON.stringify(payload) });
      console.log(`Updated: ${name} (${mpPersonas[name].id})`);
    } else {
      const p = await fetch(`${MB}/personas`, { method: "POST", headers: MH, body: JSON.stringify(payload) }).then((r) => r.json());
      console.log(`Created: ${name} (${p.id})`);
    }
    await new Promise((r) => setTimeout(r, 300));
  }
  ```
</CodeGroup>

### Example Output

```text theme={"dark"}
Processed events for 8,420 users

Updated: Mixpanel: Power User (per_mp_power_1) — 489 users
  Avg events: 1,342 | Features: 7.2 | Days: 22.4
  Top: Dashboard View, API Call, Report Create, Alert Set, Export Data

Updated: Mixpanel: Regular (per_mp_reg_2) — 2,891 users
  Avg events: 186 | Features: 3.1 | Days: 9.8
  Top: Dashboard View, Report Create, Settings Visit

Created: Mixpanel: Casual (per_mp_cas_3) — 5,040 users
  Avg events: 12 | Features: 1.4 | Days: 2.1
  Top: Dashboard View, Login
```

### Error Handling

<AccordionGroup>
  <Accordion title="Export API returns JSONL">The Export API returns newline-delimited JSON (one event per line), not a JSON array. The code parses line by line. Large exports (millions of events) should use streaming and process in chunks.</Accordion>
  <Accordion title="Export volume limits">The Export API can return millions of events. For properties with high volume, filter by specific events using the `event` parameter, or use shorter date ranges.</Accordion>
  <Accordion title="PATCH vs POST for persona updates">If Mavera doesn't support PATCH, use DELETE + POST to replace personas. The code checks for existing personas by name to decide whether to update or create.</Accordion>
</AccordionGroup>

***

## What's Next

<CardGroup cols={2}>
  <Card title="Mixpanel Integration" icon="chart-bar" href="/integrations/mixpanel">
    Back to Mixpanel integration overview
  </Card>

  <Card title="Cohort Analysis → Content Strategy" icon="layer-group" href="/integrations/mixpanel/cohort-content-strategy">
    Retention strategies from cohort behavior
  </Card>

  <Card title="Feature Adoption → Messaging" icon="bullhorn" href="/integrations/mixpanel/feature-adoption-campaigns">
    Feature awareness campaigns from adoption data
  </Card>

  <Card title="Personas API" icon="users" href="/api-reference/personas">
    Full reference for POST /api/v1/personas
  </Card>
</CardGroup>
