> ## 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.

# Dispute Analysis → Messaging Fix

> Analyze Stripe disputes by reason code and generate revised product and checkout copy with Mavera

### Scenario

Pull disputes, categorize by reason code (`product_not_received`, `duplicate`, `fraudulent`, etc.), and use Mavera's structured-output chat to rewrite product descriptions and checkout copy. JSON output feeds directly into a CMS or A/B test.

### Architecture

```mermaid theme={"dark"}
flowchart LR
A["GET /v1/disputes"] --> B["Categorize by reason"] --> C["POST /responses with json_schema"] --> D["Revised descriptions + checkout copy"]
```

### Code

<CodeGroup>
  ```python Python theme={"dark"}
  import os, json, stripe, requests
  from collections import defaultdict

  stripe.api_key = os.environ["STRIPE_API_KEY"]
  MAVERA_API_KEY = os.environ["MAVERA_API_KEY"]
  MAVERA_BASE = "https://app.mavera.io/api/v1"

  def fetch_disputes():
      cats = defaultdict(lambda: {"count": 0, "examples": []})
      for d in stripe.Dispute.list(limit=100).auto_paging_iter():
          cats[d.reason]["count"] += 1
          if len(cats[d.reason]["examples"]) < 3:
              cats[d.reason]["examples"].append({"dispute_id": d.id, "amount": d.amount / 100,
                  "product_description": (d.evidence or {}).get("product_description", "")})
      return dict(cats)

  def rewrite_copy_for_reason(reason, data):
      schema = {"type": "object", "properties": {
          "reason_code": {"type": "string"},
          "original_issues": {"type": "array", "items": {"type": "string"}},
          "revised_product_description": {"type": "string"},
          "revised_checkout_message": {"type": "string"},
          "revised_confirmation_email_snippet": {"type": "string"},
      }, "required": ["reason_code", "original_issues", "revised_product_description",
                       "revised_checkout_message", "revised_confirmation_email_snippet"]}
      resp = requests.post(f"{MAVERA_BASE}/responses", json={
          "input": [
              {"role": "system", "content": "You are a conversion copywriter reducing chargebacks."},
              {"role": "user", "content": f"{data['count']} disputes for '{reason}':\n"
                  f"{json.dumps(data['examples'], indent=2)}\n\n"
                  "Rewrite the product description, checkout message, and confirmation email."},
          ],
          "response_format": {"type": "json_schema", "json_schema": {"name": "dispute_fix", "schema": schema}},
          "max_tokens": 1500,
      }, headers={"Authorization": f"Bearer {MAVERA_API_KEY}"})
      resp.raise_for_status()
      return json.loads(resp.json()["choices"][0]["message"]["content"])

  for reason, data in fetch_disputes().items():
      if data["count"] >= 2:
          print(f"\n=== {reason} ({data['count']}) ===")
          print(json.dumps(rewrite_copy_for_reason(reason, data), indent=2))
  ```

  ```javascript JavaScript theme={"dark"}
  const STRIPE_API_KEY = process.env.STRIPE_API_KEY;
  const MAVERA_API_KEY = process.env.MAVERA_API_KEY;
  const MAVERA_BASE = "https://app.mavera.io/api/v1";

  async function stripeGet(path, params = {}) {
    const url = new URL(`https://api.stripe.com/v1/${path}`);
    Object.entries(params).forEach(([k, v]) => url.searchParams.append(k, v));
    const res = await fetch(url, { headers: { Authorization: `Bearer ${STRIPE_API_KEY}` } });
    if (!res.ok) throw new Error(`Stripe ${res.status}: ${await res.text()}`);
    return res.json();
  }

  async function fetchDisputes() {
    const categories = {};
    const data = await stripeGet("disputes", { limit: "100" });
    for (const d of data.data) {
      if (!categories[d.reason]) categories[d.reason] = { count: 0, examples: [] };
      categories[d.reason].count++;
      if (categories[d.reason].examples.length < 3)
        categories[d.reason].examples.push({
          dispute_id: d.id, amount: d.amount / 100,
          product_description: d.evidence?.product_description || "",
        });
    }
    return categories;
  }

  async function rewriteCopyForReason(reason, data) {
    const fields = ["reason_code", "original_issues", "revised_product_description",
      "revised_checkout_message", "revised_confirmation_email_snippet"];
    const schema = { type: "object", required: fields, properties: Object.fromEntries(
      fields.map((f) => [f, f === "original_issues" ? { type: "array", items: { type: "string" } } : { type: "string" }])
    ) };
    const res = await fetch(`${MAVERA_BASE}/responses`, {
      method: "POST",
      headers: { Authorization: `Bearer ${MAVERA_API_KEY}`, "Content-Type": "application/json" },
      body: JSON.stringify({
        input: [
          { role: "system", content: "You are a conversion copywriter reducing chargebacks." },
          { role: "user", content: `${data.count} disputes for '${reason}':\n${JSON.stringify(data.examples, null, 2)}\n\nRewrite the product description, checkout message, and confirmation email.` },
        ],
        response_format: { type: "json_schema", json_schema: { name: "dispute_fix", schema } },
        max_tokens: 1500,
      }),
    });
    if (!res.ok) throw new Error(`Mavera ${res.status}: ${await res.text()}`);
    return JSON.parse((await res.json()).output[0].content[0].text);
  }

  (async () => {
    const categories = await fetchDisputes();
    for (const [reason, data] of Object.entries(categories)) {
      if (data.count >= 2) {
        const fix = await rewriteCopyForReason(reason, data);
        console.log(`\n=== ${reason} (${data.count}) ===\n${JSON.stringify(fix, null, 2)}`);
      }
    }
  })();
  ```
</CodeGroup>

### Example Output

```json theme={"dark"}
{
  "reason_code": "product_not_received",
  "original_issues": [
    "Says 'instant delivery' but fulfillment takes 2-4 hours",
    "No confirmation page after purchase"
  ],
  "revised_product_description": "Digital access within 4 hours. Email with credentials and dashboard link.",
  "revised_checkout_message": "Credentials arrive via email within 2-4 hours. Confirmation page appears immediately.",
  "revised_confirmation_email_snippet": "Access provisioned — login details within 4 hours. Order #{{order_id}}."
}
```

### Error Handling

<AccordionGroup>
  <Accordion title="Stripe: Dispute evidence object empty">
    Older disputes may have sparse `evidence`. Skip empty `product_description` examples — `reason` code and `amount` alone produce useful recommendations.
  </Accordion>

  <Accordion title="Mavera: JSON schema validation failure (400)">
    Simplify the schema and retry. Set `"strict": true` inside `json_schema` to force adherence (may increase latency).
  </Accordion>
</AccordionGroup>
