All field notes

Tutorial

Build a Self-Hosted Finance App with SimpleFIN and BankSync

A practical architecture for a hobbyist bank-sync app: token claim, secure storage, account mapping, idempotent imports and scheduling.

By BankSync15 min read
A maker assembling a self-hosted SimpleFIN finance application

You do not need to become a bank-data company to build a useful finance tool. Let BankSync handle supported bank connectivity and expose a SimpleFIN-compatible feed. Your app can concentrate on a small read-only importer, durable local storage and the personal workflow you actually want.

The best first version is boring: connect, map, sync, reconcile and export. Budgets, charts and AI come later.

A maker assembling a self-hosted SimpleFIN finance application
A maker assembling a self-hosted SimpleFIN finance application

Draw the trust boundary

Keep the setup-token claim and every /accounts call in a backend or local trusted process. The browser should never see the access URL. Store the credential encrypted, with the key outside the database. If the app is single-user, a local secret store is sufficient; if hosted, separate keys and data by tenant.

Use a small domain model

You need a connections table, an accounts table and a transactions table. Store remote IDs, provider, currency and raw source JSON beside normalized values. Give transactions a unique constraint on connection + remote transaction ID. Add a sync_runs table when you need observability, not before.

Build order

  1. Implement token claim

    Decode Base64, validate HTTPS, POST once and store the access URL securely.

  2. Fetch synthetic accounts

    Use demo data before connecting a real institution.

  3. Persist remote accounts

    Keep stable IDs and require explicit local mapping.

  4. Normalize transactions

    Preserve original timestamp, amount, currency, pending state and raw data.

  5. Make import idempotent

    Upsert by provider ID and test repeated fixtures.

  6. Add bounded history and overlap

    Request no more than 90 days and re-read recent days.

  7. Schedule with jitter

    Stay under provider quotas and separate attempt from success.

  8. Add export and disconnect

    Users should be able to leave without losing their local history.

Sign conventions and money types

Never use binary floating point for money. Store decimal strings or integer minor units with the currency. Do not assume a negative amount always means “bad”: source conventions and account types matter. Define one canonical sign model and test checking, credit card, transfer and refund fixtures.

Pending transactions

Pending records can disappear, change IDs or become posted depending on provider behavior. Preserve the pending flag and use a recent overlap. If the provider offers a stable ID across state changes, upsert it; otherwise create a deliberate matching policy and surface uncertainty.

Observability without leaking data

Record connection ID, requested window, HTTP status, record counts, structured error code, duration and timestamps. Do not record the access URL or full transaction descriptions. A redacted diagnostic export is more useful than a giant raw log.

Minimal production controls

StageWhat to doCheck
SecretsEncrypt access URL and redact logsNo credential in browser
Data modelUnique remote transaction IDRepeat import is a no-op
HistoryBounded range plus overlapLate posts are caught
SchedulingJitter and backoffProvider quota respected
ErrorsParse `errlist` and show stale stateNo false success
PortabilityExport and disconnectLow exit cost

Small projects can still be robust.

Add AI last

If you expose the local database to Claude or another assistant, create a read-only query surface over a derived schema. Keep the model out of the import write path. Deterministic categorization rules are easier to audit; an assistant can suggest categories without mutating the ledger.

A weekend milestone

By Sunday evening, aim for: one claimed token, one mapped account, a 30-day import, a second zero-change re-import, a redacted sync report and a CSV export. That is a useful system. Everything after it is product design.

Frequently asked questions

Primary sources