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

Segment’s identity graph merges user identities across sources — the same person visiting your website, using your mobile app, and opening your emails appears as one unified profile. You pull the merged identity data showing which channels a user engages with, then send it to Mave with the instruction to create a unified persona that accounts for cross-channel behavior differences. The result is a persona that understands the user’s full journey, not just a single-channel slice.

Architecture

Code

import os, requests, time
from collections import defaultdict

SEG_TOKEN = os.environ["SEGMENT_TOKEN"]
MV = os.environ["MAVERA_API_KEY"]
MB = "https://app.mavera.io/api/v1"
MH = {"Authorization": f"Bearer {MV}", "Content-Type": "application/json"}
SH = {"Authorization": f"Bearer {SEG_TOKEN}", "Content-Type": "application/json"}

SPACE_ID = os.environ.get("SEGMENT_SPACE_ID", "spa_xxxxx")

r = requests.get(
    f"https://api.segmentapis.com/spaces/{SPACE_ID}/collections/users/profiles",
    headers=SH,
    params={"limit": 100, "include": "traits,external_ids"},
)

profiles = []
if r.status_code == 200:
    profiles = r.json().get("data", {}).get("profiles", [])

if not profiles:
    profiles = [
        {
            "traits": {"email": "j.doe@example.com", "ltv": 890, "plan": "pro", "last_web_visit": "2026-03-15", "last_mobile_open": "2026-03-16", "last_email_click": "2026-03-12", "web_sessions_30d": 14, "mobile_sessions_30d": 22, "emails_opened_30d": 8},
            "external_ids": [{"type": "user_id", "id": "usr_001"}, {"type": "ios_id", "id": "ios_abc"}, {"type": "email", "id": "j.doe@example.com"}],
        },
        {
            "traits": {"email": "s.chen@example.com", "ltv": 340, "plan": "starter", "last_web_visit": "2026-03-10", "last_mobile_open": "2026-02-28", "last_email_click": "2026-03-14", "web_sessions_30d": 4, "mobile_sessions_30d": 1, "emails_opened_30d": 12},
            "external_ids": [{"type": "user_id", "id": "usr_002"}, {"type": "email", "id": "s.chen@example.com"}],
        },
        {
            "traits": {"email": "m.patel@example.com", "ltv": 1450, "plan": "enterprise", "last_web_visit": "2026-03-17", "last_mobile_open": "2026-03-17", "last_email_click": "2026-03-16", "web_sessions_30d": 28, "mobile_sessions_30d": 35, "emails_opened_30d": 15},
            "external_ids": [{"type": "user_id", "id": "usr_003"}, {"type": "ios_id", "id": "ios_xyz"}, {"type": "android_id", "id": "and_789"}, {"type": "email", "id": "m.patel@example.com"}],
        },
    ] * 10

channel_patterns = {"web_dominant": [], "mobile_dominant": [], "email_dominant": [], "omnichannel": []}

for p in profiles:
    traits = p.get("traits", {})
    ext_ids = p.get("external_ids", [])
    id_types = [e.get("type", "") for e in ext_ids]

    web = int(traits.get("web_sessions_30d", 0) or 0)
    mobile = int(traits.get("mobile_sessions_30d", 0) or 0)
    email = int(traits.get("emails_opened_30d", 0) or 0)
    total = web + mobile + email

    if total == 0:
        continue

    profile_data = {
        "ltv": float(traits.get("ltv", 0) or 0),
        "plan": traits.get("plan", "unknown"),
        "web": web, "mobile": mobile, "email": email,
        "id_count": len(id_types),
        "channels": id_types,
    }

    web_pct = web / total
    mobile_pct = mobile / total
    email_pct = email / total

    if web_pct > 0.5:
        channel_patterns["web_dominant"].append(profile_data)
    elif mobile_pct > 0.5:
        channel_patterns["mobile_dominant"].append(profile_data)
    elif email_pct > 0.5:
        channel_patterns["email_dominant"].append(profile_data)
    else:
        channel_patterns["omnichannel"].append(profile_data)

def summarize_pattern(users, label):
    if not users:
        return ""
    avg_ltv = sum(u["ltv"] for u in users) / len(users)
    avg_web = sum(u["web"] for u in users) / len(users)
    avg_mobile = sum(u["mobile"] for u in users) / len(users)
    avg_email = sum(u["email"] for u in users) / len(users)
    avg_ids = sum(u["id_count"] for u in users) / len(users)
    plans = defaultdict(int)
    for u in users:
        plans[u["plan"]] += 1
    top_plans = sorted(plans, key=plans.get, reverse=True)[:3]

    return (
        f"**{label}** (N={len(users)})\n"
        f"  Avg LTV: ${avg_ltv:.0f} | Web sessions: {avg_web:.0f} | Mobile: {avg_mobile:.0f} | Email opens: {avg_email:.0f}\n"
        f"  Avg identity count: {avg_ids:.1f} | Plans: {', '.join(top_plans)}"
    )

pattern_block = "\n\n".join(
    summarize_pattern(users, label.replace("_", " ").title())
    for label, users in channel_patterns.items()
    if users
)

mave = requests.post(
    f"{MB}/mave/chat",
    headers=MH,
    json={"message": f"""Create a unified persona framework that accounts for cross-channel behavior. Users interact with our product via website, mobile app, and email — and the same user behaves differently on each channel.

CROSS-CHANNEL PATTERNS (from Segment identity graph, 30 days):

{pattern_block}

For each channel pattern, provide:
1. A persona name and 2-sentence description capturing their cross-channel identity
2. Why they prefer their dominant channel — what need does it serve?
3. How to reach them on their weaker channels — what message/format would work?
4. Cross-channel journey map — how do they typically move between channels?
5. Content format recommendations per channel
6. Risk of losing them — which channel loss would cause churn?

Then provide an overall cross-channel strategy:
- Where to invest in consistency vs. channel-specific adaptation
- Identity resolution opportunities (matching anonymous to known)
- Moments when users switch channels and what triggers the switch"""},
).json()

print("--- Cross-Channel Unified Persona Framework ---")
print(mave.get("content", "")[:3000])

Example Output

--- Cross-Channel Unified Persona Framework ---

## 1. Mobile Dominant — "The On-The-Go Operator"
Power user who runs their business from their phone. 22 mobile sessions
vs. 14 web sessions per month. LTV: $890. Pro plan.

**Why mobile:** They manage in motion — between meetings, on commutes,
during events. Mobile gives them quick-check capability without context-
switching from other work.

**Reach on web:** Desktop email with "Continue on Desktop" CTAs for
complex tasks (reports, settings). They use web for setup, mobile for
monitoring.

**Journey:** Discovers on web → installs mobile → daily mobile check-ins
→ monthly web deep-dive → upgrade decision on desktop.

## 2. Email Dominant — "The Inbox Dweller"
Engages primarily through email. 12 opens vs. 4 web sessions and 1
mobile session. LTV: $340. Starter plan.

**Why email:** They're not heavy product users — they consume insights
passively. Email delivers value without requiring them to log in.

**Risk:** HIGH churn risk if email quality drops. They don't have a
product habit — email IS their product experience. If you stop sending
useful content, they'll forget you exist.

## 3. Omnichannel — "The Power Orchestrator"
Uses all channels heavily. 28 web + 35 mobile + 15 email. LTV: $1,450.
Enterprise plan. 4+ identity graph connections.

**Journey:** Morning email digest → mobile check during commute →
desktop deep work session → mobile monitoring → evening email summary.
This user expects seamless continuity across all channels.

## Cross-Channel Strategy
- **Consistency:** Value proposition, core metrics, notification content
- **Adaptation:** Layout (mobile-optimized cards vs. desktop tables),
  depth (email summaries → web details), interaction (mobile gestures
  vs. desktop keyboard shortcuts)
- **Identity gaps:** 30% of profiles have only 2 identifiers. Push
  mobile app install and email verification to resolve anonymous web
  visitors to known profiles.

Error Handling

The identity resolution features (external_ids, merged profiles) require Segment Engage. On Connections-only plans, profiles exist per-source without cross-source merging.
Common external_id types include user_id, email, ios_id, android_id, anonymous_id, and ga_client_id. The code counts ID types to estimate cross-channel coverage — more ID types = more channels connected.
The example uses traits like web_sessions_30d and mobile_sessions_30d. These must be set up as computed traits in Segment Engage. If they don’t exist, compute from raw events using the Events API or set up custom computed traits.

What’s Next

Segment Integration

Back to Segment integration overview

Real-Time Persona Triggers

Auto-trigger research from live events

Mave Agent

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

All Integrations

50+ API integrations with full code