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

A personal finance app is a categorization engine wearing a chart. The connection is table stakes; what users actually notice is whether “SQ *BLUE BOTTLE COFFEE OAK” becomes a coffee shop, whether the gym they forgot shows up in subscriptions, and whether last month's numbers change when they open the app tomorrow.

That means you need three things at once: a sync loop that genuinely handles modification and removal, enrichment good enough to trust by default, and a path for the user whose institution nobody supports. Buying those from three vendors is how PFM roadmaps disappear.

This platform ships all three behind one key, and the consumer app on this domain is built on exactly that — same Link flow, same cursor loop, same nine AI endpoints.

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.

Endpoints used by budgeting and personal finance apps integrations
POST/v1/link/token/createStart the connection for one end user
POST/v1/link/token/exchangepublic_token → permanent access_token
POST/v1/accounts/getAccounts, types, masks and balances
POST/v1/transactions/syncCursor deltas: added, modified, removed
POST/v1/transactions/getDate-range backfill with count and offset
POST/v1/recurring/getRecurring inflows and outflows with next dates
POST/v1/categories/getThe taxonomy, so your UI can render every branch
POST/v1/ai/categorizeCategory and clean merchant name per transaction
POST/v1/ai/subscriptionsDetected subscriptions with monthly and annual totals
POST/v1/ai/cashflowForecast 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.

tssync.ts
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;  }}
tsenrich.ts
// 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.

  1. A sync loop that handles the hard cases

    added, modified and removed with a cursor and has_more. Pending charges that settle at a different amount, and transactions the bank withdraws, both arrive as changes rather than as a mystery.
  2. 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.
  3. 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.
  4. 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.
  5. Webhooks so you are not polling

    transactions.sync_updates_available tells you when a sync is worth running. Signed, timestamped and retried.
  6. 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.

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.