Repository navigation
Stats and Wallet
Two read-only namespaces: client.stats (account analytics — scope read)
and client.finance (wallet data — the opt-in finance scope, except where
noted). Neither can change anything: no generation, no organising, and — by
construction — no money movement.
The numbers behind the dashboard's home page, from code. All three methods
need only the read scope.
Storage, counters, streak, smart-collection counts and per-tool recents:
const o = await client.stats.overview();
o.storage; // { usedBytes, quotaBytes, imageBytes, videoBytes, uploadBytes, trashBytes }
o.counters; // { totalCreations, weekCreations, weekDelta, privateFolders,
// sharedFolders, favoriteFolders, trashCreations }
o.streak.days; // consecutive days with at least one creation
o.smartSummaries; // [{ id: 'faces', count }, …] — the smart-collection lenses
o.toolsRecent; // { [toolKind]: { total, recentIds: [creationId, …] } }
o.cache; // { computedAt, ageSeconds, stale }overview (and top) are served from a server-side cache rather than
recomputed per call — that's what makes them cheap enough to poll. The
cache block tells you how fresh the numbers are: stale: true means the
cache had aged out and a background refresh has been kicked — the response you
got is the previous snapshot, so re-fetch in a bit for fresher numbers.
Generation volume and spend over a window — days is 30 (default) or 90,
anything else is a 400:
const g = await client.stats.generations({ days: 30 });
g.totals; // { count, costUsd }
g.byDay; // [{ day, count, costUsd }, …]
g.byTool; // [{ toolKind, count, costUsd }, …]
g.bySource; // { machine: { count, costUsd }, studio: { count, costUsd } }bySource splits your volume between machine (SDK, MCP, raw REST — work
made through keys) and studio (the web app) — the quickest way to see what
your automation costs versus your hands-on work. Unlike overview, this is a
live aggregate; the server memoizes it for ~60 s per window, so tight loops
read the memo rather than hammering the aggregation.
What you actually use — most-used references, styles and keywords:
const t = await client.stats.top();
t.topRefs; // [{ referenceCreationId, useCount, suggestedTool, latestUsedAt,
// suggestedConfig: { quality, style, motionStyle, requestedDuration,
// isEnhanced, frameInterpolation } }, …]
t.topStyles; // [{ style, count }, …]
t.topKeywords; // [{ tag, count }, …]
t.cache; // same freshness contract as overviewsuggestedConfig is the configuration the studio would pre-fill for that
reference — your own most-common settings when using it — so an agent can
re-run "the usual" without hardcoding anything:
const fav = t.topRefs[0];
await client.generate.imageWithRefs({
tool: (fav.suggestedTool as any) ?? 'character-ref',
prompt: 'in a winter forest',
refCreationIds: [fav.referenceCreationId],
quality: fav.suggestedConfig.quality ?? undefined,
});Read-only wallet data, named 1:1 with the finance key scope. The scope is
opt-in and never pre-selected when minting a key; a key without it gets
403 MISSING_SCOPE with requiredScope: 'finance' on every method below
except the last two. And it is read-only in the strongest sense: deposits,
withdrawals, swaps and locks simply don't exist on the API-key surface, so no
key — whatever its scopes — can move funds. See
Keys & Scopes.
The balance snapshot:
const w = await client.finance.wallet();
w.tokens; // $2DAI token balance (withdrawable, via the dashboard)
w.creditUsd; // USD credit — what generations burn
w.tier; // effective tier id, 'tier0'..'tier5'
w.pendingWithdrawal; // a withdrawal is mid-flight
w.pendingSwap; // a swap is mid-flight
w.lockActive; // a staking lock is runningYour effective tier and the four legs it is the max of — useful when you want to know why you're the tier you are:
const t = await client.finance.tier();
t.effective; // e.g. 'tier3'
t.legs; // { db, watermark, holdWatermark, dynamic } — each a tier id or nulldb is an explicit assignment, watermark comes from your staking lock,
holdWatermark is the 24-hour hold that keeps your tier stable across a
re-lock or swap, and dynamic is what your live wallet value qualifies for.
The staking-lock lifecycle:
const l = await client.finance.lock();
// { kind: 'none' }
// { kind: 'active', lock: {…}, remainingMs }
// { kind: 'expiring-soon', lock: {…}, remainingMs } // under 24h left (or ended, pending the sweep)Deposit / withdrawal / swap history, newest first. page is 0-based (the
one offset in the SDK that is — it mirrors the wallet dashboard) and limit
clamps to 5..50 (default 20):
const tx = await client.finance.transactions({ limit: 20, page: 0 });
tx.transactions; // [{ id, type, status, asset: '2DAI'|'USD', amount, hash, date,
// processedDate, counterparty, reason, needsAdminReview, secondaryUsd }, …]
tx.hasMore; // fetch page + 1 for older rowsamount is a magnitude — the direction comes from type. secondaryUsd is
set on swap rows only: the USD credited alongside the debited tokens.
Chart points over a window of limit days (7..365, default 90) — token
balance for the first, USD credit for the second:
const h = await client.finance.creditHistory({ limit: 30 });
h.currentCredit; // (balanceHistory: currentBalance)
h.points; // [{ date, balance, delta, type }, …] — balance is AFTER the eventWhere credit came from — and went — over days (1..365, default 30):
const s = await client.finance.creditSources({ days: 30 });
s.accruedUsd; // earned (e.g. staking accrual)
s.bonusesUsd; // bonuses
s.swappedUsd; // swapped in from tokens
s.spentUsd; // burned on generationsThe cached $2DAI/USD quote. Machine calls never trigger a live price refresh — you get the platform's cache, with its age:
const p = await client.finance.tokenPrice();
p.usdPrice; // the quote
p.asOf; // when it was computed (or null)
p.staleMs; // how old the cache is (or null)When no cache exists yet the server answers 503 PRICE_UNAVAILABLE —
retry later rather than treating it as a zero price.
The public tier catalogue — names, USD lock-value floors and per-tier settings, the same data the pricing page shows:
const tiers = await client.finance.tiers();
// [{ key: 'tier1', name: 'Explorer', valueUSD: 300, icon?, color?, settings? }, …]A morning digest that never risks a cent:
const [o, g, w] = await Promise.all([
client.stats.overview(),
client.stats.generations({ days: 30 }),
client.finance.wallet(), // needs the finance scope
]);
console.log(
`${o.counters.totalCreations} creations (+${o.counters.weekCreations} this week), ` +
`streak ${o.streak.days}d — 30d spend $${g.totals.costUsd.toFixed(2)} ` +
`(machine ${g.bySource.machine.count} / studio ${g.bySource.studio.count}) — ` +
`credit $${w.creditUsd.toFixed(2)}`
);