Agent wallets & spend limits
Pro- and Max-tier accounts can attach additional Solana or EVM wallets to their Candle identity, beyond the primary embedded wallet used for headless launches. A linked wallet can pay for its own self-signed launch or trade, or simply count toward attribution.
Two ways to add a wallet
Section titled “Two ways to add a wallet”| Path | Grants | Use it for |
|---|---|---|
| Import | A Candle-managed signer and a spend policy, so the agent key can sign with it | Self-signed launches, linked-payer trades |
| Link existing | Attribution only, no signer, no spend authority | Counting an already-owned wallet’s activity toward the account |
Both kinds count toward the tier’s linked-wallet cap (10 on Pro, 1,000 on Max) and are revocable at any time.
Importing a wallet
Section titled “Importing a wallet”Importing is a ciphertext-only exchange: Candle’s server never receives, stores, or logs the wallet’s plaintext private key, at any point.
The interactive path is the CLI: candle wallets import reads the key from a file or a hidden prompt and stores the generated signer in your OS keychain, so nothing sensitive ever sits in a shell history or a loose file. The SDK’s KeychainSecretStore reads that same storage, so a CLI-imported wallet is immediately usable from an agent process: pass secretStore: KeychainSecretStore.detect() ?? undefined in the client options and linked-wallet signing finds the key itself. Programmatically, the SDK’s CandleClient.importWallet({ chain, address, privateKey, signerPublicKey, label? }) does the same work locally:
POST /api/v1/agent/wallets/import/initreturns Privy’s HPKE receiver public key for the wallet’s chain and address.- Client-side, never sent to Candle. The private key is decoded to raw bytes and HPKE-sealed to that public key. Only the resulting ciphertext, an encapsulated key, and a caller-generated signer public key ever leave the calling process.
POST /api/v1/agent/wallets/import/submitposts just those three values. Candle registers a signer from the public key and attaches a default-deny spend policy to it atomically, in the same call, so an agent-controlled signer is never created without a policy already on it, then links the wallet.
{ "success": true, "id": "...", "address": "...", "chain": "solana", "privyWalletId": "..." }Linking an existing wallet
Section titled “Linking an existing wallet”POST /api/v1/agent/walletsAttaches a wallet Candle’s own Privy project already manages on the caller’s account, for attribution only (agent key or Privy session). The address is verified against the caller’s Privy account first, so an account can never claim a wallet it does not own. No signer or policy is created; there is nothing spend-capable to revoke later.
Listing and revoking
Section titled “Listing and revoking”GET /api/v1/agent/walletsLists the account’s linked wallets, active ones first.
DELETE /api/v1/agent/wallets/:idTombstones the wallet immediately; an unknown or foreign id 404s either way. For an imported (spend-capable) wallet, revocation also neutralizes its Privy spend policy back to default-deny, reported as policyNeutralized: true/false. The field is omitted for an attribution-only link, which never had a policy to neutralize.
Attribution
Section titled “Attribution”Every linked wallet, spend-capable or attribution-only, counts toward trade attribution once linked: a transaction it signs is recorded against the Candle account that linked it, exactly like a transaction from the account’s own primary wallet. This applies both to activity an agent reports itself and to trades the Hood curve-trade ingester picks up directly from the chain.
Spend limits
Section titled “Spend limits”Every agent-controlled signer, the account’s own delegated launch wallet and every spend-capable linked wallet, starts unlimited: no cap exists until the account sets one. Limits live in three layers, and the narrowest one that names an asset wins:
| Scope | Governs | Enforced |
|---|---|---|
main | The account’s own delegated wallet | Server-side, when Candle builds a launch dev buy or a trade |
linked | Every spend-capable linked wallet | Server-side at build time, and compiled into each wallet’s own Privy policy |
| Per key | One API key, per asset, via PUT /api/v1/agent/keys/:prefix/limits | Server-side at build time, replacing the account scope for any asset it names |
A key’s cap replaces the account’s for the assets it names rather than combining with them; an asset the key does not name still falls through to the account scope. A SPEND_LIMIT_EXCEEDED response says which layer bit, in spendLimit.limitSource (key or account). Only a signed-in session may loosen an effective cap; an agent key may only tighten one (LOOSEN_REQUIRES_SESSION, 403). A key can read its own effective caps with GET /api/v1/agent/keys/self/limits, which is the check to make before a large trade.
Two things this gate deliberately does not cover: sells (a sell spends the token being sold), and base-pair swaps between SOL, USDC, and CNDL, which are never spend-capped.
A separate, independent dimension is the per-key transaction limit (txLimit): a cumulative USD volume ceiling over a rolling window rather than a per-transaction cap. See transaction limits.
GET /api/v1/agent/limits{ "success": true, "main": [{ "asset": "sol", "maxPerTxRaw": "2000000000" }], "linked": null }null means unlimited for that scope.
PUT /api/v1/agent/limitscontent-type: application/json
{ "scope": "main", "limits": [{ "asset": "sol", "maxPerTxRaw": "2000000000" }] }Each limit is { "asset": "sol" | "usdc" | "cndl" | "eth" | "usdg", "maxPerTxRaw": "<raw units>" }. Send limits: null (or []) to clear a scope back to unlimited.
Tightening (a new cap, or a lower one) works from either an agent key or a Privy session. Loosening (raising or clearing a cap) requires a Privy session: an agent key that tries is rejected LOOSEN_REQUIRES_SESSION (403) before anything is written, so a compromised key can never widen its own leash.
See the Agent trading API for how a trade’s total spend, amount plus fee, is checked against the payer’s scope at build time.
The wallet manager
Section titled “The wallet manager”staging.candle.tv/dev/agent lists an account’s linked wallets with revoke, and exposes both spend-limit scopes as editors, next to the tier strip.
Errors
Section titled “Errors”| Code | Status | Meaning |
|---|---|---|
TIER_REQUIRED | 403 | Free or Believer account attempted an import, link, self-signed launch, or linked-payer trade. |
WALLET_LIMIT_REACHED | 400 | The account already has the maximum active linked wallets for its tier; revoke one first. |
WALLET_ALREADY_LINKED | 409 | The address already has an active linked-wallet row, on this account or another one. |
LOOSEN_REQUIRES_SESSION | 403 | PUT /api/v1/agent/limits loosened a cap from an agent key instead of a Privy session. |
Next steps
Section titled “Next steps”- Launch from a linked wallet: Headless launch API
- Trade from a linked wallet: Agent trading API
- The tier a wallet needs, and how to earn it: Agent access & API keys
- A typed client for the import flow: TypeScript SDK