Inspiration

A small seller gets a PayPal dispute and has days to answer. Fighting takes hours and often loses; refunding everything is expensive. A partial offer is often the cheapest way out, but nobody does the math. I wanted an advisor that does the math, explains itself in plain words, and never moves money without the seller's OK. And because disputes are stressful, I gave it a face: Parley, a snowy owl in a navy waistcoat.

What it does

Parley reads every open dispute through PayPal's Disputes API and webhooks and makes a call on each one: fight it, refund it, or offer a specific amount back. Every call comes with:

  • an estimated chance to win, shown as a range, e.g. "23% (5–46%)";
  • how similar past cases ended;
  • how strong the evidence on file is;
  • what the seller will likely keep, in dollars and cents.

On the demo's sleeping-bag case, Parley offers $97.50 back on a $179.00 claim: a 59% chance the buyer takes it, and on average the seller keeps about $32 more than by fighting.

One click shows the exact PayPal call (POST /v1/customer/disputes/{id}/make-offer); a second click sends it. Parley's chat can answer "which disputes are due this week?", stage an offer as a "Needs your OK" card, and remember seller facts ("I always ship with UPS tracking"). Behind the scenes it keeps an activity log of every PayPal call and approval, an all-disputes grid with CSV/JSON export, and a read-only API with an OpenAPI spec.

How I built it

  • Front end: React 18 + TypeScript, AG Grid; a mobile layout with the owl docked at the bottom.
  • Server: Node; Postgres full-text search over resolved cases finds the precedents.
  • Decision core: Rust, integer cents, behind a versioned contract (decide.v2).
  • Win chance: a beta posterior from precedents and a per-reason prior, with \( K = 4 \) pseudo-cases: $$ p = \frac{\text{wins} + K\,p_0}{n + K} $$
  • Offer amount: expected value of each choice, with \( A \) the amount, \( F \) the fee and \( c \) the cost of fighting: $$ EV_{\text{accept}} = -A, \qquad EV_{\text{fight}} = -(1-p)(A+F) - c $$ $$ EV_{\text{offer}}(x) = q(x)\,(-x) + \big(1-q(x)\big)\,EV_{\text{fight}} $$ where \( q(x) \) is a logistic acceptance curve. Parley scans offers in 50-cent steps and only recommends one when it beats both fighting and refunding.
  • AI: a real LLM drafts replies, runs the chat and picks tools. Tool names follow PayPal's Agent Toolkit (list_disputes, get_dispute, accept_dispute_claim); I added make_dispute_offer and provide_dispute_evidence in the same style. The model never computes money.
  • Runtime: Kubernetes (k3d) via Nix, or Docker Compose for anyone; webhooks verified through PayPal's verify-webhook-signature.

Challenges I ran into

  • Keeping the model out of the arithmetic while still letting it explain the numbers.
  • Making the chat resolve "send the offer" to exactly one actionable case, and making its counts match the list's.
  • PayPal sandbox disputes have to be opened by a buyer in the sandbox web UI, so the write calls were exercised against a faithful simulator of the Disputes API.
  • Running a public demo safely: 10-minute demo sessions and per-visitor rate limits so the AI budget can't be drained.

Accomplishments that I'm proud of

  • An offer engine that finds a specific dollar amount, not a fixed percentage.
  • Every recommendation shows its working, down to the exact PayPal request.
  • An owl people actually want to talk to.

What I learned

Sellers trust a number more when they can open the exact PayPal call and the plain-words reasoning behind it. Honesty about uncertainty ("estimated", with a range) beats false precision.

What's next for Parley

  • A real sandbox dispute end to end, and evidence file upload.
  • Calibrating the priors on real outcomes.
  • An animated Rive owl that reacts to the conversation.
  • "Discuss with Parley" on every case, with what-if offers re-run by the engine.
  • Exposing Parley's tools as an MCP server.

What is real

The demo runs on a simulated PayPal Disputes API with 60 synthetic disputes. Against the real PayPal sandbox, my app authenticated and listed disputes (HTTP 200). Win chances are estimates from stated assumptions (served at /api/assumptions), not calibrated against PayPal outcomes.

Built With

Share this project:

Updates

Submission history