Skip to content

Keys and Scopes

2DAITeam edited this page Aug 4, 2026 · 3 revisions

Keys & Scopes

Every SDK call authenticates with an API key created in your dashboard (Integrations → API keys → + New key). Keys look like 2dai_sk_<id>_<secret>; the secret is shown once at creation. The full step-by-step (account → dashboard → dialog → storing the secret) is in Getting Started. An account can hold up to 20 active keys.

Scope → method matrix

Scope Unlocks
read me(), creations.list/random/feed/get, queue.get/waitFor, folders.list, folders.groups.list, uploads.checkDuplicate, stats.*, finance.tokenPrice/tiers, all cdn.* downloads
generate generate.* (image / refs / wallpaper / video / similar), uploads.image, queue.cancel, creations.explainNsfw/scoreFaceRef/scoreCharacterRef (their first, uncached call burns an LLM pass)
manage creations.move/trash/restore/delete/clone/like/flagNsfw/reorder/batch, folders.create/update/delete, folders.groups.create/rename/remove
publish creations.publish/unpublish
finance finance.wallet/tier/lock/transactions/balanceHistory/creditHistory/creditSources — read-only wallet data, off by default (see below)

Calling a method your key doesn't cover throws a ScopeError (HTTP 403, MISSING_SCOPE) carrying requiredScope and the key's actual scopes — e.g. a read-only key calling generate.image():

403 { error: 'MISSING_SCOPE', requiredScope: 'generate', scopes: ['read'] }

The finance scope

finance unlocks the read side of your wallet — balance, tier, staking lock, transaction history, balance/credit charts, credit sources. It exists as its own scope because that data is more sensitive than your creation library, so it follows two deliberate rules:

  • Opt-in, never default. The key dialog never pre-selects it — a key only carries finance if you ticked it at mint time. Your existing keys don't gain it retroactively either; mint a new key when you want it.
  • Read-only, structurally. There is no deposit, withdraw, swap or lock operation anywhere on the API-key surface — money mutations live in the dashboard, behind its own confirmations. A leaked finance key can therefore see your numbers, but there is no call it could make to move funds.

Two reads are deliberately outside the scope: finance.tiers() (the public tier catalogue) and finance.tokenPrice() (the cached $2DAI quote) carry no account data and only need read.

403 { error: 'MISSING_SCOPE', requiredScope: 'finance', scopes: ['read','generate'] }

The namespaces are documented in depth in Stats & Wallet.

Spend caps

A key may carry a spend cap — a USD ceiling on what that one key can burn. Your account credit is always the real limit; the cap just contains a single integration.

const me = await client.me();
const k = me.key; // KeyContext
const remaining = k.spendLimitUsd === null
  ? me.creditUsd                                   // uncapped → account credit
  : Math.min(me.creditUsd, k.spendLimitUsd - k.spentUsd);
  • Cap reached → generations throw SpendLimitError (HTTP 402, KEY_SPEND_LIMIT_REACHED) with costUsd / spentUsd / spendLimitUsd. The refusal happens before any charge — a cap of 0 blocks the very first generation:

    402 { error: 'KEY_SPEND_LIMIT_REACHED', costUsd: 0.07, spentUsd: 0, spendLimitUsd: 0 }
    
  • Account credit too low → InsufficientCreditError with deficitUsd.

  • Refunded generations (failures) are credited back to both the account and the key's spentUsd.

Key hygiene

  • One key per integration (laptop agent, CI, server) — so revoking one doesn't break the others, and the dashboard Usage tab attributes spend per key.
  • Keys are create/revoke only: to rotate, create a new key, switch your integration, then revoke the old one.
  • Revocation is instant — in-flight clients get HTTP 401 INVALID_API_KEY (AuthError) on their very next call, including read-only ones like me().
  • Store secrets in env vars (TWODAI_API_KEY), never in code or client-side bundles you ship publicly.

Clone this wiki locally