A spending limit written in a prompt is not a control. It is a piece of text, sitting in the same context window as everything else the model reads, competing for the model's attention with the task, the tool output, and whatever a third party managed to get in front of it. When that limit and an instruction to spend disagree, nothing technical decides between them. The model decides. That is the whole problem.
We build Veyline at Payload, so our position is stated plainly up front. Enforcement of an agent's budget has to live outside the model, in a scoped, signed authorization that a server checks before any money moves. Prompts do not enforce budgets. Signatures do.
What uncontrolled spending actually costs
This is not a hypothetical failure. In a public report, issue #427 in the Conway-Research/automaton repository, the project's own self-hosted agent ran an auto-topup loop that paid 10 USDC on-chain while every credit grant failed. The purchase kept failing. The payments kept succeeding. And no authorization gate existed to stop the repeat payments.
Read that sequence again, because it is the normal shape of this loss. Nothing dramatic happened. No attacker drained a wallet in one move. A loop did what loops do, a payment rail did what payment rails do, and the only layer that could have said stop was not there. Ten dollars is a cheap way to learn that lesson. The same loop, running longer, against a larger balance, with a more expensive resource on the other side, does not stay cheap.
Three failure shapes, one root cause
1. Retry and top-up loops. An agent pays, the thing it bought fails, and the sensible looking recovery is to try again. So it pays again. And again. Each individual payment looks legitimate, because each one is small, well formed, and connected to a real task. The loss is in the repetition, and repetition is exactly what a model focused on completing its task will not police in itself.
2. Prompt injection past a text limit. If your spending limit exists as words in a prompt, then words can move it. Injected instructions, a poisoned tool result, or a persuasive page the agent was asked to read all operate on the same surface as the limit itself. The agent is not disobeying a control. It is weighing two pieces of text, and the limit has no authority beyond being text it happened to read earlier.
3. Child agents that outlive their budget. Work gets delegated. A parent agent creates sub agents to handle parts of a task, and those agents inherit the ability to spend without inheriting the full budget context that justified the spending in the first place. The original limit stays behind in a conversation the child never saw. The spending ability travels. The constraint does not.
Different triggers, same root cause. In all three cases the budget was information given to the model, not a condition enforced on the payment.
What enforcement has to mean
Real enforcement has four properties, and a prompt provides none of them.
First, the check happens outside the model. The component deciding whether a payment is allowed cannot be the component that can be persuaded, distracted, or injected. It has to sit on the server side, before the spend.
Second, the authorization is scoped. It covers a defined agent, a defined kind of action, a defined amount, and a defined window, rather than a general permission to spend. Scope is what turns a vague intention about a budget into a boundary a specific payment either fits inside or does not.
Third, it is signed. A signed authorization cannot be quietly edited by a conversation, a tool result, or the agent itself. The agent can present it, and the server can verify it, but neither the model nor anything feeding the model can rewrite its terms mid task.
Fourth, it fails closed. A failed or absent authorization means no payment. Not a payment with a warning, not a payment that gets reviewed later, and not a payment because the task seemed important. No valid authorization, no spend. That single property is what stops the Conway loop, because the second and third top-up attempts meet the same closed gate as each other instead of an open rail.
This is how Veyline works. Veyline checks a scoped, signed authorization outside the model before an agent spends, and the check is enforced server-side. Veyline is noncustodial, and the agent never holds funds. For context, Payload operates in x402 and agent payments, with real Base mainnet USDC transactions and a public payout-evidence record in x402 Foundation issue #3758.
The one next step: run a free preflight
Do not take any of this on faith, and do not start with a subscription. Start with one free Veyline preflight evaluation, which checks an authorization without executing anything.
- Get a free rail API key from the console at https://payload-rail.fly.dev/revrule-console/
- Connect the Veyline MCP server from Smithery at https://smithery.ai/servers/kyler-simmons-partners/veyline
- Run a preflight authorization check. The wrapper exposes veyline_status, veyline_usage, and veyline_preflight, and the preflight evaluates an authorization without executing anything.
That is the entire ask. One evaluation, against your own scenario, showing you what an outside the model check says before money is involved. If your current setup is a prompt and a hope, the contrast will be immediate.
Why the paid step exists
The free preflight matters because it demonstrates the control that the paid product applies to real traffic. Veyline Developer, at $99/mo through the live guest checkout at https://payloadhq.github.io/veyline.html, enforces these controls for real agent traffic, so the budget boundary holds when your agents are actually spending, not only when you are testing.
If you want the fast-start implementation path first, the Veyline Developer Primer is $79 one-time on Gumroad at https://payloadtools.gumroad.com/l/x402-paid-api-starter-kit, and it carries a $79 migration credit toward a first paid Veyline subscription, within a 90-day window.
Your agent will spend money again. The only open question is whether anything other than the model itself gets a say first. Put the signature in charge of that answer, and leave the prompt to do the job it is actually good at.
Top comments (0)