Budgeting and PFM
The categorization is the product. Ours is included.
Users judge a budgeting app on whether the categories are right and the subscriptions are all there. That is enrichment work, and on most platforms it is a second contract. Here it is nine endpoints on the same key as the sync — rules-first, with the engine that produced each answer named in the response.
who this is for
Two years of transactions, correctly labelled.
Consumer money apps that live or die on categorization quality
what you'd call
The endpoints this actually uses.
A budgeting integration is a small number of endpoints called a lot. These are the ones you will spend your time in.
| POST | /v1/link/token/create | Start the connection for one end user |
| POST | /v1/link/token/exchange | public_token → permanent access_token |
| POST | /v1/accounts/get | Accounts, types, masks and balances |
| POST | /v1/transactions/sync | Cursor deltas: added, modified, removed |
| POST | /v1/transactions/get | Date-range backfill with count and offset |
| POST | /v1/recurring/get | Recurring inflows and outflows with next dates |
| POST | /v1/categories/get | The taxonomy, so your UI can render every branch |
| POST | /v1/ai/categorize | Category and clean merchant name per transaction |
| POST | /v1/ai/subscriptions | Detected subscriptions with monthly and annual totals |
| POST | /v1/ai/cashflow | Forecast over a 7–120 day horizon |
in code
What the integration looks like.
The sync loop first, because it is the one piece that has to be right. Then enrichment, which works on a connection or on a raw payload.
import { FeelConnect } from "@feelconnect/node"; const fc = new FeelConnect({ apiKey: process.env.FEELCONNECT_SECRET_KEY!, baseUrl: "https://connect.feels.money/v1",}); // The whole sync. Persist the cursor AFTER applying a page, so a crash// mid-loop replays that page instead of skipping it.export async function syncItem(itemId: string, accessToken: string) { let cursor = await loadCursor(itemId); for (;;) { const page = await fc.transactions.sync({ access_token: accessToken, cursor, count: 500, }); await applyChanges(page.added, page.modified, page.removed); cursor = page.next_cursor; await saveCursor(itemId, cursor); if (!page.has_more) break; }}// Categorization and subscription detection on the same key.// Both accept a raw payload instead of an access_token, so you can// enrich a CSV import or data you already hold with no connection.const { categorized, engine } = await fc.ai.categorize({ transactions: [ { id: "t1", description: "SQ *BLUE BOTTLE COFFEE OAK", amount: 6.75 }, { id: "t2", description: "COMCAST CABLE COMM", amount: 89.99 }, ],}); // engine: "rules" | "ai" | "ai+rules" — a deterministic merchant table// answers first, the model refines, and a model outage degrades to rules.console.log(engine, categorized); const subs = await fc.ai.subscriptions({ access_token: accessToken });console.log(subs.monthly_total, subs.annual_total, subs.subscriptions.length);what you get
Mechanisms, in the order you would meet them.
A sync loop that handles the hard cases
added,modifiedandremovedwith a cursor andhas_more. Pending charges that settle at a different amount, and transactions the bank withdraws, both arrive as changes rather than as a mystery.Enrichment on the same key
Categorization, merchant cleanup, subscription detection, cash-flow forecasting, health scoring and purchase analysis — nine endpoints, no second contract, metered by their own quota.Deterministic before probabilistic
A merchant rule table answers first; the model handles what the table does not know. When the model is unreachable the response degrades to rules instead of failing.A path for every user
CSV, OFX and QFX import is a real connector that runs in live mode with no provider agreement, so an unsupported institution is an extra step rather than a dead end.Webhooks so you are not polling
transactions.sync_updates_availabletells you when a sync is worth running. Signed, timestamped and retried.Crypto and brokerage in the same net worth
Holdings from brokerages, exchanges and public-address wallets land in the same shape as bank balances, so one figure covers all of it.
questions
Straight answers.
Other use cases
- AI agents and assistantsAn MCP server with 15 tools, an OpenAPI 3.1 document and an llms.txt — so an agent can discover the platform, connect an account and read real balances without you writing a tool wrapper.
- Lending and underwritingIdentity, income streams, assets and liabilities from the source, with cash-flow forecasting and anomaly signals on top.
- Accounting and bookkeepingA reconciliation-shaped sync loop with added, modified and removed sets, plus merchant cleanup and categorization on the same key.
- Crypto and wealthBrokerage holdings, exchange balances and self-custody wallets in one holdings shape, with net worth computed across all of it.
- Platform pricingPublished tiers with the real quotas: Free for the sandbox, Startup at $99 for live access, Growth at $499, Enterprise quoted.
Test the categorization before you believe it.
Paste your own messy descriptors into /v1/ai/categorize with a free sandbox key. No connection, no card, no call — just the output, next to what you would have shipped.
The sandbox needs no approval and no card. You can have the shapes in front of you in a few minutes and decide from there.