Spend caps and BYOK: paying without handing over keys

A self-custodial x402 payer keeps the signing key on the operator's machine — which means the spend caps and the price check have to be code the operator runs.

By Matthias Begot ·

An agent that pays for APIs raises three separate questions, and they are worth keeping separate: who holds the signing key, what bounds the spend, and who checks that the amount demanded is the amount published. A managed wallet platform answers all three by moving them into its own infrastructure. A self-custodial payer — bring your own key, signing locally — keeps the key on the operator’s machine and therefore has to answer the other two questions in code the operator runs. This article describes what that code has to do, and states plainly what it cannot do.

Three articles on this blog now cover adjacent ground and are deliberately not the same subject: machine-readable discovery is about how an agent finds a paid endpoint; managed agent wallets is about custody handled by a platform, seen from the seller’s side. This one is about custody and guardrails on the client side — the controls nobody can enforce on the agent’s behalf.

What an x402 signature actually authorizes

The mechanics decide the risk model, so start there. In the exact scheme on EVM — the one nearly every deployment uses — the payer signs an EIP-3009 transferWithAuthorization payload. Per the scheme specification, the authorization object carries from, to, value, validAfter, validBefore and a random nonce, and the resulting signature is one-time use, bound to a fixed amount, inside a validity window. The facilitator submits the transaction and pays the gas; the payer never touches the chain.

Two consequences follow, and they point in opposite directions.

The reassuring one: this is not a standing allowance. Unlike an ERC-20 approve, a signed x402 authorization cannot be drawn down repeatedly, and it cannot be inflated by the facilitator — the amount is committed in the signed typed data. Exposure per signature is exactly the amount in that signature.

The uncomfortable one: once the signature exists, the payer controls nothing. It is a bearer authorization. There is no “cancel” between signing and settlement, and cumulative exposure across a session is simply the sum of every amount the client agreed to sign. Which means the only real control a self-custodial payer has is the decision to refuse to sign — and that decision has to be made before the signature is produced, deterministically, outside the model.

Two custody shapes, stated without a verdict

Managed walletSelf-custodial (BYOK) payer
Key locationCustodian (CDP, Privy, Fireblocks…)Operator’s machine, in the payer process
Policy enforcementPlatform infrastructure, outside the agentClient code the operator runs
Audit trailCentralized, tied to the agent runWhatever the operator’s host logs
RevocationRevoke delegated authority at the custodianStop the process; move the funds
Blast radius on compromiseBounded by the delegation policyBounded by the wallet’s balance
DependencyThe platform must be up, and must support the railNone beyond the gateway

Neither column is the safe one. A custodian gives you revocation and a real audit trail, and gives you a third party who can be down, can change terms, or can be compromised. Self-custody removes that party and hands you the entire operational burden: the key is on a machine you administer, and the spend policy is only as good as the code enforcing it. Most operators will pick by which failure they can actually live with, not by which architecture reads better.

What does not change between them is the wire protocol. Both produce the same request, the same 402, the same signed replay — the loop described in What happens between a 402 challenge and the paid response.

The three checks that belong before a signature

A self-custodial payer that only enforces “the user set a limit somewhere” is not enforcing anything. Three independent checks are worth running on every call, each of which can only be done at the moment of signing, and each of which should be a hard refusal rather than a retry.

1. The challenge amount versus the published price

The seller announces prices on its discovery surfaces; the seller also writes the amount into the 402 challenge. Those are two different statements, and a payer that never compares them has taken the second one on faith. Comparing them is what catches a forged challenge, a compromised gateway, or a mis-priced endpoint — before any signature exists.

This is not a hypothetical worry. The May 2026 study Five Attacks on x402 Agentic Payment Protocol (Li, Wang & Wang) documents payer-side failure modes across the HTTP–chain boundary, and its guidance to payers is direct: verify that the requested amount matches published pricing rather than trusting the challenge alone. The same paper’s discovery-layer attack — adversarial or Sybil registry entries steering agents toward attacker-controlled endpoints — is the reason this check matters even when the protocol behaved correctly: an agent can be steered to the wrong seller entirely, and a price check is the last thing standing between the steering and the signature.

For a flat price the comparison is trivial. For a per-unit endpoint it is not: the legitimate amount is base + N × unit, where N is the number of items in the body the client is about to send, floored and capped by the published bounds. A payer that wants this check on batch calls has to recompute the expected price from the body it is sending — the mechanism is dissected in One settlement, N units.

2. A per-call ceiling

A per-call cap is the answer to a single catastrophic signature — the mis-parsed decimal, the endpoint that changed price by three orders of magnitude, the challenge that asks for an amount no endpoint in the catalog charges. It is a blunt instrument and that is the point: it does not need to understand the call to refuse it.

Doing the arithmetic in integer atomic units rather than floats is not pedantry here. Prices in this space run to fractions of a cent, token decimals differ per asset, and a rounding artifact in a comparison is a guardrail that silently stops guarding — a failure mode covered from the seller’s side in Token decimals: why an x402 rail stays switched off.

3. A session budget

The per-call cap bounds one signature; nothing in it bounds a loop. An agent that retries a failing task, or a task decomposed into far more sub-calls than anticipated, spends within the per-call cap every single time. A cumulative budget for the process lifetime is the only check that sees the aggregate.

One design detail decides whether the budget is honest: count at signature, not at settlement. A payment whose settlement failed still exists as an authorization someone may submit; treating it as unspent is optimism, not accounting. The corollary is that a re-presentation of the same authorization — after a failed settle — must not be counted twice, or a client near its budget refuses calls it already paid for.

Where the key lives, concretely

The Invoket MCP server (@invoket/mcp) is one implementation of the shape described above, and its choices are auditable in the open — the mechanisms below are documented in its threat model and in the MCP docs page.

  • The key is read from the process environment, once, at startup. Not from argv, not from a config file the package ships, not from the repo.
  • The environment variable is then deleted from process.env. A later state dump, a log of the environment, or a spawned subprocess no longer carries it.
  • What survives is a signing account whose key lives in closures — never a readable property of an object that could be serialized into a tool result.
  • A global output scrubber redacts key-shaped material from every log line and every error message, as a backstop rather than as the primary defense.
  • Nothing is written to disk, and there is no telemetry. The spend counters live in memory and die with the process — which also means they reset on restart, a limitation stated below rather than buried.
  • One configured origin, no arbitrary fetch. Every request goes to the single gateway origin; no model-supplied input can redirect it elsewhere.
  • Without a key, the server starts in discovery-only mode. The catalog and the schemas are browsable; paid tools refuse with an actionable message instead of failing obscurely.

Running over stdio is a security property, not just a packaging choice: the MCP specification (revision 2026-07-28, Security Best Practices) recommends that locally-run servers “use the stdio transport to limit access to just the MCP client”, precisely to prevent other local processes from reaching a server that holds capabilities. A payer process is exactly the kind of server that recommendation is about. The same document is candid that local servers are attractive targets in general — which is the honest framing for anything holding a key on a developer machine.

The flow, end to end

What actually happens on a paid call, with the guardrails in place:

  1. The MCP host launches the payer process over stdio and passes the key and the caps through the environment. Nothing is installed and nothing is hosted remotely.
  2. The payer reads the gateway’s discovery surfaces and derives one typed tool per paid endpoint — no hard-coded endpoint list, so the tool set follows the catalog. See For agents for those surfaces.
  3. The agent calls a tool. The payer sends the request unpaid first.
  4. The gateway answers 402 with the challenge: rail, asset, network, amount.
  5. Before signing, the three checks run against the challenge: expected rail and asset, amount versus the published price, per-call cap, session budget. Any breach is a refusal that names the variable to adjust — never a silent retry, never a partial signature.
  6. The payer signs the EIP-3009 authorization locally and replays the request with the payment attached.
  7. The gateway settles and returns the result. A 2xx with no payment response means the call was not charged — a free call or a trial, reported as such, per the settlement line and the zero-amount trial rail.

The seller saw an ordinary x402 exchange, identical to the one in the Quickstart. Nothing on the wire reveals where the key was, and nothing needs to.

What spend caps do not protect against

A guardrail article that only lists guardrails is marketing. These are the limits, stated as such.

  • Caps bound spend, not exposure. The hard ceiling is the balance of the wallet whose key you provided. Every serious deployment uses a dedicated, low-balance payer wallet; the caps are a convenience on top of that, not a substitute for it.
  • An in-memory session budget resets on restart. A host that respawns the process resets the counter. Anyone who needs a true daily or monthly ceiling needs it enforced somewhere that outlives a process — which is one of the things a managed wallet actually sells.
  • A cap cannot tell a good call from a bad one. A compromised or prompt-injected agent that stays under the per-call cap spends the whole session budget on calls that were never wanted. Caps limit the damage; they do not detect the intent.
  • A price check is not a trust check. Verifying the amount against a published price proves the seller charged what it advertised. It says nothing about whether the endpoint was worth calling, or whether discovery steered the agent to the wrong seller in the first place.
  • Settlement-path risks are protocol-level, not client-level. The x402 attack study describes charged-but-unserved outcomes and replayed grants across the HTTP–chain boundary. Those are properties of how a seller and its facilitator handle idempotency and finality; no client-side cap fixes them, which is a reason to care which sellers you buy from.
  • Self-custody has no revocation story. There is no third party to call. If the key is exposed, the response is to move the funds — and that is on the operator, at operator speed.

Choosing between the two

The useful question is not “which is more secure” but which failure you are staffed to handle. Reach for a managed wallet when you need revocation, a centralized audit trail, or spend policy that survives a process restart and spans many agents — and accept a dependency and a counterparty. Reach for a self-custodial payer when you want no third party in the payment path, a small auditable surface, and a wallet whose balance is the policy — and accept that the operational burden is entirely yours.

What both share is worth ending on: the seller is not part of this decision. A standard x402 endpoint is payable either way, which is exactly why the custody question belongs to the operator running the agent, and to nobody else. The prices and rails that either client checks against live on the gateway’s /catalog — this article, per house rule, quotes none of them.

Sources