Skip to main content
RapidDev - Software Development Agency
bolt-ai-integrationsBolt Chat + API Route

How to Integrate Bolt.new with Mint

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.

What you'll learn

  • Why Mint was shut down and what replacement options are available for personal finance features in Bolt.new
  • How to integrate Plaid's Link widget to connect bank accounts and fetch real transaction data via Next.js API routes
  • How to build a manual personal budget tracker in Bolt.new with Supabase for income, expenses, categories, and goals
  • How to display financial data with charts using Recharts in a Bolt-built finance dashboard
  • Which financial data APIs (Plaid, Yodlee, MX) work with Bolt.new's WebContainer and API route pattern
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate22 min read25 minutesOtherApril 2026RapidDev Engineering Team
TL;DR

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

Bolt Chat + API Route

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

1

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.

Bolt.new Prompt

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

supabase/migrations/001_finance_app.sql
1-- supabase/migrations/001_finance_app.sql
2CREATE 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);
12
13CREATE 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);
26
27CREATE 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);
34
35-- RLS policies
36ALTER TABLE accounts ENABLE ROW LEVEL SECURITY;
37ALTER TABLE transactions ENABLE ROW LEVEL SECURITY;
38ALTER TABLE budget_limits ENABLE ROW LEVEL SECURITY;
39
40CREATE 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.

2

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.

Bolt.new Prompt

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

app/api/plaid/link-token/route.ts
1// app/api/plaid/link-token/route.ts
2import { NextResponse } from 'next/server';
3import { createClient } from '@/lib/supabase/server';
4
5const PLAID_BASE = `https://${process.env.PLAID_ENV ?? 'sandbox'}.plaid.com`;
6
7interface 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}
16
17export async function POST() {
18 const supabase = await createClient();
19 const { data: { user } } = await supabase.auth.getUser();
20
21 if (!user) {
22 return NextResponse.json({ error: 'Not authenticated' }, { status: 401 });
23 }
24
25 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 };
34
35 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 });
41
42 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 }
49
50 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.

3

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.

Bolt.new Prompt

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

app/api/plaid/transactions/route.ts
1// app/api/plaid/transactions/route.ts
2import { NextRequest, NextResponse } from 'next/server';
3
4const PLAID_BASE = `https://${process.env.PLAID_ENV ?? 'sandbox'}.plaid.com`;
5
6// Map Plaid primary categories to simplified app categories
7const 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};
22
23export async function GET(request: NextRequest) {
24 // In production: get access_token from database for current user
25 // For development: use env var as a shortcut
26 const accessToken = process.env.PLAID_ACCESS_TOKEN;
27
28 if (!accessToken) {
29 return NextResponse.json(
30 { error: 'No Plaid access token configured. Connect a bank account first.' },
31 { status: 400 }
32 );
33 }
34
35 const endDate = new Date().toISOString().split('T')[0];
36 const startDate = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString().split('T')[0];
37
38 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 });
51
52 if (!response.ok) {
53 const err = await response.json();
54 return NextResponse.json({ error: err.error_message }, { status: response.status });
55 }
56
57 const data = await response.json();
58
59 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 }));
78
79 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.

4

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.

Bolt.new Prompt

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

app/budget/actions.ts
1// app/budget/actions.ts
2'use server';
3import { createClient } from '@/lib/supabase/server';
4import { revalidatePath } from 'next/cache';
5import { z } from 'zod';
6
7const 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});
14
15export 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' };
19
20 const parsed = TransactionSchema.safeParse(input);
21 if (!parsed.success) {
22 return { error: parsed.error.errors[0].message };
23 }
24
25 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 });
34
35 if (error) return { error: error.message };
36
37 revalidatePath('/budget');
38 return { success: true };
39}
40
41export 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];
45
46 const { data } = await supabase
47 .from('transactions')
48 .select('amount, type, category')
49 .eq('user_id', userId)
50 .gte('date', startDate)
51 .lte('date', endDate);
52
53 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;
55
56 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 }, {}) ?? {};
62
63 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.

Bolt.new Prompt

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.

Bolt.new Prompt

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.

Bolt.new Prompt

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.

typescript
1// Add a short delay after Link success before fetching transactions
2async function onPlaidSuccess(publicToken: string) {
3 await exchangeToken(publicToken);
4 // Wait for Plaid to load transaction data
5 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.

typescript
1// Correct Recharts PieChart data shape
2const pieData = Object.entries(spendingByCategory).map(([category, amount]) => ({
3 name: category, // must match nameKey='name'
4 value: amount, // must match dataKey='value'
5}));
6
7// If pieData is empty, show placeholder
8if (pieData.length === 0) return <p>No spending data this month</p>;
9
10<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.

typescript
1// Normalize Plaid amount to expense/income
2const 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 differs
6 // Use t.category to distinguish investment transactions
7});

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

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.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.