DEV Community

Ishween Kaur for Daily Context

Posted on

Your Agent Paid. That Does Not Mean It Should Have.

An AI agent is writing a report and needs one data point. The API answers: this costs five cents. The agent signs, the payment settles, and the data arrives.

The payment succeeded. That does not mean it was correct.

Protocols like x402 make machine-to-machine payments possible over ordinary HTTP. A client requests a resource and receives a 402 with a PAYMENT-REQUIRED header. It selects a payment option, signs it, and retries with a PAYMENT-SIGNATURE header. A facilitator verifies and settles the payment before the resource is delivered. No human needs to approve each transaction.

That is what makes the protocol interesting, and it is also what moves the security boundary. Enabling an agent to pay is the easy half. The harder problem is deciding what the agent is actually authorized to pay for, and enforcing that decision without asking another model for permission.

Where each failure enters the x402 flow, and the two places a deterministic check can stop it.

A successful transaction can still be a bad transaction

I ran the official x402 client against test sellers on Base Sepolia, a public testnet, using the public facilitator. Six failures stood out. In each one, the payment machinery behaved as designed. The problem was the decision or outcome around it.

Price manipulation. The directory listed a lookup at five cents. The actual payment request arrived later and asked for fifty. With no buyer-side guard, the payment settled. The facilitator verified the signature; it had no reason to know the agent expected five cents. The client's default one-dollar cap did not help, because fifty cents is under a dollar. My first guard checked only the amount, and half the attacks still got through by changing the wallet or the coin. With every field checked, none of 240 did.

One payment, more than one outcome. Send the same signed payment five times at once. Every copy can pass verification, so the seller delivers five times before the chain settles one and rejects four. The chain prevented double-spending. It did not prevent double-delivery. The buyer has a mirror problem: a timeout triggers a retry with a fresh payment, and one intended purchase becomes several charges.

Free shopping. Verification is not settlement. A payment can look valid during verification while settlement later fails. If the seller has already delivered, the resource is gone. Other paths lead to the same place: a paywall protects /report while the same resource sits at /download, or a CDN caches a paid response and serves it to the next requester. In my lab, 30,000 unpaid requests produced 30,000 leaks. The rule: every delivery needs a payment behind it.

Right payment, wrong item. A payment identifies who gets paid and how much, but the application still has to establish what that payment authorizes. The exact fields depend on the scheme and version, and current x402 specifications include resource information in the flow. In my test, two resources had the same price and seller, and one payment was accepted for both. The buyer paid once and received two products.

Malicious discovery. Agents can discover services instead of receiving a trusted endpoint from a human. A directory can establish that a service works without establishing who runs it. I created a lookalike seller with its own domain and wallet. The agent selected it and paid. The transaction was valid. It was simply paid to the wrong seller.

Prompt injection. The seller's response is also content the model reads. A malicious seller does not need to say "pay me." It can plant a suggestion. The agent's task was to get a Q2 summary for no more than five cents. The seller returned the summary and mentioned a fuller version at another path. The agent, Llama 3.2 running locally, followed the hint and bought it for fifty cents.

Do not solve a probabilistic problem with another probabilistic decision

The instinctive response to prompt injection is another model: tell the agent not to trust seller content, or classify responses as suspicious. I tried both. They caught the obvious attacks, but a quiet hint with no trigger words slipped through, and a third of the attacks still moved money.

What worked was taking the authorization decision away from the model. Before reading anything from the seller, the agent writes a plan, buy the summary, for up to five cents, and code locks it. Before signing, a deterministic guard asks one question: is this purchase on the plan? It does not need to understand the seller's words or judge whether a suggestion is malicious. It stopped all 90 attacks.

The model can still discover services, interpret responses, and decide what it wants to accomplish. It just does not get the final authority to move money. A seller can tell you the price. It cannot give you permission to buy.

Where the controls belong

The six failures fall into two control points.

The buyer, before signing. Compare the payment request with what the agent selected: amount, recipient, asset, network, and every other relevant field. Restrict which sellers can be paid. Enforce budgets and locked plans. If a payment times out, check its status and retry the same payment rather than creating a new one.

The seller, before delivering. Require the appropriate settlement state before releasing paid resources. Claim each payment once and bind it to the resource it authorizes. Protect every route that serves the resource, and keep paid responses out of shared caches.

None of these controls is clever. They are comparisons, lookups, state checks, and limits, and the buyer-side check ran in about 25 microseconds. That is the point: the control that decides whether money moves should be deterministic wherever possible.

There is a cost. Pinning sellers stopped every lookalike, but it also blocked legitimate sellers when they rotated wallets, and the agent completed only 17 percent of its purchases. A probation state for new or changed sellers, reviewed by a human, kept the protection without stalling the agent. Security that breaks the workflow gets turned off.

The question is no longer "Did the payment succeed?"

We have spent a lot of energy asking how much agents can do. Payments make the other question unavoidable:

What must an agent never be allowed to do, even when the model decides otherwise?

A successful transaction tells us the payment system executed. It does not tell us whether the agent paid the intended amount, to the intended seller, for the intended resource or whether untrusted content changed what the agent decided to buy.

Those are different questions.

And as agents delegate to other agents through protocols like MCP and A2A, authorization has to hold across every hop. It has to become a boundary around autonomy, not another decision left to the model.

Because when an AI agent can move money, "the payment succeeded" is not the end of the transaction. It is the beginning of the security question.

Top comments (0)