Skip to content

[META] Bank integrations: local-first bank connectivity #13052

Description

@LefterisJP

Goal

This is an actionable part of #4640 which describes how we can start integrating banks. The goal is to make rotki an all-around financial tracking tool by adding bank account support:
current balances and, most importantly, full transaction history while staying
true to rotki's architecture: local-first, privacy-preserving, no intermediaries.
No aggregator services, no rotki servers in the data path. The app talks to the
bank directly from the user's machine, or ingests files the user downloaded
themselves.

Initial market focus: Germany (best protocol situation, and we have test
accounts), with an architecture that scales to other regions via per-bank
connectors and universal file import.

Access tiers

Every bank is classified into one of five access tiers. We always implement the
highest tier available for a given bank. The tier is recorded in the connector
manifest and shown to the user.

  • Tier 0 — FinTS/HBCI: the German multi-bank protocol. One implementation
    covers essentially all traditional German banks (Sparkassen, VR banks,
    Deutsche Bank, Commerzbank, ING, DKB, comdirect, ...). Direct client-to-bank
    with the user's own credentials. Legally clean for local software (no AISP
    license needed; product registration with Deutsche Kreditwirtschaft is filed,
    ID pending). Provides balances (HKSAL), transactions (HKKAZ as MT940/camt),
    and securities depots (HKWPD).
  • Tier 1 — Official customer API: documented, supported APIs banks offer
    their own customers (e.g. Qonto Business API, bunq, Wise). Stable, keyed,
    sometimes sandboxed.
  • Tier 2 — Unofficial API: reverse-engineered mobile/web JSON APIs
    (e.g. N26). Structured but undocumented; expect occasional breakage.
  • Tier 3 — Web scraping: last resort for banks with nothing better.
    Expect frequent breakage.
  • Tier 4 — File import: universal fallback and history-depth backfill for
    every bank. 4a: standard formats (camt.052/053, MT940, OFX/QIF) parsed
    deterministically. 4b: bank-specific CSV via heuristic column mapping with
    one-time user confirmation, saved fingerprinted mapping templates, and a
    community template pack. LLM assistance is an optional accelerator, never a
    dependency.

Architecture

All tiers feed one normalization layer: a canonical even schema with incremental sync cursors, stable IDs where the source provides
them, and content-hash idempotency/dedup where it doesn't (file imports overlap
with live syncs by design — dedup is a core requirement, not an edge case).

Non-FinTS banks are implemented as connectors against a framework contract:

  • Manifest: bank identity, access tier, capabilities (balances /
    transactions / holdings), declared secrets schema (UI renders setup forms
    generically), version.
  • Auth primitives (shared vocabulary with the FinTS TAN UX):
    static_secret, username_password, otp_input, app_approval_poll
    (decoupled confirmation on a paired device), challenge_display
    (e.g. photoTAN image). Auth flows are declarative state machines over these
    primitives; the UI implements each primitive once.
  • Session persistence: tokens stored encrypted locally with refresh logic,
    so users don't re-approve every sync.
  • Error taxonomy: AuthExpired, MFARequired, RateLimited,
    SchemaDrift. SchemaDrift = the bank changed something; triggers the repair
    process.
  • Testing & repair: every connector ships redacted request/response
    fixtures (cassettes). Contract tests run all connectors against their
    cassettes in CI. On breakage: re-record against a live account, hand the
    failing diff to a coding agent, regenerate, re-run contract tests. Breakage
    is routine maintenance, not an emergency.
  • Distribution & security: connectors are versioned packages updatable
    outside the app release cycle. Secrets live in rotki's encrypted store /
    OS keychain; connectors request secrets by name and never touch storage or
    logs. Signed releases and mandatory review for community connectors.

Task list

Infrastructure

  • #___ Normalization layer: canonical bank account/transaction schema,
    sync cursors, dedup/idempotency
  • #___ Connector framework: manifest, auth primitives, secrets handling,
    error taxonomy, packaging/updates
  • #___ Cassette recording, redaction rules, contract test harness,
    repair process documentation
  • #___ Tier 4a: camt.052/053, MT940, OFX/QIF file import
  • #___ Tier 4b: CSV heuristic mapper, mapping preview UI, fingerprinted
    mapping templates, community template pack
  • #___ FinTS integration: python-fints, bank list handling, TAN/SCA UX
    (blocked on product ID for production, NOT blocked for development)
  • #___ UI: account linking flow, auth primitive dialogs, import preview,
    sync status

Some example Banks

  • #___ Qonto (Tier 1, official API) — first framework consumer
  • #___ N26 (Tier 2, unofficial API) — auth primitive stress test
  • #___ DKB (Tier 0, FinTS)
  • #___ ING (Tier 0, FinTS)

Activity

  1. stek29 commented on Sep 16, 2026

    @stek29

    jfyi: ZenMoney is an expense tracker app which is also facing this same issue, and they have a custom plugin system for it, as well as SMS/Push messages parsing. Some of their plugins are open-source, maybe you can reuse something from there if license permits.

    I'm just an active ZenMoney user starting to use rotki, saw this issue and decided to share what I know.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions