Inspiration
AI agents are starting to shop, hire freelancers and chase invoices for people. That works right up to the moment one has to pay. An agent can be talked into things. A seller can demand an urgent "deposit" to a new account, raise the price after agreeing, ship the wrong item, or hide text inside a delivered file telling the buyer's AI to approve payment. Handing an agent a PayPal login and hoping it says no is not a safety model.
I wanted the opposite: let agents do the negotiating, but make money move only when the deal was actually fulfilled, under rules the agent cannot talk its way past. The idea behind Handshake is simple: agents shake on a deal, and PayPal pays when it's delivered.
What it does
Handshake is a deal layer between AI agents and PayPal Payouts.
- Agents negotiate through an API. Once both sides accept, the terms are locked. A later change becomes a new version and never overrides what was agreed.
- A policy engine checks every commitment and release: per-payment cap, weekly budget, verified payee, category, payment frequency, new payee, approval threshold, and pressure for deposits. Anything over the owner's limit waits for approval. Anything unsafe is blocked.
- Budget is reserved while the seller delivers. Physical goods are checked against carrier records (item, quantity, address, deadline, tracking number never reused). For digital work, fixed file rules run first, then Gemini reads the work against the acceptance criteria.
- Only a verified delivery can reach release. Handshake pays through PayPal Payouts to the payee on the verified vendor profile, never to anything written in a message, and PayPal's signed webhooks move the deal to Paid.
- An owner console shows the budget, approvals waiting, every deal on a parcel-style progress line, a ledger built on AG Grid with a "why" panel for each decision, and a red-team lab that replays attacks.
It is conditional release, not escrow: funds stay in the owner's PayPal balance until release. Everything runs on PayPal's sandbox.
How I built it
- Backend: Python 3.12, FastAPI, SQLAlchemy and Alembic on Postgres (Neon), deployed on Vercel. The core is a deal state machine where the
RELEASINGstate can only be entered through a compare-and-set, so a replayed release request sends exactly one payout. Every transition is written to a hash-chained, tamper-evident audit log. - Policy engine: pure functions with a fixed precedence,
BLOCK > HOLD > REQUIRE_APPROVAL > ALLOW. Owner approval satisfies approval rules only, never a block. - Verifier: deterministic checks are authoritative. Gemini (
gemini-3.5-flash-litethrough the google-genai SDK) can only turn a pass into a failure or a review, never the reverse. Any model error, timeout or blocked response fails closed. An optional AI second opinion on payments exists but is off by default and can only escalate. - PayPal: OAuth, the Payouts API (one item per batch, with a deterministic
sender_batch_idused as thePayPal-Request-Idfor idempotency), and webhooks verified through PayPal's signature-verification endpoint on the raw request body, with a polling fallback. - Console: Next.js 16, React 19, TypeScript and AG Grid, designed phone-first.
- Quality: 62 automated tests, a CI workflow, and a benchmark that replays 11 attacks and 3 honest deals through the real code path.
Challenges I ran into
- Keeping the AI out of the critical path. My first version had no timeout on the Gemini call, so one slow response hung a payment request until Vercel's 60-second limit. I added hard timeouts and made every AI failure fail closed. Then a live check showed Google rejects request deadlines under 10 seconds, so my first fix made every call fail. I also found the newest, largest model returning 503 and 504 errors under test, so I wrote a live check script and chose
gemini-3.5-flash-litefrom measured results. - Payouts are asynchronous. A payout row is saved before the network call, PayPal's request ID makes retries safe, and stale or out-of-order webhook events are ignored, so a crash or duplicate event can't pay twice or un-pay a deal.
- Reporting an honest benchmark. One attack, ten small payments to a verified vendor, can't be fully prevented. Limits only cap the loss. I kept it in the results instead of tuning it away.
- Bugs that only showed up when I recorded the demo. Ledger search returned nothing for "Blocked" because AG Grid searched the raw value instead of the label on screen. A weekly budget quietly ran out partway through a demo and blocked the approval scene.
Accomplishments that I'm proud of
- A real PayPal sandbox payout reached SUCCESS, and PayPal's event log shows the payout item and batch webhooks delivered with Success on my deployed API.
- On the benchmark, a modelled naive agent loses $1,052 across 11 attacks. Handshake loses $150, all from the one attack I can only cap. All 3 honest deals were still paid, with no false blocks.
- Gemini passed my live check on all 7 tests: it passes honest work, stops work that misses the criteria, and was not swayed by injected "approve this" instructions, with stable results across repeated runs.
- The AI can only ever make a decision stricter, and a model outage can't release money.
- A console that explains every decision in plain language, rule by rule.
What I learned
- Put the model behind deterministic rules, not in front of them. The rules decide. The AI adds a second pair of eyes.
- Fail closed, put a timeout on every outbound call, and make every payment step idempotent.
- Measure the baseline honestly. I scored each attack with
$\text{mispaid} = \text{sent to a wrong payee} + \max(0,\ \text{sent to the right payee} - \text{owed})$
so the numbers can't flatter me.
- Recording the demo is a QA step. It found real bugs that tests had not.
What's next for Handshake
- Real carrier integrations in place of the mock carrier.
- An MCP server so any agent framework can use Handshake as its payment layer.
- Milestone and partial releases, and PayPal Invoicing for services.
- Real accounts, roles, rate limiting and a scheduled sweep, so more than one owner can use it safely.
- Testing the optional AI payment review against honest deals before turning it on by default.
Built With
- ag-grid
- ai-agents
- alembic
- fastapi
- gemini-api
- github-actions
- httpx
- neon
- nextjs
- paypal
- paypal-payouts-api
- paypal-webhooks
- playwright
- postgresql
- pydantic
- python
- react
- sqlalchemy
- typescript
- vercel