Bank connections for your app, without an aggregator
If you build a personal-finance app, you already know the wall: reading your users' bank transactions programmatically means paying an aggregator, and the entry price is out of reach for a small team. Habeas is another way in. It is free, open source, and any app can use it.
How it works
- Your user installs Habeas and enables the sources they care about (their bank, their broker).
- Your app asks for a connection. Habeas opens its own consent screen. Nothing runs until the user approves it there.
- Data arrives at your endpoint, already normalized, as new items appear. You acknowledge what you actually stored; the rest is retried.
Two ways to integrate
Pull — you fetch
You propose a specific source, and once the user approves it you can trigger collection whenever you want (a "Sync" button in your UI).
await habeas('propose-workflow', {
source: 'ing-es',
sink: {
type: 'http',
url: 'https://your-app.com/ingest',
headers: { 'x-pair-token': '…' },
},
});
// then, later:
await habeas('collect', { grantId });
Push — they send
You register only as a destination, with no source and no permission to fetch anything. The user routes whichever sources they want to you, from Habeas.
await habeas('register-sink', {
sink: {
type: 'http',
url: 'https://your-app.com/ingest',
headers: { 'x-pair-token': '…' },
},
});
Both are documented in external-hooks.md. Most apps start with pull and add push later.
What you receive
- Normalized records, not raw HTML: dates, amounts with sign, description, balance, an id for de-duplication, and the account they belong to. Each record also carries the raw source fields, so nothing is lost.
- Banking and brokerage. Transactions, and for brokers, trades and cash movements (units, price, fees, withholding, instrument).
- Only what is new. Habeas keeps a per-destination ledger, so re-syncing does not re-send what you already have.
- Per-record acknowledgment. Your endpoint replies with the ids it actually incorporated; anything else stays pending and is retried. No silent data loss.
The rules that make this safe
These are enforced in code, not promised in a contract:
- Origin-bound. The endpoint's host must equal your own origin. A site can only ever route data back to itself. There is no allowlist because there does not need to be one.
- Explicit consent. Nothing is registered or runs until the user approves it on Habeas's own screen, and an existing permission can never be widened silently.
- No credentials, ever. Habeas never reads, stores or types your user's passwords. They log in themselves, including any MFA.
- Revocable. The user can drop any integration at any time from Habeas's settings.
The bank your users need: they can add it themselves
The obvious objection to any catalog is "my users' bank isn't in it". Here's the part that matters: nobody has to wait for us to add it.
Reading happens inside the account holder's own session, which means the person who can teach Habeas a new bank is whoever already banks there — not us. So the extension ships a recorder for exactly that, in Settings → Record & contribute:
- The user browses their bank as they normally would, signed in, while it records.
- Habeas drafts the source automatically from what it saw — endpoints, paging, fields — and the user maps anything left over in a visual editor, with a Test button that runs against their live session so they see real rows before sharing anything.
- What reaches us is redacted: every value is replaced by a type placeholder and only the structure survives. We author and verify the source without ever seeing their data.
- Once published, that source works for everyone — your users and ours.
No code is written by anyone, and a source is a definition file rather than a program, which is why this can be a commons at all. If you'd rather write one by hand, that path is open too: AUTHORING-SOURCES.md and ASSISTED-AUTHORING.md.
This is where collaborating actually pays off. Adapters are shared plumbing; the product you build on top is where you compete.
What this is not
So you can decide with the real trade-offs on the table:
- It is not silent background sync. Your user must have a live session; if there is none, Habeas surfaces the login tab and waits for them. That is a design decision, not a missing feature.
- Sources need maintenance. When a service changes its site, its definition has to be updated. The difference is that you can fix it yourself instead of waiting for a vendor's roadmap.
- It runs on desktop browsers, where the extension lives.
- Each service has its own terms, and complying with them is the user's responsibility. Habeas operates on a person's own data, in their own session.
Start here
Integration guide API reference Source (AGPL-3.0)
Habeas is licensed under AGPL-3.0: if you improve it, the improvements come back to the commons. Cuéntamo was the first app to integrate it, and it is not meant to be the last.
Building something and hit the same wall? Open an issue on GitHub, or write to hola@habeas.dev.