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.
Tier 1 reads these on the API and worker. Tier 2 replaces platform providers
with cloud adapters and needs only a Scani Cloud key. Setting a cloud URL alone
is not a tier switch: set SCANI_DEPLOYMENT_TIER=2 on both services.
See the Tier 2 setup guide.
Pricing
Section titled “Pricing”Note: fiat / FX pricing uses Frankfurter, which requires no key.
AI / parsing
Section titled “AI / parsing”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/.
Exchanges
Section titled “Exchanges”No exchange needs an operator-side key. Every exchange — Binance included — uses API-key + secret credentials the user pastes into the app, encrypted per user. There is nothing on this page to set for them.
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 sh -c \ 'curl -fsS -H "Authorization: Bearer $DIAGNOSTICS_TOKEN" http://localhost:3001/health/deep' \ | jq .providerCredentialsproviderCredentials needs DIAGNOSTICS_TOKEN set on the api (any
random string of 32+ characters) and sent as a bearer, as above. Without
it the endpoint answers every caller with only status and each check’s
ok, so it does not tell the internet which keys you are missing.
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.
Only AI parsing refuses a call because a key is missing, so do not
wait for any other route to tell you. A keyless pricing or chain
provider degrades silently. Starting a screenshot or document parse
with no usable AI provider returns PRECONDITION_FAILED, and nothing is
queued.