Spend-policy control plane · non-custodial · a sidecar you run

A corporate card and expense policy for your AI agents.

spendctl is the spend-policy control plane for AI agents: budgets, approvals, receipts and chain tracing across every wallet and every HTTP 402 protocol — enforced in a sidecar you run, never by holding your keys or funds.

5 minutes, no keys, no chain — the mock facilitator ships in the box.

demo — 4 paid calls, 1 held, approved, exported
$ # The agent may spend $0.03/day on weather. Over that, a human decides.
$ spendctl budget
researcher  enforce · max $0.05/request · approval ≥ $0.50
  daily       $0.00 of $0.03      $0.03 left
  monthly     $0.00 of $20.00    $20.00 left
$ node ../agent.mjs &   # an agent with no keys, asking for 4 paid calls
[1] 66461
agent ✔ Tokyo: paid $0.01 — sunny, 18°C
agent ✔ Kyoto: paid $0.01 — cloudy, 19°C
agent ✔ Sapporo: paid $0.01 — light rain, 20°C
agent ‖ Osaka: spendctl is holding this request until a human decides

$ spendctl pending
ID                  AGENT       ENDPOINT                AMOUNT  EXPIRES  WHY
apr_mvmp68jvjnsmy6  researcher  localhost:4021/weather  $0.01   595s     $0.01 would exceed the daily budget of $0.03
$ spendctl approve --latest
✔ approved apr_mvmp68jvjnsmy6
agent ✔ Osaka: paid $0.01 (approved by a human) — clear, 21°C

[1]+  Done                    node ../agent.mjs
$ spendctl ledger
TIME (UTC)      ID                  AGENT       ENDPOINT                    USD    STATUS   TX / REASON
10-24 17:53:19  pay_mvmp68ju3jze4c  researcher  GET localhost:4021/weather  $0.01  settled  0x0d2aa1e97a887…
10-24 17:53:19  pay_mvmp68jbbn6exg  researcher  GET localhost:4021/weather  $0.01  settled  0x6c0acb888dc4d…
10-24 17:53:19  pay_mvmp68in2q3dka  researcher  GET localhost:4021/weather  $0.01  settled  0x8265dc5b3ae92…
10-24 17:53:19  pay_mvmp68g8nvwcj3  researcher  GET localhost:4021/weather  $0.01  settled  0xe68f3a8554452…
$ spendctl export --format csv --out receipts.csv   # for the expense report
✔ wrote 4 row(s) to receipts.csv
$
demo — 4 paid calls, 1 held, approved, exported
  1. $ spendctl budget → researcher: daily $0.00 of $0.03 · max $0.05/request · approval ≥ $0.50
  2. $ node ../agent.mjs & # an agent with no keys, asking for 4 paid calls
  3. agent ✔ Tokyo: paid $0.01 — sunny, 18°C
  4. agent ✔ Kyoto: paid $0.01 — cloudy, 19°C
  5. agent ✔ Sapporo: paid $0.01 — light rain, 20°C
  6. agent ‖ Osaka: spendctl is holding this request until a human decides
  7. $ spendctl pending → apr_mvmp68jvjnsmy6 · $0.01 · 595s · $0.01 would exceed the daily budget of $0.03
  8. $ spendctl approve --latest → ✔ approved apr_mvmp68jvjnsmy6
  9. agent ✔ Osaka: paid $0.01 (approved by a human) — clear, 21°C
  10. $ spendctl ledger → 4 rows, all settled, each with a tx hash
  11. $ spendctl export --format csv --out receipts.csv → ✔ wrote 4 row(s) to receipts.csv

Prompts are not spending controls

The x402 spec leaves client-side budgets out of scope, SDK guardrails run inside the agent's own process, and the official client will pay whatever a server asks (x402#3866). Builders are saying it out loud:

“The trust unit is the policy, not the payment.”
— novachen, Hacker News, 2026-03-15
“Asking the assistant to follow instructions more carefully does not provide a dependable spending control.”
— mschilingno, GitHub openai/codex#45536, 2026-09-14
“I gave openclaw a bitcoin wallet and $100 to spend. ... With in minutes it had spent all the budget buying fake follows on an X account that was similar to mine.”
— dangerface, Hacker News, 2026-10-20

How it works

Agent to sidecar to seller; the control plane sits off the payment pathAgentholds no keysrequestspendctl sidecaryou run it, next to the agentpolicy checklocal signing402 · paymentSellerx402 / MPPControl planepolicies, approvals,ledger · off the pathAgent to sidecar to seller; the control plane sits off the payment pathAgentholds no keysrequestspendctl sidecaryou run it,next to the agentpolicy checklocal signing402 · paymentSellerx402 / MPPControl planepolicies, approvals,ledger · off the path
1

Agent asks

Your agent calls an API through spendctl. It holds no keys.

2

Seller says 402

spendctl reads the price from x402 (v1/v2) or MPP and checks the agent's policy.

3

Policy decides

Allow, deny with a reason the model can read, or hold for a human via CLI, Slack or a review link.

4

Pay once, record

Budget is reserved atomically, your wallet signs once, the receipt and tx hash land in the ledger.

Rules it will not break

Each one is an end-to-end scenario that runs twice on every CI run.

  • A payment that would exceed a budget is never signed.
  • Hosts outside the allowlist are never paid.
  • A swapped recipient or a ×10 price jump needs a human.
  • Concurrent requests never overspend, not even by one payment.
  • An agent cannot attach, replay or approve its own payment.
  • A failed settlement is never silently retried.

Security model →

Works with

Gateway URL for any language, a zero-dependency fetch wrapper, and an MCP server.

  • x402 v1 / v2
  • Stripe MPP
  • MCP
  • Claude Agent SDK
  • OpenAI Agents SDK
  • LangGraph
  • Coinbase CDP wallets
  • Base

Pricing

Free is the full sidecar with every enforcement feature, no card. Pro and Team add the spendctl control plane — Slack approvals, hosted ledgers, scheduled exports, SSO, pooled budgets — and are founding pre-orders until it ships (in development).

Free

$0

No card. Every enforcement feature.

  • 2 agents · 1 policy · 1 seat
  • 1,000 governed payments / month
  • Local ledger with CSV / JSON export, 7-day hosted copy, local sandbox
  • CLI / webhook approvals
  • Hard stop at the cap — no overage
Get started

Team

$149/mofoundingregular $249/mo

Founding pre-order — the control plane is in development.

  • Unlimited agents
  • 500k governed events / month
  • 1-year immutable ledger
  • SSO / RBAC, pooled budgets
  • Chain trace across sidecars, policy-as-code CI, region pinning
Reserve founding price

Enterprise

Contact

By conversation.

  • SIEM export, SCIM
  • Custom retention (up to 7 years)
  • Private control plane
  • Four-eyes approvals, SLA
Talk to us

Founding: first 100 members, price locked for 12 months from GA. Overage $5 per 100k events on Pro and Team. 14-day Team trial, no card. No percentage of spend, ever.

Reserve your founding price

Pick a plan and the organisation id your licence will carry, then pay on Stripe. The key arrives by email within a minute.

2 to 40 characters: lowercase letters, digits and dashes. It is what spendctl license shows and what you quote to support; it cannot be changed later.

Payment is handled by Stripe. Founding prices are locked for 12 months from the control-plane GA; cancel any time from the Stripe receipt.

Get control plane early access

One email when the spendctl control plane opens. No newsletter.

We store your email and language only, to tell you when the control plane opens. Privacy

FAQ

Where do the keys live?

Never in the agent. The sidecar signs with a Coinbase CDP server wallet by default, so the private key stays in CDP and only an EIP-712 signature request leaves your machine. A raw key from an environment variable is opt-in. Agents talk to the sidecar with a per-agent identity and never see the admin token.

Can a prompt-injected agent make it pay?

It can make the agent ask. It cannot change the answer. Budgets, allowlists, recipient pins and approval thresholds live in a policy file the model cannot edit, evaluated in a separate process. Refusals come back as plain sentences, so the model can explain them instead of looping.

What about fake 402s and swapped recipients?

Policy is checked against the host you actually called, never the URL a 402 claims. You can pin recipients per host, cap the first payment to a new recipient, and require a human when a price jumps (e.g. more than 3× the last price for that endpoint).

Can it pay twice?

spendctl signs at most once per decision, never re-sends a signed payment after a failure, dedupes retries by Idempotency-Key, and uses the x402 payment-identifier extension when the seller supports it. Outcomes it cannot know (a timeout after the payment left) are marked unknown and count against the budget until you reconcile them.

x402 has no refunds. How do I protect myself?

Limit the blast radius before paying: allowlist hosts, pin recipients, keep per-request caps low, require approval for new recipients and price jumps, and keep the wallet balance small. The ledger keeps tx hashes and any signed receipts as evidence for disputes.

Do you hold my money?

No. Signing happens in the sidecar you run, against a wallet you own — a Coinbase CDP server wallet or your own key. spendctl never holds keys or funds; they move from your wallet straight to the seller on chain. The control plane (in development) stores policies, approvals and ledgers, never keys or funds, and never relays a signature. Not legal advice.

Is the ledger good enough for accounting?

Every row has a UTC timestamp, agent, endpoint, amount, asset, network, recipient and tx hash, exportable to CSV or JSON. The USD value assumes the stablecoin at 1:1. How USDC payments are treated for tax varies by country — please check with an accountant.

Why not just use Stripe, my wallet's policy engine, or the SDK's spend controls?

Use them too — spendctl sits in front of them. SDK controls run inside the agent and cap single payments; wallet engines see only their own keys (and often miss x402's typed-data signatures); hosted platforms govern the wallet they hold. Caps are free in Coinbase's and AWS's own tooling — what is missing is one policy, one approval flow and one ledger across all of them. spendctl governs the wallets you already have, in a sidecar you run.

x402 and MPP keep changing. Will this break?

Wire formats live in small adapters that reuse the official SDKs' codecs, both x402 v1 and v2 are parsed today, and a weekly CI job opens an issue when the upstream specs change.