Skip to main content
Idem is currently self-hosted only — every deployment needs this configuration. Managed cloud is on the roadmap; this page won’t apply once it ships.
Idem detects on-chain stablecoin transfers through three provider integrations, one per chain family: Alchemy for EVM chains, QuickNode for Solana, and Tronscan for Tron. Each needs its own environment variables set before it will do anything — an unconfigured provider doesn’t error, it just silently never detects transfers on that chain.

How detection works

EVM and Solana are event-driven — the provider pushes a webhook to Idem the moment a matching transfer happens. Tron has no webhook mechanism, so Idem polls the Tronscan REST API on a fixed interval instead. All three also run a one-time startup recovery sweep that replays anything missed since the last recorded checkpoint (for example, during a deploy) — this is not a second polling loop, just a catch-up pass. Whichever path a transfer arrives through, it only becomes a ledger entry if it matches a row in watched_addresses — see Registering which addresses to watch below. Configuring a provider is necessary but not sufficient on its own.

Alchemy (EVM — Ethereum, Base, Polygon)

These map to idem.chain.evm.rpc-url, idem.chain.evm-base.rpc-url, idem.chain.evm-polygon.rpc-url, and idem.chain.alchemy-webhook-signing-key in application.yaml, if you’re setting properties directly instead of via environment variables.
1

Set ALCHEMY_API_KEY

Leaving this blank silently disables all three EVM readers — no error at startup, they simply never activate. This is a deliberate opt-in design, not a bug, but it means a half-configured EVM setup looks identical to a fully-disabled one from the logs.
2

Create a Notify webhook in the Alchemy dashboard

Configure an “Address Activity” webhook pointed at https://<your-host>/internal/webhooks/alchemy, signed with the same value you set as ALCHEMY_WEBHOOK_SIGNING_KEY.

QuickNode (Solana)

These map to idem.chain.solana.rpc-url, idem.chain.solana.batch-size (default 100), and idem.chain.quicknode-webhook-secret.
1

Set QUICKNODE_SOLANA_URL

Same silent-disable behavior as Alchemy applies — a blank URL means no Solana reader is ever constructed.
2

Create a QuickNode Stream

Point it at https://<your-host>/internal/webhooks/quicknode, signed with QUICKNODE_WEBHOOK_SECRET.
Signature validation is HMAC-SHA256 over nonce + timestamp + rawBody — not the body alone. Idem expects all three headers together on every delivery: X-QN-Signature, X-QN-Nonce, and X-QN-Timestamp.

Tron (Tronscan REST polling)

Maps to idem.chain.tron.api-url (defaults to https://apilist.tronscan.org, so Tron detection is active out of the box) and idem.chain.tron.polling-interval-ms (default 5000). Unlike Alchemy and QuickNode, there’s no dashboard or webhook to register — Tron polling starts working as soon as the app is running.

Fail-fast on missing webhook secrets

Outside the dev Spring profile, a blank ALCHEMY_WEBHOOK_SIGNING_KEY or QUICKNODE_WEBHOOK_SECRET crashes the app at startup — this is intentional, so a production deployment can’t silently accept unsigned webhook traffic. In the dev profile only, both are optional: a blank value logs a warning and skips signature validation.

Registering which addresses to watch

Configuring providers makes Idem capable of seeing a transfer — it doesn’t tell Idem which transfers matter to you. That mapping lives in the watched_addresses table: a row ties a specific wallet address and token contract, per tenant, to the debit/credit account pair that should receive the resulting journal entry.
There is currently no tenant-facing API to register a watched address — this is an operator/database-level step today. If you’re self-hosting, your deployment administrator needs to insert the row directly; it isn’t something you can do through the REST API or a dashboard yet.

Multi-replica deployments

If you’re running more than one replica (for example, on GKE), set IDEM_SCHEDULING_DISTRIBUTED_LOCK_ENABLED=true (maps to idem.scheduling.distributed-lock.enabled, default false). This ensures only one replica runs Tron polling and the startup recovery sweep at a time, backed by a ShedLock table. Single-instance and local setups can leave this unset.

Sample configuration