-
-
Home: what Behest does, with the three-minute guided demo one click away.
-
Priya's mandate in her own words, and what her agent can do now: budget left, approval line, categories.
-
The signed mandate: an Ed25519 JWS co-signed with her passkey, checkable by anyone with the public key.
-
Her ledger in AG Grid: an allowed, a held and a blocked purchase, each with its reasons and signed receipt.
-
Held for Priya: $40 was over her $25 approval line, so she approved it at PayPal before it was paid.
-
Blocked by twelve fixed rules, with the smallest change to her mandate that would have allowed it.
-
Ravi's shop: a listing written to talk to AI agents is flagged and hidden from every agent.
-
Ravi's orders: a tampered cart and a replayed receipt were both rejected. Verified orders were paid out.
-
The receipt check: signature, freshness, seller, cart, amount and replay, using only Behest's public key.
-
The AG Studio dashboard: decision ledger, budget burn-down and receipt card widgets, plus a Claude analyst.
-
The security page: a published prompt-injection attack replayed against every layer. 7 of 8 stopped, 1 held.
Inspiration
AI agents are starting to buy things for people, and a lot of that money is going to move through PayPal. But nobody in that chain can prove what the human actually agreed to.
The seller gets an order from a bot and has to take it on faith. PayPal sees a charge. And if the agent gets it wrong and the buyer disputes it, it's the buyer's word against an agent's log that nobody else can check. Prompt injection makes this worse. The "Whispers of Wealth" paper (arXiv 2601.22569) shows how a few lines hidden in a product listing can steer a payment agent into buying something the person never asked for.
I don't think sellers will trust agent orders until every one of them carries proof. So that's what I built: a record of what the human said yes to, signed and checkable by anyone, that follows the purchase from the first instruction all the way to a dispute.
What it does
The demo follows three people: Priya, who lets an agent shop for her, Ravi, who runs a small spice shop, and the operator who handles disputes. There's no sign-up; you pick who you are on the home page.
Priya sets the rules once. She types what her agent may spend, the way she'd say it: "Up to $60 a month on groceries and spices. Ask me before anything over $25. Never electronics." Claude turns that into a strict policy. Then Behest reads it back to her in plain sentences, and those sentences are written by code from the policy, not by a model, so what she confirms is exactly what gets enforced. If she asks for something Behest can't enforce, like "only on weekends", it comes back as a question instead of being quietly dropped.
She signs it with a passkey. Behest signs the mandate too (an Ed25519 JWS), and the mandate carries her passkey's public key, so anyone can check her signature without trusting Behest. She links her PayPal wallet once, through a Vault v3 setup token, without buying anything.
Her agent shops inside those rules. She asks for "turmeric and cumin, one jar of each". A Claude shopping agent searches the sellers on Behest, checks open-web prices through Channel3, and proposes a seller and a cart. It has no payment tool and it never sets a price.
Then a gate decides. It's twelve rules in plain TypeScript with no model in it. It prices the cart again from Behest's own database, checks the budget, the per-purchase limit, the categories, the sellers and the purchase rate, and holds anything priced over 1.5 times its market median. The verdict is ALLOW, ASK or BLOCK. After that, Claude compares the cart with what Priya actually asked for, and it can only make the verdict stricter. No error anywhere can turn into ALLOW.
- ALLOW: Behest charges her saved wallet with Orders v2. She doesn't have to click anything.
- ASK: the $40 gift box is over her $25 line, so it waits. She approves it at PayPal from a link or by scanning a QR code with her phone.
- BLOCK: two gift boxes would break her monthly budget, so no money moves. Behest also shows her the smallest change to her mandate that would have allowed it, or tells her that no change would.
Her ledger, an AG Grid, lists every purchase with its verdict, the reasons for it, and a signed receipt she can verify right there.
Ravi checks every order himself. He onboards his shop from a CSV or from product photos. Claude drafts the listings (it reads the photos too), benchmarks each one against Channel3 market prices, and screens it for text aimed at AI agents. A listing that tries to talk to an agent gets hidden from agents altogether, and the gate blocks any cart that contains one. Outside agents can read his catalog as plain JSON.
Every order reaches Ravi with Behest's signed receipt. His shop checks it using only Behest's public key: the signature, that it's less than 15 minutes old, that it's for his shop, that the cart and amount match, and that it hasn't been used before. In the demo I send the same receipt back with the cart changed to two gift boxes, then send it again unchanged. Both get rejected live (cart_mismatch and replayed). Behest is the merchant of record. When Ravi accepts an order, he's paid with PayPal Payouts, and if he rejects one, Priya gets a refund.
Disputes come with evidence. When Priya opens a dispute at PayPal, Behest matches it to the purchase and builds an evidence pack: her signed mandate, the readback she confirmed, the signed receipt, the cart, the agent's decision record (its hash is inside the receipt), the purchase log, and her own approval if she gave one. Claude drafts the answer, and every claim has to cite one of those items. A claim without a citation gets dropped. The operator reviews and edits it. Before sending, Behest asks PayPal what this dispute accepts right now and answers that way, with evidence or with a message to the buyer. The evidence agent reads PayPal through the PayPal Agent Toolkit, limited to read-only tools.
Everyone gets a dashboard. It's built on AG Studio and scoped to whoever's looking. It has three widgets I built for it: a decision ledger, a budget burn-down and a receipt card. There's also a Claude analyst you can ask things like "Which purchases needed approval this week and why?" The operator also sees PayPal Transaction Search for reconciliation.
It's easy to check. The home page has a three-minute guided demo that walks through real records from one run on the live site, then hands over so you can try it yourself. The security page replays that published attack against every layer of Behest and lists what still gets through. The public key set is at /.well-known/jwks.json, and there's an llms.txt for AI agents that read the site.
How I built it
- PayPal (sandbox). Vault v3 for the saved wallet, Orders v2 for both buyer-not-present charges and buyer-approved orders, Payouts, Refunds, Disputes (provide-evidence and send-message), Webhooks with signature verification, Transaction Search, and the PayPal Agent Toolkit, bridged into Claude tools for the dispute agent with only its read-only tools exposed. Every call that moves money carries an idempotency key, so a retry can't charge, pay or refund twice.
- Claude (Opus 5.5) through the Anthropic API, in six roles: mandate compiler, shopping agent, intent check, catalog builder with vision, dispute evidence agent, and the dashboard analyst. Every call goes through one module that logs tokens and cost and enforces daily spend caps. Seller text, market results and dispute notes only ever reach the model as quoted data.
- Receipts and mandates are Ed25519 JWS signed with jose. The policy and cart hashes inside them are SHA-256 over RFC 8785 canonical JSON, and the public key set is open to anyone.
- AG Grid and AG Studio for every table and the dashboard. Channel3 for open-web prices, used by the agent and as each listing's price benchmark.
- Next.js 16, React 19, TypeScript, Postgres with Drizzle, a background worker for payouts, approval expiry, dispute sync and charge retries, and more than 4,300 Vitest tests. A stub mode replays recorded Claude answers, so the whole thing runs locally for free. It's deployed with Docker Compose and Caddy on a Google Cloud VM.
Challenges I ran into
- Saving a wallet without a purchase. PayPal's vault only allows this for merchants in certain countries, and a developer account opened outside them gets sandbox accounts from its own country by default. I moved everything to US sandbox accounts and proved each step (save, then charge with no buyer present) with scripts before building on it. That runbook is in the repo as docs/vault-proof.md.
- Disputes have stages. I'd assumed every dispute accepts evidence. In testing, an "item not received" case took evidence, but a "not as described" case at the same stage only accepted a message to the buyer. Now Behest reads which actions PayPal allows before it answers.
- Keeping the model away from the money. It's tempting to let the agent decide. I made the gate deterministic, gave the models power only to tighten a verdict, and made every failure fall back toward the human: a refused, failed or over-budget model call sends the purchase to Priya instead of letting it through.
- Untrusted text everywhere. Listings, market results, even the agent's own reasoning can carry instructions. So all of it reaches the model as quoted data, listings are screened at onboarding, and the gate blocks any cart with a flagged listing in it.
Accomplishments that I'm proud of
- I replayed the attack from that paper against every layer. In the live run, 7 of 8 attacks were stopped and 1 was held for the buyer, across 38 model calls costing about $0.31. The security page also lists what Behest doesn't stop yet, because I'd rather show that than hide it.
- A seller, a judge or PayPal can verify any mandate or receipt with the public key, without trusting Behest.
- Everything in the video happens for real on the PayPal sandbox: a saved wallet, a charge with no click, an approval at PayPal, a payout, a rejected tampered order and a dispute answer drafted with cited evidence.
What I learned
Models are good at reading what someone meant, and that makes them useful for checking intent. But they shouldn't have the final say over money. Trust in agent payments comes from records someone else can check: what the person signed, what the agent proposed, what the rules decided and what the seller verified. I also learned more about PayPal disputes than I expected to.
What's next for Behest
Signing-key rotation, a page for the alerts operators currently find with database queries, better defenses against sellers gaming the agent's ranking, and turning the receipt into a small open format other agent payment flows could adopt.
Built With
- ag-grid
- ag-studio
- anthropic
- caddy
- channel3
- claude
- docker
- drizzle
- ed25519
- google-cloud
- next.js
- paypal
- paypal-agent-toolkit
- paypal-disputes
- paypal-orders
- paypal-payouts
- paypal-refunds
- paypal-transaction-search
- paypal-vault
- paypal-webhooks
- postgresql
- react
- typescript
- vitest
- webauthn