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

Amplitude’s User Activity endpoint shows exactly what individual users did — every event, in order, with timestamps. You pull activity timelines for a sample of converting and non-converting users, identify common paths to conversion and points where users get stuck, then send the aggregated journey data to Mave for analysis. The result is a journey map grounded in actual behavioral sequences.

Architecture

Code

import os, requests, time, json
from collections import Counter, defaultdict

AMP_KEY = os.environ["AMPLITUDE_API_KEY"]
AMP_SECRET = os.environ["AMPLITUDE_SECRET_KEY"]
MV = os.environ["MAVERA_API_KEY"]
amp_auth = (AMP_KEY, AMP_SECRET)

CONVERTER_IDS = os.environ.get("CONVERTER_USER_IDS", "user_101,user_102,user_103,user_104,user_105").split(",")
NON_CONVERTER_IDS = os.environ.get("NON_CONVERTER_USER_IDS", "user_201,user_202,user_203,user_204,user_205").split(",")

def fetch_activity(user_id):
    r = requests.get(
        "https://amplitude.com/api/2/useractivity",
        auth=amp_auth,
        params={"user": user_id},
    )
    if r.status_code == 429:
        time.sleep(int(r.headers.get("Retry-After", 10)))
        return fetch_activity(user_id)
    if r.status_code != 200:
        return []
    data = r.json()
    return data.get("userData", {}).get("events", [])

def extract_path(events, max_events=30):
    path = []
    for e in events[:max_events]:
        name = e.get("event_type", "unknown")
        if name.startswith("$"):
            continue
        path.append(name)
    return path

converter_paths = []
for uid in CONVERTER_IDS:
    events = fetch_activity(uid)
    path = extract_path(events)
    if path:
        converter_paths.append(path)
    time.sleep(1)

non_converter_paths = []
for uid in NON_CONVERTER_IDS:
    events = fetch_activity(uid)
    path = extract_path(events)
    if path:
        non_converter_paths.append(path)
    time.sleep(1)

def aggregate_paths(paths):
    step_freq = Counter()
    bigrams = Counter()
    path_lengths = []
    last_events = Counter()

    for path in paths:
        path_lengths.append(len(path))
        for event in path:
            step_freq[event] += 1
        for i in range(len(path) - 1):
            bigrams[f"{path[i]}{path[i+1]}"] += 1
        if path:
            last_events[path[-1]] += 1

    return {
        "count": len(paths),
        "avg_length": sum(path_lengths) / max(len(path_lengths), 1),
        "top_events": step_freq.most_common(10),
        "top_transitions": bigrams.most_common(10),
        "last_events": last_events.most_common(5),
    }

conv_agg = aggregate_paths(converter_paths)
non_conv_agg = aggregate_paths(non_converter_paths)

def format_agg(agg, label):
    events = "\n".join(f"    {e}: {c} occurrences" for e, c in agg["top_events"])
    transitions = "\n".join(f"    {t}: {c} times" for t, c in agg["top_transitions"])
    last = "\n".join(f"    {e}: {c}" for e, c in agg["last_events"])
    return (
        f"  **{label}** ({agg['count']} users, avg {agg['avg_length']:.0f} events/journey)\n"
        f"  Top events:\n{events}\n"
        f"  Top transitions:\n{transitions}\n"
        f"  Final events before exit:\n{last}"
    )

journey_data = f"""CONVERTING USERS:
{format_agg(conv_agg, "Converters")}

NON-CONVERTING USERS:
{format_agg(non_conv_agg, "Non-Converters")}"""

mave = requests.post(
    "https://app.mavera.io/api/v1/mave/chat",
    headers={"Authorization": f"Bearer {MV}", "Content-Type": "application/json"},
    json={"message": f"""Analyze these user journey patterns from Amplitude. Common paths to conversion? Where do users get stuck?

{journey_data}

Provide:
1. The "golden path" — the most common event sequence leading to conversion
2. Divergence points — where converters and non-converters split
3. Friction signals — events that appear in non-converter paths but not converter paths
4. Drop-off cliffs — transitions where non-converters commonly exit
5. Recommendations for reducing friction at each identified stuck point
6. A simplified journey map with 4-6 key stages"""},
).json()

print("--- Journey Analysis ---")
print(mave.get("content", "")[:3000])

Example Output

--- Journey Analysis ---

## Golden Path (Converting Users)
Sign Up → Complete Profile → Create Project → Invite Teammate → Run First Report → Upgrade

This 6-step sequence appears in 72% of converter journeys. Average
completion time: 3.2 days. The critical transition is Create Project →
Invite Teammate — converters who invite in session 1 convert at 4.1x
the rate of those who wait.

## Divergence Points
| Stage | Converters | Non-Converters |
|-------|-----------|----------------|
| After Sign Up | 89% → Complete Profile | 61% → Dashboard View (browsing) |
| After Create Project | 74% → Invite Teammate | 82% → Settings Visit (fiddling) |
| After First Report | 68% → Upgrade | 45% → Export Data (extracting value without paying) |

## Friction Signals
Events in non-converter paths but rare in converter paths:
- **Help Doc Visit** (3.2x more common) — they're confused
- **Settings Visit** after project creation — looking for something
- **Repeated Dashboard View** without action — passive consumption

## Drop-Off Cliffs
1. Sign Up → (nothing) — 18% of non-converters have 0 events after signup
2. Create Project → Settings — suggests unclear next step
3. Run Report → Export — they got what they needed without paying

## Recommendations
1. Add a "Complete Your Profile" nudge within 30 seconds of signup
2. Auto-prompt team invites immediately after first project creation
3. Gate CSV export behind a trial activation to prevent value extraction
4. Replace help doc links with inline tooltips at friction points

Error Handling

The /api/2/useractivity endpoint returns events for a single user ID. You must loop through user IDs, which counts against the 360/hour rate limit. For large samples, batch requests with 1-second delays.
User IDs in Amplitude can be your internal IDs or Amplitude-generated device IDs. Use the same ID format you pass to amplitude.identify(). Check in Amplitude → Users → search to verify.
Amplitude includes system events prefixed with $. The code filters these out since they represent SDK calls, not user actions. Include $revenue events if you need purchase signals.

What’s Next

Amplitude Integration

Back to Amplitude integration overview

Behavioral Cohort Focus Group

Compare fast vs slow onboarders in a Focus Group

Mave Agent

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

Personas API

Full reference for POST /api/v1/personas