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

Responding to negative reviews is high-stakes — a great response can convert a detractor into an advocate. You pull 1-3 star reviews, load your customer service brand voice from Mavera, then generate personalized responses that acknowledge the specific issue, offer a solution, and maintain your brand’s tone. Each response is unique to the review, not a generic template. Flow: Trustpilot GET /reviews (1-3 star) → Mavera POST /brand-voices or load existing → POST /generations (per review) → Personalized response drafts

Code

import os, requests, time

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"}

# 1. Pull negative reviews without replies
negative = []
for stars in [1, 2, 3]:
    r = requests.get(f"{TP_BASE}/business-units/{BU_ID}/reviews",
        params={"apikey": TP_KEY, "stars": stars, "perPage": 20,
                 "orderBy": "createdat.desc"})
    r.raise_for_status()
    for rev in r.json().get("reviews", []):
        if not rev.get("companyReply"):
            negative.append(rev)
    time.sleep(0.2)

print(f"Found {len(negative)} unreplied negative reviews")

# 2. Create or load customer service brand voice
CS_VOICE_SAMPLES = [
    "We're sorry to hear about your experience. This isn't the standard we hold ourselves to.",
    "Thank you for taking the time to share this feedback. We take every review seriously.",
    "We'd love to make this right. Please reach out to support@company.com with your order number.",
    "Your satisfaction matters to us, and we're actively working on improving this.",
]

bv = requests.post(f"{MV_BASE}/brand-voices", headers=MV_H, json={
    "name": "Customer Service Voice",
    "samples": ["\n\n---\n\n".join(CS_VOICE_SAMPLES)],
    "preferred_terms": ["we appreciate", "make this right", "your experience matters"],
    "avoid_terms": ["unfortunately", "per our policy", "as stated in our terms"],
}).json()
bv_id = bv["id"]
print(f"Brand Voice: {bv_id}")
time.sleep(2)

# 3. Generate responses per review
responses = []
for rev in negative[:10]:
    stars = rev.get("stars", 0)
    title = rev.get("title", "No title")
    text = rev.get("text", "")[:500]
    reviewer = rev.get("consumer", {}).get("displayName", "Customer")
    review_id = rev.get("id", "")

    gen = requests.post(f"{MV_BASE}/generations", headers=MV_H, json={
        "brand_voice_id": bv_id,
        "prompt": (
            f"Write a response to this {stars}-star Trustpilot review.\n\n"
            f"Reviewer: {reviewer}\n"
            f"Title: {title}\n"
            f"Review: {text}\n\n"
            f"Rules:\n"
            f"- Acknowledge the SPECIFIC issue (not generic)\n"
            f"- Offer a concrete next step\n"
            f"- Keep under 80 words\n"
            f"- Never be defensive\n"
            f"- Use the customer's name\n"
            f"- End with a way to reach your team"
        ),
    }).json()

    response_text = gen.get("output", gen.get("content", gen.get("text", "")))
    responses.append({
        "review_id": review_id,
        "stars": stars,
        "title": title,
        "response": response_text,
    })
    print(f"\n[{stars}★] {title}")
    print(f"  Review: {text[:100]}...")
    print(f"  Response: {response_text[:200]}")
    time.sleep(0.5)

# 4. Post responses (requires OAuth — uncomment in production)
# TP_OAUTH_TOKEN = os.environ["TRUSTPILOT_OAUTH_TOKEN"]
# for resp in responses:
#     requests.post(
#         f"{TP_BASE}/private/reviews/{resp['review_id']}/reply",
#         headers={"Authorization": f"Bearer {TP_OAUTH_TOKEN}", "Content-Type": "application/json"},
#         json={"message": resp["response"]})

Example Output

[1★] Worst customer service ever
  Review: Waited 3 weeks for my order. Called 4 times. Each time I was told...
  Response: Hi Sarah, I'm genuinely sorry about the 3-week delay and the
  repeated calls without resolution. That's not the experience we want for
  anyone. I've flagged your order for priority attention — please email
  me directly at alex@company.com with your order number and I'll
  personally ensure this is resolved within 24 hours.

[2★] Product is fine but the website is terrible
  Review: I like the product but the checkout process crashed twice and...
  Response: Hi Mark, glad you like the product — and I hear you on the
  checkout issues. We're actively rebuilding the checkout flow this quarter.
  Your feedback accelerates that work. If you run into issues again,
  our team at support@company.com can process orders directly.

Error Handling

Posting replies uses POST /private/reviews/{id}/reply, which requires OAuth 2.0 (not just API key). Set up the OAuth flow before automating responses.
Trustpilot allows one company reply per review. Attempting a second returns 409. Check companyReply field before responding.
Trustpilot limits replies to 10,000 characters. The 80-word prompt constraint keeps responses well under this limit.