To integrate Plaid with Bolt.new, use Plaid's sandbox environment for development. The Plaid Link frontend component (JavaScript) works in Bolt's WebContainer. Server-side token exchange — creating link tokens and swapping public tokens for access tokens — goes through a Next.js API route using the plaid Node.js package. Plaid webhooks (real-time account updates) require a deployed URL. Production access requires Plaid approval and a production application.
Adding Bank Account Connectivity to Bolt.new Apps with Plaid
Plaid has become the standard infrastructure for fintech apps that need to connect to users' bank accounts. Personal finance tools, expense trackers, loan applications, income verification platforms, and budgeting apps all use Plaid to let users securely link their checking, savings, and investment accounts. Plaid's network covers 12,000+ financial institutions, and its Link component provides a pre-built, compliance-verified bank selection and credential UI that handles the complexity of connecting to each bank's unique authentication flow.
The Plaid integration in Bolt.new has a clear two-part architecture. The frontend uses Plaid Link — a JavaScript library that renders a modal overlay where users select their bank, enter credentials, and authorize data access. Plaid Link is loaded via a script tag and runs in the browser, making it fully compatible with Bolt's WebContainer. The backend uses the plaid Node.js SDK in Next.js API routes for all sensitive operations: creating link tokens (required to initialize Plaid Link), exchanging temporary public tokens for permanent access tokens, and fetching financial data. The Plaid secret key never leaves the server.
Plaid provides three environments: Sandbox (for development — uses fake banks with test credentials), Development (for testing with real financial institutions, up to 100 items), and Production (requires Plaid approval, for live apps). The Sandbox environment is the right starting point for Bolt development. Plaid's sandbox includes 15 test institutions with specific behaviors: 'ins_109508' simulates a successful connection, 'ins_109509' simulates a bank requiring MFA. Test user credentials are always 'user_good' and 'pass_good' for the basic success case.
Integration method
Plaid integration has two parts that work differently in Bolt. The Plaid Link JavaScript library (the user-facing bank selection UI) loads via a script tag and works in Bolt's WebContainer preview. The server-side plaid Node.js SDK handles sensitive operations (creating link tokens, exchanging public tokens for access tokens) through Next.js API routes — keeping your Plaid secret key server-side. Plaid's sandbox environment uses fake bank credentials for development, enabling full testing without real bank connections.
Prerequisites
- A Plaid account at dashboard.plaid.com (free to create, sandbox access is immediate)
- Your Plaid Client ID and Sandbox Secret from the Plaid Dashboard under Team Settings → Keys
- A Plaid sandbox redirect URI configured in the dashboard (required for OAuth-based institutions like Chase) — use http://localhost:3000 for development
- A Bolt.new project using Next.js (required for the server-side API routes that handle token exchange)
- Understanding that production Plaid access requires applying for a production environment and Plaid approval — sandbox is fully functional for development and testing
Step-by-step guide
Get Plaid Sandbox Credentials
Get Plaid Sandbox Credentials
Plaid's sandbox environment is completely separate from production — it uses simulated financial institutions with fake data and never touches real bank accounts. Sign up at plaid.com and log into dashboard.plaid.com. Go to Team Settings → Keys. You'll see two sets of credentials: Sandbox and Development (and Production after approval). For Bolt development, you only need the Sandbox keys. Copy your Client ID (shared across environments) and your Sandbox Secret. Also configure your OAuth redirect URI: go to API → Allowed redirect URIs and add http://localhost:3000. This is required for some institutions (primarily those using OAuth like Chase and Bank of America) even in sandbox. In the sandbox, the redirect URI isn't strictly enforced, but add it to build good habits. Note the test credentials for Plaid Link: when testing, enter username 'user_good' and password 'pass_good' in the Plaid Link modal — this simulates a successful bank connection. For testing MFA, use 'user_good' and 'pass_good' then enter any code when prompted.
Pro tip: The Plaid Dashboard's Sandbox environment lets you generate test bank accounts and transactions — navigate to Sandbox → Manage Items to create test accounts with custom balances and transactions for your specific testing scenarios.
Expected result: You have your Plaid Client ID and Sandbox Secret. You've noted the sandbox test credentials (user_good / pass_good). The OAuth redirect URI for localhost:3000 is configured in the Plaid Dashboard.
Install Plaid SDK and Configure Environment Variables
Install Plaid SDK and Configure Environment Variables
Install the plaid Node.js SDK and set up your environment variables. The plaid package handles the HTTP communication with Plaid's API and provides TypeScript types for all request and response objects. Your Plaid Secret Key must never be exposed to the browser — it must only be read in API routes. The Client ID is also sensitive (it identifies your application) but less critical than the secret. Store both as server-side environment variables without any NEXT_PUBLIC_ prefix. The Plaid base URL changes between environments: the sandbox uses sandbox.plaid.com, development uses development.plaid.com, and production uses production.plaid.com. Storing the environment name as a variable makes switching cleaner than changing URLs manually.
Install the plaid npm package. Create a .env file with PLAID_CLIENT_ID, PLAID_SECRET, and PLAID_ENV (set to 'sandbox'). Create a lib/plaid.ts utility file that exports an initialized Plaid Configuration and PlaidApi client, configured to use the correct base path based on PLAID_ENV.
Paste this in Bolt.new chat
1# .env2PLAID_CLIENT_ID=your-client-id-here3PLAID_SECRET=your-sandbox-secret-here4PLAID_ENV=sandbox5# For development: PLAID_ENV=development, PLAID_SECRET=your-development-secret6# For production: PLAID_ENV=production, PLAID_SECRET=your-production-secretExpected result: The plaid package is in package.json. The .env file has your real Plaid sandbox credentials. lib/plaid.ts compiles without TypeScript errors and exports an initialized PlaidApi client.
Build the Plaid API Client Utility
Build the Plaid API Client Utility
The Plaid SDK requires a Configuration object that sets the environment (sandbox/development/production) and authentication headers. Creating this once in a utility module prevents duplication across API routes. The Configuration maps your Plaid environment name to the correct API base URL. You'll import this client in every Plaid-related API route.
1// lib/plaid.ts2import { Configuration, PlaidApi, PlaidEnvironments } from 'plaid';34const env = process.env.PLAID_ENV as keyof typeof PlaidEnvironments || 'sandbox';56const configuration = new Configuration({7 basePath: PlaidEnvironments[env],8 baseOptions: {9 headers: {10 'PLAID-CLIENT-ID': process.env.PLAID_CLIENT_ID!,11 'PLAID-SECRET': process.env.PLAID_SECRET!,12 },13 },14});1516export const plaidClient = new PlaidApi(configuration);Pro tip: PlaidEnvironments is an enum from the Plaid SDK with values for 'sandbox', 'development', and 'production'. Using it prevents typos in base URL strings.
Expected result: lib/plaid.ts exports a configured PlaidApi client. Importing it in an API route and making a test call returns a valid Plaid response.
Create the Link Token and Token Exchange API Routes
Create the Link Token and Token Exchange API Routes
The Plaid connection flow has three server-side steps. First, your server creates a link token — a short-lived token that initializes the Plaid Link UI with your app's configuration and the current user's context. The create-link-token route takes the current user's ID and creates a link token. Second, the user interacts with Plaid Link (handled client-side), selects their bank, and enters credentials. When the user successfully connects, Plaid Link calls your onSuccess callback with a public_token — a temporary, one-time-use token. Third, your server exchanges this public_token for an access_token (permanent, used for all future API calls) and an item_id (identifies the bank connection). You must save both the access_token and item_id to your database securely, associated with the user. The access_token never leaves your server — it's your permanent credential for this user's bank connection.
Create three Plaid API routes using the plaid client from lib/plaid.ts. First, /api/plaid/create-link-token (POST): call plaidClient.linkTokenCreate with user.client_user_id from the request body, products ['transactions'], country_codes ['US'], and language 'en'. Return the link_token. Second, /api/plaid/exchange-token (POST): receive public_token from body, call plaidClient.itemPublicTokenExchange, save the returned access_token and item_id to the database with the userId, return success. Third, /api/plaid/accounts (GET): retrieve the access_token from database for the current user, call plaidClient.accountsGet, return the accounts array.
Paste this in Bolt.new chat
1// app/api/plaid/create-link-token/route.ts2import { NextResponse } from 'next/server';3import { plaidClient } from '@/lib/plaid';4import { CountryCode, Products } from 'plaid';56export async function POST(request: Request) {7 try {8 const { userId } = await request.json();910 const response = await plaidClient.linkTokenCreate({11 user: { client_user_id: userId },12 client_name: 'My Finance App',13 products: [Products.Transactions],14 country_codes: [CountryCode.Us],15 language: 'en',16 });1718 return NextResponse.json({ link_token: response.data.link_token });19 } catch (error: unknown) {20 const e = error as { message: string };21 return NextResponse.json({ error: e.message }, { status: 500 });22 }23}2425// app/api/plaid/exchange-token/route.ts26import { NextResponse } from 'next/server';27import { plaidClient } from '@/lib/plaid';2829export async function POST(request: Request) {30 try {31 const { publicToken, userId } = await request.json();3233 const response = await plaidClient.itemPublicTokenExchange({34 public_token: publicToken,35 });3637 const { access_token, item_id } = response.data;3839 // IMPORTANT: Save access_token and item_id to your database40 // associated with userId. Never return access_token to the client.41 // await db.plaidItems.create({ userId, accessToken: access_token, itemId: item_id });4243 return NextResponse.json({ success: true, itemId: item_id });44 } catch (error: unknown) {45 const e = error as { message: string };46 return NextResponse.json({ error: e.message }, { status: 500 });47 }48}Pro tip: Never return the access_token from your API to the client. It provides read access to the user's financial data and must only exist in your database and server-side code.
Expected result: POST to /api/plaid/create-link-token returns a link_token string. After Plaid Link completes, POST to /api/plaid/exchange-token with the public_token returns success. The access_token is saved to your database.
Integrate Plaid Link in the React UI
Integrate Plaid Link in the React UI
The react-plaid-link package provides a React hook and component that handles the Plaid Link modal lifecycle. The flow: your component calls /api/plaid/create-link-token on mount to get a link token, passes it to the usePlaidLink hook, and the hook provides an open() function to launch the Plaid Link modal. When the user completes the bank connection, the onSuccess callback receives the public_token and linked account metadata. Your component then calls /api/plaid/exchange-token with the public_token to complete the server-side token exchange. The entire Plaid Link UI runs in the browser and works in Bolt's WebContainer preview. For OAuth-based banks (Chase, Bank of America), Plaid Link opens a popup window — this popup works in the Bolt preview as long as your browser allows popups from the WebContainer domain.
Create a PlaidLink component using react-plaid-link. On mount, fetch a link token from /api/plaid/create-link-token. Use the usePlaidLink hook with the link token and an onSuccess callback that sends the public_token to /api/plaid/exchange-token. Show a 'Connect Bank Account' button that calls plaidOpen(). Display loading state while fetching the link token. After successful connection, show the list of connected account names and types returned in the onSuccess metadata. Install react-plaid-link.
Paste this in Bolt.new chat
1'use client';2import { useEffect, useState, useCallback } from 'react';3import { usePlaidLink, PlaidLinkOptions, PlaidLinkOnSuccess } from 'react-plaid-link';45interface PlaidLinkButtonProps {6 userId: string;7 onConnected?: (accounts: Array<{ name: string; type: string }>) => void;8}910export function PlaidLinkButton({ userId, onConnected }: PlaidLinkButtonProps) {11 const [linkToken, setLinkToken] = useState<string | null>(null);12 const [loading, setLoading] = useState(true);1314 useEffect(() => {15 fetch('/api/plaid/create-link-token', {16 method: 'POST',17 headers: { 'Content-Type': 'application/json' },18 body: JSON.stringify({ userId }),19 })20 .then((res) => res.json())21 .then((data) => {22 setLinkToken(data.link_token);23 setLoading(false);24 });25 }, [userId]);2627 const onSuccess = useCallback<PlaidLinkOnSuccess>(async (publicToken, metadata) => {28 await fetch('/api/plaid/exchange-token', {29 method: 'POST',30 headers: { 'Content-Type': 'application/json' },31 body: JSON.stringify({ publicToken, userId }),32 });33 const accounts = metadata.accounts.map((acc) => ({34 name: acc.name,35 type: acc.type,36 }));37 onConnected?.(accounts);38 }, [userId, onConnected]);3940 const config: PlaidLinkOptions = {41 token: linkToken,42 onSuccess,43 };4445 const { open, ready } = usePlaidLink(config);4647 if (loading) return <p className="text-gray-500">Loading...</p>;4849 return (50 <button51 onClick={() => open()}52 disabled={!ready}53 className="px-4 py-2 bg-green-600 text-white rounded hover:bg-green-700 disabled:opacity-50"54 >55 Connect Bank Account56 </button>57 );58}Pro tip: In the Plaid sandbox, use username 'user_good' and password 'pass_good' in the Plaid Link modal to simulate a successful bank connection. No real credentials are ever used in sandbox.
Expected result: The 'Connect Bank Account' button opens the Plaid Link modal. Entering sandbox credentials (user_good / pass_good) completes the connection. The onConnected callback fires with the connected account names. The access token is exchanged and saved server-side.
Fetch Financial Data and Set Up Webhooks Post-Deployment
Fetch Financial Data and Set Up Webhooks Post-Deployment
With an access token stored in your database, you can fetch account balances and transaction data at any time. The transactions endpoint returns up to 500 transactions per request, paginated via a cursor for large histories. Balances are real-time for most institutions. For production apps, Plaid webhooks are essential — they notify your server when new transactions are available (TRANSACTIONS_UPDATED), when a bank connection has an error (ITEM_ERROR), or when re-authentication is required (PENDING_EXPIRATION). These webhooks require a publicly accessible URL that Plaid can call. During Bolt development, webhooks cannot arrive in the WebContainer. Deploy to Netlify or Bolt Cloud, then configure your Plaid item's webhook URL using plaidClient.itemWebhookUpdate with your deployed /api/plaid/webhook endpoint URL. For production use, you must apply for Plaid's production environment (which includes a review process for compliance and security).
Create an API route at /api/plaid/transactions (GET) that retrieves the Plaid access token for the current user from the database, calls plaidClient.transactionsGet for the last 30 days, and returns the transactions array with fields: date, name, amount, category, merchant_name. Also create /api/plaid/balances (GET) that calls plaidClient.accountsBalanceGet and returns account balances.
Paste this in Bolt.new chat
1// app/api/plaid/transactions/route.ts2import { NextResponse } from 'next/server';3import { plaidClient } from '@/lib/plaid';45export async function GET(request: Request) {6 try {7 // Retrieve access_token from your database for the authenticated user8 // const accessToken = await db.plaidItems.findByUserId(userId);9 const accessToken = 'access-sandbox-xxxx'; // replace with DB lookup1011 const thirtyDaysAgo = new Date();12 thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);1314 const response = await plaidClient.transactionsGet({15 access_token: accessToken,16 start_date: thirtyDaysAgo.toISOString().split('T')[0],17 end_date: new Date().toISOString().split('T')[0],18 });1920 return NextResponse.json({21 transactions: response.data.transactions,22 total: response.data.total_transactions,23 });24 } catch (error: unknown) {25 const e = error as { message: string };26 return NextResponse.json({ error: e.message }, { status: 500 });27 }28}Pro tip: Plaid webhooks during development can be simulated using the Plaid Dashboard's sandbox webhook simulator (Dashboard → Sandbox → Simulate Webhook) to test your webhook handler without needing a deployed URL.
Expected result: GET /api/plaid/transactions returns an array of the last 30 days of transactions. GET /api/plaid/balances returns current account balances. For production apps, webhook handler at /api/plaid/webhook is registered after deployment.
Common use cases
Personal Finance Dashboard
Build a dashboard where users connect their bank accounts and see their spending categorized automatically. Plaid provides transaction data with merchant names and spending categories (food, transportation, shopping, etc.) that you display in charts and summaries.
Build a personal finance dashboard with Plaid integration. Create API routes for: /api/plaid/create-link-token (generates a Plaid link token for the current user), /api/plaid/exchange-token (exchanges the public_token from Plaid Link for an access_token and item_id, saves both to the database), and /api/plaid/transactions (fetches transactions for the last 30 days using the stored access token). Use react-plaid-link for the Plaid Link button. Show transactions in a table grouped by category with spending totals. Display account balances at the top.
Copy this prompt to try it in Bolt.new
Income Verification for Applications
Add instant income verification to a loan application or rental platform by pulling payroll deposit history from the applicant's connected bank account. Plaid identifies direct deposits and can summarize income patterns for underwriting.
Add income verification to my application form using Plaid. Create the Plaid Link connection flow with /api/plaid/create-link-token and /api/plaid/exchange-token API routes. After connection, fetch the last 12 months of transactions from /api/plaid/transactions, filter for transactions with category 'Payroll' or amounts over $2,000, and display a summary showing: average monthly income, employer name (from transaction merchant), pay frequency (weekly/biweekly/monthly). Show a 'Verified Income' badge if at least 3 months of consistent deposits are detected.
Copy this prompt to try it in Bolt.new
Account Balance Monitoring
Display real-time account balances from multiple linked bank accounts in a unified view. Useful for treasury management tools, personal net worth trackers, and any app where users need an overview of their financial position across institutions.
Create an account overview page that shows all linked bank accounts and their current balances. After Plaid Link connects an institution, call /api/plaid/accounts to fetch account details (account name, type, subtype, current balance, available balance). Display accounts grouped by institution with the institution logo. Allow users to link multiple banks. Show total balance across all accounts. Add a refresh button that re-fetches current balances.
Copy this prompt to try it in Bolt.new
Troubleshooting
Plaid Link modal shows 'Invalid Credentials' even after entering user_good / pass_good
Cause: You may be using a Development or Production Plaid secret with the sandbox environment configuration, or the environment is misconfigured.
Solution: Verify PLAID_ENV is set to 'sandbox' and PLAID_SECRET is your Sandbox secret (not Development). In the Plaid Dashboard, under Team Settings → Keys, make sure you're copying from the Sandbox row. The sandbox, development, and production environments have different secrets.
linkTokenCreate fails with 'INVALID_PRODUCT' error
Cause: The requested Plaid product (e.g., Transactions, Income) is not enabled for your Plaid application.
Solution: Go to the Plaid Dashboard → Team Settings → Your Application and verify which products are enabled. For new applications, Transactions and Auth are typically enabled. Income Verification, Assets, and other products require separate approval. Only request products in the Products array that are enabled for your app.
Public token exchange fails with 'ITEM_NOT_FOUND'
Cause: Public tokens are one-time-use and expire after 30 minutes. If the exchange is delayed or the token has already been used, the exchange fails.
Solution: Exchange the public token immediately in the onSuccess callback — don't store it for later use. If the token expired during development (e.g., you were debugging), re-run the Plaid Link flow to get a fresh public token.
Plaid webhooks never arrive during Bolt development
Cause: Bolt's WebContainer runs inside a browser tab with no public IP or URL. Plaid cannot make outbound HTTP calls to deliver webhook events to the WebContainer.
Solution: Use Plaid Dashboard's webhook simulator (Sandbox → Simulate Webhook) to test your webhook handler logic without a real webhook delivery. For full webhook testing, deploy to Netlify or Bolt Cloud and register your deployed /api/plaid/webhook URL using plaidClient.itemWebhookUpdate.
Best practices
- Never return or log access tokens — treat them like passwords and store them encrypted in your database associated only with the owning user
- Request only the Plaid products you actually need in the Products array — requesting unnecessary products can delay Plaid approval and confuse users about data access
- Use Plaid's sandbox test institutions and credentials (user_good / pass_good) throughout development — never use real bank credentials in development
- Apply for Plaid Production access early if you plan to launch — the review process can take 1-2 weeks and requires demonstrating your use case and security practices
- Handle Plaid item errors gracefully — banks frequently require re-authentication, and your app should prompt users to reconnect through a new Plaid Link flow when ITEM_LOGIN_REQUIRED webhooks arrive
- Cache transaction data in your database rather than fetching from Plaid on every page load — Plaid charges per API call and most financial data doesn't change minute-to-minute
- Implement Plaid webhooks for transaction updates in production rather than polling — TRANSACTIONS_UPDATED webhooks are more efficient and timely than scheduled API calls
Alternatives
Stripe Connect handles payment accounts and marketplace payouts rather than bank data access — choose it when you need to pay users or split revenue, not when you need to read their transaction history.
Coinbase API connects to cryptocurrency wallets and exchanges rather than traditional bank accounts, suited for crypto-native fintech apps.
Robinhood's API provides brokerage account connectivity specifically for investment portfolio data, serving apps focused on stock and options trading rather than general banking.
QuickBooks integration provides access to business accounting data and bookkeeping rather than personal banking, better for B2B financial tools.
Frequently asked questions
Does Plaid Link work in Bolt's WebContainer preview?
Yes. The Plaid Link JavaScript library is a browser-based component that loads via a script tag and opens a modal overlay. It works in Bolt's WebContainer preview for the complete user flow including bank selection, credential entry, and account selection. OAuth-based banks (Chase, Bank of America) open a popup window which also works in the preview. The limitation is receiving webhooks — Plaid cannot call your WebContainer since it has no public URL.
Do I need to apply for Plaid production access before building?
No. Plaid's sandbox environment is available immediately after creating a Plaid account and provides full functionality with simulated bank data. You only need production access when you're ready to let real users connect real bank accounts. Apply for production access when your app is nearly complete — the review process typically takes 1-2 weeks and requires demonstrating your use case, security practices, and compliance measures.
Is Plaid free to use?
Plaid's sandbox environment is completely free with no usage limits, making it ideal for development. Production pricing is per-item (connected bank account): Auth costs $0.50/item, Transactions costs $0.30/item/month, Balance costs $0.50/item per call. Plaid also offers custom enterprise pricing for high-volume applications. The Development environment (real bank connections, up to 100 items) is free.
How do I handle users who need to re-authenticate their bank connection?
Banks periodically require re-authentication (password changes, security checks, session expiry). Plaid sends a PENDING_EXPIRATION or ITEM_LOGIN_REQUIRED webhook to alert you. When received, store a flag on the user's account and prompt them to go through Plaid Link again — passing the existing item_id in a new link token triggers an update mode that re-authenticates the existing connection without creating a duplicate item.
Can I use Plaid to read data from international bank accounts?
Plaid primarily covers US financial institutions plus Canada and parts of Europe (UK, France, Spain, Netherlands, Germany, Ireland, Denmark, Poland, Portugal). Coverage varies by country. The country_codes parameter in your link token creation specifies which countries to enable. For fully international bank connectivity, consider Plaid alternatives like TrueLayer (Europe-focused) or MX Technologies.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation