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

# Behavioral Cohort → Focus Group

> Map Amplitude behavioral cohorts to Mavera personas and run Focus Groups to understand onboarding speed differences

### Scenario

You've identified two behavioral cohorts in Amplitude — users who onboarded within 24 hours versus those who took more than 7 days. These groups have radically different retention curves, but you don't know *why* the experience differs. You map each cohort to Mavera personas and run a Focus Group exploring how fast-onboarders and slow-onboarders experience the product differently — what drives urgency, what causes delay, and what each group needs.

### Architecture

```mermaid theme={"dark"}
flowchart LR
A["Amplitude cohort definitions"] --> B["Pull behavioral profiles per cohort"] --> C["POST /api/v1/personas"] --> D["POST /api/v1/focus-groups"] --> E["Onboarding speed insights"]
```

### Code

<CodeGroup>
  ```python Python theme={"dark"}
  import os, requests, time
  from collections import Counter

  AMP_KEY = os.environ["AMPLITUDE_API_KEY"]
  AMP_SECRET = os.environ["AMPLITUDE_SECRET_KEY"]
  MV = os.environ["MAVERA_API_KEY"]
  MB = "https://app.mavera.io/api/v1"
  MH = {"Authorization": f"Bearer {MV}", "Content-Type": "application/json"}
  amp_auth = (AMP_KEY, AMP_SECRET)

  FAST_IDS = os.environ.get("FAST_ONBOARD_IDS", "").split(",")
  SLOW_IDS = os.environ.get("SLOW_ONBOARD_IDS", "").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 {"events": [], "properties": {}}
      data = r.json().get("userData", {})
      return {"events": data.get("events", []), "properties": data.get("userProperties", {})}

  def profile_cohort(user_ids, label):
      events_all = Counter()
      platforms = Counter()
      countries = Counter()
      event_counts = []

      for uid in user_ids[:20]:
          data = fetch_activity(uid)
          user_events = [e.get("event_type", "") for e in data["events"] if not e.get("event_type", "").startswith("$")]
          events_all.update(user_events)
          event_counts.append(len(user_events))
          props = data["properties"]
          if props.get("platform"):
              platforms[props["platform"]] += 1
          if props.get("country"):
              countries[props["country"]] += 1
          time.sleep(1)

      return {
          "label": label,
          "n": len(user_ids),
          "sampled": min(len(user_ids), 20),
          "avg_events": sum(event_counts) / max(len(event_counts), 1),
          "top_events": events_all.most_common(8),
          "platforms": platforms.most_common(3),
          "countries": countries.most_common(5),
      }

  fast_profile = profile_cohort(FAST_IDS, "Fast Onboarders (<24h)")
  slow_profile = profile_cohort(SLOW_IDS, "Slow Onboarders (>7d)")

  fast_persona = requests.post(f"{MB}/personas", headers=MH, json={
      "name": "Amplitude: Fast Onboarder (<24h)",
      "description": (
          f"Users who completed onboarding within 24 hours. "
          f"Avg events: {fast_profile['avg_events']:.0f}. "
          f"Top actions: {', '.join(e for e, _ in fast_profile['top_events'][:5])}. "
          f"Platforms: {', '.join(p for p, _ in fast_profile['platforms'])}."
      ),
      "psychographic": {
          "onboarding_speed": "fast",
          "avg_events": fast_profile["avg_events"],
          "top_actions": [e for e, _ in fast_profile["top_events"][:5]],
      },
  }).json()
  time.sleep(0.3)

  slow_persona = requests.post(f"{MB}/personas", headers=MH, json={
      "name": "Amplitude: Slow Onboarder (>7d)",
      "description": (
          f"Users who took 7+ days to complete onboarding. "
          f"Avg events: {slow_profile['avg_events']:.0f}. "
          f"Top actions: {', '.join(e for e, _ in slow_profile['top_events'][:5])}. "
          f"Platforms: {', '.join(p for p, _ in slow_profile['platforms'])}."
      ),
      "psychographic": {
          "onboarding_speed": "slow",
          "avg_events": slow_profile["avg_events"],
          "top_actions": [e for e, _ in slow_profile["top_events"][:5]],
      },
  }).json()

  def format_profile(prof):
      events = ", ".join(f"{e} ({c})" for e, c in prof["top_events"][:6])
      return f"  N={prof['n']}, sampled={prof['sampled']}, avg events={prof['avg_events']:.0f}\n  Top: {events}"

  context = f"""Two behavioral cohorts from Amplitude onboarding data:

  FAST ONBOARDERS (completed onboarding in <24 hours):
  {format_profile(fast_profile)}

  SLOW ONBOARDERS (took >7 days to complete onboarding):
  {format_profile(slow_profile)}

  Fast onboarders retain at 68% (30d). Slow onboarders retain at 22% (30d)."""

  fg = requests.post(f"{MB}/focus-groups", headers=MH, json={
      "name": "Amplitude: Onboarding Speed Cohort Study",
      "persona_ids": [fast_persona["id"], slow_persona["id"]],
      "questions": [
          "Walk me through your first day with a new software tool. What makes you complete setup quickly versus putting it off?",
          "When you signed up, what was your immediate goal? Were you trying to solve a specific problem or just exploring?",
          "What would have made you complete onboarding faster? Be specific — was something confusing, unnecessary, or missing?",
          "If a tool required you to invite teammates during onboarding, would that speed you up or slow you down? Why?",
          "Rank what matters most in your first session: (1) Seeing sample data, (2) Completing a real task, (3) Customizing settings, (4) Reading documentation, (5) Watching a tutorial. Explain your #1.",
      ],
      "context": context,
      "responses_per_persona": 2,
  }).json()

  for _ in range(30):
      time.sleep(5)
      data = requests.get(f"{MB}/focus-groups/{fg['id']}", headers=MH).json()
      if data.get("status") == "completed":
          break

  print(f"Focus Group: {data.get('id')} — {data.get('status')}\n")
  for resp in data.get("responses", []):
      print(f"[{resp.get('persona_id','?')}] {resp.get('question','')[:80]}")
      print(f"  → {resp.get('answer','')[:300]}\n")
  ```

  ```javascript JavaScript theme={"dark"}
  const AMP_KEY = process.env.AMPLITUDE_API_KEY;
  const AMP_SECRET = process.env.AMPLITUDE_SECRET_KEY;
  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 ampAuth = "Basic " + Buffer.from(`${AMP_KEY}:${AMP_SECRET}`).toString("base64");

  const FAST_IDS = (process.env.FAST_ONBOARD_IDS || "user_101,user_102,user_103").split(",");
  const SLOW_IDS = (process.env.SLOW_ONBOARD_IDS || "user_201,user_202,user_203").split(",");

  async function fetchActivity(userId) {
    const res = await fetch(
      `https://amplitude.com/api/2/useractivity?user=${encodeURIComponent(userId)}`,
      { headers: { Authorization: ampAuth } }
    );
    if (res.status === 429) {
      await new Promise((r) => setTimeout(r, parseInt(res.headers.get("Retry-After") || "10") * 1000));
      return fetchActivity(userId);
    }
    if (!res.ok) return { events: [], properties: {} };
    const data = (await res.json()).userData || {};
    return { events: data.events || [], properties: data.userProperties || {} };
  }

  async function profileCohort(ids, label) {
    const eventsAll = {}, platforms = {}, eventCounts = [];
    for (const uid of ids.slice(0, 20)) {
      const data = await fetchActivity(uid);
      const userEvents = data.events.map((e) => e.event_type || "").filter((n) => !n.startsWith("$"));
      userEvents.forEach((e) => { eventsAll[e] = (eventsAll[e] || 0) + 1; });
      eventCounts.push(userEvents.length);
      if (data.properties.platform) platforms[data.properties.platform] = (platforms[data.properties.platform] || 0) + 1;
      await new Promise((r) => setTimeout(r, 1000));
    }
    const topN = (o, n) => Object.entries(o).sort(([, a], [, b]) => b - a).slice(0, n);
    return {
      label, n: ids.length, sampled: Math.min(ids.length, 20),
      avgEvents: eventCounts.reduce((s, c) => s + c, 0) / (eventCounts.length || 1),
      topEvents: topN(eventsAll, 8), platforms: topN(platforms, 3),
    };
  }

  const fastProf = await profileCohort(FAST_IDS, "Fast (<24h)");
  const slowProf = await profileCohort(SLOW_IDS, "Slow (>7d)");

  const fastPersona = await fetch(`${MB}/personas`, { method: "POST", headers: MH,
    body: JSON.stringify({
      name: "Amplitude: Fast Onboarder (<24h)",
      description: `Completed onboarding <24h. Avg events: ${fastProf.avgEvents.toFixed(0)}. Top: ${fastProf.topEvents.slice(0, 5).map(([e]) => e).join(", ")}.`,
      psychographic: { onboarding_speed: "fast", avg_events: fastProf.avgEvents },
    }),
  }).then((r) => r.json());
  await new Promise((r) => setTimeout(r, 300));

  const slowPersona = await fetch(`${MB}/personas`, { method: "POST", headers: MH,
    body: JSON.stringify({
      name: "Amplitude: Slow Onboarder (>7d)",
      description: `Took 7+ days to onboard. Avg events: ${slowProf.avgEvents.toFixed(0)}. Top: ${slowProf.topEvents.slice(0, 5).map(([e]) => e).join(", ")}.`,
      psychographic: { onboarding_speed: "slow", avg_events: slowProf.avgEvents },
    }),
  }).then((r) => r.json());

  const context = `Cohorts:\nFAST (<24h): N=${fastProf.n}, avg events=${fastProf.avgEvents.toFixed(0)}, top: ${fastProf.topEvents.slice(0, 5).map(([e, c]) => `${e}(${c})`).join(", ")}\nSLOW (>7d): N=${slowProf.n}, avg events=${slowProf.avgEvents.toFixed(0)}, top: ${slowProf.topEvents.slice(0, 5).map(([e, c]) => `${e}(${c})`).join(", ")}\n\nFast retain 68% (30d). Slow retain 22% (30d).`;

  const fg = await fetch(`${MB}/focus-groups`, { method: "POST", headers: MH,
    body: JSON.stringify({
      name: "Amplitude: Onboarding Speed Study",
      persona_ids: [fastPersona.id, slowPersona.id],
      questions: [
        "Walk me through your first day with new software. What makes you complete setup quickly vs putting it off?",
        "When you signed up, what was your immediate goal — solving a problem or exploring?",
        "What would have made you finish onboarding faster? Something confusing, unnecessary, or missing?",
        "If a tool required teammate invites during onboarding, speed you up or slow you down?",
        "Rank first-session priorities: (1) Sample data, (2) Real task, (3) Settings, (4) Docs, (5) Tutorial. Explain #1.",
      ],
      context,
      responses_per_persona: 2,
    }),
  }).then((r) => r.json());

  let data;
  for (let i = 0; i < 30; i++) {
    await new Promise((r) => setTimeout(r, 5000));
    data = await fetch(`${MB}/focus-groups/${fg.id}`, { headers: MH }).then((r) => r.json());
    if (data.status === "completed") break;
  }

  console.log(`Focus Group: ${data.id} — ${data.status}\n`);
  for (const resp of data.responses || []) {
    console.log(`[${resp.persona_id}] ${(resp.question || "").slice(0, 80)}`);
    console.log(`  → ${(resp.answer || "").slice(0, 300)}\n`);
  }
  ```
</CodeGroup>

### Example Output

```text theme={"dark"}
Focus Group: fg_amp_cohort_9k2a — completed

[Fast Onboarder] Walk me through your first day with new software
  → I have a problem to solve TODAY. I sign up, skip the welcome tour,
    go straight to the feature I need. If I can't do something useful in
    10 minutes, I'll try the next tool. Setup wizards that force me
    through 8 steps before I can do anything are the fastest way to
    lose me — let me jump to the thing I came for.

[Slow Onboarder] Walk me through your first day with new software
  → I usually sign up during a meeting or after reading a blog post,
    then forget about it for a few days. When I come back, I've lost
    context. I need the tool to remind me why I signed up and pick up
    where I left off, not start from scratch.

[Fast Onboarder] If a tool required teammate invites during onboarding
  → Speed up. If I can invite my team early, I look like the person
    who found the solution. But ONLY if the invite step takes <30
    seconds and I can add them by email without configuring permissions.

[Slow Onboarder] Rank first-session priorities
  → #1: Seeing sample data. I don't have my own data ready on day 1.
    If the product shows me what it looks like with real content, I
    can evaluate it without doing any work. Empty states kill momentum.
```

### Error Handling

<AccordionGroup>
  <Accordion title="Cohort user IDs">You must supply user IDs for each cohort. Export these from Amplitude cohorts (Amplitude → Cohorts → Export CSV) or query via the Behavioral Cohorts API. The User Activity endpoint does not accept cohort IDs directly.</Accordion>
  <Accordion title="Rate limiting per user lookup">Each `/api/2/useractivity` call counts against the 360/hour limit. For 20 users per cohort (40 total), this uses 40 of your 360 hourly queries. Add 1-second delays between calls.</Accordion>
  <Accordion title="Focus Group polling duration">2 personas × 5 questions × 2 responses = 20 total responses. Allow 90–150s for completion. The loop provides \~150s.</Accordion>
</AccordionGroup>

***

## What's Next

<CardGroup cols={2}>
  <Card title="Amplitude Integration" icon="wave-square" href="/integrations/amplitude">
    Back to Amplitude integration overview
  </Card>

  <Card title="Pricing Focus Group" icon="dollar-sign" href="/integrations/amplitude/pricing-focus-group">
    Validate willingness-to-pay with revenue-driven personas
  </Card>

  <Card title="Focus Groups API" icon="comments" href="/api-reference/focus-groups">
    Full reference for POST /api/v1/focus-groups
  </Card>

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