Inspiration
PayPal's Agent Toolkit lets AI agents issue refunds, concede disputes, cancel invoices and capture payments. But the toolkit can only switch tools on or off. Issues filed against it describe the gap: no approval step, no amount ceiling or payee check (#95), a PayPal-Request-Id set once per client instead of per call, so a retry can run twice (#98), and pay_order running with no confirmation or record (#82). Businesses need a way to let agents help with payments without handing them unchecked control of money.
What it does
Handrail sits between an agent and PayPal. To the agent it offers the same 46 tools, so an existing agent needs no code changes. Every money-moving call goes through:
- A deterministic policy engine that allows, holds or blocks each call, using amount limits, per-tool rules, rate limits, payee checks, and running totals that catch refunds split into small pieces.
- An approval queue with email and Slack alerts. Approval cards separate facts verified with PayPal (amount, payer, earlier refunds) from what the agent claims. Held calls expire after 24 hours.
- Idempotency: one PayPal-Request-Id per operation. In sandbox tests, 10 identical refund requests produced exactly one refund.
- A hash-chained audit log recording every decision and who approved it, matched against signature-checked PayPal webhooks. Refunds or cancellations that bypassed Handrail are flagged as unattributed.
- AI that advises, never decides. Claude writes a short brief on each held call and turns plain-English rules into structured policy, which a person must confirm. If the AI is unavailable, enforcement keeps working.
Agents connect over MCP, a REST tool-call API, or drop-in wrappers for LangChain, CrewAI and the Vercel AI SDK. Each wrapper ships next to PayPal's own official example, so the switch is visible as a small diff.
How we built it
TypeScript and Node.js with the MCP TypeScript SDK (Handrail is an MCP server to the agent and a client to PayPal), SQLite, React and AG Grid for the dashboard, and Python and TypeScript wrapper packages. PayPal's Orders, Payments, Refunds, Disputes, Invoicing, Subscriptions and Webhooks APIs run on the sandbox. Accounts use Argon2id passwords, CSRF protection and admin, approver and viewer roles; agent keys are stored only as hashes. 258 tests, secret scanning with gitleaks in CI, hosted on Render. Judges get an isolated demo workspace and a scripted demo agent that runs against the real sandbox.
Challenges we ran into
Partway through, PayPal's sandbox MCP server began returning PAYPAL_API_SETUP_ERROR ("Unsupported cache mode: default") on tool calls, while reporting them as successful (isError: false). I confirmed it wasn't a credentials problem, added a reproduction to paypal/AI-Toolkit#34, and PayPal confirmed the regression. Handrail detects the error and falls back to PayPal's REST API for refunds, dispute concessions, invoice and subscription actions, so the core flows kept working. The fallback switches off on its own once the server is fixed.
Other challenges: making "drop-in" true (a test fails if Handrail's tools ever drift from PayPal's), keeping AI out of enforcement, and isolating simultaneous demo sessions.
Accomplishments that we're proud of
- A held refund approved by a named person, sent to PayPal exactly once, and confirmed by webhook in a verifiable audit log.
- Taking PayPal's own LangChain example, changing a few lines, and watching the same agent get held.
- Catching split refunds and refunds made outside Handrail.
What we learned
In payments, the safe default matters most: when anything is uncertain, hold the call. And a model's output should be shown clearly as advice, kept separate from facts verified with the payment provider.
What's next for Handrail
Publishing the wrapper packages, persistent hosted storage, delivering late approval outcomes back to the agent, escalation as dispute deadlines approach, and conversations with businesses running agents on payments.