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.

Mavera Surfaces Used

SurfaceRole
Personas (POST /personas, GET /personas)Create and retrieve 5 ICP-specific personas
Focus Groups (POST /focus-groups)Run positioning test with Ranking + Semantic Differential + Open-Ended questions
Chat + response_formatSynthesize focus group results into a final positioning recommendation
Traditional positioning workshops take days and cost $10K+. This playbook produces persona-validated positioning in under an hour with no external tooling — just Mavera’s Personas and Focus Groups APIs.

What Value Does Mavera Add?

ValueHow
InsuranceTest positioning with 5 distinct ICP segments before committing. Catch blind spots that internal teams miss.
Opening new doorsRun positioning sprints weekly during product pivots — cost and speed make rapid iteration practical.
Saving timeA full positioning workshop (persona creation → focus group → recommendation) runs in ~30 minutes instead of 2–3 days.

When to Use This

  • You’re defining or refreshing your positioning statement and want audience validation before committing.
  • You have 2–5 candidate positioning statements and need to pick a winner — fast.
  • You want to test how different ICP segments interpret the same positioning.
  • You’re preparing for a rebrand, product launch, or funding round and need data-backed positioning.

What You Need

RequirementDetails
Mavera API keyStarts with mvra_live_. Get one at Developer Settings.
Workspace IDFrom your dashboard URL (ws_...).
3 positioning statementsCandidate statements to test (follow the template: For [audience], [product] is the [category] that [differentiator]).
ICP definitionEnough detail to create 5 representative personas (title, industry, pain points, budget authority).
Credits~200–500 total. See Credits Estimate.
Python 3.8+ or Node.js 18+requests / openai for Python; native fetch for Node.
MAVERA_API_KEY=mvra_live_your_key_here
MAVERA_WORKSPACE_ID=ws_your_workspace_id

The Workshop Framework

Question Types

QuestionTypeWhat It Measures
”Rank these 3 positioning statements from most to least compelling”RankingOverall preference order across all personas
”Rate Statement A on: Generic ←→ Differentiated”Semantic DifferentialPerception on a bipolar scale
”Rate Statement A on: Confusing ←→ Clear”Semantic DifferentialComprehension
”Rate Statement A on: Forgettable ←→ Memorable”Semantic DifferentialStickiness
”What would make the top-ranked statement stronger?”Open-EndedQualitative improvement suggestions

The Flow

1

Define your 3 positioning statements

Use the standard template: For [audience], [product] is the [category] that [differentiator]. Having exactly 3 keeps the ranking question manageable for respondents.
2

Create 5 ICP personas

Build personas representing distinct segments of your ideal customer profile. Vary by seniority, function, company size, and pain point so each perspective is unique.
3

Run the Focus Group

30 respondents across the 5 personas (6 per persona). Questions: 1 Ranking, 3 Semantic Differentials, 1 Open-Ended.
4

Poll for completion

Focus Groups with 30 respondents and 5 questions typically complete in 3-8 minutes.
5

Analyze results

Extract ranking distribution, semantic differential averages, and open-ended themes.
6

Synthesize recommendation

Use Chat with structured output to produce a final positioning recommendation with segment-level detail.

Code: Full Positioning Workshop

Setup and Positioning Statements

import os
import json
import time
import requests
from openai import OpenAI

MAVERA_API_KEY = os.environ["MAVERA_API_KEY"]
WORKSPACE_ID = os.environ["MAVERA_WORKSPACE_ID"]
BASE = "https://app.mavera.io/api/v1"
HEADERS = {
    "Authorization": f"Bearer {MAVERA_API_KEY}",
    "Content-Type": "application/json",
}
mavera = OpenAI(api_key=MAVERA_API_KEY, base_url=BASE)

POSITIONING_STATEMENTS = [
    {
        "label": "A",
        "statement": (
            "For growth-stage B2B companies, Acme is the market intelligence platform "
            "that replaces guesswork with persona-validated data — so you ship messaging "
            "that resonates on the first try."
        ),
    },
    {
        "label": "B",
        "statement": (
            "For marketing leaders, Acme is the AI research assistant that simulates "
            "your target audience — giving you focus group insights in minutes, "
            "not months."
        ),
    },
    {
        "label": "C",
        "statement": (
            "For product marketers, Acme is the positioning engine that tests your "
            "messaging against synthetic buyers — so every campaign launches with "
            "confidence backed by data."
        ),
    },
]

ICP_PERSONAS = [
    {
        "name": "VP of Marketing — Growth-Stage SaaS",
        "description": (
            "VP Marketing at a Series B SaaS company (50-200 employees). "
            "Owns positioning, messaging, and demand gen. Budget: $500K-$2M/year. "
            "Pain: can't afford agency research but needs data-backed messaging. "
            "Evaluates tools on speed-to-insight and ROI."
        ),
    },
    {
        "name": "Head of Product Marketing — Enterprise",
        "description": (
            "Head of PMM at a $50M+ ARR enterprise software company. "
            "Manages positioning for multiple product lines. "
            "Pain: positioning workshops take weeks and results are subjective. "
            "Needs quantitative validation for executive buy-in."
        ),
    },
    {
        "name": "CMO — Mid-Market B2B",
        "description": (
            "CMO at a mid-market B2B company (200-1000 employees). "
            "Reports to CEO, presents to board. Budget: $2M-$10M/year. "
            "Pain: marketing is perceived as a cost center, needs measurable impact. "
            "Values tools that produce board-ready outputs."
        ),
    },
    {
        "name": "Content Marketing Manager",
        "description": (
            "Content marketing manager at a B2B startup. "
            "Produces blog posts, whitepapers, and social content daily. "
            "Pain: writes copy without knowing if it resonates with the buyer. "
            "Wants fast feedback loops, not quarterly brand studies."
        ),
    },
    {
        "name": "Founder / CEO — Early Stage",
        "description": (
            "Technical founder at a pre-Series A startup (5-20 employees). "
            "Wears the marketing hat. Budget: $50K-$200K/year total. "
            "Pain: can't hire a brand strategist yet but needs sharp positioning "
            "for investor decks and landing pages. Values simplicity and speed."
        ),
    },
]

Stage 1 — Create Personas

def create_personas() -> list[str]:
    """Create 5 ICP personas and return their IDs."""
    persona_ids = []

    for persona in ICP_PERSONAS:
        resp = requests.post(
            f"{BASE}/personas",
            headers=HEADERS,
            json={
                "name": persona["name"],
                "description": persona["description"],
                "workspace_id": WORKSPACE_ID,
            },
        ).json()

        if "error" in resp:
            raise Exception(f"Failed to create persona '{persona['name']}': {resp['error']['message']}")

        persona_ids.append(resp["id"])
        print(f"✓ Created persona: {persona['name']} ({resp['id']})")

    return persona_ids
If you already have personas from a previous session, skip creation and pass their IDs directly. Use GET /personas to list existing ones.

Stage 2 — Run the Focus Group

The focus group uses 5 questions: 1 Ranking, 3 Semantic Differentials (one per statement), and 1 Open-Ended.
def build_statements_block() -> str:
    """Format positioning statements for inclusion in questions."""
    return "\n".join(
        f"Statement {s['label']}: \"{s['statement']}\""
        for s in POSITIONING_STATEMENTS
    )


def run_positioning_focus_group(persona_ids: list[str]) -> dict:
    """Run the positioning workshop focus group."""
    statements_block = build_statements_block()

    payload = {
        "name": "Positioning Workshop — 3 Statements × 5 Personas",
        "sample_size": 30,
        "persona_ids": persona_ids,
        "workspace_id": WORKSPACE_ID,
        "questions": [
            {
                "question": (
                    "Read these 3 positioning statements carefully, then rank them "
                    "from most compelling (1st) to least compelling (3rd).\n\n"
                    f"{statements_block}"
                ),
                "type": "RANKING",
                "options": [
                    f"Statement {s['label']}" for s in POSITIONING_STATEMENTS
                ],
                "order": 1,
            },
            {
                "question": (
                    f"Rate Statement A on this scale:\n\n"
                    f"\"{POSITIONING_STATEMENTS[0]['statement']}\""
                ),
                "type": "SEMANTIC_DIFFERENTIAL",
                "left_anchor": "Generic / could be anyone",
                "right_anchor": "Differentiated / clearly unique",
                "scale": 7,
                "order": 2,
            },
            {
                "question": (
                    f"Rate Statement B on this scale:\n\n"
                    f"\"{POSITIONING_STATEMENTS[1]['statement']}\""
                ),
                "type": "SEMANTIC_DIFFERENTIAL",
                "left_anchor": "Confusing / hard to understand",
                "right_anchor": "Clear / instantly understood",
                "scale": 7,
                "order": 3,
            },
            {
                "question": (
                    f"Rate Statement C on this scale:\n\n"
                    f"\"{POSITIONING_STATEMENTS[2]['statement']}\""
                ),
                "type": "SEMANTIC_DIFFERENTIAL",
                "left_anchor": "Forgettable / bland",
                "right_anchor": "Memorable / would stick with me",
                "scale": 7,
                "order": 4,
            },
            {
                "question": (
                    "You ranked the statements above. For the one you ranked #1:\n"
                    "- What specifically makes it compelling?\n"
                    "- What would make it even stronger?\n"
                    "- Is there anything misleading or unclear?"
                ),
                "type": "OPEN_ENDED",
                "order": 5,
            },
        ],
    }

    resp = requests.post(
        f"{BASE}/focus-groups",
        headers=HEADERS,
        json=payload,
    ).json()

    if "error" in resp:
        raise Exception(resp["error"]["message"])

    print(f"✓ Focus group created: {resp['id']}")
    print(f"  Sample size: {payload['sample_size']}")
    print(f"  Personas: {len(persona_ids)}")
    print(f"  Questions: {len(payload['questions'])}")
    return resp


def poll_focus_group(fg_id: str, timeout_min: int = 15) -> dict:
    """Poll until the focus group completes."""
    start = time.time()
    for attempt 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"])

        status = resp.get("status", "UNKNOWN")
        elapsed = int(time.time() - start)

        if status == "COMPLETED":
            print(f"✓ Focus group completed in {elapsed}s")
            return resp

        if status == "FAILED":
            raise Exception(f"Focus group failed: {resp.get('error', 'Unknown error')}")

        print(f"  Polling... status={status} ({elapsed}s elapsed)")
        time.sleep(10)

    raise TimeoutError(f"Focus group {fg_id} did not complete in {timeout_min} min")

Stage 3 — Analyze Results

Extract ranking distributions, semantic differential averages, and open-ended themes.
def analyze_results(fg_results: dict) -> dict:
    """Parse focus group results into a structured analysis."""
    analysis = {
        "ranking": {},
        "semantic_differentials": {},
        "open_ended_themes": [],
        "per_persona": {},
    }

    for result in fg_results.get("results", []):
        q_type = result.get("type")
        question = result.get("question", "")

        if q_type == "RANKING":
            analysis["ranking"] = {
                "distribution": result.get("ranking_distribution", {}),
                "summary": result.get("summary", ""),
            }

        elif q_type == "SEMANTIC_DIFFERENTIAL":
            label = "Unknown"
            for s in POSITIONING_STATEMENTS:
                if f"Statement {s['label']}" in question:
                    label = s["label"]
                    break

            analysis["semantic_differentials"][label] = {
                "mean": result.get("mean_score", 0),
                "left_anchor": result.get("left_anchor", ""),
                "right_anchor": result.get("right_anchor", ""),
                "distribution": result.get("score_distribution", {}),
                "summary": result.get("summary", ""),
            }

        elif q_type == "OPEN_ENDED":
            analysis["open_ended_themes"] = result.get("themes", [])
            analysis["open_ended_responses"] = result.get("responses", [])

    # Per-persona breakdown (if available)
    for result in fg_results.get("results", []):
        for persona_result in result.get("per_persona", []):
            pid = persona_result.get("persona_id", "unknown")
            if pid not in analysis["per_persona"]:
                analysis["per_persona"][pid] = {}
            analysis["per_persona"][pid][result.get("question", "")[:50]] = persona_result

    return analysis


def print_analysis(analysis: dict):
    """Print a human-readable summary."""
    print("\n" + "=" * 60)
    print("POSITIONING WORKSHOP RESULTS")
    print("=" * 60)

    print("\n--- Ranking Distribution ---")
    ranking = analysis["ranking"]
    if ranking.get("distribution"):
        for option, counts in ranking["distribution"].items():
            print(f"  {option}: {counts}")
    if ranking.get("summary"):
        print(f"  Summary: {ranking['summary']}")

    print("\n--- Semantic Differentials ---")
    for label, data in analysis["semantic_differentials"].items():
        print(f"  Statement {label}: {data['mean']:.1f}/7 "
              f"({data['left_anchor']} ←→ {data['right_anchor']})")

    print("\n--- Open-Ended Themes ---")
    for theme in analysis.get("open_ended_themes", []):
        print(f"  • {theme}")

Stage 4 — Synthesize Recommendation

RECOMMENDATION_SCHEMA = {"type": "json_schema", "json_schema": {
    "name": "positioning_recommendation", "strict": True,
    "schema": {
        "type": "object",
        "properties": {
            "winning_statement": {"type": "string", "description": "A, B, or C"},
            "confidence": {"type": "string", "description": "High, Medium, or Low"},
            "ranking_summary": {"type": "string"},
            "differentiation_winner": {"type": "string", "description": "Which statement scored highest on differentiation"},
            "clarity_winner": {"type": "string", "description": "Which statement scored highest on clarity"},
            "memorability_winner": {"type": "string", "description": "Which statement scored highest on memorability"},
            "segment_insights": {
                "type": "array",
                "items": {
                    "type": "object",
                    "properties": {
                        "segment": {"type": "string"},
                        "preferred_statement": {"type": "string"},
                        "reasoning": {"type": "string"},
                    },
                    "required": ["segment", "preferred_statement", "reasoning"],
                },
            },
            "revision_suggestions": {
                "type": "array",
                "items": {"type": "string"},
                "description": "Specific edits to strengthen the winning statement",
            },
            "final_recommendation": {"type": "string", "description": "2-3 sentence final recommendation"},
        },
        "required": [
            "winning_statement", "confidence", "ranking_summary",
            "differentiation_winner", "clarity_winner", "memorability_winner",
            "segment_insights", "revision_suggestions", "final_recommendation",
        ],
    },
}}


def synthesize_recommendation(analysis: dict) -> dict:
    """Use Chat to produce a structured positioning recommendation."""
    prompt = (
        "You are a positioning strategist. Analyze these focus group results "
        "and produce a positioning recommendation.\n\n"
        "## Positioning Statements Tested\n"
    )
    for s in POSITIONING_STATEMENTS:
        prompt += f"Statement {s['label']}: \"{s['statement']}\"\n"

    prompt += f"\n## Ranking Results\n{json.dumps(analysis['ranking'], indent=2)}\n"
    prompt += f"\n## Semantic Differential Scores\n{json.dumps(analysis['semantic_differentials'], indent=2)}\n"
    prompt += f"\n## Open-Ended Themes\n{json.dumps(analysis.get('open_ended_themes', []), indent=2)}\n"
    prompt += "\nProduce a recommendation with segment-level insights and specific revision suggestions."

    resp = mavera.responses.create(
        model="mavera-1",
        input=[{"role": "user", "content": prompt}],
        extra_body={"response_format": RECOMMENDATION_SCHEMA},
    )

    return json.loads(resp.output[0].content[0].text)

Running the Full Workshop

def run_workshop():
    print("=" * 60)
    print("POSITIONING WORKSHOP")
    print("=" * 60)

    # Stage 1: Create personas
    print("\n--- Stage 1: Creating ICP Personas ---")
    persona_ids = create_personas()

    # Stage 2: Run focus group
    print("\n--- Stage 2: Running Focus Group ---")
    fg = run_positioning_focus_group(persona_ids)
    fg_results = poll_focus_group(fg["id"])

    # Stage 3: Analyze
    print("\n--- Stage 3: Analyzing Results ---")
    analysis = analyze_results(fg_results)
    print_analysis(analysis)

    # Stage 4: Synthesize
    print("\n--- Stage 4: Synthesizing Recommendation ---")
    recommendation = synthesize_recommendation(analysis)

    print(f"\nWinner: Statement {recommendation['winning_statement']}")
    print(f"Confidence: {recommendation['confidence']}")
    print(f"\n{recommendation['final_recommendation']}")

    if recommendation.get("revision_suggestions"):
        print("\nRevision suggestions:")
        for s in recommendation["revision_suggestions"]:
            print(f"  • {s}")

    # Save outputs
    with open("positioning_results.json", "w") as f:
        json.dump({
            "analysis": analysis,
            "recommendation": recommendation,
            "statements": POSITIONING_STATEMENTS,
        }, f, indent=2)

    print("\n✓ Saved positioning_results.json")
    return recommendation


if __name__ == "__main__":
    run_workshop()

Example Output

{
  "winning_statement": "A",
  "confidence": "High",
  "ranking_summary": "Statement A ranked #1 by 19 of 30 respondents. Statement B ranked #2 by most, with Statement C trailing. VP-level personas strongly preferred A; content marketers leaned toward B.",
  "differentiation_winner": "A",
  "clarity_winner": "B",
  "memorability_winner": "A",
  "segment_insights": [
    {
      "segment": "VP of Marketing",
      "preferred_statement": "A",
      "reasoning": "Resonated with 'replaces guesswork with data' — directly addresses their accountability pressure."
    },
    {
      "segment": "Content Marketing Manager",
      "preferred_statement": "B",
      "reasoning": "Preferred the speed emphasis ('minutes, not months') — maps to their daily workflow pain."
    },
    {
      "segment": "Founder / CEO",
      "preferred_statement": "A",
      "reasoning": "Valued 'resonates on the first try' — limited budget means they can't afford messaging misses."
    }
  ],
  "revision_suggestions": [
    "Add a speed element from Statement B ('in minutes') to Statement A's value prop",
    "Replace 'market intelligence platform' with a more specific category name",
    "Test whether 'persona-validated data' is understood by less technical buyers"
  ],
  "final_recommendation": "Ship Statement A as your primary positioning, with the speed language from B incorporated. Statement A wins on differentiation and memorability across 4 of 5 segments. Run a follow-up test after incorporating the revision suggestions."
}

Variations

For 4–6 statements, split into two focus groups of 3. Head-to-head the winners in a final round:
# Round 1: Statements A-C
fg1 = run_positioning_focus_group(persona_ids)  # with statements A, B, C
# Round 2: Statements D-F
fg2 = run_positioning_focus_group(persona_ids)  # with statements D, E, F
# Final: Winners from each round
fg_final = run_positioning_focus_group(persona_ids)  # winner of fg1 vs winner of fg2
Include competitor positioning in the open-ended question so personas can compare:
{
    "question": (
        "Here's how 3 competitors position themselves:\n"
        f"- Competitor 1: '{comp1_positioning}'\n"
        f"- Competitor 2: '{comp2_positioning}'\n\n"
        "Does Statement A stand out against these? What's missing?"
    ),
    "type": "OPEN_ENDED",
    "order": 6,
}
If your VP persona matters 3× more than a Content Manager, weight the ranking:
WEIGHTS = {
    vp_marketing_id: 3.0,
    head_pmm_id: 2.0,
    cmo_id: 3.0,
    content_manager_id: 1.0,
    founder_id: 2.0,
}
After getting revision suggestions, update the winning statement and run another workshop:
POSITIONING_STATEMENTS[0]["statement"] = revised_statement_a
# Re-run with same personas
fg2 = run_positioning_focus_group(persona_ids)
Track scores across rounds to see the improvement trajectory.

Credits Estimate

StageTypical CostNotes
Create 5 personas0Persona creation is free
Focus Group (N=30, 5 questions)150–300 creditsSample size × question count
Synthesize recommendation (1 chat call)5–15 creditsSingle structured output
Total~155–315 credits
Start with N=15 (3 per persona) for a quick directional read at half the cost. Scale to N=30+ for statistically meaningful results you’d put in a board deck.

See Also

Focus Groups

All 12 question types including Ranking and Semantic Differential

Personas

Create, list, and manage personas

Market Entry Research

Research the market before testing positioning

Pricing Research

Test pricing alongside positioning

Brand Perception Audit

Measure how your brand is currently perceived

Message Testing Matrix

5 messages × 5 personas for granular message testing