Skip to content

Optional integration keys

Scani’s integrations are independently unlockable. You can enable them one at a time as you obtain keys; nothing else breaks while a key is missing.

These are read by the api and the worker, on every tier. All three backend services boot the provider registry in direct mode and call these upstreams themselves, so pointing SCANI_CLOUD_URL at a hosted data-provider does not move them — see Tier 2: you still need your provider API keys.

VariableProviderWhat it unlocks
COINGECKO_API_KEYCoinGeckoPaid-tier crypto prices (current + historical). Without a key, falls back to the public CoinGecko tier (rate-limited).
FINNHUB_API_KEYFinnhubPublic-equity prices (NYSE, NASDAQ, LSE, …).

Note: fiat / FX pricing uses Frankfurter, which requires no key.

VariableProviderWhat it unlocks
OPENAI_API_KEYOpenAIScreenshot and document parsing via Vision. Without a key the provider throws on every call, so the parse job fails — the upload itself still succeeds.

PERPLEXITY_API_KEY and DEEPSEEK_API_KEY are read by provider implementations that no backend service registers (aiPerplexityFactory and aiDeepseekFactory are exported and never passed to buildProviderRegistry). Setting them has no effect today.

The model is not configurable: gpt-5.6-luna is a constant in packages/clients/providers/src/providers/ai-openai/index.ts, used for both text and vision. It is pinned rather than merely undocumented — the token-limit parameter name, the temperature handling, whether a PDF may be sent as a file part, and the per-token pricing used for cost attribution were all measured against that model, so changing the id alone would leave four other settings wrong.

VariableProviderWhat it unlocks
ETHERSCAN_API_KEYEtherscan V2EVM wallet balances + transactions for every EVM chain V2 supports — Ethereum, Polygon, Arbitrum, Optimism, Base, BNB, etc. One key covers all of them.
HELIUS_API_KEYHeliusSolana balances and SPL token transactions.

Bitcoin, Tron, TON, and ENS resolution use public RPCs without key requirements. The provider implementations live in packages/clients/providers/src/providers/.

Most exchanges use API-key + secret credentials the user pastes into the app. Binance is the exception — it uses OAuth, which requires operator-side configuration:

VariableWhat it does
BINANCE_OAUTH_CLIENT_IDIssued when you register your deployment with Binance.
BINANCE_OAUTH_CLIENT_SECRETIssued alongside the client ID.
BINANCE_OAUTH_REDIRECT_URIThe callback URL Binance will redirect to after the user authorises. Must match what you register with Binance, e.g. https://api.scani.example.com/auth/binance/callback.

Without these set, the Binance integration is unavailable; every other exchange continues to work via the standard API-key flow.

VariableWhat it does
SENTRY_DSNServer-side error tracking. No DSN = SDK no-op; nothing is sent.
SENTRY_ENVIRONMENTOptional tag (production, staging).
SENTRY_RELEASEOptional release identifier.
VITE_SENTRY_DSNBrowser-side error tracking.
VITE_SENTRY_ENABLEDSet to true to enable client-side reporting.

Payloads are passed through packages/business/shared/src/utils/sentry-scrubber.ts before send, which strips credentials / tokens / known PII.

Cloud-management (Tier 2/3 hosted data-provider only)

Section titled “Cloud-management (Tier 2/3 hosted data-provider only)”

Ignored in Tier 1 single-tenant mode.

VariableWhat it does
CLOUD_MANAGEMENT_ENABLEDTurns on the cloud-management surface on the data-provider — DB-backed API keys, Better-Auth cookie sessions for a management console, per-request metering.
BETTER_AUTH_URLPublic URL of the data-provider (used for cookie scope on the management console).
CLOUD_FRONTEND_ORIGINOrigin of the cloud-management console (for CORS).

Don’t wait for an error — most of these never produce one. The provider registry emits one summary line at boot in every backend service, whether or not anything is degraded:

Terminal window
docker compose -f docker-compose.prod.yml logs api worker \
| grep 'provider credentials:'

Production logs are JSON (LOG_PRETTY is forced off when NODE_ENV=production), so the line arrives as a msg field:

{… "mode":"direct","msg":"✅ provider credentials: 5/5 keyed · keyed: coingecko, etherscan, finnhub, openai, solana · degraded: none"}
{… "degraded":["COINGECKO_API_KEY","OPENAI_API_KEY"],"mode":"direct","msg":"⚠️ provider credentials: 3/5 keyed · keyed: etherscan, finnhub, solana · degraded: coingecko [COINGECKO_API_KEY unset → drops to the public rate-limited tier instead of the Pro host]; openai [OPENAI_API_KEY unset → throws on every call, so screenshot and document parsing fail]"}

The degraded line also carries a degraded array of just the unset variable names, which is the cheaper thing to alert on.

The healthy line prints too, so a provider quietly dropping out of the keyed set shows up as a change rather than as silence.

The api serves the same record over HTTP:

Terminal window
docker compose -f docker-compose.prod.yml exec api \
curl -fsS http://localhost:3001/health/deep | jq .providerCredentials

An unkeyed provider deliberately does not turn /health/deep red — it is a configuration choice, not an outage. The worker has no HTTP health endpoint; its boot line is the only signal.

Routes that genuinely refuse rather than degrade return PRECONDITION_FAILED naming the variable, and the SPA renders those as a soft empty-state rather than a crash:

{ "error": { "code": "PRECONDITION_FAILED",
"message": "OPENAI_API_KEY is not configured" } }