Mint was shut down by Intuit on January 1, 2024 — there is no Mint API and the service no longer exists. For personal finance features in a Bolt.new app, use Plaid (bank account linking and transaction data), Yodlee (enterprise financial aggregation), or build a manual budget tracker with Supabase. Plaid is the modern replacement for what Mint did programmatically.
Building Personal Finance Features in Bolt.new After Mint's Shutdown
Mint was one of the most popular personal finance tools in the United States, helping millions of users track spending, set budgets, and monitor account balances by connecting to their banks. Intuit acquired Mint in 2009 and ran it until shutting it down permanently on January 1, 2024, migrating users to Credit Karma. There is no Mint API, no Mint data export for developers, and no way to integrate Mint into a Bolt.new app — the service simply does not exist anymore.
If you arrived here looking to build Mint-like features in a Bolt.new app, you have two main paths. The first is Plaid, the financial data infrastructure platform that powers many fintech apps including Venmo, Coinbase, and thousands of others. Plaid is essentially the API that Mint used internally to connect to banks — it handles the bank authentication (OAuth or credential-based depending on the institution), data normalization across thousands of financial institutions, and provides clean JSON endpoints for accounts, transactions, balances, and identity. Plaid has a free Sandbox environment for development and a developer account that starts free. This is the closest modern equivalent to what Mint did.
The second path is building a manual budget tracker entirely within Bolt.new using Supabase as the backend. No bank connections, no external APIs — users manually enter their income and expenses, categorize them, and the app provides analytics and budget tracking. This approach is simpler, requires no third-party financial API approval, and works completely in Bolt's WebContainer preview. For many personal finance apps aimed at non-US markets (where Plaid has limited coverage) or at privacy-focused users who prefer not to link bank accounts, the manual approach is actually the better product choice. This guide covers both approaches.
Integration method
Mint no longer exists as of January 1, 2024. There is no Mint API to integrate with. The practical alternatives for personal finance features in a Bolt.new app are Plaid (for bank account linking and real transaction data), Yodlee (enterprise-grade financial aggregation), or building a manual budget tracker entirely within Bolt using Supabase. This guide focuses on Plaid as the primary replacement — it provides the bank connectivity infrastructure that Mint used under the hood, accessible through a server-side API route.
Prerequisites
- A Bolt.new project using Next.js (for API routes) and TypeScript — Plaid requires server-side API calls to protect credentials
- A Plaid developer account at dashboard.plaid.com (free to create) — get your PLAID_CLIENT_ID and PLAID_SECRET from the dashboard, and start in Sandbox mode for development
- A Supabase account (free tier) if building the manual budget tracker or storing Plaid access tokens
- Understanding that Plaid's Sandbox mode uses test credentials (username: user_good, password: pass_good) and does not connect to real banks during development
- A deployed URL on Netlify or Bolt Cloud for Plaid Link testing in production — Plaid Link works in Bolt's WebContainer preview in Sandbox mode, but Production mode requires a stable HTTPS domain
Step-by-step guide
Understand Mint's shutdown and choose between Plaid or manual tracking
Understand Mint's shutdown and choose between Plaid or manual tracking
Mint was shut down permanently on January 1, 2024. Intuit's announcement stated that 'after careful consideration, we have made the difficult decision to discontinue Mint.' All user data was deleted, accounts were terminated, and users were encouraged to migrate to Credit Karma (another Intuit product) for credit score monitoring and basic spending insights. There are no Mint APIs, no data export options for developers, and no way to connect to Mint from any application. This shutdown has a direct impact on Bolt.new developers: if you previously planned to build a product that integrated with Mint, that integration is no longer possible. However, the functionality that made Mint popular — bank account aggregation, transaction categorization, spending analysis, and budget tracking — is still very much achievable in a Bolt.new app, just through different services. Plaid is the infrastructure-level replacement for what Mint did. Plaid was the bank connectivity layer that many fintech apps (including many that competed with Mint) used to connect user bank accounts. Plaid handles the complex authentication with thousands of financial institutions through a JavaScript widget called Plaid Link. When a user connects their bank in Plaid Link, your app receives a short-lived public token, which you exchange server-side for a long-lived access token. With the access token, your API routes can fetch account balances, 24 months of transaction history, account identity data, and real-time balance updates. The manual tracking approach is worth serious consideration for several reasons. First, it requires no regulatory compliance work — Plaid and other financial data aggregators fall under GLBA and PCI DSS regulations that add compliance overhead for production apps. Second, it works for users in any country, while Plaid has strong US/Canada coverage but limited coverage in Europe and Asia. Third, many privacy-conscious users are uncomfortable linking bank credentials to new apps. A manual tracker with clear Supabase-backed data ownership can actually be a competitive advantage. For a Bolt.new development decision: start with the manual tracker approach to validate your product idea quickly — it works entirely in the Bolt WebContainer preview with no third-party approval needed. Add Plaid integration later if automatic bank data becomes a required feature.
Set up the foundation for a personal finance app in Bolt.new. Create a Supabase schema: (1) accounts table: id uuid pk, user_id uuid references auth.users(id), name text, type text check(type in ('checking','savings','investment','credit','loan','other')), institution text, is_manual bool default true, plaid_account_id text, created_at timestamptz default now(). (2) transactions table: id uuid pk, user_id uuid references auth.users(id), account_id uuid references accounts(id), amount decimal, type text check(type in ('income','expense','transfer')), category text, description text, date date not null, is_plaid bool default false, plaid_transaction_id text, created_at timestamptz default now(). (3) budget_limits table: id uuid pk, user_id uuid references auth.users(id), category text, monthly_limit decimal, unique(user_id, category). Enable RLS on all tables with policies for user_id = auth.uid().
Paste this in Bolt.new chat
1-- supabase/migrations/001_finance_app.sql2CREATE TABLE accounts (3 id UUID PRIMARY KEY DEFAULT gen_random_uuid(),4 user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE NOT NULL,5 name TEXT NOT NULL,6 type TEXT CHECK (type IN ('checking', 'savings', 'investment', 'credit', 'loan', 'other')) NOT NULL,7 institution TEXT,8 is_manual BOOLEAN DEFAULT true,9 plaid_account_id TEXT,10 created_at TIMESTAMPTZ DEFAULT now()11);1213CREATE TABLE transactions (14 id UUID PRIMARY KEY DEFAULT gen_random_uuid(),15 user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE NOT NULL,16 account_id UUID REFERENCES accounts(id) ON DELETE CASCADE,17 amount DECIMAL(12, 2) NOT NULL,18 type TEXT CHECK (type IN ('income', 'expense', 'transfer')) NOT NULL,19 category TEXT NOT NULL,20 description TEXT,21 date DATE NOT NULL,22 is_plaid BOOLEAN DEFAULT false,23 plaid_transaction_id TEXT,24 created_at TIMESTAMPTZ DEFAULT now()25);2627CREATE TABLE budget_limits (28 id UUID PRIMARY KEY DEFAULT gen_random_uuid(),29 user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE NOT NULL,30 category TEXT NOT NULL,31 monthly_limit DECIMAL(12, 2) NOT NULL,32 UNIQUE(user_id, category)33);3435-- RLS policies36ALTER TABLE accounts ENABLE ROW LEVEL SECURITY;37ALTER TABLE transactions ENABLE ROW LEVEL SECURITY;38ALTER TABLE budget_limits ENABLE ROW LEVEL SECURITY;3940CREATE POLICY "Own accounts" ON accounts USING (user_id = auth.uid()) WITH CHECK (user_id = auth.uid());41CREATE POLICY "Own transactions" ON transactions USING (user_id = auth.uid()) WITH CHECK (user_id = auth.uid());42CREATE POLICY "Own budget_limits" ON budget_limits USING (user_id = auth.uid()) WITH CHECK (user_id = auth.uid());Pro tip: Start building with the manual transaction entry approach first — you can validate the UI and data model without any third-party API approval. Add Plaid integration as an enhancement once the core budgeting features are working and you know users want bank auto-sync.
Expected result: The Supabase database has accounts, transactions, and budget_limits tables with row-level security ensuring users can only see their own financial data. The schema supports both manual entry and Plaid-synced transactions via the is_plaid flag.
Integrate Plaid Link for automatic bank account connection
Integrate Plaid Link for automatic bank account connection
Plaid Link is the JavaScript widget that handles bank authentication. When a user clicks 'Connect Bank,' Plaid Link opens as a modal overlay, guides the user through selecting their bank and authenticating (using the bank's official OAuth flow for major banks, or credential-based login for smaller institutions), and returns a short-lived public_token to your app. Your server then exchanges this public_token for a permanent access_token. The Plaid Link flow has three server-side API routes and one client component. First, GET /api/plaid/link-token calls Plaid's /link/token/create to generate a link_token tied to your client ID and the current user. The link_token expires after 30 minutes. Second, POST /api/plaid/exchange-token accepts the public_token from the client after successful Link completion and calls Plaid's /item/public_token/exchange to get the permanent access_token. Store this access_token in your database — it is the credential for all future API calls for that bank connection. Third, GET /api/plaid/accounts uses the stored access_token to call Plaid's /accounts/get and return account names, types, and current balances. Plaid's Sandbox environment provides test credentials that work without connecting to real banks. Use username user_good and password pass_good in the Plaid Link modal during development to test the complete flow. Plaid Sandbox returns realistic fake data (account balances, transactions) that mirrors real API responses. Important: Plaid has strict rules about what countries and bank connection methods are supported. In the US, most major banks use OAuth (Chase, Bank of America, Wells Fargo) — the user is redirected to their bank's website to authorize. Smaller banks use legacy credential-based authentication. In sandbox mode, all institutions use the same test credentials. When moving to production, apply for Plaid's Production access — you need to complete an application explaining your use case, and consumer-facing apps may require additional compliance review.
Set up Plaid Link integration in this Next.js app. Add PLAID_CLIENT_ID, PLAID_SECRET, and PLAID_ENV (set to 'sandbox') to .env. Create three API routes: (1) app/api/plaid/link-token/route.ts that calls POST https://sandbox.plaid.com/link/token/create with client_id, secret, user.client_user_id (use auth user id), client_name 'My Finance App', products ['transactions'], country_codes ['US'], language 'en', and returns { link_token }. (2) app/api/plaid/exchange/route.ts that accepts { public_token } in POST body, calls /item/public_token/exchange, and returns { access_token } (in production: save to DB, not return to client). (3) app/api/plaid/accounts/route.ts that calls /accounts/get with a Bearer-style POST using access_token from request header, returns account list. Create a ConnectBankButton client component that loads the Plaid Link SDK and handles the onSuccess callback.
Paste this in Bolt.new chat
1// app/api/plaid/link-token/route.ts2import { NextResponse } from 'next/server';3import { createClient } from '@/lib/supabase/server';45const PLAID_BASE = `https://${process.env.PLAID_ENV ?? 'sandbox'}.plaid.com`;67interface PlaidLinkTokenRequest {8 client_id: string;9 secret: string;10 client_name: string;11 user: { client_user_id: string };12 products: string[];13 country_codes: string[];14 language: string;15}1617export async function POST() {18 const supabase = await createClient();19 const { data: { user } } = await supabase.auth.getUser();2021 if (!user) {22 return NextResponse.json({ error: 'Not authenticated' }, { status: 401 });23 }2425 const requestBody: PlaidLinkTokenRequest = {26 client_id: process.env.PLAID_CLIENT_ID ?? '',27 secret: process.env.PLAID_SECRET ?? '',28 client_name: 'My Finance App',29 user: { client_user_id: user.id },30 products: ['transactions'],31 country_codes: ['US'],32 language: 'en',33 };3435 try {36 const response = await fetch(`${PLAID_BASE}/link/token/create`, {37 method: 'POST',38 headers: { 'Content-Type': 'application/json' },39 body: JSON.stringify(requestBody),40 });4142 if (!response.ok) {43 const error = await response.json();44 return NextResponse.json(45 { error: error.error_message ?? 'Failed to create link token' },46 { status: response.status }47 );48 }4950 const data = await response.json();51 return NextResponse.json({ link_token: data.link_token });52 } catch (error) {53 const message = error instanceof Error ? error.message : 'Request failed';54 return NextResponse.json({ error: message }, { status: 500 });55 }56}Pro tip: In Sandbox mode, use test credentials username: user_good, password: pass_good in the Plaid Link modal. Plaid Sandbox returns realistic transaction data for testing. Never use real bank credentials during development — always test exclusively in Sandbox until you are ready for production review.
Expected result: Clicking 'Connect Bank' opens the Plaid Link modal in Sandbox mode. After entering the test credentials (user_good / pass_good), the modal closes and the app receives account data. The console shows the access token returned from the exchange endpoint.
Fetch and display transactions from Plaid
Fetch and display transactions from Plaid
With a Plaid access token stored for the user's bank connection, you can now fetch real transaction data. The transactions endpoint returns up to 500 transactions per call with pagination support. Each transaction has: amount (negative = income for most institutions, positive = expense), date, name (merchant name), category (array with primary and detailed categories), account_id, and pending (boolean). Plaid's transaction data needs two important normalizations. First, amount signs vary by institution — most institutions use positive for debits (money leaving account) and negative for credits (money coming in), but this is not universal. Always check the transaction type or use Plaid's category data to determine income vs expense. Second, Plaid's category taxonomy has two levels: primary (e.g., 'Food and Drink') and detailed (e.g., 'Restaurants'). Map Plaid's categories to your app's simpler category list (Food, Housing, Transport, etc.) using a lookup table. Transactions are synced using the /transactions/get endpoint with a date range. Call it with access_token, start_date, and end_date. The endpoint returns up to 500 transactions and a total_transactions count. For more than 500 transactions, implement pagination using the offset parameter. For most personal finance apps showing 30-90 days of history, pagination is rarely needed. After fetching Plaid transactions, store them in your Supabase transactions table with is_plaid = true and the plaid_transaction_id for deduplication. On subsequent syncs, use the Transactions Sync API (/transactions/sync) which returns only new, modified, and removed transactions since your last sync — much more efficient than re-fetching the full date range each time. Critical WebContainer note: fetching Plaid transactions from a Next.js API route works in Bolt's WebContainer during development, since it is an outbound HTTP call. Plaid webhooks (which notify your app when new transactions are available) require a deployed URL and cannot be received in the WebContainer preview.
Build a transaction sync and display feature for the Plaid integration. Create app/api/plaid/transactions/route.ts that: (1) Reads PLAID_ACCESS_TOKEN from process.env (for development simplicity) or from a Supabase query, (2) Calls POST https://sandbox.plaid.com/transactions/get with client_id, secret, access_token, start_date (30 days ago), end_date (today), and options.count=50, (3) Maps each Plaid transaction to the app's transaction format (description from name, category from categories[0], amount as positive number, type as 'expense' if amount>0 else 'income'), (4) Returns the formatted transaction list. Then build a transactions page that fetches from this route and displays transactions in a list with date, description, category badge, and amount. Color positive amounts red and income amounts green.
Paste this in Bolt.new chat
1// app/api/plaid/transactions/route.ts2import { NextRequest, NextResponse } from 'next/server';34const PLAID_BASE = `https://${process.env.PLAID_ENV ?? 'sandbox'}.plaid.com`;56// Map Plaid primary categories to simplified app categories7const CATEGORY_MAP: Record<string, string> = {8 'Food and Drink': 'Food',9 'Shops': 'Shopping',10 'Travel': 'Transport',11 'Recreation': 'Entertainment',12 'Healthcare': 'Healthcare',13 'Service': 'Services',14 'Transfer': 'Transfer',15 'Payment': 'Bills',16 'Bank Fees': 'Bills',17 'Community': 'Other',18 'Income': 'Income',19 'Tax': 'Bills',20 'Cash Advance': 'Other',21};2223export async function GET(request: NextRequest) {24 // In production: get access_token from database for current user25 // For development: use env var as a shortcut26 const accessToken = process.env.PLAID_ACCESS_TOKEN;2728 if (!accessToken) {29 return NextResponse.json(30 { error: 'No Plaid access token configured. Connect a bank account first.' },31 { status: 400 }32 );33 }3435 const endDate = new Date().toISOString().split('T')[0];36 const startDate = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString().split('T')[0];3738 try {39 const response = await fetch(`${PLAID_BASE}/transactions/get`, {40 method: 'POST',41 headers: { 'Content-Type': 'application/json' },42 body: JSON.stringify({43 client_id: process.env.PLAID_CLIENT_ID,44 secret: process.env.PLAID_SECRET,45 access_token: accessToken,46 start_date: startDate,47 end_date: endDate,48 options: { count: 50, offset: 0 },49 }),50 });5152 if (!response.ok) {53 const err = await response.json();54 return NextResponse.json({ error: err.error_message }, { status: response.status });55 }5657 const data = await response.json();5859 const transactions = data.transactions.map((t: {60 transaction_id: string;61 name: string;62 amount: number;63 date: string;64 category?: string[];65 pending: boolean;66 account_id: string;67 }) => ({68 id: t.transaction_id,69 description: t.name,70 // Plaid: positive = debit (expense), negative = credit (income)71 amount: Math.abs(t.amount),72 type: t.amount > 0 ? 'expense' : 'income',73 category: CATEGORY_MAP[t.category?.[0] ?? ''] ?? 'Other',74 date: t.date,75 pending: t.pending,76 accountId: t.account_id,77 }));7879 return NextResponse.json({ transactions, total: data.total_transactions });80 } catch (error) {81 const message = error instanceof Error ? error.message : 'Request failed';82 return NextResponse.json({ error: message }, { status: 500 });83 }84}Pro tip: Plaid transaction amounts can be confusing: for checking and savings accounts, positive amounts are debits (money leaving) and negative are credits. For credit cards, the signs are the same but represent charges vs payments. Always use Math.abs(amount) for display and determine income/expense based on the amount sign and account type.
Expected result: The transactions page shows the last 30 days of transactions from the connected bank account, sorted by date. Each transaction shows the merchant name, category, and amount. Plaid Sandbox returns realistic fake transactions for test accounts.
Build the budget dashboard with spending charts
Build the budget dashboard with spending charts
Whether using Plaid for automatic transaction sync or manual entry, the budget dashboard is where financial data becomes actionable. The dashboard shows monthly spending by category, progress toward budget limits, monthly income vs expenses summary, and a spending trend chart. Spending by category is a Supabase aggregation query using .select('category, amount.sum()') filtered by the current month's date range. Group the results by category, compare against the budget_limits table, and calculate how much of each category's budget has been used as a percentage. Recharts is the recommended charting library for Bolt.new apps — it is a React component library that works without any native dependencies, making it fully compatible with the WebContainer. Use PieChart for spending distribution (what percentage of spending goes to each category), BarChart for budget vs actual comparison (each category as a bar group showing budget limit in gray and actual spending in blue/red), and LineChart for the spending trend over the last 6 months. The monthly summary card shows three numbers: total income for the month, total expenses for the month, and net (income - expenses). Color code the net as green (positive) or red (negative). This is the core 'at a glance' summary that Mint users relied on. For manual budget tracker users (no Plaid), the transaction entry form is the primary data input. Build it as a modal form with fields: amount (number input), type (income/expense radio), category (select dropdown with the standard categories), description (text), and date (date picker defaulting to today). Client-side validation with Zod ensures amounts are positive numbers and required fields are filled. The form calls a Server Action to insert into the transactions table and revalidates the dashboard page.
Build a budget dashboard page at app/budget/page.tsx. It should: (1) Fetch the current month's transactions from Supabase grouped by category, (2) Fetch budget_limits for the current user, (3) Display a PieChart (Recharts) showing spending distribution by category, (4) Display a BarChart comparing budget limit (gray bar) vs actual spending (colored bar) per category — color the bar red if over budget, blue if under, (5) Show a monthly summary card with total income, total expenses, and net savings, (6) Show a transaction entry modal form (trigger with 'Add Transaction' button) with amount, type, category, description, and date fields using react-hook-form and zod validation, calling a server action on submit. Use Tailwind CSS for layout.
Paste this in Bolt.new chat
1// app/budget/actions.ts2'use server';3import { createClient } from '@/lib/supabase/server';4import { revalidatePath } from 'next/cache';5import { z } from 'zod';67const TransactionSchema = z.object({8 amount: z.number().positive('Amount must be greater than 0'),9 type: z.enum(['income', 'expense']),10 category: z.string().min(1, 'Category is required'),11 description: z.string().optional(),12 date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, 'Invalid date format'),13});1415export async function addTransaction(input: z.infer<typeof TransactionSchema>) {16 const supabase = await createClient();17 const { data: { user } } = await supabase.auth.getUser();18 if (!user) return { error: 'Not authenticated' };1920 const parsed = TransactionSchema.safeParse(input);21 if (!parsed.success) {22 return { error: parsed.error.errors[0].message };23 }2425 const { error } = await supabase.from('transactions').insert({26 user_id: user.id,27 amount: parsed.data.amount,28 type: parsed.data.type,29 category: parsed.data.category,30 description: parsed.data.description ?? null,31 date: parsed.data.date,32 is_plaid: false,33 });3435 if (error) return { error: error.message };3637 revalidatePath('/budget');38 return { success: true };39}4041export async function getMonthlySummary(userId: string, year: number, month: number) {42 const supabase = await createClient();43 const startDate = `${year}-${String(month).padStart(2, '0')}-01`;44 const endDate = new Date(year, month, 0).toISOString().split('T')[0];4546 const { data } = await supabase47 .from('transactions')48 .select('amount, type, category')49 .eq('user_id', userId)50 .gte('date', startDate)51 .lte('date', endDate);5253 const income = data?.filter((t) => t.type === 'income').reduce((sum, t) => sum + Number(t.amount), 0) ?? 0;54 const expenses = data?.filter((t) => t.type === 'expense').reduce((sum, t) => sum + Number(t.amount), 0) ?? 0;5556 const byCategory = data?.reduce((acc: Record<string, number>, t) => {57 if (t.type === 'expense') {58 acc[t.category] = (acc[t.category] ?? 0) + Number(t.amount);59 }60 return acc;61 }, {}) ?? {};6263 return { income, expenses, net: income - expenses, byCategory };64}Pro tip: Install Recharts in your Bolt project by prompting: 'Install recharts and create a spending pie chart component.' Recharts is a pure JavaScript library with no native dependencies, making it fully compatible with Bolt's WebContainer. The PieChart, BarChart, and LineChart components work out of the box.
Expected result: The budget dashboard shows a pie chart of spending by category, a bar chart comparing budget limits to actual spending (bars turn red when over budget), and a monthly summary card. The 'Add Transaction' button opens a form for manual entry. Charts update after each transaction is added.
Common use cases
Bank Account Dashboard Using Plaid Link
A personal finance dashboard that connects the user's bank accounts via Plaid's Link widget, then displays real account balances and recent transactions. Plaid handles the bank authentication flow entirely — the user logs into their bank inside the Plaid modal, and your app receives the account data through Plaid's API. Account balances and transactions are fetched server-side through Next.js API routes.
Build a bank account dashboard using Plaid. Create Next.js API routes: (1) /api/plaid/link-token that calls Plaid's /link/token/create endpoint using PLAID_CLIENT_ID and PLAID_SECRET from process.env to generate a link token for the current user, (2) /api/plaid/exchange-token that accepts a public_token from the frontend and exchanges it for an access_token via Plaid's /item/public_token/exchange endpoint, storing the access_token in the database, (3) /api/plaid/transactions that calls Plaid's /transactions/get endpoint with the stored access_token and returns the last 30 days of transactions. In the frontend, load the Plaid Link JavaScript SDK and open it with the link token when user clicks 'Connect Bank'.
Copy this prompt to try it in Bolt.new
Manual Budget Tracker with Supabase
A fully manual personal finance tracker where users log income and expenses, set monthly budget limits per category, and see charts of their spending patterns. No bank connection required — pure data entry with visualizations. Built entirely with Supabase and Recharts in Bolt.new, works completely in the preview without deployment.
Build a personal budget tracker with Supabase. Create tables: transactions (id, user_id, amount decimal, type text check(type in ('income','expense')), category text, description text, date date, created_at timestamptz), budget_limits (id, user_id, category text, monthly_limit decimal, unique(user_id, category)). Build: (1) a transaction entry form with fields for amount, type, category (dropdown: Food, Housing, Transport, Entertainment, Healthcare, Other), description, and date, (2) a monthly summary page showing total income, total expenses, and net savings, (3) a spending by category chart using Recharts PieChart, (4) a budget vs actual bar chart comparing monthly_limit against actual spending per category. Use Supabase Auth for user sessions.
Copy this prompt to try it in Bolt.new
Net Worth Tracker with Manual Account Balances
A privacy-first net worth tracking app where users manually input their account balances (checking, savings, investments, loans, mortgage) and track how their net worth changes over time. Monthly snapshots are saved to Supabase and displayed as a line chart. No bank linking required — designed for users who prefer manual control over financial app permissions.
Build a net worth tracker. Create Supabase tables: accounts (id, user_id, name, type text check(type in ('asset','liability')), category text, created_at), balance_snapshots (id, account_id, balance decimal, snapshot_date date, created_at). Build: (1) an accounts setup page where users add their accounts (Chase Checking, Vanguard IRA, Car Loan etc.) with type and category, (2) a monthly balance entry form where users update each account's current balance, (3) a net worth dashboard showing current total assets minus total liabilities, (4) a line chart using Recharts LineChart showing net worth over the last 12 months. Use Supabase Auth.
Copy this prompt to try it in Bolt.new
Troubleshooting
Plaid Link opens but shows 'Product not supported' or errors when selecting a bank in Sandbox mode
Cause: The Plaid product (e.g., 'transactions') specified in the link token creation request is not enabled for your Plaid developer account, or you are using an institution in Sandbox that does not support the requested product.
Solution: Log into your Plaid dashboard at dashboard.plaid.com and verify that the 'Transactions' product is enabled for your application under the Products settings. In Sandbox mode, use 'First Platypus Bank' as the institution — it supports all products. The test credentials are username: user_good, password: pass_good. If testing other institutions in Sandbox, use the Plaid Sandbox institution list at plaid.com/docs/sandbox/institutions.
Plaid returns PRODUCT_NOT_READY error when fetching transactions immediately after connecting an account
Cause: Plaid transactions take a short time to load after a new bank connection is established — typically 10-30 seconds in Sandbox, and up to a few minutes for real bank connections. The initial /transactions/get call before data is ready returns this error.
Solution: Add a brief loading state after the Plaid Link onSuccess callback. Wait at least 5 seconds before the first transactions fetch in Sandbox, or implement polling with exponential backoff. In production, use Plaid webhooks (TRANSACTIONS_REMOVED, DEFAULT_UPDATE events) to know when transaction data is ready — but webhook registration requires a deployed URL, not the Bolt WebContainer.
1// Add a short delay after Link success before fetching transactions2async function onPlaidSuccess(publicToken: string) {3 await exchangeToken(publicToken);4 // Wait for Plaid to load transaction data5 await new Promise(resolve => setTimeout(resolve, 3000));6 await fetchTransactions();7}Recharts PieChart renders but shows an empty chart with no data despite transactions existing in Supabase
Cause: Recharts requires data in a specific array format with named keys that match the dataKey props. If the data array is empty, undefined, or the key names do not match the component props, the chart renders empty without throwing an error.
Solution: Verify the data array passed to PieChart has the exact shape Recharts expects: an array of objects where each object has the field names matching your dataKey and nameKey props. Add a console.log of the data before passing to the chart. Ensure the spending aggregation query returns results and that the results are mapped to the correct object shape.
1// Correct Recharts PieChart data shape2const pieData = Object.entries(spendingByCategory).map(([category, amount]) => ({3 name: category, // must match nameKey='name'4 value: amount, // must match dataKey='value'5}));67// If pieData is empty, show placeholder8if (pieData.length === 0) return <p>No spending data this month</p>;910<PieChart width={300} height={300}>11 <Pie data={pieData} dataKey="value" nameKey="name" cx="50%" cy="50%" outerRadius={100} />12 <Tooltip />13 <Legend />14</PieChart>Plaid transactions show wrong signs for amounts — expenses appear as income and vice versa
Cause: Plaid's amount sign convention: for depository accounts (checking, savings), positive amounts are debits (money leaving) and negative are credits (money coming in). Some institutions and account types reverse this. The raw Plaid amount field is not the same as 'expense' or 'income.'
Solution: Normalize amounts during the transformation step: use Math.abs(amount) for the display value and determine transaction type from the sign. For most accounts: amount > 0 means expense, amount < 0 means income. Also check the pending field — pending transactions may get modified or removed in final settlement. For credit cards, the sign convention is the same (positive = charge, negative = payment) but the financial direction is different.
1// Normalize Plaid amount to expense/income2const normalizeTransaction = (t: PlaidTransaction) => ({3 amount: Math.abs(t.amount),4 type: t.amount > 0 ? 'expense' as const : 'income' as const,5 // Note: for investment accounts, income/expense interpretation differs6 // Use t.category to distinguish investment transactions7});Best practices
- Never store Plaid access tokens in the client-side code or return them from API routes to the browser — store them server-side in a Supabase table encrypted at rest, associated with the user's account
- Start development with Plaid Sandbox and manual budget tracker functionality — getting Plaid Production approval for a consumer-facing app requires a compliance review that can take days or weeks
- Display a clear data source indicator for each transaction — label Plaid-synced transactions differently from manually entered ones, so users know which data is automatic vs which they entered
- Implement the Plaid Transactions Sync API (/transactions/sync) for ongoing syncs rather than re-fetching full date ranges — it is significantly more efficient and returns only changes since the last sync
- Always use Row Level Security in Supabase for financial data — a financial data breach is one of the most serious privacy violations possible; ensure each user's transactions, accounts, and budget data are completely isolated
- Add a budget warning notification when a category reaches 80% of its monthly limit — early warnings are more useful than over-budget alerts after the fact
- For Plaid webhooks (to receive real-time transaction updates), deploy to Netlify or Bolt Cloud first — Plaid webhooks require a public HTTPS URL that Bolt's WebContainer cannot provide during development
Alternatives
Plaid is the direct replacement for Mint's bank connectivity infrastructure — use Plaid if you need automatic bank account linking and real transaction data in your Bolt.new finance app.
Choose Yodlee over Plaid if you are building an enterprise fintech application with higher data volume requirements — Yodlee is more expensive but has broader international bank coverage and is used by major financial institutions.
Choose QuickBooks integration if your focus is small business accounting and expense tracking rather than personal finance — QuickBooks has a REST API that works with Bolt.new for business-oriented financial features.
Consider Wave for free small business accounting features if QuickBooks is too expensive — Wave has a GraphQL API and is a free alternative for invoicing and basic bookkeeping in a Bolt.new app.
Frequently asked questions
Is Mint still available? Can I integrate it with Bolt.new?
No. Intuit permanently shut down Mint on January 1, 2024. The service no longer operates, all user accounts have been deleted, and there is no Mint API. Users were migrated to Credit Karma for basic financial tracking. If you are looking for Mint-like functionality in a Bolt.new app, use Plaid for bank account connectivity and transaction data, or build a manual budget tracker with Supabase.
How do I connect bank accounts in a Bolt.new app now that Mint is gone?
Use Plaid, the financial data infrastructure platform that powers most bank account linking in the US. Plaid provides the Link widget (a JavaScript modal) for bank authentication and REST API endpoints for account balances and transaction history. Create a free Plaid developer account at dashboard.plaid.com, use Sandbox mode for development with test credentials (user_good / pass_good), and implement the bank linking flow through Next.js API routes to keep your Plaid credentials server-side.
Does Plaid work in Bolt.new's WebContainer preview?
Partially. The Plaid API calls (link token creation, token exchange, transactions fetch) work in Bolt's WebContainer because they are outbound HTTP calls from Next.js API routes. The Plaid Link modal also loads in the preview. However, Plaid webhooks (which notify your app of new transactions or bank connection issues) cannot be received in the WebContainer since it has no public URL. Deploy to Netlify or Bolt Cloud to receive Plaid webhooks and test end-to-end real-time syncing.
Do I need to deploy to test Plaid integration in Bolt.new?
Not for basic development — Plaid's Sandbox mode works in the Bolt preview for link token creation, token exchange, and transactions fetching. You only need to deploy for: (1) Plaid Production access (real bank connections), (2) Plaid webhooks, and (3) OAuth bank connections for major banks like Chase or Bank of America. For initial development and feature validation, Sandbox mode in the Bolt preview is sufficient.
Can I build a manual budget tracker in Bolt.new without any external API?
Yes, and it is often the better starting point. A manual budget tracker with Supabase for the backend requires no third-party API approval, works completely in Bolt's WebContainer preview, and can be deployed in minutes. Users enter their income and expenses manually, set budget limits per category, and see spending analytics. This approach also works for international users where Plaid has limited bank coverage. Add Plaid integration later if automated bank sync becomes a required feature.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation