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.
Pricing
Section titled “Pricing”Note: fiat / FX pricing uses Frankfurter, which requires no key.
AI / parsing
Section titled “AI / parsing”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.
On-chain
Section titled “On-chain”Bitcoin, Tron, TON, and ENS resolution use public RPCs without
key requirements. The provider implementations live in
packages/clients/providers/src/providers/.
Exchange OAuth
Section titled “Exchange OAuth”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:
Without these set, the Binance integration is unavailable; every other exchange continues to work via the standard API-key flow.
Sentry (error tracking)
Section titled “Sentry (error tracking)”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.
How to tell what’s enabled
Section titled “How to tell what’s enabled”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:
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:
docker compose -f docker-compose.prod.yml exec api \ curl -fsS http://localhost:3001/health/deep | jq .providerCredentialsAn 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" } }