Inspiration

Small cross-border sellers lose money on almost every complaint. Picture Aisyah (our demo persona), who runs a batik studio in Kelantan, Malaysia, and ships hand-drawn silk scarves worldwide. When a $50 scarf arrives with one pulled thread, she has two bad options: refund it in full, or pay for an international return label that often costs more than the scarf. When a buyer falsely claims "item not received" on a parcel the carrier marked delivered, she can lose both the scarf and the money if she doesn't answer the PayPal dispute in time.

We wanted an AI agent that handles these complaints end to end, through PayPal, and that a seller can actually trust near their money.

What it does

PayGuard AI takes a buyer complaint (a chat message with a photo, or a PayPal dispute webhook) and resolves it through four agents:

  1. Intake reads the order, shipment tracking and any dispute from PayPal, and classifies the claim.
  2. Vision grades the buyer's photo as no damage, minor/cosmetic or severe/unusable.
  3. Policy applies the shop's own rules to pick the cheapest fair resolution. Plain, unit-tested code decides the amount; the AI only drafts the reply to the buyer.
  4. Payment moves the money through PayPal: a refund, a dispute offer, or proof of delivery.

Three scenarios run live on the PayPal Sandbox:

  • Small flaw: the AI offers 30% back if the buyer keeps the item (\( 0.3 \times \$50 = \$15 \)). The buyer accepts, and the refund is issued automatically, saving the shop $35.
  • Ruined item: the AI proposes a full $50 refund. That's above the $25 auto-approve limit, so a guardrail holds it until the owner taps Approve.
  • False "not received": a real PayPal dispute arrives; tracking says DELIVERED, so PayGuard sends proof of delivery to PayPal instead of conceding.

Built to be trusted with money

  • Never pays twice. Every payment gets a deterministic key, stored in our database and sent to PayPal as PayPal-Request-Id: $$k = \mathrm{sha256}(\text{caseId} \,|\, \text{action} \,|\, \text{amount})$$ A retry, crash or double click returns the first result. The demo has a "Try paying again" button to prove it.
  • Big refunds wait for a human. Anything over the merchant's threshold stops at an approval gate.
  • No AI in the money path. The model reads photos and writes messages; code decides amounts and makes the PayPal calls.
  • Verified webhooks. Dispute webhooks are only trusted after PayPal's signature verification passes.

How we built it

  • PayPal: the @paypal/agent-toolkit (AI SDK flavour) for orders, shipment tracking, products, disputes and refunds. For the endpoints the toolkit doesn't cover, we wrote REST calls with help from the APIMatic PayPal context plugin: make-offer, provide-evidence (multipart), provide-supporting-info, accept-claim and verify-webhook-signature.
  • AI: OpenAI GPT-4.1 mini through the Vercel AI SDK, for claim classification, vision damage grading and the buyer-facing replies.
  • App: Next.js 15, Prisma + SQLite, Tailwind v4 + shadcn/ui, with motion design using the motion library. Every tool call is written to an audit log, which the UI shows in plain English.
  • Hosting: Render, with a persistent disk for the database.
  • Quality: 19 Vitest tests cover the policy rules, idempotency and the execution guardrails, with PayPal mocked.

Challenges we ran into

  • Dispute states are strict. provide-evidence only works while a dispute is WAITING_FOR_SELLER_RESPONSE. Our real sandbox dispute went straight to the CHARGEBACK stage with status UNDER_REVIEW, where the only seller action PayPal allows is provide-supporting-info. We added that path and a test for it.
  • A toolkit bug. The toolkit's accept_dispute_claim validates the free-text note against the dispute-ID pattern, so we call the endpoint directly instead.
  • Sandbox setup. Our first app was attached to a Malaysian sandbox merchant, which couldn't process card payments (PAYEE_NOT_ENABLED_FOR_CARD_PROCESSING) and got a 403 from shipment tracking. Moving to a US merchant fixed both.
  • An LLM promising money it hadn't sent. In early runs, the reply said "we've approved your refund" while the guardrail was still holding it. We tightened the prompt and added a deterministic check that falls back to a fixed message if the draft claims the money was sent.
  • Webhooks need a public URL, so we deployed to Render early and tested dispute events against the live service.

Accomplishments that we're proud of

  • All three scenarios move real Sandbox money, end to end: a $15 partial refund, a retry that PayPal deduplicates, a $50 refund released only after approval, and a real dispute contested with proof of delivery.
  • A UI that judges can follow without reading code: buyer chat on one side, the owner's view on the other, and every agent step explained in plain English.

What we learned

  • With money involved, the most valuable thing an AI agent can do is know when not to act alone.
  • Idempotency has to happen in two places: our own database, and the PayPal request ID.
  • PayPal's dispute lifecycle (inquiry, chargeback, under review) decides which actions are possible, so the agent has to read state before acting.

What's next

  • Real carrier APIs (Pos Malaysia, DHL) for delivery proof.
  • Replies in the buyer's language, including Bahasa Melayu.
  • A merchant policy editor (thresholds, offer percentages).
  • Intake from Instagram and WhatsApp DMs, where many social sellers actually get complaints.
  • Running the agent pipeline on Render Workflows.

Built With

Share this project:

Updates

Submission history