Laxmikant Sharma

[writing]

Who Is Your Agent Allowed to Pay?

Give an agent a trip to book: the airline up to $500, the hotel up to $600, $1,000 in all, nobody else. I checked whether 42 agent payment products and standards (Visa, Mastercard, Stripe, Coinbase, AWS, AP2 and more) can hold it to that. Three can: MetaMask's Delegation Framework, Rhinestone Smart Sessions and Crossmint's signer scopes. The surprise was the part that sounds easiest: who the agent is allowed to pay.

[agents][payments][authorization][security]

Say next week’s offsite moves to Austin and I hand the booking to my agent. It can spend up to $500 on a flight with our usual airline and up to $600 on a room at the hotel next to the venue, but no more than $1,000 between them, so a cheap flight can buy a nicer room. It should all be booked today, and nobody else gets paid.

Written as an instruction to the money rather than to the agent, that errand is one sentence: pay only the people this task names, up to this task’s budget, until this task ends, and let me take it back in one step that anyone can check.

On 8 October I checked that sentence against 42 agent payment products and standards: card networks (Visa, Mastercard, American Express), corporate cards (Stripe Issuing, Ramp, Brex), wallet platforms (Coinbase, AWS, Privy, Fireblocks), smart-account modules (MetaMask, Rhinestone, Safe), x402 for payments over HTTP, and the new “mandate” formats that carry an agent’s spending authority to the merchant or its processor (AP2, the Agentic Commerce Protocol, Skyfire’s KYAPay). I read public docs, specs and code and signed into nothing, and a part counted only where a documented field, rule or contract provided it.

Three of the 42 can hold the whole sentence: MetaMask’s Delegation Framework, Rhinestone’s Smart Sessions and Crossmint’s wallet signer scopes. All three are smart accounts (crypto wallets run by a contract), and each works on one chain at a time. Everywhere else something gives way.

The five parts

The trip written as a permission slip: the airline up to $500, the hotel up to $600, nobody else, $1,000 in all, from 1 pm today until both are booked or midnight, a key made for this trip only, and a tear-off line marked “take it back here, where anyone can see”. Each clause is labelled (a) to (e).

The task is the right unit because it comes with a budget, a list of people to pay and an end of its own. An agent runs many tasks, so a card or a key per agent outlives every one of them. Counting the task’s own key, the sentence has five parts:

  • (a) only the payees the task names
  • (b) a budget for the task
  • (c) authority that ends when the task ends
  • (d) revocation in one step that a payee or an auditor can verify
  • (e) a credential for this task, narrower than the agent’s own key

That last part needs a word on what the agent’s key is, because the rest of the story turns on it. To pay, an agent presents something only it holds. On a blockchain that’s a private key, which signs each payment; on a card network, a token that stands in for your card number; on a platform like Stripe or AWS, an API key or other credentials sent with each request. Whatever form it takes, I’ll call it the agent’s key, and whoever holds it can spend whatever it allows. So (e) asks for a key made for this trip alone: if it leaks, or the agent goes wrong, the damage stops at the trip’s own limits.

One catch on (c): nothing in the 42 can hear that a task is finished, so I scored it on the backstop, an expiry set at the task’s deadline.

The scorecard

How many of the 42 hold each part in full: (a) named payees 16, (b) task budget 17, (c) ends on time 20, (d) revocation someone else can verify 10, (e) a key for the task 23. All ten for (d) are contracts on a public chain.

A per-task key is the easiest part: 23 of the 42 can issue one. Revocation someone else can check is the rarest at 10, and all ten are contracts on a public chain.

Each of the three comes with a qualifier. MetaMask’s framework holds the sentence by configuration, Rhinestone’s is in beta (Biconomy builds on it), and Crossmint’s is confirmed in public code on Stellar, while its EVM and Solana versions rest on the vendor’s word. A later pass found two more outside the 42, also one chain each: Tempo’s access keys and Squads’ smart account on Solana. Kite’s whitepaper claims the whole sentence on Kite’s own chain, but I couldn’t find the contract behind it in the company’s public repositories, so for now it stays a claim. In the near misses below, the piece most often missing is the payee.

Near misses, each with what it lacks, against MetaMask’s Delegation Framework for comparison: Coinbase Spend Permissions has no payee list; MetaMask Advanced Permissions allows one ERC-20 payee; Zodiac Roles needs custom code for an expiry; Stripe Shared Payment Tokens bind one seller per token and the revocation is seen inside Stripe; Nevermined binds one seller plan and you learn of a revocation by asking it; AWS AgentCore payments has a budget and a clock, no payee rule of its own and no revocation others can check.

The easiest way to see where the rest give way is to watch the afternoon go wrong.

A page from a day planner for the trip. 1 pm: hand the trip to the agent, its authority starts. 1:20: (a) a lookalike booking site asks for the $600 deposit. 2:00: (b) flight $480, room $590, $1,070 on a $1,000 budget. 2:10: (d) or the offsite is cancelled while the agent is booking. 3:40: or all goes well and both are booked. 9 pm: (c) a $10 seat upgrade while the authority is still live. Midnight: the authority ends. A bar down the margin marks the agent’s authority from 1 pm to midnight, shaded from 3:40 on and labelled “authority with no trip behind it”. A sticky note: (e) any time, the agent’s key does more than you meant.

1:20 pm: a lookalike asks for the deposit

While comparing rooms, the agent lands on a page that looks like the hotel’s booking partner. The page asks for the $600 deposit. Maybe it carried an injected instruction, maybe the agent just got it wrong. Either way the money is about to go to someone the task never named.

The lookalike’s request, “pay 600.00 USD to lookalike-bookings, signed by the agent’s key”, dropped into three trays. Stopped: a payee list on a smart account (MetaMask, Rhinestone, Crossmint); a token made out to the hotel (Stripe, ACP, Skyfire, Nevermined), if the agent can’t get another; a payee or merchant list in a wallet policy or on a corporate card (Privy, Turnkey, Ramp, Brex). Goes through: Coinbase Spend Permissions pays the spender, which is the agent; Coinbase’s address rule, once it lists the USDC contract, passes a USDC payment to anyone; AWS AgentCore doesn’t enforce payTo; Safe’s Allowance module takes the payee from the request. Can’t tell from public docs: Visa’s agent pilot, where the merchant is a “preference”; Mastercard, which has a merchant allowlist that is set through the issuer.

Anything holding a payee list refuses it. So does a token made out to the hotel alone, like a Stripe shared payment token, as long as the agent can’t get a new one made out to the lookalike.

Plenty of others let it through, flagship products included. Coinbase’s Spend Permissions can only pay the spender, which here is the agent itself, and from there the agent can send it anywhere. A router contract that would forward to a named recipient sits in the same repository, audited in March, with its deployment listed as “TBD”. Coinbase’s server-wallet policy engine has an address rule the docs describe as matching “the recipient address of the transaction”, and it hides a nice trap.

A USDC payment drawn as an envelope addressed to the USDC token contract, with a letter inside reading transfer(to: the hotel, amount: 600000000), which is 600.00 USDC at six decimals. The address rule reads the envelope. The payee is on the letter. An evmData rule can read it. List the hotel and every USDC payment to the hotel fails; list the USDC contract and a USDC payment to anyone passes, the lookalike included.

The rule compares the transaction’s to field, and for a USDC payment sent as a transaction, that’s the USDC contract. The fix, a calldata rule on the transfer’s own to argument, shows up only in the schema’s examples, and Coinbase’s own worked example, “Limiting USDC Spend”, caps the amount and says nothing about where it goes. AWS’s AgentCore payments don’t enforce the recipient, payTo, on the server, and AWS tells you to check it in your own application, or with a gateway policy or the wallet provider’s rules.

2:00 pm: the room that breaks the budget

The flight comes in at $480, under its $500 cap, and the best room left is $590, under its $600 cap. Together that’s $1,070 on a $1,000 budget.

Flight $480, room $590, budget $1,000. Left, a grant per payee: an airline jar capped at $500 holding $480, a hotel jar capped at $600 holding $590, each says yes and nobody adds them up, $1,070 spent. Right, one budget for the trip: $480 goes in, $590 bounces off because $1,070 is over $1,000, and a $510 room fits. Split the budget in advance, $450 and $550, and the $480 flight fails instead. Below, who keeps a running total and the catch: Coinbase’s policy engine has none; Stripe’s is best effort, up to 30 seconds behind; Privy’s lasts 72 hours at most and counts two signing methods; Rhinestone’s is per action and per chain; Mastercard’s lifetime limit stands alone; Ramp allows overage by design and Brex has soft limits; AP2 names no keeper; x402 puts budgets out of scope.

Where each payee gets its own grant, as with the single-seller tokens from Stripe, the Agentic Commerce Protocol (ACP) and Skyfire, each grant checks only its own limit, so both say yes. Split the $1,000 in advance and you lose what the errand asked for, a cheap flight buying a nicer room. It takes one place keeping a running total for the whole trip, and plenty of the totals I found come with a catch. Coinbase’s policy engine judges each request on its own, so “$1,000 for this trip” can’t be written there at all.

AP2, the Agent Payments Protocol, is the most honest about the problem. Its open Payment Mandate, the one you sign for purchases made while you’re not there, carries a budget, and the spec says it needs a running total but never says who keeps it. In the project’s sample code it’s a local file written by the shopping agent, the very party the budget exists to limit.

The user and three of AP2’s roles drawn around a table: shopping agent, credential provider, merchant. The spec says the budget needs a running total and names no one to keep it. A sticky note on the shopping agent: “in the sample: a file I write myself”.

2:10 pm: the offsite is cancelled

Run the afternoon again. This time the flight is booked, the agent is partway through paying the hotel, and the offsite is called off. You hit revoke.

A hand-drawn sequence diagram with four lifelines, you, the rail, the agent and the hotel, in three bands. On chain: you revoke at 2:10, the contract sets a public bit, the agent’s payment at 2:11 is refused, and the hotel can read the bit without asking anyone; MetaMask’s can be undone and Rhinestone’s has to land on every chain. Card or corporate card: you cancel with a private call, the agent pays at 2:11, the issuer declines, and the hotel sees just the decline, with nothing that says when you revoked; a hosted wallet sends no payment at all. Mandate formats: there is no revoke call to make, the agent pays at 2:11, and the mandate still checks out at 2:11, unless the card or signing key under it is killed first.

On a smart account, revoking is a public state change. Coinbase’s Spend Permissions expose isRevoked, MetaMask’s framework keeps disabledDelegations(hash) and Rhinestone’s contract emits an event, so the hotel, or an auditor next month, can read it without asking anyone. The details still bite. MetaMask’s revocation has an undo, enableDelegation. A grant on several chains has to be revoked on each of them. Rhinestone’s embedded-wallet SDK had no self-service revoke when I read it on 8 October and pointed integrators at support. By 12 October its docs showed one, revokeSessionGrant(), which runs on every chain the grant covers, with the warning that “authority stays active on any chain whose revoke failed.”

On a card or a corporate card, revoking is a private call to the issuer, one call or close to it, and the hotel finds out from a decline. A hosted wallet just stops signing, so no payment arrives at all. The hotel may already have held the room for the agent, and nothing it can read says when your authority ended. Stripe’s preview shared payment tokens are the exception, since they tell the seller through Stripe.

None of the four mandate formats (AP2, the Agentic Commerce Protocol’s allowance, Mastercard’s Verifiable Intent and Skyfire’s KYAPay) defines a way to call one back. Verifiable Intent “does not define a credential-layer revocation protocol”, and Skyfire says “Deactivating an account does not void tokens that were already issued.” Each runs until it expires, or for ACP’s one-time allowance until it’s used, unless the card, token or signing key underneath dies first. A validated Skyfire token stays chargeable for a day past its expiry.

Visa’s April 2026 rules treat the company running the agent, which Visa calls an Agentic Payment Provider, as the cardholder for an agentic transaction, and hold the real cardholder responsible for what that provider does there “as if the Cardholder initiated the Transaction.” Whoever answers for the agent will want to prove its authority ended at 2:10, and a mandate with no revocation leaves nothing to prove it with.

9 pm: the trip is booked and the key still works

Now take the afternoon where nothing goes wrong: flight $480, a cheaper room at $510, both booked by 3:40. The agent’s authority runs until midnight, the backstop on the permission slip. At 9 pm the airline emails a $10 seat upgrade nobody asked for, and the agent, still holding a valid key, buys it.

A timeline from 1 pm to midnight. The agent’s authority runs the whole way. Both bookings are done at 3:40, so the stretch from 3:40 to midnight, 8 hours 20 minutes, is authority with no task behind it. At 9 pm a sticky note reads “airline: upgrade your seat, $10”, and a checklist beside it passes every check: airline listed, $490 under its $500 cap, $1,000 total within the $1,000 budget, before midnight. Is the trip over? Nobody asks. The agent buys it. An end tied to the trip would have closed it at 3:40.

Every check passes. The rule that would stop it is “the trip is over”, and nothing in the 42 can hear that. Grants there end at a time, after a count of payments or when their money runs out, and only Kite’s whitepaper claims an end tied to the task, with nothing public to show how. Two designs outside the set tie the end to the task. Ant International’s Agentic Mobile Protocol closes a mandate when its task completes (it has a finished mandate state), and an IETF individual draft, draft-chen-oauth-rar-agent-extensions, says that on a terminal task state the server “MUST immediately revoke the grant and all associated tokens.”

Any time: the key does more than you meant

Part (e) looked like the easy one. Say the developer who wired up your agent passed the trip’s rules but forgot one flag. The thirdweb SDK makes a session key a wildcard unless the developer says otherwise, and the contract skips every policy check for a wildcard session, so that key ignores the rules and is held back only by its expiry. In Coinbase’s policy engine, a request that no rule targets is accepted, so deleting an allowlist, the move that feels like revoking, opens the account.

A taped note card of defaults read from code and docs: Biconomy, “If no policies are configured, sudo policy will be added by default”; thirdweb, isWildcard: grantFullPermissions ?? true; Coinbase’s x402 client removes the upstream $1 cap and logs a warning when none is set; a hosted Vincent wallet operates “without policy restrictions” until claimed; Coinbase’s policy engine accepts any operation no rule targets.

Or the agent can reach the tool that made its trip card. In the card and wallet APIs I checked for this, the permission that makes a narrow grant can also make a wide one, or widen the ones already out. A Stripe restricted key that can create Issuing cards can raise the limits on any of them, so that agent can make itself a $5,000 card. Ramp, Brex and Coinbase have the same shape, and AWS warns that a role holding both CreatePaymentSession and ProcessPayment “can bypass payment limits by creating new sessions with elevated budgets.”

One big key, the key that mints task keys, with two tags: “can create a narrow key” and “can mint a wide one, or widen the rest”. Three small task keys hang off its ring. The agent must never hold the ring.

So a service you run has to hold the minting key, the one that makes the trip keys, out of the agent’s reach, and in all five of those products keeping it there is your job. It’s also the condition left open at 1:20. A token made out to the hotel stops the lookalike only while the agent can’t mint another.

The framing isn’t mine

Every scene above ends at the same fix: the rules belong to the money, set one task at a time, somewhere the agent can’t change them. I’m far from the first to say so. “Account” gets used two ways in agent payments: an account the agent holds, or your money, run by an agent under your rules. The second is the idea that keeps turning up. Anchorage Digital made the case in banking terms in August (“An allowance is a banking primitive.”), Kite’s whitepaper says permissions “are scoped at the task level, not the agent level”, and Lead Bank wrote in late September that each agent task gets its own account number. A paper posted to arXiv on 23 September, “Issuer-Sovereign Agentic Payments”, moves the spending check into the card issuer’s 3-D Secure server.

Closest of all is a crosswalk that Pink Agentic AI Payments, by PinkWallet, published on GitHub at the end of September: 14 providers and protocols sorted by eight kinds of spending control, with a verbatim quote behind every row. It doesn’t ask the question I care most about, which is who besides you can check that a grant was taken back.

The trip as one object

If the sentence is the spec, it should fit in one object that any rail can be checked against. For the trip, issued per task and signed by you, it looks like this:

The task-account object for the trip as a taped card with handwritten notes: agent_key, “made for this trip, and only this one”; budget_minor 100000, “$1,000 in cents, integers, so no rail argues decimals”; two counterparties, the airline capped at 50000 and the hotel at 60000, with rail bindings, “who may be paid, and how much each”; not_before and expires, “the window: 1 pm to midnight (UTC in this example)”; revocation, “one signed statement ends it, and anyone can check it here”; signature, “you sign it all”.

Its rules are short. Pay only a listed payee, keep each payee at or under its cap and the total at or under the budget, and pay nothing outside the window. One statement signed by you, naming the task and the object’s digest, ends it, and anyone holding the object can check that statement. Only the task’s key signs requests, and its signature covers the task id.

Mapping the object onto two rails. Onto MetaMask’s Delegation Framework, every field has a home: a root delegation carries the budget and the window, one leaf per payee carries its address and cap, the agent key is the only redeemer, and disableDelegation on the root revokes. Onto a card agent token, half the arrows end in a question mark.

On MetaMask’s Delegation Framework at its v1.3.0 release every field has a home. I read that from the contracts and haven’t deployed it, and the figure notes its seams. On a card I couldn’t make it map. In Visa’s public API spec the merchant is an optional preference and the amount a per-transaction maximum, and I couldn’t verify a budget across purchases on the agent token, or any way for the hotel to check an instruction’s state or limits before the authorization comes back.

To test the object, I wrote a small offline engine around it. It binds a fresh key to one task, runs each signed payment request through ten checks in a fixed order, and writes every payment decision, allow or deny, into a signed record chained to the one before. Then I replayed the afternoon through it.

Two receipts printed by the engine. The first, the trip goes ahead: 13:20 lookalike-bookings 600.00 denied, counterparty not listed; 14:00 airline 480.00 allowed; 14:05 hotel 590.00 denied, task budget exceeded, with a note “480 + 590 > 1,000”; 14:30 hotel 510.00 allowed; 21:00 airline 10.00 allowed, shaded, with a note that the trip was booked at 3:40 and nothing told the engine; 21:01 the same request again, denied as a replay; 21:02 the Austin key on the Denver trip, denied, bad signature; midnight, hotel 1.00 denied, task expired; audit clean. The second, the offsite is cancelled: 14:00 airline 480.00 allowed; 14:10 a revocation signed by you, accepted; 14:11 hotel 510.00 denied, task revoked; audit clean. Each line carries its record number and the digest of the record before it; records #0 and #1 register the two trips.

It refuses the lookalike and the $590 room, and after the 2:10 revocation it refuses the hotel request the agent had already signed. It also allows the 9 pm upgrade, as would any rail holding the trip’s caps, budget and clock: nothing told it the trip was booked.

It has gaps of its own. It’s one trusted evaluator, so its log can be re-checked afterwards but can’t stop a wrong decision in the moment, and an engine that lies about the time passes that re-check. A thief holding the trip’s key looks exactly like the agent until you revoke. And its revocation list lives inside it, out of the hotel’s reach, which is where most of the rails keep theirs too.

What I’d ask for

None of it needs new cryptography. Every piece of the sentence already exists somewhere in the 42, so the asks are small:

  • The mandate formats need a revocation status a payee can query before it acts, and AP2 needs a named keeper of the running total.
  • Coinbase could give Spend Permissions a payee, perhaps through the router already in its repository, and AWS could enforce payTo on the server.
  • Wallet SDKs should deny by default, with no sudo for a session without a policy and no wildcard when the caller passed policies.
  • Card networks could let the cardholder set a list of merchants on one agent token, and give the merchant something to read besides a decline.
  • An open x402 proposal sketches a mandate to “spend up to CAP, to recipients in R, until T”. It needs a revocation before it merges.
  • Minting a narrow credential shouldn’t need a permission that can loosen one.
  • And everyone needs a “this task is finished” call beside the clock.

What I couldn’t find is one product or standard documented to hold all five parts on more than one rail. The gap I least expected is the first clause, pay only the people this task names. Until that’s native, a line AWS wrote about its AgentCore payments fits more of the 42 than it should: “Session limits bound how much an agent can spend, not who it can pay.”

The full matrix has 42 rows, the 13 dimensions under the five parts and the evidence behind every cell. Pilot and preview products are marked, cells that rest on a spec’s text or a vendor’s word alone are flagged, and rows I couldn’t read are left blank, American Express’s among them. The engine, its tests and the afternoon’s replay are public too. Everything was read on 8 October 2026, and vendors move fast, so if your product does better than its row says, tell me where that’s documented and I’ll fix the row.