Agent access & API keys
Any Candle account can issue its own agent API keys and launch tokens headlessly, on Solana or Hood, with no partner agreement required. The keys authenticate the Headless launch API and activity reporting; market and feed reads need no key at all.
Getting access
Section titled “Getting access”A headless launch needs a wallet that can sign server-side with no human in the loop. That is only possible for a wallet whose keys Candle’s wallet infrastructure actually holds: an embedded wallet, the kind an account gets by signing up with Google, X, or email. Getting a working key takes three steps, in order:
- Get an embedded wallet. Sign up, or add a login method, with Google, X, or email. An account that already has an embedded wallet for a chain skips this step.
- Delegate agent access. From the Agent access section on the Agents tab of the Agent Console (
alpha.candle.tv/agents?tab=keys), grant Candle’s backend permission to sign with that embedded wallet on the account’s behalf. Delegation is per chain: an account can delegate its Solana wallet, its Hood (EVM) wallet, or both independently. It is revocable at any time from the same panel. - Issue a key, as below.
Delegation is re-resolved live from the wallet provider on every launch; it is never cached or trusted from an earlier check. Revoke it and the very next launch attempt fails closed (DELEGATION_REVOKED), even though the key itself stays valid and works again the moment delegation is re-approved.
Managing keys
Section titled “Managing keys”Keys are managed from the Agent access panel or directly against the API. The management endpoints accept the normal authenticated Candle session, or a device token, on every /keys management route below. POST /agent/enable takes the session only. An agent API key is never accepted here, so a leaked key cannot mint more keys.
Re-enabling agent features
Section titled “Re-enabling agent features”Agent features are on by default. If an admin turned them off, this turns them back on:
POST /api/v1/agent/enablecontent-type: application/json
{ "enabled": true }Session only, and it requires a linked Solana wallet on the account. Only an admin can turn agent features off: { "enabled": false } is refused (403).
Issue a key
Section titled “Issue a key”POST /api/v1/agent/keyscontent-type: application/json
{ "environment": "production" }| Field | Notes |
|---|---|
environment | production (default) or test. See test keys below. |
scopes | Optional subset of launch:write, launch:read, account:read, activity:write, swap:write, transfer:write, transfer:bound, lp:write. transfer:bound requires transfer:write in the same list. Omitting scopes on this call grants launch:write, launch:read, account:read, and activity:write. swap:write, transfer:write, transfer:bound, and lp:write are granted on POST /keys only when named. A device approval that names no scopes grants launch:write, launch:read, account:read, activity:write, and swap:write (the Write level). transfer:write, lp:write, transfer:bound, and the partner pair (entitlement:read and hacc:sync) are excluded from that default. No default path grants transfer:write: a key moves funds only when it is Withdraw or names the scope. account:read reads the whole account across every profile (trades, positions, P&L, transactions, workers) and writes nothing. The three access levels below are fixed lists of these scopes. Keys issued before account:read existed keep the scopes they were issued with until their owner changes them; to give an existing integration account-wide read, change its access level to Read or Write in place. launch:read is enforced by the launch jobs endpoint; a key issued without it cannot poll job status. |
label | Optional display name, 1 to 64 characters. Renameable later. Without one, the dashboard derives a name from the key’s capabilities. |
expiresInDays | Optional integer, 1 to 365, converted to an absolute expiresAt at creation. Mutually exclusive with expiresAt. |
expiresAt | Optional absolute epoch ms, strictly in the future. Omit both for a key that never expires (the default). |
txLimit | Optional { "usdMicros": <positive integer>, "reset": "never" | "daily" | "weekly" | "monthly" }. See Transaction limits. |
embeddedWallet | Optional allowed or denied (default denied). Whether this key may use the account’s embedded wallet; see The embedded wallet: a per-key permission. |
ref | Optional referral code. Attributes the account once, best-effort, and never blocks issuance. A referred account then pays 10% less on the platform fee for every trade. |
Issuance fails VALIDATION_FAILED (403) while agent features are off, and AGENT_WALLET_NOT_DELEGATED (403) when no chain is delegated.
{ "success": true, "key": "cndl_live_...", "keyPrefix": "a1b2c3d4", "scopes": ["launch:write", "launch:read", "account:read", "activity:write"], "environment": "production", "rateLimitPerMin": 30, "dailyLaunchCap": 5, "launchChains": ["solana"], "embeddedWallet": "denied"}The response also carries issuedCaps, effectiveCaps, raisedBy and checkedAt, echoes label, expiresAt and txLimit when they were set, and, when known, apiBaseUrl: the API this key was minted against. Send the key there.
The plaintext key is returned only in this response. Store it immediately; it cannot be recovered later. keyPrefix identifies the key in listings and in the revoke call.
launchChains records which chains were delegated at issuance. It is a display snapshot only, not the authority checked at launch time; delegation can change at any moment, so every launch re-resolves it live.
An account may hold at most 12 active keys. Revoke an unused key before issuing a thirteenth; past the limit, POST /api/v1/agent/keys returns VALIDATION_FAILED with the message Active key limit reached (max 12 active keys).
Access levels
Section titled “Access levels”Every key is one of three access levels, or a custom scope set. The dashboard’s key manager (Agents tab of the Agent Console, alpha.candle.tv/agents?tab=keys) offers the three levels as presets, and the CLI mints them with candle keys create --access:
| Access | Scopes | Can | Cannot |
|---|---|---|---|
| Read | account:read | See the whole account: every profile, the books, P&L | Change anything: no trades, launches, or transfers |
| Write | launch:write, launch:read, activity:write, swap:write, account:read | Everything Read can, plus trade, swap, bridge, launch, and report activity | Move funds: no POST /transfer, candle_transfer, candle_sweep, or candle wallet close-empty |
| Withdraw | Write plus transfer:write and transfer:bound | Everything Write can, plus move funds: between the account’s own wallets, to an address on the withdrawal allowlist, and out of the TEE wallet the key is bound to, to that wallet’s pinned vault or to a wallet you linked while signed in or marked trusted | Send to any other address |
Withdraw is the only level that moves funds. Until 2026-10-08 Write also held transfer:write. A key minted as Write before then keeps its scopes, matches no level, and shows as custom: Custom: Launch, Trade, Transfer, Report in the web key manager and Launch, Trade, Transfer, Report in candle keys list. The Transfer word is the flag. Nothing is migrated. Change its access to Write to drop transfer:write (a narrowing), or to Withdraw to keep it and add transfer:bound (a widening).
candle keys create --access read --label reportercandle keys create --access write --label tradercandle keys create --access withdraw --label rebalancer--scopes <a,b,c> mints a custom set instead (never both flags). An existing key’s level can be changed in place with PUT /api/v1/agent/keys/:prefix/access (see Changing a key’s access). candle keys create also takes --label, --expires-in <days>, --tx-limit <usd> with --reset daily|weekly|monthly|never (see Transaction limits), --environment production|test, and --embedded-wallet allow|deny (default deny; see Agent wallets).
Where each path starts. The web key-create form starts on Write for every plan; pick Read or Withdraw before you create the key. candle keys create with neither --access nor --scopes sends no scopes, so the key gets the omitted-scopes set from Issue a key: launch:write, launch:read, account:read and activity:write. That is a custom set, listed as Launch, Report, and it cannot trade; pass --access write for a trading key. A device approval (candle auth login without --scopes) grants Write, as Device authorization describes. No path starts on a level that moves funds: pick Withdraw yourself when an agent needs to transfer. On every path the embedded wallet starts denied unless you allow it.
lp:write is opt-in and in no preset: name it in a custom set to let a key provide liquidity. A key whose scopes match none of the three levels is shown by its capability words instead of a level name in candle keys list (for example Trade for a key holding only swap:write). The web key manager always shows the capability chips, never a level name. candle keys list --scopes adds the raw scopes.
The documented --access values are read, write and withdraw. The older read-write, transfer and read-write-transfer values remain accepted as silent, permanent aliases.
A device approval cannot grant transfer:bound yet, so candle auth login never mints a Withdraw key. Mint one with candle keys create --access withdraw or in the web key manager, or widen an existing key to that level over the device token with PUT /api/v1/agent/keys/:prefix/access. TEE wallets and their pinned vaults are explained in Custody tiers: the vault and TEE wallets.
List and revoke
Section titled “List and revoke”GET /api/v1/agent/keysLists every key the account owns, revoked keys included, with keyPrefix, label (absent when the key was never named), scopes, environment, caps, createdAt, lastUsedAt, and revokedAt. Keys created over a device token also carry mintedByDevicePrefix, naming the device that minted them; the panel renders it as “Minted by device”. Attribution only, never authority: the field grants the device nothing.
Each row also carries a usage object (todayUsdMicros, weekUsdMicros, monthUsdMicros, lifetimeUsdMicros, days30UsdMicros, tradeCount30, launchCount30, lifetimeLaunches) beside a top-level usageSince.
DELETE /api/v1/agent/keys/:prefixRevokes one key. An unknown prefix returns 404.
Renaming, and per-key usage
Section titled “Renaming, and per-key usage”PATCH /api/v1/agent/keys/:prefixCarries exactly one of { "label" }, { "avatar" }, { "txLimit" }, or { "clearTxLimit": true } per call; sending more than one is VALIDATION_FAILED. There is deliberately no expiration field here, because expiration is fixed at creation, and no scopes field: a key’s access level is its own route, below.
GET /api/v1/agent/keys/:prefix/usage?days=NA zero-filled daily series for charting: days clamped 1 to 90 (default 30), ascending, exactly one entry per calendar day whether or not anything happened that day.
Changing a key’s access
Section titled “Changing a key’s access”PUT /api/v1/agent/keys/:prefix/accessMoves one key between the three access levels in place. The prefix, the secret, the profile, the wallets, the caps, and the txLimit stay; only the stored scopes change, to exactly the target level’s list. Any key on the account can be the starting point, a legacy or custom scope set included (scopes the target lacks are removed and named in the preview), except a key issued for a partner integration, which is refused. Widening is an owner action: this route runs on a signed-in session or the CLI device token, and an agent key is refused before the handler runs. A key can never widen itself.
Every change is a preview, then a commit that must name the scopes the preview showed:
{ "access": "readwritetransfer", "dryRun": true }{ "access": "readwritetransfer", "expectScopes": ["account:read", "activity:write", "launch:read", "launch:write", "swap:write"] }| Field | Notes |
|---|---|
access | Required. One of read, readwrite, readwritetransfer. |
dryRun | true runs every check and writes nothing. |
expectScopes | Required on a commit: the key’s stored scopes as the preview returned them, compared as a set. A mismatch is KEY_ACCESS_STALE (409) carrying the current scopes, so a change made in another tab or on another device between preview and commit is refused rather than overwritten. |
Both modes answer the same shape:
{ "success": true, "dryRun": false, "keyPrefix": "Ab3dEf9h", "label": "cndl", "direction": "widen", "from": { "access": "readwrite", "scopes": ["account:read", "activity:write", "launch:read", "launch:write", "swap:write"] }, "to": { "access": "readwritetransfer", "scopes": ["account:read", "activity:write", "launch:read", "launch:write", "swap:write", "transfer:bound", "transfer:write"] }, "added": ["transfer:bound", "transfer:write"], "removed": [], "effects": { "boundTeeWallets": { "count": 3, "sample": [{ "id": "k57…", "label": "tr-01", "address": "7xKX…gAsU" }] }, "walletScope": "all", "linkedTransferReady": { "txLimit": true, "assets": { "sol": true, "usdc": false, "cndl": false } } }, "warnings": [{ "code": "LINKED_TRANSFER_NEEDS_LIMITS", "message": "…" }], "changeId": "j97…", "at": 1790000000000}direction is decided by the server from the stored scopes: unchanged (nothing written), narrow (the target is a strict subset), or widen (anything is added, a lateral move included). from.access is null for a custom or legacy source. effects is advisory and never gates the commit; each field appears only when it matters to this direction: the TEE wallets bound to the key, the wallet scope, whether a linked-wallet transfer would be admitted today (linkedTransferReady), and, on a narrowing, the key’s open sign claims, open limit orders, and workers. warnings names each effect in words (MOVES_FUNDS, VAULT_TRANSFER_READY, LINKED_TRANSFER_NEEDS_LIMITS, BOUND_WALLETS_STOP_TRADING, OPEN_ORDERS_STRANDED, OPEN_SIGNS_REFUSED, WORKERS_STOP, DROPS_UNLISTED_SCOPES). changeId and at are present on a committed change only.
Widening to Withdraw grants its two transfer scopes (only transfer:bound from an old Write key that already holds transfer:write) and nothing else. A transfer to a linked wallet still needs a spend cap for the asset and a USD txLimit on the key, which this route does not set; a transfer to each bound wallet’s own vault needs neither and is possible as soon as the change commits. Narrowing takes effect on the key’s next request: there is no cache between the key row and the check. A Withdraw transfer built but not yet signed is refused at the sign relay once the key no longer holds transfer:bound (SCOPE_MISSING, nothing signed), and POST /transfer/submit refuses it too. Open limit orders stay open (any key on the account holding swap:write can cancel them), spend reservations stay counted, and wallets bound to the key stay bound.
| Code | HTTP | When |
|---|---|---|
VALIDATION_FAILED | 400 | malformed body, access not one of the three ids, expectScopes missing on a commit |
VALIDATION_FAILED | 404 | the prefix is not one of the caller’s keys (Key not found, the same answer for a missing key and another account’s key) |
VALIDATION_FAILED | 409 | the key is revoked (Key has been revoked) or expired (Key has expired) |
KEY_ACCESS_STALE | 409 | expectScopes differs from the stored scopes; the body carries the current scopes |
KEY_ACCESS_PARTNER_KEY | 409 | the key holds entitlement:read or hacc:sync |
LOOSEN_REQUIRES_SESSION | 403 | the self route below, and the direction is widen |
SWAP_FAILED | 503 | the change could not be verified, or a deploy is half rolled out and the two halves disagree on a level’s scopes (retryable; nothing changed) |
Every refusal writes nothing.
PUT /api/v1/agent/keys/self/accessThe same body and response, authenticated by the agent key itself (x-api-key, any scope). A key may narrow its own access and never widen it: a widening is LOOSEN_REQUIRES_SESSION (403). There is no prefix, so a key cannot change another key. It is not stopped by the kill switches or by a pause, because a rein-in must survive an incident.
GET /api/v1/agent/keys/:prefix/access-changes?limit=20One key’s access changes, newest first (limit 1 to 100), on a session or the device token. Each row carries changeId, at, direction, from and to (level and scopes), actor (session, device, or agent for the key itself), actorDevicePrefix (device only), and actorKeyPrefix (agent only). The device prefix is attribution, not a credential: a device token cannot revoke devices, so the owner revokes a named device from the Connected devices (Agents tab) panel, with a session.
Each committed change fires key.access_widened or key.access_narrowed (see the webhook events table in Agent trading). Both are new events, so an endpoint receives them only once it adds them to its event list.
Expiration
Section titled “Expiration”A key’s expiresAt is set at creation and immutable. There is no renewal call and no PATCH field for it. Once it passes, every authenticated call with that key, on any endpoint and any scope, fails KEY_EXPIRED (401) rather than the generic UNAUTHORIZED a bad key gets. The check runs after the key’s secret is verified, so a bare prefix (which can leak through a display UI) still cannot probe a key’s expiry. Recovery is always the same: issue a new key.
Transaction limits
Section titled “Transaction limits”A key’s txLimit caps its own cumulative USD-valued trading volume over the window reset names: daily (UTC day), weekly (ISO week), monthly (UTC calendar month), or never (its whole lifetime). It is checked at build time on every rail that spends (POST /api/v1/trade/agent/build, /agent/swap/build, and transfers); exceeding it is KEY_LIMIT_REACHED (403), carrying resetsAt (epoch ms, or null for a lifetime cap, which is why only a windowed cap reports retryable: true). A token sell on /trade/agent/build is the exception: it counts toward the cap when it books and is never refused by it, so a position can always be exited. A base-pair trade (a base asset against a base quote) keeps the cap on both sides. See KEY_LIMIT_REACHED in Agent trading.
An unsigned TEE transfer to a linked wallet counts until a bounded sweep page reaches and releases its hold, strictly after the hold is 30 minutes old and each unconsumed sign claim has passed its own 30-minute lifetime (and, on Hood, the operation’s expiry).
This is a different mechanism from the per-asset spend limits: a spend limit caps one asset’s raw amount on a single transaction, while txLimit caps cumulative USD volume over time. A key can carry both.
Device authorization
Section titled “Device authorization”The session-based flow above assumes a browser. Agent machines usually do not have one, so the rail also supports an OAuth-style device flow: the machine asks for access, a human approves it once from any signed-in browser, and the machine ends up holding its own key-management credential. The Candle CLI drives this end to end: install it with curl -fsSL https://candle.tv/install.sh | bash (or brew install candledottv/tap/candle), then run candle auth login.
How it works:
- The device requests a code and shows a verification URL plus a short user code.
- You open the URL in a signed-in browser. The approval screen names the client and lists the exact scopes being granted before you approve; a request that names no scopes gets
launch:write,launch:read,account:read,activity:write, andswap:write(the Write level, which cannot move funds).transfer:write,lp:writeand the partner pair (entitlement:readandhacc:sync) are excluded, and the screen says so; a request may still nametransfer:write, and the screen flags it as moving funds.transfer:boundis refused on this route even when named. - On approval the device exchanges its code for a device token (
cndl_dvc_...) and an agent API key is provisioned immediately with the approved scopes. If provisioning fails (no delegation, or the account is at its key ceiling) the token is still issued and the response says why the key was not.
A device token is deliberately narrow. It authenticates only the owner’s management routes: the key-management ones above, changing a key’s access level included, plus owner-only wallet administration such as rebinding TEE wallets to another key and marking linked wallets trusted. It cannot launch or trade (that is what the keys it mints are for), it cannot list or revoke devices, and it cannot approve another device’s login. The last two mean a stolen token can neither widen its own access nor cut off the session that could revoke it. Device tokens do not expire; revocation is the lifecycle.
Revoking a device
Section titled “Revoking a device”The Connected devices (Agents tab) panel on your Agents tab lists every device authorization with its label and last use, and groups the API keys each device minted. Revoking a device offers to also revoke those keys; accepting revokes the device and every active key it minted in a single atomic operation, so a containment action cannot be interrupted halfway. Keys you leave in place keep working: minting is attribution, not a dependency.
Using a key
Section titled “Using a key”Send the key on every headless request as the x-api-key header:
x-api-key: cndl_live_... # production keyx-api-key: cndl_test_... # test keyThe same header works on the hosted MCP server at https://mcp.candle.tv/mcp, using a client or platform that can send x-api-key through its documented secret mechanism. Hosted access is the intersection of the key’s permissions and the hosted read-tool subset; Use a dedicated key with account:read for account reads. A chat app without custom-header support can use only the public tools there.
Plans and caps
Section titled “Plans and caps”Every account is on a plan. The plan sets rate limits, daily launch caps, image-upload limits, how many wallets can be linked (see Agent wallets & spend limits), the platform fee on API-built transactions (see Headless launch API and Agent trading API), and what the account’s agents can do. Plans, pricing & limits has the full tables, generated from the API’s own plan table.
Read the plan table from the API, not from a page. GET /api/v1/agent/plans needs no key and returns the table this deployment enforces and charges, overrides included: each plan’s price, feeBps, perpFeeBps (the Hyperliquid builder fee), limits and capabilities, one boolean per plan gate, plus promoMaxDays. candle plans, the SDK’s getPlans() and the MCP tool candle_get_plans read it.
The defaults:
| Plan | Price | Requests/min | Launches/day | Uploads/min | Linked wallets | Fee |
|---|---|---|---|---|---|---|
| Free | free | 30 | 5 | 10 | 0 | 1% |
| Pro | $50 a month, or through CNDL | 300 | 50 | 30 | 10 | 0.5% |
| Max | $200 a month | 600 | 1,000 | 60 | 1,000 | 0.25% |
A web-app swap pays the same plan rate as an agent trade.
A key’s limits are at least its plan’s limits; a key issued above them keeps its higher issued limits. Every account has a live plan, re-evaluated continuously, and the cap enforced on each request is the higher of the key’s issued cap and the plan’s, so a key issued under older, lower limits reads today’s limits without being reissued, and an individually raised key is never lowered by a later plan change. The daily launch cap is per account, not per key: every launch attempt, confirmed or still in flight, consumes one slot of it, whichever key made the request.
Staking from agent wallets
Section titled “Staking from agent wallets”Agent staking uses the embedded Solana wallet or an eligible linked/TEE wallet, with swap:write.
The staking write rail is off by default. No agent surface offers instant unstake. Read the pool’s
withdrawalTimelockSeconds before starting a withdrawal; CNDL stays unavailable until finish.
| Rule | What it reads | Wallet outside the account’s primary address |
|---|---|---|
| Pro by staking or holding | CNDL and sCNDL summed over the account’s eligible Solana wallets | Counts, when only this account claims the wallet |
| Hood staking rewards | Each Solana holder mapped to the account it is an eligible wallet of, paid to that account’s EVM wallet | Earns the account’s Hood reward. A holder of no account, of more than one account, or of an account with no EVM wallet earns none, and its share goes to the others, permanently for that token |
| Solana staking rewards | The holding wallet | Earned normally; claimable by a key allowed to use that wallet |
| Pending withdrawal CNDL | Neither the CNDL nor sCNDL balance | Counts toward nothing for Pro throughout the wait |
client.staking in the SDK, candle stake in the CLI and the five staking connector tools drive
these routes. The hosted connector is embedded-wallet only and registers the tools only while
the flag is on. See the staking reference
for raw versus decimal amounts, costs, wallet selection and replay-safe retries.
Earning Pro
Section titled “Earning Pro”An account qualifies for Pro by staking 500,000 CNDL (as sCNDL) or holding 1,000,000 CNDL, whichever comes first, summed over all of the account’s Solana wallets: the primary wallet, the embedded wallet and every active linked wallet (TEE wallets included) that only this account claims. A wallet another account also claims counts for neither. At most 200 wallets count, the primary and embedded wallets first and then linked wallets oldest first. The same wallets earn Hood staking rewards for the account, in snapshots taken from 2026-10-14; earlier snapshots are final. This is checked live, continuously, not just at key issuance. There is no grace window: if the balance drops below both thresholds, the account is Free on the next check, within 5 minutes, unless it pays for Pro. Pro can also be bought, $50 a month by card or in USDC, and a paying Pro member who qualifies through CNDL stops being billed automatically.
Reaching Max
Section titled “Reaching Max”Max is self-serve: $200 a month, by card or in USDC, for a paid period that renews unless you stop it. A first Pro purchase can include a period of Max; promoMaxDays in GET /api/v1/agent/plans gives its length (30 on 2026-10-07, and 0 when no promotion runs). An admin can also grant Max manually, with an expiry date.
Which plan applies
Section titled “Which plan applies”Where a display value is needed (the /agents console’s plan line, the tier endpoint below), plans resolve in order: max > pro > free.
The Agent Console (alpha.candle.tv/agents) opens with a plan line above its tabs: the current plan, the Max grant source (subscription or trial) and, for a trial, the days left, with a link to the pricing page (/agents/pricing). The console’s Overview shows the account’s current fee rate and caps once the agent has made its first request; the Pro-qualification progress line appears there while it applies.
Agent tier snapshot
Section titled “Agent tier snapshot”GET /api/v1/agent/tier returns the calling account’s full tier picture in one call. It accepts either an agent key or a Privy session (dual auth), is never tier-gated, and stays available even when headless launches are disabled. It also carries planTable (the same table GET /api/v1/agent/plans serves), maxPricing, proPricing (null while Pro is not sold), tierCaps and checkedAt.
{ "success": true, "tier": "pro", "liveTier": "pro", "stakedCndl": 600000, "heldCndl": 0, "thresholds": { "minStakedCndl": 500000, "minHeldCndl": 1000000, "graceMs": 0 }, "grace": { "active": false, "startedAt": null, "endsAt": null }, "maxTierExpiresAt": null, "maxExpired": false, "feeBps": 50, "feeTotals": [{ "chain": "solana", "quoteAsset": "sol", "feeRawSum": "9007199254740993", "count": 3 }]}| Field | Notes |
|---|---|
tier / liveTier | tier is the display tier (max > pro > free); liveTier is the live plan. Neither reports believer. |
grace.startedAt / grace.endsAt | null unless grace.active is true; endsAt is startedAt + graceMs. There is no grace window, so grace.active is always false and thresholds.graceMs is 0. |
maxExpired | Always present: true when the account’s Max has ended. It then carries maxExpiredAt, maxExpiredSource, maxExpiredNotice (a pre-written sentence to show as is) and renewalUrl. |
maxExpiredSource | Present when maxExpired: subscription, trial, team, or promo (the Max included with a first Pro purchase has ended; not reported while the account still pays for Pro). Treat an unknown value as a generic Max expiry. |
maxTierExpiresAt | null unless the account is currently Max. A lapsed or revoked grant reports null rather than a past date, so this field never has to be date-compared. |
feeBps | The account’s resolved platform fee, in basis points, on API-built transactions. |
feeTotals[].feeRawSum | A raw-unit BigInt string (lamports, wei, and so on), never a JSON number. Format it with the quote asset’s own decimals; do not coerce it with Number(). |
Test keys
Section titled “Test keys”A test-environment key authenticates as the real account, but the server scopes what it can launch: the headless endpoint rejects a test-key request whose visibility is not test or hidden (VALIDATION_FAILED, field visibility). Use a test key for integration testing so nothing lands in the production feed by accident.
Keys issued before self-funding shipped
Section titled “Keys issued before self-funding shipped”Agent launches were previously funded by Candle service wallets. That path no longer exists: a key issued before the change cannot launch again until its owner delegates at least one chain, with no grandfathering and no platform-funded fallback.
Next steps
Section titled “Next steps”- Authorize from a terminal instead of a browser: Candle CLI
- Launch headlessly: Headless launch API
- Trade on an existing market: Agent trading API
- Import or link a wallet, set spend limits: Agent wallets & spend limits
- Get pushed the outcome instead of polling: Webhooks
- Poll markets after a launch: Market state API
- A typed client for all of it: TypeScript SDK
- Use the tools from an MCP-capable agent: Candle MCP server