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

# Generational Content Testing

> Test campaign concepts against Gen Z, Millennial, Gen X, and Boomer personas — same questions, generation-specific insights

## The Scenario

You're preparing a campaign that needs to land across age groups. A single message rarely resonates the same way with a 22-year-old and a 62-year-old. Instead of guessing, you run the same Focus Group questions through Mavera's generational personas and compare responses side by side. The output is a generation-specific content playbook built from structured audience data.

<Info>
  **Mavera-only workflow.** No survey tools, no panel recruitment, no incentive budgets. Just Mavera's Personas and Focus Groups surfaces.
</Info>

***

## When to Use This

* Campaign planning where the audience spans multiple age demographics.
* Content strategy reviews — which generation does your messaging over-index for?
* Product launches targeting "everyone" — find which generation is the best beachhead.
* Social media strategy — match message to the generational skew of each platform.

***

## Architecture

```mermaid theme={"dark"}
flowchart LR
    A["Create Generational Personas"] --> B["Run 4 Focus Groups"]
    B --> C["Compare by Generation"]
    C --> D["Recommendations"]
```

| Mavera Surface                          | Role in Pipeline                                          |
| --------------------------------------- | --------------------------------------------------------- |
| **Personas** (`POST /personas`)         | Create 4 generational archetypes                          |
| **Focus Groups** (`POST /focus-groups`) | Run identical questions with each generation              |
| **Chat** (OpenAI-compatible)            | Synthesize comparison into per-generation recommendations |

***

## What You Need

| Requirement                        | Details                                                                                              |
| ---------------------------------- | ---------------------------------------------------------------------------------------------------- |
| **Mavera API key**                 | Starts with `mvra_live_`. Get one at [Developer Settings](https://app.mavera.io/settings/developer). |
| **Python 3.8+** or **Node.js 18+** | `requests` for Python; native `fetch` for Node.                                                      |
| **Credits**                        | \~85–190 total. See [Credits Estimate](#credits-estimate).                                           |

```
MAVERA_API_KEY=mvra_live_your_key_here
```

***

## Step 1 — Create Generational Personas

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

  API_KEY = os.environ["MAVERA_API_KEY"]
  BASE = "https://app.mavera.io/api/v1"
  HEADERS = {"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}

  GENERATIONS = [
      {"name": "Jordan — Gen Z Consumer", "role": "Gen Z (born 1997–2012)",
       "description": "Digital native, 24 years old, lives on TikTok and Instagram. Values authenticity and social proof. Skeptical of corporate messaging, responds to creator-style content. Short attention span for traditional ads.",
       "traits": ["digitally native", "authenticity-driven", "socially conscious", "short-form preference"], "gen": "gen_z"},
      {"name": "Morgan — Millennial Professional", "role": "Millennial (born 1981–1996)",
       "description": "35, mid-career professional. Responds to experiences over things, purpose-driven brands, and data-backed claims. Active on Instagram, LinkedIn, and podcasts. Willing to pay premium for convenience.",
       "traits": ["experience-oriented", "purpose-driven", "research-heavy", "subscription-comfortable"], "gen": "millennial"},
      {"name": "Casey — Gen X Decision-Maker", "role": "Gen X (born 1965–1980)",
       "description": "48, peak earning years. Pragmatic and self-reliant. Responds to straightforward value propositions without hype. Brand-loyal when value is consistent. Email and web-first.",
       "traits": ["pragmatic", "self-reliant", "brand-loyal", "hype-resistant"], "gen": "gen_x"},
      {"name": "Pat — Boomer Evaluator", "role": "Baby Boomer (born 1946–1964)",
       "description": "65, retired or senior advisory role. Values trust, credentials, and track record over novelty. Prefers longer-form content — articles, whitepapers, webinars. Active on Facebook and email.",
       "traits": ["trust-driven", "detail-oriented", "authority-responsive", "long-form preference"], "gen": "boomer"},
  ]

  def create_persona(g):
      resp = requests.post(f"{BASE}/personas", headers=HEADERS, json={
          "name": g["name"], "role": g["role"], "description": g["description"], "traits": g["traits"],
      })
      resp.raise_for_status()
      data = resp.json()
      print(f"  Created: {data['name']} → {data['id']}")
      return data["id"]

  gen_personas = {}
  for g in GENERATIONS:
      gen_personas[g["gen"]] = {"id": create_persona(g), "name": g["name"]}
  ```

  ```javascript JavaScript theme={"dark"}
  const API_KEY = process.env.MAVERA_API_KEY;
  const BASE = "https://app.mavera.io/api/v1";
  const HEADERS = { Authorization: `Bearer ${API_KEY}`, "Content-Type": "application/json" };

  const GENERATIONS = [
    { name: "Jordan — Gen Z Consumer", role: "Gen Z (born 1997–2012)",
      description: "Digital native, 24. Lives on TikTok/Instagram. Values authenticity and social proof. Skeptical of corporate messaging.",
      traits: ["digitally native", "authenticity-driven", "socially conscious", "short-form preference"], gen: "gen_z" },
    { name: "Morgan — Millennial Professional", role: "Millennial (born 1981–1996)",
      description: "35, mid-career. Purpose-driven, data-backed claims. Active on Instagram, LinkedIn, podcasts. Premium-for-convenience.",
      traits: ["experience-oriented", "purpose-driven", "research-heavy", "subscription-comfortable"], gen: "millennial" },
    { name: "Casey — Gen X Decision-Maker", role: "Gen X (born 1965–1980)",
      description: "48, peak earning. Pragmatic, straightforward value props. Brand-loyal, email/web-first.",
      traits: ["pragmatic", "self-reliant", "brand-loyal", "hype-resistant"], gen: "gen_x" },
    { name: "Pat — Boomer Evaluator", role: "Baby Boomer (born 1946–1964)",
      description: "65, senior advisory. Trust and credentials over novelty. Long-form content, Facebook/email.",
      traits: ["trust-driven", "detail-oriented", "authority-responsive", "long-form preference"], gen: "boomer" },
  ];

  async function createPersona(g) {
    const resp = await fetch(`${BASE}/personas`, { method: "POST", headers: HEADERS,
      body: JSON.stringify({ name: g.name, role: g.role, description: g.description, traits: g.traits }) });
    const data = await resp.json();
    if (data.error) throw new Error(data.error.message);
    return data.id;
  }

  const genPersonas = {};
  for (const g of GENERATIONS) genPersonas[g.gen] = { id: await createPersona(g), name: g.name };
  ```
</CodeGroup>

***

## Step 2 — Define Campaign and Run Focus Groups

One campaign concept, identical questions, one Focus Group per generation.

<CodeGroup>
  ```python Python theme={"dark"}
  CAMPAIGN = """
  Campaign: "Own Your Future"
  Product: Personal finance app for investing, budgeting, and retirement planning.
  Tagline: "Smart money moves start here."
  Visual: Split-screen — stressed about finances vs. confident and relaxed.
  CTA: "Start your free plan today."
  Channels: Instagram Reels, YouTube pre-roll, email nurture, LinkedIn ads.
  """

  QUESTIONS = [
      {"question": f"Share your honest first impression:\n\n{CAMPAIGN}", "type": "OPEN_ENDED", "order": 1},
      {"question": "How compelling is the tagline 'Smart money moves start here'? (0-10)", "type": "NPS", "order": 2},
      {"question": "Which channel would most effectively reach you?", "type": "MULTIPLE_CHOICE",
       "options": ["Instagram Reels", "YouTube pre-roll", "Email nurture", "LinkedIn ads", "None"], "order": 3},
      {"question": "What emotion does the split-screen visual evoke?", "type": "OPEN_ENDED", "order": 4},
      {"question": "Would you click 'Start your free plan today'?", "type": "MULTIPLE_CHOICE",
       "options": ["Yes — immediately", "Maybe — need more info", "No — not relevant", "No — CTA too generic"], "order": 5},
      {"question": "What one change would make this more appealing to you?", "type": "OPEN_ENDED", "order": 6},
  ]

  def run_gen_fg(gen_key, persona_id):
      resp = requests.post(f"{BASE}/focus-groups", headers=HEADERS, json={
          "name": f"Generational Test — {gen_key}", "persona_ids": [persona_id],
          "sample_size": 10, "questions": QUESTIONS,
      })
      resp.raise_for_status()
      return resp.json()

  def poll_fg(fg_id, timeout_min=10):
      for _ in range(timeout_min * 6):
          resp = requests.get(f"{BASE}/focus-groups/{fg_id}", headers=HEADERS).json()
          if "error" in resp: raise Exception(resp["error"]["message"])
          if resp["status"] == "COMPLETED": return resp
          time.sleep(10)
      raise TimeoutError(f"Focus Group {fg_id} timed out")

  # Create all FGs before polling — they run concurrently
  fg_jobs = {}
  for gen_key, info in gen_personas.items():
      fg = run_gen_fg(gen_key, info["id"])
      fg_jobs[gen_key] = fg["id"]

  gen_results = {gk: poll_fg(fid) for gk, fid in fg_jobs.items()}
  print("All 4 Focus Groups complete.")
  ```

  ```javascript JavaScript theme={"dark"}
  const CAMPAIGN = `Campaign: "Own Your Future"\nProduct: Personal finance app.\nTagline: "Smart money moves start here."\nCTA: "Start your free plan today."`;

  const QUESTIONS = [
    { question: `Share your honest first impression:\n\n${CAMPAIGN}`, type: "OPEN_ENDED", order: 1 },
    { question: "How compelling is the tagline? (0-10)", type: "NPS", order: 2 },
    { question: "Which channel would most effectively reach you?", type: "MULTIPLE_CHOICE",
      options: ["Instagram Reels", "YouTube pre-roll", "Email nurture", "LinkedIn ads", "None"], order: 3 },
    { question: "What emotion does the split-screen visual evoke?", type: "OPEN_ENDED", order: 4 },
    { question: "Would you click 'Start your free plan today'?", type: "MULTIPLE_CHOICE",
      options: ["Yes — immediately", "Maybe — need more info", "No — not relevant", "No — CTA too generic"], order: 5 },
    { question: "What one change would make this more appealing?", type: "OPEN_ENDED", order: 6 },
  ];

  async function pollFG(fgId, timeoutMin = 10) {
    for (let i = 0; i < timeoutMin * 6; i++) {
      const resp = await fetch(`${BASE}/focus-groups/${fgId}`, { headers: HEADERS }).then((r) => r.json());
      if (resp.error) throw new Error(resp.error.message);
      if (resp.status === "COMPLETED") return resp;
      await new Promise((r) => setTimeout(r, 10000));
    }
    throw new Error(`Focus Group ${fgId} timed out`);
  }

  const fgJobs = {};
  for (const [gk, info] of Object.entries(genPersonas)) {
    const resp = await fetch(`${BASE}/focus-groups`, { method: "POST", headers: HEADERS,
      body: JSON.stringify({ name: `Gen Test — ${gk}`, persona_ids: [info.id], sample_size: 10, questions: QUESTIONS }) });
    fgJobs[gk] = (await resp.json()).id;
  }

  const genResults = {};
  for (const [gk, fid] of Object.entries(fgJobs)) genResults[gk] = await pollFG(fid);
  ```
</CodeGroup>

<Tip>
  All four Focus Groups are created before polling. Mavera processes them concurrently, so total wait time is roughly the same as a single group.
</Tip>

***

## Step 3 — Compare Results Across Generations

<CodeGroup>
  ```python Python theme={"dark"}
  def compare_generations(results_by_gen):
      print("\n" + "=" * 70)
      print("GENERATIONAL COMPARISON REPORT")
      print("=" * 70)

      labels = {"gen_z": "Gen Z", "millennial": "Millennial", "gen_x": "Gen X", "boomer": "Boomer"}
      order = ["gen_z", "millennial", "gen_x", "boomer"]

      for q_idx, q in enumerate(QUESTIONS):
          print(f"\n{'─' * 60}\nQ{q_idx+1}: {q['question'].split(chr(10))[0][:70]}\n{'─' * 60}")
          for gk in order:
              qr = results_by_gen[gk]["results"][q_idx]
              lbl = labels[gk]
              if q["type"] == "NPS":
                  print(f"  {lbl:12s} NPS: {qr.get('nps_score', 'N/A')}")
              elif q["type"] == "MULTIPLE_CHOICE":
                  counts = qr.get("option_counts", {})
                  top = max(counts, key=counts.get) if counts else "N/A"
                  print(f"  {lbl:12s} Top: {top}")
              else:
                  print(f"  {lbl:12s} {(qr.get('summary', 'N/A'))[:90]}")

  compare_generations(gen_results)
  ```

  ```javascript JavaScript theme={"dark"}
  function compareGenerations(resultsByGen) {
    console.log("\n" + "=".repeat(70));
    console.log("GENERATIONAL COMPARISON REPORT");
    console.log("=".repeat(70));
    const labels = { gen_z: "Gen Z", millennial: "Millennial", gen_x: "Gen X", boomer: "Boomer" };
    const order = ["gen_z", "millennial", "gen_x", "boomer"];
    QUESTIONS.forEach((q, idx) => {
      console.log(`\n${"─".repeat(60)}\nQ${idx+1}: ${q.question.split("\n")[0].slice(0, 70)}\n${"─".repeat(60)}`);
      for (const gk of order) {
        const qr = resultsByGen[gk].results[idx];
        if (q.type === "NPS") console.log(`  ${labels[gk].padEnd(12)} NPS: ${qr.nps_score ?? "N/A"}`);
        else if (q.type === "MULTIPLE_CHOICE") {
          const top = Object.entries(qr.option_counts || {}).sort((a, b) => b[1] - a[1])[0];
          console.log(`  ${labels[gk].padEnd(12)} Top: ${top ? top[0] : "N/A"}`);
        } else console.log(`  ${labels[gk].padEnd(12)} ${(qr.summary || "N/A").slice(0, 90)}`);
      }
    });
  }
  compareGenerations(genResults);
  ```
</CodeGroup>

***

## Step 4 — Generate Recommendations via Chat

<CodeGroup>
  ```python Python theme={"dark"}
  from openai import OpenAI

  mavera = OpenAI(api_key=API_KEY, base_url="https://app.mavera.io/api/v1")

  def generate_recs(results_by_gen):
      labels = {"gen_z": "Gen Z", "millennial": "Millennial", "gen_x": "Gen X", "boomer": "Boomer"}
      parts = []
      for gk in ["gen_z", "millennial", "gen_x", "boomer"]:
          p = f"## {labels[gk]}\n"
          for qr in results_by_gen[gk].get("results", []):
              q_short = qr["question"][:50]
              if qr["type"] == "NPS": p += f"- {q_short}: NPS {qr.get('nps_score')}\n"
              elif qr["type"] == "MULTIPLE_CHOICE": p += f"- {q_short}: {qr.get('option_counts', {})}\n"
              else: p += f"- {q_short}: {qr.get('summary', 'N/A')}\n"
          parts.append(p)

      resp = mavera.responses.create(model="mavera-1", input=[{"role": "user", "content":
          "Analyze these Focus Group results from 4 generational segments.\n\n" + "\n".join(parts) +
          "\n\nProduce: 1) Generation resonance ranking 2) Per-generation content recs "
          "3) A universal version 4) Channel allocation by generation"}])
      return resp.output[0].content[0].text

  recs = generate_recs(gen_results)
  print("\n=== RECOMMENDATIONS ===\n")
  print(recs)
  ```

  ```javascript JavaScript theme={"dark"}
  const OpenAI = require("openai").default;
  const mavera = new OpenAI({ apiKey: API_KEY, baseURL: "https://app.mavera.io/api/v1" });

  async function generateRecs(resultsByGen) {
    const labels = { gen_z: "Gen Z", millennial: "Millennial", gen_x: "Gen X", boomer: "Boomer" };
    const parts = ["gen_z", "millennial", "gen_x", "boomer"].map((gk) => {
      let p = `## ${labels[gk]}\n`;
      for (const qr of resultsByGen[gk].results || []) {
        const qs = (qr.question || "").slice(0, 50);
        if (qr.type === "NPS") p += `- ${qs}: NPS ${qr.nps_score}\n`;
        else if (qr.type === "MULTIPLE_CHOICE") p += `- ${qs}: ${JSON.stringify(qr.option_counts)}\n`;
        else p += `- ${qs}: ${qr.summary || "N/A"}\n`;
      }
      return p;
    });
    const resp = await mavera.responses.create({ model: "mavera-1", input: [{ role: "user",
      content: "Analyze these results from 4 generations.\n\n" + parts.join("\n") +
        "\n\nProduce: 1) Resonance ranking 2) Per-gen recs 3) Universal version 4) Channel allocation" }] });
    return resp.output[0].content[0].text;
  }
  console.log(await generateRecs(genResults));
  ```
</CodeGroup>

***

## Example Output

```
NPS by Generation:
  Gen Z        NPS: 45
  Millennial   NPS: 62
  Gen X        NPS: 38
  Boomer       NPS: 25

Preferred Channel:
  Gen Z        Top: Instagram Reels
  Millennial   Top: YouTube pre-roll
  Gen X        Top: Email nurture
  Boomer       Top: Email nurture

CTA Click Intent:
  Gen Z        Top: Maybe — need more info
  Millennial   Top: Yes — immediately
  Gen X        Top: Maybe — need more info
  Boomer       Top: No — CTA too generic
```

***

## Variations

<AccordionGroup>
  <Accordion title="Add sub-segments within each generation">
    Create two personas per generation — one urban, one suburban — for 8 Focus Groups:

    ```python theme={"dark"}
    for g in GENERATIONS:
        for locale in ["urban", "suburban"]:
            variant = {**g, "name": f"{g['name']} ({locale})",
                       "description": g["description"] + f" Lives in a {locale} area."}
            create_persona(variant)
    ```
  </Accordion>

  <Accordion title="Weight results by actual audience mix">
    ```python theme={"dark"}
    weights = {"gen_z": 0.20, "millennial": 0.40, "gen_x": 0.30, "boomer": 0.10}
    weighted_nps = sum(
        gen_results[gk]["results"][1].get("nps_score", 0) * w for gk, w in weights.items()
    )
    print(f"Weighted NPS: {weighted_nps:.1f}")
    ```
  </Accordion>

  <Accordion title="Test multiple campaigns per generation">
    ```python theme={"dark"}
    for concept_idx, concept in enumerate([CAMPAIGN_A, CAMPAIGN_B]):
        QUESTIONS[0]["question"] = f"React to this:\n\n{concept}"
        for gk, info in gen_personas.items():
            fg = run_gen_fg(gk, info["id"])
    ```
  </Accordion>

  <Accordion title="Generate per-generation ad copy with Generate">
    ```python theme={"dark"}
    for gk in ["gen_z", "millennial", "gen_x", "boomer"]:
        feedback = gen_results[gk]["results"][5].get("summary", "")
        gen_resp = requests.post(f"{BASE}/generations", headers=HEADERS, json={
            "app": "ad_copy", "inputs": {"audience_feedback": feedback, "generation": gk}
        })
        print(f"{gk}: {gen_resp.json().get('content', '')[:200]}")
    ```
  </Accordion>

  <Accordion title="Use Mavera's curated personas">
    ```python theme={"dark"}
    resp = requests.get(f"{BASE}/personas", headers=HEADERS).json()
    gen_z_id = next(p["id"] for p in resp if "Gen Z" in p.get("name", ""))
    ```
  </Accordion>
</AccordionGroup>

***

## Credits Estimate

| Operation                                     | Typical Cost | Notes                       |
| --------------------------------------------- | ------------ | --------------------------- |
| Persona creation (×4)                         | 0–20         | One-time; reuse across runs |
| Focus Group (×4, 10 respondents, 6 questions) | 80–160       | \~20–40 per group           |
| Chat recommendations                          | 3–8          | Single synthesis call       |
| **Total**                                     | **\~85–190** |                             |

***

## What's Next

<CardGroup cols={2}>
  <Card title="Industry Panel Simulation" icon="users" href="/playbooks/industry-panel-simulation">
    Test with 10 buying-committee personas instead of generational segments
  </Card>

  <Card title="Message Testing Matrix" icon="table-cells" href="/playbooks/message-testing-matrix">
    Combine multiple messages with multiple personas for a full fit matrix
  </Card>

  <Card title="Persona Debate" icon="scale-balanced" href="/playbooks/persona-debate">
    Pit opposing buyer types against each other for pricing insights
  </Card>

  <Card title="Content Localization" icon="globe" href="/playbooks/content-localization">
    Adapt content across cultural regions, not just generations
  </Card>

  <Card title="Persona Selection Guide" icon="user" href="/cookbooks/persona-selection">
    Choose the right persona types for your research goal
  </Card>

  <Card title="Credits & Budget" icon="coins" href="/cookbooks/credits-budget-alerts">
    Pre-flight checks and usage tracking
  </Card>
</CardGroup>
