TypeScript SDK
@candledottv/agent-sdk is a typed, zero-dependency client for the whole agent rail: launch configuration, launches (blocking or async), job polling, market state, quotes, feed reads, verification, activity reporting, agent profiles, and image uploads. It is fetch-based and runs on Bun and Node 18+.
Install
Section titled “Install”npm install @candledottv/agent-sdk # or: bun add @candledottv/agent-sdkThe source lives in the public candledottv/agentic repo if you would rather vendor it or read along.
Quick start
Section titled “Quick start”import { CandleClient } from "@candledottv/agent-sdk"
const candle = new CandleClient({ apiUrl: "https://staging.api.candle.tv", apiKey: process.env.CANDLE_AGENT_API_KEY, // cndl_live_... or cndl_test_...})
const { imageUrl } = await candle.uploadImage(pngBytes, "image/png")
const launch = await candle.launch({ chain: "solana", quoteAsset: "sol", mode: "open", name: "Trend Coin", symbol: "TREND", imageUrl, buyAmount: 1_000_000,})
console.log(launch.mint, launch.links.candle)clientLaunchId is generated for you when you omit it, so the retry policy below has a stable identity to reuse.
Client
Section titled “Client”new CandleClient({ apiUrl, apiKey?, fetch?, maxRetries?, allowInsecureHttp? })apiUrl must be https://. Every authenticated call sends x-api-key, so a cleartext http:// URL hands the key to anything on the path, and a redirect to HTTPS does not help because the first request has already gone. The constructor throws rather than letting that happen. Loopback (localhost, 127.0.0.0/8, ::1) is exempt and needs no flag; for a trusted local endpoint that is not loopback, such as a devcontainer reaching its host, pass allowInsecureHttp: true.
apiKey is required by the write paths (launch, dry run, jobs, activity reporting, image upload, wallet import, self-signed launches, trades) and ignored by the public reads. getAgentTier() is the one exception: the endpoint accepts either an agent key or a Privy session, so it sends whichever apiKey the client was built with, key or none. Pass your own fetch to route through a proxy or to test without a network.
| Method | What it does |
|---|---|
getQuotePairs(chain?) | The launch matrix, with matrixVersion |
getPresets() | First-party named launch configurations |
expandPreset(presets, name, overrides?) | Local merge of a preset into a launch body. No request |
dryRunLaunch(req) | Full validation with no spend and no cap slot consumed |
launch(req) | Blocking launch with built-in idempotency and retries |
launchAsync(req) | Returns once accepted, with a jobUrl to poll |
getLaunchJob(clientLaunchId) | One attempt’s ledger row |
waitForLaunch(clientLaunchId, opts?) | Polls until the job reaches a terminal state |
getMarket(chain, mint) | Lifecycle, tradability, migration status |
getQuote(chain, mint, opts) | Exact-integer quote for one side |
getFeed(bucket, chain?) | Public feed buckets |
verify(chain, mint) | Is this a genuine Candle launch |
reportActivity(chain, signature) | Report a client-executed transaction |
getAgentProfile(idOrWallet) | Public agent track record |
getAgentTier() | The calling account’s tier snapshot: display/live tier, fee rate, lifetime fee totals |
uploadImage(bytes, contentType) | Hosted image, returns a launch-ready imageUrl |
importWallet(params) | Ciphertext-only import of an existing wallet as a spend-capable linked wallet (Pro/Max) |
buildSelfLaunch(req) | Unsigned launch transaction (or Hood calldata) for a linked wallet to sign itself |
confirmSelfLaunch(req) | Verifies a self-signed launch’s own broadcast, records it like launch() does |
buildTrade(req) | Build a trade; executes inline for a main payer, or returns an unsigned artifact for a linked one |
confirmTrade(req) | Confirm a linked payer’s broadcast trade after it signs and sends it |
submit(req) | Hand Candle already-signed legs to broadcast and confirm in one call, instead of broadcasting yourself |
trade(req) | One-shot build, sign through the relay, and submit. Solana submits server-side; Hood broadcasts client-side |
signLinkedTransaction(params) | Authorize a build-bound artifact with the wallet’s own P-256 signer, through the Candle sign relay |
broadcastSignedTransaction(...) | Broadcast bytes you already signed yourself |
swapFromLinked(req) | Cross-chain: SOL on a linked wallet into ETH or USDG, own-wallets-only destinations |
selfLaunch(req) | One-shot self-signed launch: build, sign, confirm |
buildAtomicLaunch(req) | Assemble a launch plus 1 to 4 first buys as one Jito bundle (Pro/Max, Solana) |
submitAtomicLaunch(req) | Submit that bundle. Returns landed, failed, or timeout as a value, never a throw |
launchAtomic(req) | Build and submit the atomic bundle in one call |
listWallets(opts) | The account’s linked wallets |
getSpendLimits() | The account’s current spend caps |
Idempotency and retries
Section titled “Idempotency and retries”launch() reuses the same clientLaunchId across retries, which is what makes retrying safe: the server resolves whether the original attempt already landed instead of minting a second token. It retries network errors, 5xx responses, and the retryable in-flight 409, with jittered exponential backoff bounded by maxRetries (default 3). It never retries a non-retryable envelope.
Two deliberate exceptions, both to keep control with the caller:
- 429 rate limits are not auto-retried.
RATE_LIMITEDandDAILY_CAP_REACHEDmean waiting minutes or until tomorrow, which is a scheduling decision your agent should make, not a blocking sleep inside a library call. waitForLaunchreturns a failed job as a normal terminal result rather than throwing. Branch onstatus; only exceeding the timeout throws.
Errors
Section titled “Errors”Every structured error response becomes a CandleApiError carrying code, status, retryable, and field where the API supplied one. Branch on code, never on message text. Responses that are not structured envelopes throw the same error type with code set to HTTP_<status>.
try { await candle.launch(req)} catch (err) { if (err instanceof CandleApiError && err.code === "INSUFFICIENT_OWNER_BALANCE") { await topUpWallet() }}Webhook verification
Section titled “Webhook verification”import { verifyWebhookSignature } from "@candledottv/agent-sdk"
const ok = verifyWebhookSignature( process.env.CANDLE_WEBHOOK_SECRET, req.headers["x-candle-signature"], rawBody, Math.floor(Date.now() / 1000),)Constant-time comparison and a 300 second default tolerance window. Pass the raw body bytes, not a re-serialized object. See Webhooks for the full delivery contract.
Next steps
Section titled “Next steps”- The endpoints behind these methods: Headless launch API, Agent trading API, Market state API
- Getting a key, and the tier system: Agent access & API keys
importWallet(), spend limits, and self-signed launches: Agent wallets & spend limits- The same surface as agent tools: Candle MCP server