Bank sync (SimpleFIN)

Connect Actual Budget, or any app that speaks the SimpleFIN protocol, to your Endute data with a single setup token. No scripts, no polling code, no key handling in the app.

SimpleFIN is an open protocol budgeting apps use to pull read-only bank data. Endute Connect implements the server side of it, so an app with native SimpleFIN support reads your connected accounts and transactions directly, with the same enrichment the REST API serves. It is a second window onto the same data as /v1: same accounts, same visibility rules, same read-only guarantee.

Connecting an app

  • In the portal, open Bank sync and create a setup token. The token is shown once and cannot be retrieved again.
  • Paste it into the app's SimpleFIN settings (in Actual Budget: Add account, then Link bank account, then SimpleFIN).
  • The app claims the token and starts syncing. Your accounts appear with their names and balances; transactions arrive with a clean merchant name as the payee.

A setup token is a credential

Treat it like a password. Each token can be claimed by exactly one app: the claim is single-use, and a second claim of the same token fails. To connect a second app, create a second token. You can hold up to 5 active tokens, and revoking one on the Bank sync page cuts that app's access immediately.

How the protocol works

You normally never touch this directly, but if you are building your own SimpleFIN client the flow is three steps:

  • The setup token is a base64-encoded, one-shot claim URL. The client POSTs it once and receives an access URL as plain text, in the form scheme://username:password@host/path.
  • Thereafter the client requests GET {access URL}/accounts using HTTP Basic auth with the embedded credentials.
  • GET {access URL}/info returns {"versions": ["1.0"]} for the protocol handshake.

The accounts response

GET /accountsjson
{
  "errors": [],
  "accounts": [
    {
      "org": { "name": "Your Bank", "id": "YOUR_BANK_GB", "sfin-url": "https://api.endute.com/simplefin" },
      "id": "3f9a2c1e-8b47-4a10-9c55-2d6f0e8a1b23",
      "name": "Everyday Current",
      "currency": "GBP",
      "balance": "1264.35",
      "balance-date": 1784480400,
      "transactions": [
        {
          "id": "9d81f0a2-5c3b-4e6d-8a71-0b2c4d6e8f01",
          "posted": 1784419200,
          "amount": "-12.40",
          "description": "CARD PURCHASE PRET A MANGER LONDON",
          "payee": "Pret a Manger",
          "memo": "",
          "transacted_at": 1784419200,
          "pending": false
        }
      ]
    }
  ]
}
Account fieldMeaning
orgThe institution: its display name, a stable id, plus our sfin-url.
idStable account id, the same id the /v1 accounts endpoint serves. Safe to use as a dedup key.
nameThe account name.
currencyISO currency code.
balanceDecimal string. "0.00" until the first balance sync completes.
balance-dateEpoch seconds: when the balance was last fetched from the bank.
Transaction fieldMeaning
idStable transaction id, identical to the id /v1 serves for the same row.
postedEpoch seconds of the booking date.
amountSigned decimal string; negative is money out. Same sign convention as /v1.
descriptionThe raw bank text, verbatim.
payeeThe enriched merchant name when available, empty string otherwise. This is what makes synced transactions readable without your own rename rules.
transacted_atEpoch seconds of the value date when the bank reports one, else the booking date.
pendingAlways false: Connect serves booked data only.

Parameters

Query parameterEffect
start-dateEpoch seconds; include transactions booked on or after this. Without it, the last 90 days are served. The total window is capped at 730 days.
end-dateEpoch seconds; include transactions booked on or before this.
accountRepeatable; limit the response to these account ids.
balances-only=1Omit the transactions arrays.

Parsing is deliberately lenient: an unknown or malformed parameter is ignored rather than rejected, so a client never breaks on a parameter it sends differently.

Same data, same rules as the API

  • The account set and transaction visibility are identical to /v1: transactions you have discarded are excluded, everything else is served, and deactivated accounts remain readable.
  • Access follows your subscription exactly like an API key does: if it lapses, requests return 403 and resume when you renew. Nothing is deleted.
  • SimpleFIN requests count toward the same 50,000-per-day account allowance as your API keys.
  • Syncing an app does not change your billing: you pay per connected account, not per window onto it. See data freshness for how current the served data is.