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

Not all reviewers are the same. 1-star reviewers have different concerns than 3-star reviewers. You segment reviews by star rating, create a persona for each tier (1-2 star detractors, 3-star neutrals, 4-5 star promoters), then run a Focus Group asking the pivotal question: “What would move you to 5 stars?” The answers reveal the exact interventions needed for each segment. Flow: Trustpilot GET /reviews → Group by star tier → Mavera POST /personas (per tier) → POST /focus-groups (“What moves you to 5?”)

Code

import os, requests, time
from collections import defaultdict

TP_KEY = os.environ["TRUSTPILOT_API_KEY"]
MV = os.environ["MAVERA_API_KEY"]
BU_ID = os.environ["TRUSTPILOT_BU_ID"]
TP_BASE = "https://api.trustpilot.com/v1"
MV_BASE = "https://app.mavera.io/api/v1"
MV_H = {"Authorization": f"Bearer {MV}", "Content-Type": "application/json"}

TIERS = {
    "Detractor (1-2★)": [1, 2],
    "Neutral (3★)": [3],
    "Promoter (4-5★)": [4, 5],
}

# 1. Pull reviews per tier
tier_reviews = {}
for tier_name, star_list in TIERS.items():
    all_revs = []
    for stars in star_list:
        r = requests.get(f"{TP_BASE}/business-units/{BU_ID}/reviews",
            params={"apikey": TP_KEY, "stars": stars, "perPage": 50,
                     "orderBy": "createdat.desc"})
        r.raise_for_status()
        all_revs.extend(r.json().get("reviews", []))
        time.sleep(0.2)
    tier_reviews[tier_name] = all_revs

# 2. Extract themes per tier
tier_themes = {}
for tier_name, revs in tier_reviews.items():
    texts = [r.get("text", "")[:300] for r in revs if r.get("text")]
    themes = defaultdict(int)
    keywords = {"shipping": 0, "quality": 0, "support": 0, "price": 0, "easy": 0,
                "fast": 0, "broken": 0, "refund": 0, "love": 0, "recommend": 0}
    for t in texts:
        lower = t.lower()
        for kw in keywords:
            if kw in lower:
                themes[kw] += 1
    tier_themes[tier_name] = {"texts": texts, "themes": dict(themes), "count": len(revs)}

# 3. Create personas per tier
persona_ids = []
for tier_name, data in tier_themes.items():
    top_themes = sorted(data["themes"].items(), key=lambda x: -x[1])[:5]
    theme_str = ", ".join(f"{k} ({v})" for k, v in top_themes)
    sample_texts = data["texts"][:3]

    p = requests.post(f"{MV_BASE}/personas", headers=MV_H, json={
        "name": f"TP: {tier_name}",
        "description": (
            f"Trustpilot reviewer in {tier_name} segment. N={data['count']}. "
            f"Key themes: {theme_str}. "
            f"Sample: \"{sample_texts[0][:100]}...\""
        ),
        "psychographic": {
            "satisfaction_tier": tier_name,
            "top_themes": [k for k, _ in top_themes],
        },
    }).json()
    persona_ids.append({"id": p["id"], "tier": tier_name, "n": data["count"]})
    print(f"Persona: {p['id']}{tier_name} ({data['count']} reviews)")
    time.sleep(0.3)

# 4. Focus Group: "What moves you to 5 stars?"
fg = requests.post(f"{MV_BASE}/focus-groups", headers=MV_H, json={
    "name": "TrustScore Improvement Strategy",
    "persona_ids": [p["id"] for p in persona_ids],
    "questions": [
        "What would it take for you to give this company a 5-star review?",
        "What's the #1 thing this company does well that they should never change?",
        "If you could fix one thing about your experience, what would it be?",
        {"type": "likert", "text": "How likely are you to update your review if the company resolves your issue? (1-5)", "scale": 5},
        "Describe your ideal post-purchase experience in 2 sentences.",
    ],
    "responses_per_persona": 3,
}).json()

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

for resp in data.get("responses", []):
    tier = next((p["tier"] for p in persona_ids if p["id"] == resp.get("persona_id")), "?")
    print(f"\n[{tier}] {resp.get('question','')[:60]}")
    print(f"  → {resp.get('answer','')[:250]}")

Example Output

{
  "focus_group": "fg_trust_tier_9k2",
  "insights": [
    {
      "tier": "Detractor (1-2★)",
      "to_5_stars": "Ship when you say you'll ship. That's it. I ordered with 2-day shipping, waited 9 days. Fix the logistics and I'd give you 5 stars — the product itself is great.",
      "update_likelihood": 3.8
    },
    {
      "tier": "Neutral (3★)",
      "to_5_stars": "The product does what it says. But the website is clunky, checkout had errors, and I had to email support for a tracking number. Smooth that out.",
      "update_likelihood": 4.1
    },
    {
      "tier": "Promoter (4-5★)",
      "to_5_stars": "Already gave 5. But proactive shipping updates instead of me checking the tracking page would be nice. And a loyalty program.",
      "update_likelihood": 4.7
    }
  ],
  "strategy": "Shipping fixes would recover Detractors (highest ROI). UX polish converts Neutrals. Proactive communication retains Promoters."
}

Error Handling

Trustpilot requires separate API calls per star rating. The code loops through stars individually. For large volumes, paginate each star level independently.
Trustpilot reviews can be in any language. For non-English dominant businesses, filter by language param or use Mave for translation.