Inspiration
PayPal's own help pages warn sellers about scams that hide in ordinary records: staff refunding orders to friends, a customer who "overpays" and asks for the difference by wire, a request to put someone else's address on the business account, affiliate commission on orders that were later refunded (common e-commerce scams, common scams and how to spot them). Reading a list of warnings is one thing. We wanted a small shop owner to find them in records they can open themselves, and we make murder-mystery games, so we built the practice as a detective case.
What it does
Ledger Case is a detective game played on a PayPal sandbox. Hoshino Paper Studio, a three-person stationery shop, is $412 short of its own books at month-end. You audit it.
- The case is real PayPal data. A seed script creates it in the sandbox with 17 calls to PayPal's REST APIs: 9 card orders with buyer names and shipping addresses (Orders v2), 5 refunds tagged with a staff code in
custom_id(Payments v2), an invoice for $300 (Invoicing v2) and a 7-item affiliate payout batch (Payouts v1). Every clue is a field PayPal returns. - Your partner is an AI that can only read. It holds one set of tools: PayPal's Agent Toolkit served as an MCP server with only the read actions switched on (
get_order,get_refund,get_invoice,list_invoices,search_invoicing), plus a read-onlyget_payout_batchwe add. Every PayPal call it makes appears on screen, and every record it reads becomes evidence. - The people are played by the AI too. Five people you can question, each played from their own card only (what they know and what they hide). Kenji says he never met Ryo Sato until you show him his own order: it ships to the same flat as Ryo's four orders.
- You accuse and cite. Name the culprit and cite the records; only records your partner actually pulled can be cited. The game checks the citations against three findings: the four refunds on Ryo Sato's orders add up to the $412 gap, they carry Kenji's staff code, and his own order ships to the same flat.
- The ending moves real money, and only a person can move it. Mara overpaid $200 and wants it by wire. The partner cannot refund (ask it, and it says so). It can only propose. A separate clerk program, not an AI, checks the proposal against its policy (one payment, at most $200, once), shows its own approval page, refunds the original payment when a person approves, and reads the refund back from PayPal.
Play it without signing in: https://ledger-case.onrender.com/ (the hosted copy gives every visitor their own copy of the refundable payment). The case is newly written; all names, shops and addresses are fictional.
How we built it
- Four processes, so the fence is in the code. The game (AI partner and suspects) holds no PayPal credentials:
npm startstrips everyPAYPAL_*variable from its environment and the game refuses to start if one is present. The reader runs the Agent Toolkit's MCP server with read tools only. The clerk refunds only on its own approval form. In the hosted demo, a desk creates a fresh copy of the refundable payment for each game, and the clerk asks the desk which payment belongs to a game instead of trusting an id from the game. - AI. In the hosted demo the partner and suspects are Claude (Anthropic API,
claude-opus-5-5) with a key in its own workspace under a monthly spend limit. Running it yourself, you can use an Anthropic key, a local Claude Code CLI with all built-in tools removed and only the reader's MCP server configured, or a scripted mode with no AI at all (the PayPal side is identical). - Hosting. Render's free web service runs the hosted copy (build
npm ci, startnpm start,LEDGER_HOSTED=1), with the seeded case passed in as sandbox ids so a restart does not seed a new case. - Tests. 17 tests that need no keys or network check the fence rather than describe it: the reader exposes exactly the read-only tools, the game refuses PayPal variables, the clerk refuses other payments, larger amounts, repeats and wrong approval tokens, a proposal alone moves no money, and an accusation can cite only pulled records.
- The demo video was recorded in one take with the game's narrated tour (
?tour=1): real sandbox calls and real AI replies, with the browser's built-in speech voice. A person clicks the clerk's button and approves, as in the game.
Challenges we ran into
- Refunds failed on a Japanese sandbox business account (
REFUND_FAILED_INSUFFICIENT_FUNDSon USD refunds). With a US sandbox business account they completed, so the setup steps ask for a US account. - Transaction search lagged by about six hours in our sandbox (one measurement), so the game never relies on it and reads every record by id.
- The Agent Toolkit's dependency tree had 10 security advisories. We override them to fixed versions;
npm auditreports 0, and the tests and the live reader still pass. - Recording the demo found a bug: after a finished game, "New game" kept showing the last game's refund, so the next player could not reach the ending. We fixed it and recorded again.
Accomplishments that we're proud of
The money boundary is real and visible: the AI asks, a person approves, a separate program refunds once and reads the result back from PayPal, and the 17 tests check each part of that. On 2026-10-23 we played the hosted copy end to end without signing in, through to a refund that PayPal reported as COMPLETED; the demo video (2026-10-24) shows a full run in hosted mode on our machine.
What we learned
An agent's safety is easiest to show as something it cannot do: give it only read tools, keep the keys in another process, and let the screen show every call it makes. We also learned to keep our claim narrow. Ledger Case is practice at reading real PayPal records; we have not measured whether it changes how anyone runs a shop.
What's next for Ledger Case
More cases built from other scams PayPal describes, and a mode that lets a shop owner open the same tools on their own sandbox records.
Pre-existing work and third-party code
Everything in the repository was created during the submission period (from October 23, 2026). It uses PayPal's Agent Toolkit (@paypal/agent-toolkit, ISC), the MCP TypeScript SDK (MIT), the Anthropic SDK (Apache-2.0), Express (MIT) and Zod (MIT). The code, the case and the texts were written with the help of Claude Code (Anthropic); a person checked them, and we ran and tested everything in the sandbox.
Built With
- anthropic
- claude
- express.js
- javascript
- mcp
- node.js
- paypal
- paypal-agent-toolkit
- render
Log in or sign up for Devpost to join the conversation.