All field notes

Explainer

SimpleFIN Explained: The Read-Only Bank Data Protocol for Builders

A practical guide to SimpleFIN’s protocol, Bridge, setup tokens, access URLs, data model, limits and best uses for hobbyist finance apps.

By BankSync11 min read
A read-only SimpleFIN window feeding transaction records into an open-source workshop

SimpleFIN starts with a narrow question: what is the smallest useful contract between a person’s financial-data provider and the app they choose? Its own analogy is “RSS for financial data.” An application can read accounts, balances, transactions and, where supplied, holdings without receiving the user’s bank password or gaining payment authority.

Protocol, provider and app are separate layers

The protocol defines the setup-token exchange and JSON shape. A provider maintains bank connections and serves the protocol. An application claims a connection, stores its access URL and imports the data. SimpleFIN Bridge is one provider; BankSync can also deliver supported financial data to SimpleFIN-compatible destinations.

This separation lets a hobbyist app focus on budgeting, reporting or automation while the provider handles bank connectivity and the user keeps control of a revocable, read-only connection.

A read-only SimpleFIN window feeding transaction records into an open-source workshop
A read-only SimpleFIN window feeding transaction records into an open-source workshop

Setup tokens and access URLs

A setup token is Base64-encoded transport for a one-time claim URL. The application decodes it and sends one POST request. The response is the long-lived access URL. The setup token should then be discarded.

The access URL contains credentials. Anyone holding it can read the data exposed by that connection, so treat it like a password: encrypt it at rest, keep it server-side, remove it from logs and never paste it into analytics or support tickets. Future reads append /accounts and bounded query parameters.

What /accounts returns

The response groups accounts beneath a provider connection and supplies stable IDs, names, currency, balances and transaction records. Transactions have a provider ID, posting time, amount and description, with optional pending and extra metadata. Version 2 uses structured errlist, renames Organization to Connection, and adds balances-only=1 plus account= filters.

SimpleFIN is pull-based, not webhook-based. The Bridge guide expects no more than about 24 requests per day, recommends jitter away from the top of the hour, limits requested windows to 90 days, and suggests a recent overlap window so late-posting records are caught.

The complete connection lifecycle

  1. Connect institutions at the provider

    The user authorises bank access with the provider rather than sharing credentials with the budgeting app.

  2. Create a setup token

    Generate one connection for one application.

  3. Claim the token once

    Decode, POST, receive the access URL and discard the setup token.

  4. Store the access URL securely

    Encrypt it and keep it out of the browser and logs.

  5. Fetch a bounded account window

    Read `/accounts`, parse errors and normalize records.

  6. Reconcile idempotently

    Map accounts, deduplicate by stable transaction ID and overlap recent days.

Why builders like the narrow design

There is no bank-by-bank SDK in the app, no payment permission and no need to invent a new import contract. Ordinary HTTP and JSON are enough. The hard parts are familiar engineering work: secret storage, signed amounts, idempotency, account mapping, time zones and honest failure states.

It works especially well for local-first dashboards, envelope budgets, ledger importers and personal automation. It is not the right primitive for instant card events, payment initiation, identity verification or guaranteed real-time data.

BankSync’s role

BankSync is useful when bank coverage comes from regional open-banking rails or other supported connectors while the destination application expects SimpleFIN. Compatibility describes the delivery contract; it does not mean every bank and account type is automatically supported. Verify coverage before promising a specific institution.

Builder checklist

AreaGood defaultWhy
Secret storageServer-side encryption and URL redactionThe access URL authorises reads
Sync cadenceDaily or a few times daily with jitterRespects provider quotas
HistoryBounded request plus rolling overlapCaptures late-posting records
DeduplicationStable provider transaction IDAvoids collapsing recurring charges
HealthLast success, source freshness and structured errorsPrevents stale data looking current

A small protocol still deserves production discipline.

First-principles recommendation

Build a manual sync before a scheduler. Make one account map correctly before importing every account. Preserve raw provider fields beside normalized data. Give the user a disconnect path that does not erase their local history. Those choices make the integration understandable when a bank, provider or app changes later.

Frequently asked questions

Primary sources