Policy reference
spendctl.yaml is validated on start; unknown keys are errors. Amounts are USD strings ("0.05", "$0.05"). Durations: "30s", "10m", "24h", "7d".
version: 1signer: { type: cdp } # mock | cdp | private-keyserver: { host: 127.0.0.1, port: 8402, connect: tunnel, upstreamTimeout: 30s }agentAuth: header # header (local dev) | token (per-agent tokens via `spendctl token <agent>`)defaultAgent: researcher # optional: agent used when a request names noneapprovals: timeout: 10m hold: true # false → return an approval id; retry with X-Spendctl-Approval webhooks: [{ url: https://…, format: slack, secretEnv: SPENDCTL_WEBHOOK_SECRET }] publicUrl: https://spendctl.internal.example # base URL for review linkscircuitBreaker: { failures: 3, cooldown: 5m }idempotencyWindow: 10massets: [] # extra priced assets, e.g. MPP token currenciesdefaults: { … agent keys … } # merged into every agent (deny lists accumulate)agents: <name>: { … agent keys … }Agent keys
Section titled “Agent keys”| Key | Meaning |
|---|---|
mode |
enforce (default) or dry-run |
budget.perRequest |
max for a single payment |
budget.hourly / daily / weekly / monthly |
rolling windows (1h, 24h, 7d, 30d) over reserved + settled + unknown spend |
onBudgetExceeded |
deny (default) or approve (hold for a human) |
requireApprovalAbove |
any single payment at or above this needs a human |
allow |
host/path patterns that may be paid; omitted = any host |
deny |
patterns that may never be paid (checked first) |
payTo |
map of host pattern → allowed recipient addresses |
protocols |
["x402", "mpp"] subset |
networks |
CAIP-2 ids, wildcards allowed: ["eip155:84532"], ["eip155:*"] |
assets |
symbols or addresses, e.g. ["USDC"] |
maxChainDepth |
max agent→agent hops (from baggage: spend.depth) |
firstPaymentMax |
first payment to a new (host, recipient) above this needs a human |
priceJumpFactor |
price above N× the last price paid for the endpoint needs a human |
maxTimeoutSeconds |
longest authorization we sign (default 900) |
Patterns
Section titled “Patterns”api.example.com any path, any portapi.example.com:8443 that port only*.example.com subdomains (not the apex)api.example.com/v1/* path prefix* everythingOrder of evaluation
Section titled “Order of evaluation”unknown agent → chain depth → circuit breaker → deny → allow → per-option filters (protocol, signable scheme, network, asset, priced, payTo, timeout) → cheapest viable option → per-request cap → time windows → approval threshold → first payment → price jump. Any deny wins over approve; a human can only waive approve reasons.