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

You have a product launch landing page, an email campaign, or a set of ads — and you need A/B variants. Typically you’d write variant A, then manually tweak tone and angle for variant B. That’s slow, and the “tweaking” often produces variants that are too similar to produce meaningful test results. This playbook produces genuinely different variants by changing the brand voice or persona — not just individual words. Variant A might use your bold, direct brand voice while variant B uses a warmer, storytelling voice. Or variant A is written for a startup founder persona while variant B targets an enterprise buyer. Same facts, different framing. Ready for your A/B testing platform.
Mavera-only. No A/B testing platform integration. This playbook produces the creative variants — you deploy them wherever you run tests.

Architecture


What You Need

RequirementDetails
Mavera API keyStarts with mvra_live_. Get one at Developer Settings.
Workspace IDFrom your dashboard URL (ws_...).
2–3 brand voice IDsDifferent voice profiles to drive variant differentiation. Create via Brand Voice.
OR 2–3 persona IDsUse personas instead of (or alongside) voice differences.
Content briefShared topic, audience, and key points — consistent across all variants.
Credits~100–300 total. See Credits Estimate.
Python 3.8+ or Node.js 18+requests + openai SDK for Python; native fetch for Node.
MAVERA_API_KEY=mvra_live_your_key_here
MAVERA_WORKSPACE_ID=ws_your_workspace_id

The Flow

1

Create or select brand voices / personas

You need at least 2 differentiated voices or personas. Create brand voices from different source material (e.g. your blog vs. your support docs), or use personas with different psychographic profiles.
2

Define the shared brief

Write one brief with topic, audience, key points, and format. This brief is identical across all variants — the only variable is the voice or persona.
3

Generate variants

Call POST /generations once per voice/persona. Each call produces a variant shaped by its specific voice or persona context.
4

Compare variants

Use Chat with response_format to produce a structured comparison: tone analysis, readability, emotional appeal, and a recommendation for which variant to test first.

Strategy A — Different Brand Voices

Create 2–3 brand voices from different source material. Each voice encodes different tone, vocabulary, and style rules.
import os
import time
import json
import requests

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 {API_KEY}",
    "Content-Type": "application/json",
}


def create_voice(label, urls, usage_context):
    """Create a brand voice and wait for READY."""
    resp = requests.post(f"{BASE}/brand-voices", headers=HEADERS, json={
        "label": label,
        "usage_context": usage_context,
        "urls": urls,
        "workspace_id": WORKSPACE_ID,
    })
    resp.raise_for_status()
    bv = resp.json()

    for _ in range(30):
        check = requests.get(f"{BASE}/brand-voices/{bv['id']}", headers=HEADERS).json()
        if check.get("status") == "READY":
            return check
        time.sleep(10)
    raise TimeoutError(f"Brand voice {bv['id']} not ready")


VOICES = {
    "bold_direct": create_voice(
        label="Bold & Direct",
        urls=["https://yourbrand.com/blog", "https://yourbrand.com/manifesto"],
        usage_context="High-energy marketing copy that challenges the status quo",
    ),
    "warm_storytelling": create_voice(
        label="Warm & Storytelling",
        urls=["https://yourbrand.com/customer-stories", "https://yourbrand.com/about"],
        usage_context="Empathetic content that connects through customer narratives",
    ),
    "data_driven": create_voice(
        label="Data-Driven & Precise",
        urls=["https://yourbrand.com/research", "https://yourbrand.com/case-studies"],
        usage_context="Evidence-based content with metrics and benchmarks",
    ),
}

for name, voice in VOICES.items():
    print(f"  {name}: {voice['id']}{voice.get('tone_adjectives', [])}")

Generate A/B Variants

The brief is identical. Only the brand_voice_id changes.
SHARED_BRIEF = {
    "app_id": "email_sequence_generator",
    "input_data": {
        "sequence_goal": "Convert free trial users to paid plans",
        "emails_count": 3,
        "target_audience": "SaaS trial users in their first week",
        "key_points": [
            "Highlight the top 3 features they haven't tried",
            "Include a customer success story",
            "End with a time-limited upgrade offer",
        ],
    },
}


def wait_for_generation(gen_id, max_wait=300):
    for _ in range(max_wait // 10):
        resp = requests.get(f"{BASE}/generations/{gen_id}", headers=HEADERS)
        data = resp.json()
        if data.get("status") == "COMPLETED":
            return data
        time.sleep(10)
    raise TimeoutError(f"Generation {gen_id} timed out")


variants = {}
total_credits = 0

for voice_name, voice in VOICES.items():
    print(f"\nGenerating variant: {voice_name}...")

    payload = {
        **SHARED_BRIEF,
        "title": f"A/B Variant — {voice['label']}",
        "brand_voice_id": voice["id"],
        "workspace_id": WORKSPACE_ID,
    }

    resp = requests.post(f"{BASE}/generations", headers=HEADERS, json=payload)
    resp.raise_for_status()
    gen = resp.json()

    if gen.get("status") in ("PENDING", "RUNNING"):
        gen = wait_for_generation(gen["id"])

    credits = gen.get("usage", {}).get("credits_used", 0)
    total_credits += credits

    variants[voice_name] = {
        "voice_label": voice["label"],
        "voice_id": voice["id"],
        "output": gen.get("output", ""),
        "credits": credits,
    }
    print(f"  ✓ {credits} credits | {len(gen.get('output', ''))} chars")

print(f"\nGenerated {len(variants)} variants | {total_credits} total credits")

Strategy B — Different Personas

Instead of (or in addition to) voices, use different personas. The persona changes the audience lens, not just the voice — the content adapts its value propositions, examples, and framing. Pass persona_id in extra_body when calling Chat, using the same brief content but different personas (e.g. persona_startup_founder, persona_enterprise_cto, persona_smb_ops). Each response is shaped by that persona’s priorities, pain points, and communication preferences.

Compare Variants

Use Chat with response_format to produce a structured side-by-side analysis.
COMPARISON_SCHEMA = {
    "type": "json_schema",
    "json_schema": {
        "name": "ab_comparison",
        "strict": True,
        "schema": {
            "type": "object",
            "properties": {
                "variants": {
                    "type": "array",
                    "items": {
                        "type": "object",
                        "properties": {
                            "name": {"type": "string"},
                            "tone_description": {"type": "string"},
                            "emotional_appeal": {"type": "number"},
                            "clarity": {"type": "number"},
                            "urgency": {"type": "number"},
                            "cta_strength": {"type": "number"},
                            "strengths": {"type": "array", "items": {"type": "string"}},
                            "weaknesses": {"type": "array", "items": {"type": "string"}},
                        },
                        "required": ["name", "tone_description", "emotional_appeal", "clarity", "urgency", "cta_strength", "strengths", "weaknesses"],
                    },
                },
                "recommendation": {"type": "string"},
                "test_hypothesis": {"type": "string"},
            },
            "required": ["variants", "recommendation", "test_hypothesis"],
        },
    },
}

variant_texts = "\n\n---\n\n".join(
    f"### {name}\n{data['output'][:2000]}"
    for name, data in variants.items()
)

compare_resp = mavera.responses.create(
    model="mavera-1",
    input=[
        {
            "role": "user",
            "content": (
                "Compare these A/B content variants. Score each 1-10 on emotional appeal, "
                "clarity, urgency, and CTA strength. Identify strengths and weaknesses. "
                "Recommend which to test first and state the hypothesis.\n\n"
                f"{variant_texts}"
            ),
        },
    ],
    extra_body={"response_format": COMPARISON_SCHEMA},
)

comparison = json.loads(compare_resp.output[0].content[0].text)
print(json.dumps(comparison, indent=2))

Example Comparison Output

{
  "variants": [
    {
      "name": "bold_direct",
      "tone_description": "Punchy, urgent, challenge-oriented. Short sentences, imperative verbs.",
      "emotional_appeal": 8,
      "clarity": 9,
      "urgency": 9,
      "cta_strength": 8,
      "strengths": ["Creates FOMO", "Clear value proposition", "Action-oriented language"],
      "weaknesses": ["May feel aggressive to risk-averse buyers", "Less personal"]
    },
    {
      "name": "warm_storytelling",
      "tone_description": "Narrative-driven, empathetic, uses customer stories as proof.",
      "emotional_appeal": 9,
      "clarity": 7,
      "urgency": 5,
      "cta_strength": 6,
      "strengths": ["Builds trust", "Relatable examples", "Emotionally engaging"],
      "weaknesses": ["Lower urgency", "CTA feels soft", "Longer read time"]
    },
    {
      "name": "data_driven",
      "tone_description": "Metric-heavy, analytical, appeals to rational decision-making.",
      "emotional_appeal": 5,
      "clarity": 9,
      "urgency": 6,
      "cta_strength": 7,
      "strengths": ["Credible", "Appeals to analytical buyers", "Easy to skim"],
      "weaknesses": ["Low emotional engagement", "May feel dry"]
    }
  ],
  "recommendation": "Test bold_direct vs warm_storytelling first. They have the widest differentiation.",
  "test_hypothesis": "Bold/direct will drive higher click-through but warm/storytelling will drive higher conversion-to-paid."
}

Variations

Combine both strategies: 2 voices × 2 personas = 4 variants. This lets you test voice and audience framing as independent variables in a 2×2 factorial design.
Before committing to a live test, run a Focus Group with your target persona to predict which variant will win. Ask “Which version would make you more likely to upgrade?” as a MULTIPLE_CHOICE question.
Generate variants, compare, then use the comparison’s weaknesses to refine the lower-scoring variant via Chat — “Rewrite to strengthen CTA while keeping the same tone.”

Credits Estimate

OperationTypical CostNotes
Brand voice creation (×3)150 credits50 each; one-time cost — reuse IDs
Generation per variant15–30 creditsDepends on content length
Comparison analysis (Chat)1–5 creditsSingle call with response_format
Total (3 voice variants, new voices)~195–245 creditsFirst run with voice creation
Total (3 voice variants, existing voices)~45–95 creditsSubsequent runs
Total (3 persona variants, existing personas)~5–20 creditsChat calls only
Persona-based A/B is dramatically cheaper than voice-based A/B (no voice creation cost). Use personas when you want to vary the audience framing; use voices when you want to vary the brand expression.

What’s Next

Brand Voice Content Library

Create a full content library from a single brand voice

Content Localization

Adapt variants for different regional markets

Message Testing Matrix

5 messages × 5 personas = 25 quantitative data points

Brand Voice

Create and manage brand voice profiles

Personas

Pre-built and custom persona reference

Credits & Budget

Track and manage credit usage