← / WorkFinance · Treasury· 2026, ongoing

OpenTeller.

AI-native treasury for households and institutions that can't use Plaid.

In production. Multi-bank. iOS shipped. MCP live.
Banks supported
Any. Compiled per bank on first sync.
Per-bank compile cost
~$0.0001
Re-compile frequency
5 to 15 per bank per year
Credential vendors involved
0
MCP surfaces wired
Claude Code · Claude.ai · ChatGPT
Stack
iOS · Node · Firestore · MCP · GCR · OpenAI
/ Intro

Canada’s open banking rollout keeps slipping. The Bank of Canada was still in info-gathering as of March 2026 with no committed launch. Plaid does not solve this. Plaid is itself the data processor that compliance-sensitive buyers refuse.

OpenTeller takes the opposite shape. The bank session lives on the user’s device. The credentials never leave the phone. The extraction code is generated by an LLM exactly once per bank and then runs free forever.

/ What this fixes

Family offices, regulated SMBs, and multi-entity holdcos cannot adopt Plaid-bound aggregators. The data path is the deal breaker, not the feature set. They want one place to ask “what is our cash position across all of these accounts”, but they cannot send the underlying transactions to a third-party data processor.

OpenTeller is the architecture that lets that question get answered without that compromise.

/ How it is built

The iOS app opens the bank’s real website in a WKWebView. The user logs in normally. Cookies live in the WebView, on the device. The app does not see the password and the server does not see it either.

Once the user is on the transactions page, the app captures the DOM and sends a cleaned copy to GPT-5 with a strict JSON schema. The model returns a pure JavaScript extractor that conforms to that schema. The extractor is then verified against the same DOM that produced it, hashed, and cached locally and on the server.

Every subsequent sync runs the cached extractor against the live DOM. No LLM call. Deterministic. Free. Offline-capable. The model is invoked again only when the bank changes their UI and the cached extractor breaks. Across a year that is around five to fifteen calls per bank, not five hundred thousand.

01Device
WKWebView · Bank session · Cookie store · Generated JS extractor
02Server
Node/Express on Cloud Run · OpenAI gpt-5 · MCP server
03Storage
Firestore · Per-bank extractor cache · Transaction ledger
04Surface
iOS · Web · MCP for Claude.ai and ChatGPT
/ Key decisions

The trade-offs worth defending.

/ 01

LLM as compiler, not runtime

Frontier models are expensive at every request and almost free per bank when amortized over a year of syncs. Pay once. Cache forever. Re-compile only on UI drift.

/ 02

Sign convention applied client-side

Each bank shows a different sign convention. Some make purchases positive, some negative. The conversion to Fintrak’s convention (negative is expense) is computed on the device when building the upload, so server logic stays bank-agnostic.

/ 03

Static analysis allowlist before execution

LLM-generated JS running in a logged-in bank WebView is a real threat surface. Every cached extractor is checked against an allowlist that forbids fetch, XHR, WebSocket, eval, cookie access, and storage. Hash mismatch refuses to run.

/ 04

Two model tiers, not one

Frontier model for the code-gen step. Nano model for transaction categorization. The first matters and is rare. The second is high-volume and trivial. One model for both burns money.

/ Stack
iOS / SwiftUIWKWebViewNode + ExpressOpenAI gpt-5FirestoreMCP SDKCloud Run
First in the series