All field notes

Tutorial

How to Integrate the SimpleFIN API: A Production-Minded Guide

Implement SimpleFIN setup-token exchange, account sync, history windows, deduplication, errors and secure storage.

By BankSync13 min read
A one-time key opening a SimpleFIN data conduit into a handmade application

SimpleFIN’s wire contract is small. The production work lives around it: claim the token once, protect the access URL, normalize signed amounts, map remote accounts, overlap date windows and make errors actionable.

Two credentials, two jobs

The setup token is temporary transport for a claim URL. Decode it, require HTTPS and POST to it exactly once. The response body is the access URL. If the claim succeeded but your app crashed before saving the result, generate a fresh setup token; retrying a consumed token cannot recover it.

The access URL is long-lived and credential-bearing. Store it on a trusted backend or in a local operating-system credential store. Never ship it to browser JavaScript, analytics, crash reports or support screenshots.

A one-time key opening a SimpleFIN data conduit into a handmade application
A one-time key opening a SimpleFIN data conduit into a handmade application

Model an idempotent importer

Persist three concepts separately: provider connection, remote-to-local account mapping and sync state. Deduplicate transactions by the stable provider transaction ID within the connection. Date + amount + description is not safe: it can collapse legitimate recurring charges and still miss corrected pending records.

Request a bounded window. The Bridge guide documents a 90-day maximum request and recommends overlapping recent days. Re-upsert records inside the overlap so a pending item can become posted without creating a duplicate. Keep raw fields alongside normalized values for audit and future migrations.

Handle transport and data errors separately

A successful HTTP response may still include a provider or institution problem. Parse version 2 errlist and connection-level errors. Track last attempt, last successful import, provider freshness and newest transaction independently. Retry transient network errors with bounded backoff; do not hammer a service whose documented cadence is periodic.

Implementation sequence

  1. Accept the setup token on a protected route

    Use CSRF protection, size limits and redacted request logs.

  2. Decode and validate

    Require HTTPS and optionally restrict claim hosts to supported providers.

  3. Claim once

    POST and capture the returned access URL without logging it.

  4. Encrypt the access URL

    Use envelope encryption or a secret manager.

  5. Discover and map accounts

    Let users explicitly link remote accounts to local accounts.

  6. Import a bounded window

    Normalize timestamps, currencies and signed amounts before writes.

  7. Schedule with jitter

    Stay within provider quotas and avoid synchronized jobs.

  8. Surface health

    Show stale connections and structured errors honestly.

Version negotiation

Request protocol v2 only where the provider documents it. Convert v1 and v2 responses into one internal model so wire vocabulary does not control your database. Unknown optional fields should be preserved or ignored safely rather than crashing the importer.

Testing without real financial data

Use synthetic fixtures for positive and negative amounts, multiple currencies, pending-to-posted transitions, two identical recurring charges, missing optional fields, structured errors and an already-claimed setup token. Assert that re-importing the same fixture creates zero new transactions. Also test that every log and exception path redacts the access URL.

Product boundaries

Let the user replace credentials without deleting local history. For hosted multi-user software, add per-tenant key separation, access audits and a documented incident path. For a personal app, an encrypted database row and strict filesystem permissions may be enough—but do not commit secrets to source control.

Tests before live data

TestExpected behaviorFailure prevented
Repeat importZero net-new recordsDuplicate transactions
Pending becomes postedOne updated recordDouble counting
Credential in exceptionFully redactedSecret leakage
Currency fixtureExact minor-unit valueRounding or unit errors
Structured errorVisible stale stateFalse green sync status
Identical recurring chargesBoth surviveFuzzy-dedupe data loss

Use synthetic fixtures only.

A good first release

Support one provider, one manual connection flow, explicit account mapping, one daily scheduled read and a download of redacted diagnostics. Resist building categorisation into the transport layer. Bank data acquisition and budgeting logic should remain separable so either can evolve.

Frequently asked questions

Primary sources