Your agent asks for a plan, not a payment. One approval funds an envelope it cannot exceed.
An agent that spends money today either holds a funded wallet — no budget, no scope, no kill switch — or interrupts you on every call until you stop reading and just click yes. Neither is consent.
PlanBound makes the agent shop first. It searches the x402 Bazaar for real sellers, sends each one a live HTTP request to get an actual price, and comes back with a single priced plan: what each step buys, what it costs, and one line of why it earns its price. Every price is badged as a live 402 quote or an estimate, and we never blur the two. Nothing has been spent at this point — discovery and quoting are free.
You approve once, on a page rendered by our server rather than drawn by the agent, because consent an agent can render is consent a prompted agent can forge. Before approving you can check each seller's settlement history on chain — a subgraph we deployed on The Graph, read from the seller's own payout address, so it answers a question no listing and no price can: has anyone ever actually paid them?
Approving is the funding. There is no permission flag that can drift out of sync with the money. It mints a single-use Hedera account holding exactly the approved ceiling, keyed 1-of-[2-of-2(agent, policy), treasury]. The agent can propose a payment from it and can never complete one alone. The account that enforces the limit is the account that pays the seller, so there is no service in the middle that could fail open. A refund is scheduled at mint, so an abandoned plan returns itself with no keeper running.
Then the agent runs unattended. Before each purchase it re-checks the live price against the approved plan. If a seller asks for more than it quoted, the payment stops — even when the envelope still holds enough to pay — because the plan you approved is not the plan the agent found. You get a diff with three priced exits, not a popup. That is the difference between a spending limit and an approved plan.
Above a ceiling you set, approving requires World ID — and not just that a human is present, but that it is the same human the account is bound to, matched by nullifier. A leaked link is not enough.
Every decision and receipt is written to a public Hedera consensus topic, and the console diffs what we claim we paid against what The Graph says actually settled. You can check us without trusting us.
Seven MCP tools over OAuth, installable as a Claude Code plugin. Everyone else gives the agent a funded wallet and enforces the limit inside their own service. We give the agent zero funds and let consensus enforce the cap.
Monorepo: pnpm workspaces, TypeScript everywhere, Next.js 16 App Router on Vercel, Supabase (Postgres + RLS) for the control plane. 163 tests in vitest. planbound.xyz.
Hedera does the enforcement. The envelope is a real account created with @hiero-ledger/sdk v4, keyed as a nested KeyList — 1-of-[2-of-2(agent, policy), treasury] — via setKeyWithoutAlias, and funded with exactly the approved ceiling. Agent and policy must both sign, the treasury can always reclaim. The refund is a ScheduleCreateTransaction with setWaitForExpiry(true), created at mint, so an abandoned plan returns itself with nothing running. Decisions and receipts go to HCS with TopicMessageSubmitTransaction; the mirror node is what we reconcile against.
The single most useful thing we learned about Hedera, and it nearly shipped a false security claim: the fee payer's signature counts toward the threshold of every account whose balance decreases. Our "the agent cannot spend alone" test passed because the treasury was the client operator and its fee signature satisfied the outer 1-of. The dual-control guarantee was never exercised. We now pay fees from a third account. It turned out to be a feature — it means an x402 facilitator, an external fee payer, is exactly the right topology for a bounded account paying a stranger.
x402 v2 does discovery and payment: @x402/fetch with @x402/evm (ExactEvmScheme) and @x402/hedera (ExactHederaScheme), scheme picked by rail across hedera:testnet, Base and Worldchain. Discovery is the Bazaar, keyless, then a live probe of every candidate — a listing is a claim, a 402 response is a fact. The policy gate sits in the one route money can leave by, so it cannot be bypassed by calling something else.
The Graph is the independent check. A subgraph indexes USDC settlements on Base by recipient, and the console diffs our Postgres against it — our claim on one side, consensus on the other. On the approval page, a server action probes each seller live for its current payTo and queries the subgraph for what has settled there, so the human can see payment history before funding. Studio has dropped subgraph support for Worldchain, so we reach that chain with Substreams instead — and packaged it as an installable skill that streams a prebuilt ERC-20 .spkg with no Rust and no build step, covering 10 EVM chains.
World ID 4.0 Managed with IDKit 4.2.1 is the step-up. The sharper thing we built: proving a human is present doesn't stop a leaked approval link, because a different human can satisfy it. So we store the nullifier at enrolment and require later approvals to match it — "the person who enrolled was here" rather than "someone alive was here", without ever learning who they are. Selfie Check runs on the beta preset with allow_legacy_proofs.
The agent surface is MCP: seven tools, two transports (stdio and Streamable HTTP), one implementation. The remote server is protected by OAuth 2.1 with Supabase as the authorization server, RFC 9728 discovery, and our own consent screen — Claude Code registers itself, sends you to consent, and comes back with a token scoped to one agent. It's published as a plugin marketplace, so claude plugin install planbound@planbound works from a clean machine.
Verified end to end through Claude Code over OAuth, on a client we didn't write: quote → submit → human approval → envelope 0.0.9762230 funded → paid on chain → swept. Testnet only by design; mainnet purchases stay off until an operator turns them on.

