Skip to main content
Every agent gets its own onchain wallet: a USDC keypair on Base whose private key never enters the agent runtime. Signing happens in a custody plane — a Coinbase CDP TEE in the cloud, or a deterministic in-memory double for local dev — so the agent carries only an opaque handle, never key material. The wallet is read-only to the agent. It reads its address and balance; it spends through the Payments primitive (x402), bounded by a spend policy. Everything that moves the float — provision, fund, transfer, re-policy, sweep — is the operator surface.
The wallet is gated by the payments primitive, which is opt-in: it is disabled in a fresh AccountKit and must be enabled explicitly — see Account Kits. It is also per-agent: there is no company-level fallback mount, so every CLI and API call needs a subject (naive use <alice>).

CLI First

Tools

The admin verbs (create, fund, transfer, policy, sweep) are deliberately not MCP tools and not in the SDK — they are operator-surface only. See Governance.

The wallet object

naive wallet show (GET /v1/wallet) returns the agent’s wallet. Fields are camelCase; the wallet holds no key material.
custody is the backend holding the key (cdp, local, or fake), network is a CAIP-2 chain id (eip155:8453 is Base mainnet), and policy is the spend policy described below.

Reading the balance

GET /v1/wallet/balances returns a bare array (not { balances: [...] }):
amount here is a decimal USDC string. Everything in this primitive — perTxMax, dailyBudget, fund/transfer amount, --max-amount — is decimal USDC. The only place you meet atomic 6-decimal units is an x402 quote’s accepts[].amount; see Payments.

Provisioning (operator)

POST /v1/users/<user_id>/wallet. Provisioning is idempotent — re-running returns the existing wallet rather than minting a second one. --smart-account provisions the wallet as a smart account. When a --per-tx-max is supplied at create it is written into the custody plane as a static account policy (see below); otherwise the wallet gets the tenant default of 0.50 USDC. You can also declare the wallet in Agents as code so it is provisioned as part of a deploy.

Funding (operator)

POST /v1/users/<user_id>/wallet/fund. faucet works on testnets only; treasury moves USDC from the tenant treasury into the agent’s wallet. The balance is the agent’s real budget, so fund only what you can afford to lose.

Transfer (operator)

POST /v1/users/<user_id>/wallet/transfer. --to accepts a raw address or another agent’s id (to target that agent’s wallet). --purpose is recorded on the transfer for audit. Transfers are bounded by the same perTxMax that bounds agent payments.

Spend control

This is the part most worth reading. Unlike Cards and Trading, the wallet has no approval workflow — no pending_approval, no approval queue — and that is deliberate. Three controls bound every payment and transfer instead:
Why no approval prompt? perTxMax is enforced twice: once at runtime in the pay flow, and again as a static account policy inside the custody plane, checked when the payment is signed. That second check holds even if the agent runtime is fully compromised — a guarantee an approval prompt cannot make, since a compromised runtime can simply stop asking. If the custody plane can’t be reached, wallet policy fails rather than storing a cap that isn’t really enforced.
An optional lowBalanceThreshold (defaults to perTxMax) emits a wallet.balance.low event when the balance drops below it after a balance-changing op — observability only, it never blocks a spend.

Decommissioning

POST /v1/users/<user_id>/wallet/sweep moves the funds out but does not delete the wallet — the agent simply has nothing left to spend.

Governance

The wallet splits into two surfaces. Per spec §3.7 the admin verbs are operator-surface only — not agent tools: The operator verbs are gated server-side by enforceWalletAdmin, and the CLI refuses them client-side too. An agent key is refused. They are also absent from the MCP tool list and the SDK entirely — non-exposure plus an explicit gate, because the cheapest way to stop an agent moving the float is to give it no way to ask. An AccountKit can additionally deny wallet administration for a user by listing the payments.wallet.admin capability in primitives_config.payments.capabilities.deny.

Spending

An agent spends its wallet through Payments — the x402 buy-side flow — never by moving funds directly:
The effective per-call cap is min(--max-amount, perTxMax), checked (with the balance and dailyBudget) before anything is signed. See Payments for the full flow.

SDK

The SDK wallet client is read-only — it mirrors the operator/agent split. Available on the root (default user) and naive.forUser(id):
There is deliberately no fund, transfer, policy, or sweep in the SDK — those are operator-only and reachable only over REST with an operator credential.

Agents as code

Declare a wallet on an agent with has.crypto. This implies the payments slug in the allowlist and adds a crypto_wallet provisioning step.
Do not reach for limits.approve: [skills.payments.pay]. The wallet has no approval workflow, so such a rule compiles but is never enforced. Bound spend with has.crypto’s perTxMax / dailyBudget and with how much you fund the wallet.

Configuration

Network: USDC on Base (eip155:8453).
When WALLET_ENABLED is false, the crypto_wallet provisioning step resolves to needs_action — it never fails the deployment. With WALLET_PROVIDER=cdp, credentials are BYO from the tenant vault; missing creds produce a typed WalletNotConfigured error and a “connect your CDP account” prompt rather than an opaque failure.

Custody backends