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

Your store loses revenue to abandoned checkouts. You pull recent abandoned carts via the REST API, group by product type and cart value range (under 50,50, 50–150,150, 150+), create Mavera personas from checkout demographics, and run a focus group to surface reactions to abandonment triggers — shipping costs, payment options, trust signals, checkout complexity.

Architecture

Code

import os, requests
from collections import defaultdict
from datetime import datetime, timedelta

STORE = os.environ["SHOPIFY_STORE"]
TOKEN = os.environ["SHOPIFY_ACCESS_TOKEN"]
MV = os.environ["MAVERA_API_KEY"]
SH_B = f"https://{STORE}.myshopify.com/admin/api/2024-10"
SH_H = {"X-Shopify-Access-Token": TOKEN}
MB, MH = "https://app.mavera.io/api/v1", {"Authorization": f"Bearer {MV}", "Content-Type": "application/json"}
since = (datetime.utcnow() - timedelta(days=30)).isoformat()
url = f"{SH_B}/checkouts.json?status=open&created_at_min={since}&limit=250"
checkouts = []
while url:
    resp = requests.get(url, headers=SH_H); resp.raise_for_status()
    checkouts.extend(resp.json().get("checkouts", []))
    link = resp.headers.get("Link", "")
    url = None
    if 'rel="next"' in link:
        for part in link.split(","):
            if 'rel="next"' in part: url = part.split("<")[1].split(">")[0]
print(f"{len(checkouts)} abandoned checkouts")
groups = defaultdict(list)
for co in checkouts:
    a = float(co.get("total_price", "0"))
    tier = "under_50" if a < 50 else ("50_to_150" if a <= 150 else "over_150")
    types = sorted(set(i.get("product_type", "General") for i in co.get("line_items", [])))[:2]
    groups[f"{tier}|{','.join(types)}"].append(co)
pids, sums = [], []
for key, members in list(groups.items())[:5]:
    tier, types = key.split("|", 1)
    r = requests.post(f"{MB}/personas", json={"name": f"Cart Abandoner — {tier}{types}", "description": f"Abandoned {tier.replace('_',' ')} cart with {types}. {len(members)} checkouts."}, headers=MH)
    r.raise_for_status(); pids.append(r.json()["id"])
    avg = sum(float(c["total_price"]) for c in members) / len(members)
    sums.append(f"{key}: {len(members)} carts, avg ${avg:.0f}")
fg = requests.post(f"{MB}/focus-groups", json={
    "name": "Shopify Cart Abandonment Analysis", "persona_ids": pids,
    "questions": ["What would cause you to leave items in your cart?", "What would make you complete checkout?", "How important is free shipping?", "What trust signals do you need before entering payment info?"],
    "context": "Investigating cart abandonment.\n\n" + "\n".join(f"- {s}" for s in sums),
}, headers=MH)
fg.raise_for_status(); result = fg.json()
print(f"Focus group: {result['id']}")
for r in result.get("responses", []):
    print(f"\n[{r['persona_name']}] {r['question']}\n{r['answer']}")

Example Output

{
  "id": "fg_2b8e4c1a",
  "responses": [
    {
      "persona_name": "Cart Abandoner — over_150 — Outerwear",
      "question": "What would cause you to leave items in your cart?",
      "answer": "At that price point, I need a clear return policy and real customer photos. If shipping pushes the total over my budget, I close the tab."
    },
    {
      "persona_name": "Cart Abandoner — under_50 — Accessories",
      "question": "How important is free shipping?",
      "answer": "For a $30 item, paying $8 shipping feels wrong. I'd add another item to hit a free shipping threshold."
    }
  ]
}

Error Handling

GET /checkouts.json for abandoned checkouts may require Shopify Plus or specific permissions. If you get 403, verify your app has read_checkouts scope. On non-Plus stores, use GET /orders.json?status=any and filter for financial_status: pending as a proxy.
Mavera focus groups work best with 3–7 personas. If grouping produces more segments, merge similar tiers or run multiple focus groups per value tier.