SpendOS
Spending controls for autonomous AI agents
SpendOS is autonomous financial enforcement infrastructure for AI agents on Base. Owners define a spend policy — daily limits, per-transaction caps, domain allowlists, and risk mode. Agents spend within those rules. Every payment is enforced on-chain by the SpendOSVault smart contract.
No agent can exceed its policy. No payment settles without matching the owner-signed policy digest stored on-chain. When a rule is violated, the payment is blocked — not just logged.
Chain
Base (8453)
Asset
USDC
Protocol
x402 (HTTP 402)
Agent Interface
SDK + MCP (16 tools)
How It Works
SpendOS sits between an autonomous agent and the APIs it wants to pay for. Every payment request flows through three layers: policy check, on-chain enforcement, and receipt logging.
Owner sets policy
Daily limit, per-transaction cap, allowed domains, allowed contracts, risk mode. Policy digest is signed and stored on-chain via SpendOSVault.
Agent calls a paid API
The API endpoint returns HTTP 402 Payment Required with payment requirements — amount, domain, contract address.
SpendOS checks policy
Proxy evaluates the request: Is the domain allowlisted? Is the amount within limits? Is the agent paused? Risk score updated.
On-chain settlement
SpendOSVault verifies operator authorization, checks daily limit, checks per-transaction limit, and transfers USDC to the recipient.
Receipt issued
A ReceiptRecorded event is emitted on-chain. The proxy returns a receipt ID. Agent retries the original request with X-Payment: spendos:{receiptId}.
API responds
The API verifies the receipt and returns the data. The spend is logged in the audit ledger.
Quickstart
1. Install
npm install
2. Configure
Create .env.local in the project root:
SPENDOS_DEPLOY_NETWORK=base
CHAIN_ID=8453
BASE_RPC_URL=https://mainnet.base.org
PRIVATE_KEY=0x...
SPENDOS_VAULT_ADDRESS=0x...
USDC_ADDRESS=0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913
SPENDOS_REQUIRE_AUTH=true
SPENDOS_API_KEYS=spos_live_...
Create config.local.js for the frontend (gitignored):
window.SPENDOS_CONFIG = {
apiKey: "spos_live_...",
proxyUrl: "http://127.0.0.1:4191",
vaultContract: "0x...",
network: "base",
chainId: 8453,
};
3. Start
npm start
Starts three services:
- Frontend UI →
http://127.0.0.1:4180 - Proxy API →
http://127.0.0.1:4191 - x402 demo API →
http://127.0.0.1:4192
4. Run the demo agent
npm run demo:agent
The demo agent calls three paid endpoints via x402, paying real USDC on Base mainnet for each one. Each run costs ~0.23 USDC and produces three on-chain transactions.
Check launch readiness
curl "http://127.0.0.1:4191/v1/launch/readiness?agentId=research-agent"
{
"status": "ready",
"score": { "passed": 7, "total": 7 }
}
x402 Payment Flow
SpendOS implements the x402 protocol — a standard for HTTP-native API payments.
When an agent hits a paid endpoint, the server returns 402 Payment Required with payment requirements.
The agent pays through SpendOS and retries with a receipt header.
402 Response
HTTP/1.1 402 Payment Required
Content-Type: application/json
{
"error": "payment_required",
"payment": {
"domain": "api.tokensight.io",
"contract": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"amount": 0.05,
"currency": "USDC",
"network": "base"
}
}
Auto-payment with the SDK
const result = await spendos.fetch402("http://api.tokensight.io/v1/token-price", {
task: "Get ETH price for portfolio rebalancing",
});
// result.data — the API response
// result._payment — receipt id, tx hash, amount spent
Manual payment flow
// 1. Check policy
const check = await spendos.checkPolicy({
domain: "api.tokensight.io",
contract: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
amount: 0.05,
task: "Token price lookup",
});
// 2. Pay
const payment = await spendos.payX402({
domain: "api.tokensight.io",
contract: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
amount: 0.05,
task: "Token price lookup",
idempotencyKey: "price-lookup-001",
});
// 3. Retry original request with receipt
fetch("http://api.tokensight.io/v1/token-price", {
headers: { "X-Payment": `spendos:${payment.receiptId}` }
});
Policy Engine
Every agent has a policy file — a JSON document defining its spend authority. The SHA-256 digest of this policy is signed by the owner and stored on-chain. The proxy enforces the policy off-chain; the vault enforces limits on-chain.
Policy fields
{
"agentId": "research-agent",
"dailyLimit": 10,
"perTransactionLimit": 0.25,
"domains": [
"api.tokensight.io",
"risk.baseintel.net",
"decode.calldata.run"
],
"contracts": [
"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"0x4200000000000000000000000000000000000006"
],
"riskMode": "adaptive",
"paused": false
}
Risk modes
- monitor — Log all spend, never block.
- adaptive — Block high-risk payments, approve normal ones.
- strict — Block anything outside explicit allowlists.
- enforce — Every payment requires owner approval.
Approval queue
When a payment is blocked by policy (domain not allowlisted, amount over limit, high risk score),
it enters the approval queue. The owner can approve or deny via the UI or the resolve_approval MCP tool.
SpendOSVault Contract
SpendOSVault is a minimal, dependency-free Solidity contract deployed on Base. It enforces all spend rules on-chain — no off-chain enforcement can be bypassed.
What it enforces
- Per-transaction USDC limit — rejects any single payment above the cap
- Rolling 24-hour daily limit — tracks spend in a sliding window
- Owner-signed policy digest — payment calldata must match the stored digest
- Operator delegation — only the authorized operator signer can call
spendUSDC - Pause state — owner can suspend an agent instantly
- ReceiptRecorded event — every approved payment emits a verifiable on-chain receipt
Key functions
// Register an agent vault
function registerAgent(address agentVault) external
// Fund agent with USDC
function fundAgent(address agentVault, uint256 amount) external
// Authorize a new spend policy
function authorizePolicy(
address agentVault,
bytes32 policyDigest,
uint256 dailyLimit,
uint256 perTxLimit,
bytes calldata signature
) external
// Set operator (proxy signer)
function setOperator(address operator) external
// Execute a payment (operator only)
function spendUSDC(
address agentVault,
address recipient,
uint256 amount,
bytes32 receiptHash,
bytes32 policyDigest
) external
// Emergency pause
function pauseAgent(address agentVault) external
function resumeAgent(address agentVault) external
0xC3C474a7917eCFDE5A25B64A58a190f901F9241AContract source is in
contracts/SpendOSVault.sol — no external dependencies, no upgradability proxy.
Agent SDK
A lightweight ESM client for the SpendOS proxy. Use it in custom agents, backend workers, MCP servers, or any Node.js environment.
Install
import { SpendOS } from "./sdk/spendos-agent.mjs";
const spendos = new SpendOS({
baseUrl: "http://127.0.0.1:4191",
agent: "research-agent",
apiKey: "spos_live_...",
});
Budget flow
// Reserve budget before a multi-step task
await spendos.requestBudget({
task: "Analyze 10 wallets for risk scoring",
maxSpend: 1.00,
idempotencyKey: "wallet-scan-2025-001",
});
// Check individual payments
const decision = await spendos.checkPolicy({
domain: "risk.baseintel.net",
contract: "0x4200000000000000000000000000000000000006",
amount: 0.10,
task: "Wallet risk score",
});
if (decision.status === "approved") {
await spendos.payX402({ ... });
}
All methods
fetch402(url, options)— auto x402 pay and retryrequestBudget(payload)— reserve bounded budgetcheckPolicy(payload)— evaluate before payingpayX402(payload)— authorize and record paymentupdatePolicy(payload)— update limits, allowlists, risk modepreviewSettlement(payload)— build calldata without spendingpreflightSettlement(payload)— on-chain read-only checksubmitSettlement(payload)— submit on-chain transactiongetSettlementConfig()— vault readiness and operator statusgetVaultStatus(options)— on-chain vault stategetLaunchReadiness(options)— 7-point launch checkgetReceipts(options)— spend historygetApprovals(options)— pending or resolved approvalsresolveApproval(payload)— approve or deny a requestpauseAgent(options)— suspend spend authorityresumeAgent(options)— restore spend authority
Idempotency
Pass idempotencyKey on payment and budget calls when the agent might retry.
SpendOS replays the original response instead of spending twice.
MCP Server
SpendOS exposes 16 tools via the Model Context Protocol. Connect Claude Desktop, Codex, or any MCP-compatible client to give AI models direct access to spend controls.
Config (Claude Desktop)
{
"mcpServers": {
"spendos": {
"command": "node",
"args": ["/path/to/spendos/mcp/spendos-mcp-server.mjs"],
"env": {
"SPENDOS_PROXY_URL": "http://127.0.0.1:4191",
"SPENDOS_AGENT": "research-agent",
"SPENDOS_API_KEYS": "spos_live_..."
}
}
}
}
Available tools
get_agent_file
Policy, vault metadata, daily limits, policy digest.
request_budget
Reserve bounded USDC budget for a task.
check_policy
Evaluate a payment before authorizing.
update_policy
Update limits, allowlists, risk mode, pause state.
pay_x402
Authorize an x402 API payment on-chain.
preview_settlement
Build settlement calldata without spending.
preflight_settlement
Read-only on-chain preflight before submitting.
get_settlement_config
Vault readiness, operator status, function selector.
get_vault_status
On-chain policy, balance, paused state.
get_launch_readiness
Aggregated 7-point launch check.
submit_settlement
Submit vault settlement transaction.
get_receipts
Spend history for audit and accounting.
get_approvals
Pending or resolved approval requests.
resolve_approval
Approve or deny a pending spend request.
pause_agent
Suspend spend authority immediately.
resume_agent
Restore spend authority after review.
Proxy API
All requests require X-SpendOS-Key: spos_live_... except public paths
(/health, /v1/ops/readiness, /v1/settlement/config).
| Method | Endpoint | Description |
|---|---|---|
| GET | /health | Health check |
| GET | /v1/agents/:id | Agent policy and vault metadata |
| POST | /v1/request_budget | Reserve bounded USDC budget |
| POST | /v1/check_policy | Evaluate payment before authorizing |
| POST | /v1/pay_x402 | Authorize and record payment |
| POST | /v1/update_policy | Update limits, allowlists, risk mode |
| POST | /v1/settlement/preview | Build settlement calldata (read-only) |
| POST | /v1/settlement/preflight | On-chain preflight check |
| POST | /v1/settlement/submit | Submit on-chain settlement transaction |
| GET | /v1/settlement/config | Vault readiness and operator status |
| GET | /v1/vault/status | On-chain vault state for an agent |
| GET | /v1/launch/readiness | Aggregated 7-point launch check |
| GET | /v1/receipts | Spend history for audit |
| GET | /v1/receipts/:id | Single receipt verification |
| GET | /v1/approvals | Pending or resolved approvals |
| POST | /v1/approvals/:id/resolve | Approve or deny a request |
| POST | /v1/pause_agent | Suspend agent spend authority |
| POST | /v1/resume_agent | Restore agent spend authority |
Example: check policy
curl -X POST http://127.0.0.1:4191/v1/check_policy \
-H "Content-Type: application/json" \
-H "X-SpendOS-Key: spos_live_..." \
-d '{
"agentId": "research-agent",
"domain": "api.tokensight.io",
"contract": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"amount": 0.05,
"task": "Token price lookup"
}'
{
"status": "approved",
"reason": "within_policy",
"riskScore": 12,
"dailyRemaining": 9.77
}
Contract Addresses
SpendOSVault — Base Mainnet
0xC3C474a7917eCFDE5A25B64A58a190f901F9241A
USDC — Base Mainnet
0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913
WETH — Base Mainnet
0x4200000000000000000000000000000000000006
Chain ID
8453
Environment Variables
| Variable | Description |
|---|---|
| Network | |
| BASE_RPC_URL | Base mainnet RPC URL |
| CHAIN_ID | Chain ID (8453 for mainnet) |
| PRIVATE_KEY | Owner/deployer private key |
| Vault | |
| SPENDOS_VAULT_ADDRESS | Deployed SpendOSVault address |
| USDC_ADDRESS | USDC contract address |
| SPENDOS_OPERATOR_ADDRESS | Operator signer address |
| SPENDOS_AGENT_VAULT | Agent vault wallet address |
| Policy | |
| SPENDOS_DOMAINS | Comma-separated allowed domains |
| SPENDOS_CONTRACTS | Comma-separated allowed contract addresses |
| SPENDOS_POLICY_DIGEST | Active policy digest (SHA-256) |
| Auth | |
| SPENDOS_REQUIRE_AUTH | Enable API key auth (true/false) |
| SPENDOS_API_KEYS | Comma-separated valid API keys |
| SPENDOS_ALLOWED_ORIGINS | Comma-separated CORS origins |