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

G2 reviews include the reviewer’s role, company size, and industry. A VP of Marketing experiences your product differently than a DevOps engineer. You pull reviews segmented by reviewer role, create Mavera personas grounded in actual reviewer profiles, then generate role-targeted marketing content that speaks to what each persona actually cares about — using their own words. Flow: G2 GET /survey-responses → Group by reviewer role → Mavera POST /personasPOST /generations (role-targeted content)

Architecture

Code

import os, requests, time
from collections import defaultdict

G2 = os.environ["G2_API_KEY"]
MV = os.environ["MAVERA_API_KEY"]
G2_BASE = "https://data.g2.com/api/v1"
MV_BASE = "https://app.mavera.io/api/v1"
G2_H = {"Authorization": f"Token token={G2}", "Content-Type": "application/vnd.api+json"}
MV_H = {"Authorization": f"Bearer {MV}", "Content-Type": "application/json"}

# 1. Pull reviews
reviews = []
page = 1
while len(reviews) < 300:
    r = requests.get(f"{G2_BASE}/survey-responses",
        headers=G2_H,
        params={"page[size]": 50, "page[number]": page})
    if r.status_code == 429:
        time.sleep(1)
        continue
    r.raise_for_status()
    data = r.json().get("data", [])
    if not data:
        break
    reviews.extend(data)
    page += 1
    time.sleep(0.1)

# 2. Group by reviewer role
role_groups = defaultdict(list)
for rev in reviews:
    attrs = rev.get("attributes", {})
    role = attrs.get("title", "Unknown Role")
    industry = attrs.get("industry", "Unknown")
    star = attrs.get("star_rating", 0)
    love = ""
    hate = ""
    for ans in attrs.get("comment_answers", {}).values():
        text = ans if isinstance(ans, str) else ans.get("text", "")
        if "love" in str(ans).lower() or "best" in str(ans).lower():
            love = text[:300]
        elif "dislike" in str(ans).lower() or "hate" in str(ans).lower():
            hate = text[:300]

    role_bucket = role.split(",")[0].strip() if role else "Unknown"
    role_groups[role_bucket].append({
        "star": star, "industry": industry,
        "love": love, "hate": hate,
    })

# 3. Create personas per role
persona_map = []
for role, revs in sorted(role_groups.items(), key=lambda x: -len(x[1]))[:6]:
    if len(revs) < 3:
        continue
    avg_star = sum(r["star"] for r in revs) / len(revs)
    industries = list({r["industry"] for r in revs if r["industry"] != "Unknown"})[:3]
    love_samples = [r["love"] for r in revs if r["love"]][:3]
    hate_samples = [r["hate"] for r in revs if r["hate"]][:3]

    p = requests.post(f"{MV_BASE}/personas", headers=MV_H, json={
        "name": f"G2 Reviewer: {role}",
        "description": (
            f"G2 reviewer with role '{role}'. N={len(revs)}. Avg rating: {avg_star:.1f}/5. "
            f"Industries: {', '.join(industries)}. "
            f"What they love: {'; '.join(love_samples[:2])}. "
            f"What they dislike: {'; '.join(hate_samples[:2])}."
        ),
        "demographic": {"job_titles": [role], "industries": industries},
        "psychographic": {
            "product_sentiment": "positive" if avg_star >= 4 else "mixed" if avg_star >= 3 else "negative",
            "avg_rating": avg_star,
        },
    }).json()
    persona_map.append({"id": p["id"], "role": role, "n": len(revs), "avg": avg_star})
    print(f"Persona: {p['id']}{role} ({len(revs)} reviews, avg {avg_star:.1f})")
    time.sleep(0.3)

# 4. Generate role-targeted content
for pm in persona_map:
    gen = requests.post(f"{MV_BASE}/generations", headers=MV_H, json={
        "persona_id": pm["id"],
        "prompt": (
            f"Generate a 150-word marketing paragraph targeting {pm['role']}s. "
            f"This segment gave us {pm['avg']:.1f}/5 on G2. "
            f"Use language that resonates with their specific concerns and value drivers. "
            f"Include a CTA appropriate for their role."
        ),
    }).json()
    content = gen.get("output", gen.get("content", gen.get("text", "")))
    print(f"\n--- Content for {pm['role']} ---")
    print(content[:400])
    time.sleep(0.5)

Example Output

Persona: per_g2_vp_1 — VP of Marketing (34 reviews, avg 4.6)
Persona: per_g2_dev_2 — Software Engineer (28 reviews, avg 3.9)
Persona: per_g2_pm_3 — Product Manager (22 reviews, avg 4.2)

--- Content for VP of Marketing ---
Your team doesn't need another dashboard — they need decisions. Our platform
turns raw customer data into messaging that converts, tested by synthetic
audiences before you spend a dollar. G2 reviewers in your role call it
"the missing link between data and creative." Start a free pilot and see
your first persona-validated campaign in 48 hours.

--- Content for Software Engineer ---
The API does what the docs say it does. REST endpoints, JSON responses,
sub-200ms latency. No SDK required — but we have one if you want it.
Check out our GitHub examples and have your first integration running
in under an hour. Engineers on G2 gave our API a 4.8/5 for documentation.

Error Handling

G2 uses Token token={key} (not Bearer). Using the wrong format returns 401. Check the exact header format in your G2 API documentation.
The comment_answers field structure varies by survey version. Some are flat strings, others are {text, id} objects. The code handles both formats.
Reviewer titles can be verbose (“Vice President of Marketing & Communications”). The code splits on comma and takes the first part. For better grouping, use a role-normalization function.