Connect Retool to Plaid by creating a REST API Resource with Plaid's base URL and client_id/secret authentication. Use Plaid's /link/token/create, /item/get, and /transactions/get endpoints to build a financial data dashboard where your team can monitor linked bank accounts, review transaction history, and track account balances across all connected institutions.
| Fact | Value |
|---|---|
| Tool | Plaid |
| Category | Payment |
| Method | REST API Resource |
| Difficulty | Intermediate |
| Time required | 30 minutes |
| Last updated | April 2026 |
Build a Financial Data Admin Panel with Plaid and Retool
Plaid's API gives you programmatic access to bank account data — balances, transactions, identity, and more — across thousands of financial institutions. When you connect Plaid to Retool, you can build internal dashboards that give your finance, compliance, or support teams a unified view of linked bank accounts without needing to access Plaid's developer portal directly.
Retool's server-side proxy architecture is particularly well-suited to Plaid because it keeps your client_id and secret completely off the browser. All requests route through Retool's backend, satisfying Plaid's requirement that credentials never be exposed client-side. Your team can query transaction data, check account balances, and inspect item metadata through a Retool Table or Chart without ever seeing the raw API credentials.
Common use cases include fintech support panels where agents can look up a user's linked accounts, compliance dashboards that flag unusual transaction patterns, and finance operations tools that aggregate balance data across all customers. Plaid's sandbox environment lets you build and test the full integration before switching to production data.
Integration method
Plaid connects to Retool through a REST API Resource configured with Plaid's base URL and client_id/secret credentials sent in the JSON request body. All API calls are proxied server-side by Retool, keeping your Plaid credentials secure. You build queries against Plaid's endpoints to retrieve account data, transaction history, and balance information that power your internal financial dashboards.
Prerequisites
- A Plaid developer account with a client_id and secret (sandbox credentials work for testing)
- Access tokens for the Plaid items you want to query (generated through Plaid Link flow)
- A Retool account with permission to create Resources
- Basic familiarity with Retool's query editor and Table component
- Understanding of Plaid's environment tiers: sandbox, development, and production
Step-by-step guide
Create a Plaid REST API Resource in Retool
Navigate to the Resources tab in your Retool instance (top navigation bar) and click the blue Add Resource button. From the resource type list, select REST API. Give the resource a clear name like 'Plaid API - Sandbox' or 'Plaid API - Production' to distinguish between environments. In the Base URL field, enter the appropriate Plaid API base URL for your environment. For sandbox use https://sandbox.plaid.com, for development use https://development.plaid.com, and for production use https://production.plaid.com. Do not add a trailing slash. Plaid's authentication differs from most APIs: credentials are not sent as headers but as JSON fields in the request body (client_id and secret). Because of this, leave the Authentication section set to None — you will add the credentials in each query's request body instead. Click Save Changes. Retool will confirm the resource was created. You now have a base resource you can clone for different environments — create separate resources for sandbox and production so developers don't accidentally run production queries during testing.
Pro tip: Create separate Plaid resources for sandbox and production. Name them clearly (e.g., 'Plaid - SANDBOX' and 'Plaid - PRODUCTION') so no one accidentally queries production data during development.
Expected result: A saved REST API Resource named 'Plaid API' appears in your Resources list with the correct Plaid base URL.
Store Plaid credentials as Configuration Variables
Never hardcode your Plaid client_id and secret directly in query bodies. Instead, use Retool's Configuration Variables to store these credentials securely. Navigate to Settings in the left sidebar, then click Configuration Variables. Create two new variables: PLAID_CLIENT_ID (your Plaid client_id) and PLAID_SECRET (your Plaid secret). Mark both as 'Secret' by toggling the secret switch — this restricts them so they can only be used in resource configurations and server-side queries, not accessed from the frontend. If you have both sandbox and production credentials, create environment-specific variables. In Retool, you can create separate resource environments (Settings → Environments) and assign different variable values per environment. This means your sandbox queries automatically use sandbox credentials and your production queries use production credentials when you switch environments. Once the configuration variables are saved, you can reference them in query bodies using the syntax {{ retoolContext.configVars.PLAID_CLIENT_ID }} and {{ retoolContext.configVars.PLAID_SECRET }}. These values resolve server-side and are never sent to the browser.
Pro tip: Mark PLAID_SECRET as a 'Secret' configuration variable so it only resolves in server-side contexts. This prevents it from appearing in browser network tabs.
Expected result: PLAID_CLIENT_ID and PLAID_SECRET appear in your Configuration Variables list, both marked as secrets.
Build a query to fetch account balances
Open or create a Retool app and open the Code panel on the left side. Click the + icon to create a new query. Name it something descriptive like getAccountBalances. In the Resource dropdown, select your Plaid REST API resource. Set the Method to POST. In the Path field, enter /accounts/get — Retool will combine this with the base URL you configured in the resource to form the complete endpoint URL. In the Body section, select JSON as the body type. The /accounts/get endpoint requires client_id, secret, and access_token in the request body. Enter the following JSON body: { "client_id": "{{ retoolContext.configVars.PLAID_CLIENT_ID }}", "secret": "{{ retoolContext.configVars.PLAID_SECRET }}", "access_token": "{{ textInput_accessToken.value }}" } The access_token comes from a text input component where operators paste in the token for the account they want to look up. Click Run Query to test it. Plaid returns an accounts array with id, name, mask, type, subtype, and balances (available, current, limit) for each account linked to that Item. Add a Transformer in the Advanced tab to reshape the response. The transformer receives data as its input and should return a clean array for the Table component.
1// JavaScript transformer — reshapes Plaid /accounts/get response2const accounts = data.accounts || [];3return accounts.map(account => ({4 account_id: account.account_id,5 name: account.name,6 official_name: account.official_name || 'N/A',7 mask: account.mask ? `****${account.mask}` : 'N/A',8 type: account.type,9 subtype: account.subtype,10 available_balance: account.balances.available !== null11 ? `$${account.balances.available.toLocaleString('en-US', { minimumFractionDigits: 2 })}`12 : 'N/A',13 current_balance: account.balances.current !== null14 ? `$${account.balances.current.toLocaleString('en-US', { minimumFractionDigits: 2 })}`15 : 'N/A',16 currency: account.balances.iso_currency_code || 'USD'17}));Pro tip: In sandbox mode, use the access_token from Plaid's sandbox test environment. Plaid provides pre-generated sandbox access tokens in your developer dashboard under the Sandbox tab.
Expected result: Running the query returns an accounts array. The transformer reshapes it into a flat array with readable balance strings that a Table component can display directly.
Build a transactions query with date range filtering
Create a second query named getTransactions. Set the resource to your Plaid API resource, the Method to POST, and the Path to /transactions/get. The /transactions/get endpoint requires client_id, secret, access_token, start_date (YYYY-MM-DD), and end_date (YYYY-MM-DD). It also accepts optional count (max 500) and offset for pagination. Use Retool's date picker components to drive the date range dynamically. Drag a Date Range Picker component onto your canvas and name it dateRange. Now set the query body to use those component values: { "client_id": "{{ retoolContext.configVars.PLAID_CLIENT_ID }}", "secret": "{{ retoolContext.configVars.PLAID_SECRET }}", "access_token": "{{ textInput_accessToken.value }}", "start_date": "{{ moment(dateRange.value[0]).format('YYYY-MM-DD') }}", "end_date": "{{ moment(dateRange.value[1]).format('YYYY-MM-DD') }}", "options": { "count": 100, "offset": {{ (table_transactions.paginationOffset || 0) }} } } This query drives a Table component named table_transactions. Set the table's Data source to {{ getTransactions.data.transactions }}. Plaid returns a transactions array where each transaction has transaction_id, account_id, amount, date, name, merchant_name, category, and pending fields. Add a transformer to format the data for display — amounts should be formatted as currency, and the date should be human-readable. Set the table to server-side pagination mode and bind the page count to {{ Math.ceil(getTransactions.data.total_transactions / 100) }}.
1// JavaScript transformer — reshapes Plaid /transactions/get response2const transactions = data.transactions || [];3return transactions.map(txn => ({4 date: txn.date,5 description: txn.merchant_name || txn.name,6 category: txn.category ? txn.category[0] : 'Uncategorized',7 amount: txn.amount > 08 ? `-$${Math.abs(txn.amount).toFixed(2)}`9 : `+$${Math.abs(txn.amount).toFixed(2)}`,10 account_id: txn.account_id,11 pending: txn.pending ? 'Pending' : 'Posted',12 transaction_id: txn.transaction_id13}));Pro tip: Plaid amounts use a sign convention where positive amounts represent money leaving the account (debits) and negative amounts represent money coming in (credits). Account for this in your transformer when displaying amounts.
Expected result: The transactions query returns paginated transaction data. The transformer formats amounts with proper debit/credit signs and the Table shows transactions sorted by date with the date range picker controlling the query window.
Check Item status with /item/get
Items are the core Plaid object representing a user's connection to a financial institution. Items can have errors — the most common being ITEM_LOGIN_REQUIRED, which means the user needs to re-authenticate via Plaid Link. Build a query that checks item health so support agents can identify broken connections. Create a query named getItemStatus. Set method to POST, path to /item/get. Body: { "client_id": "{{ retoolContext.configVars.PLAID_CLIENT_ID }}", "secret": "{{ retoolContext.configVars.PLAID_SECRET }}", "access_token": "{{ textInput_accessToken.value }}" } Plaid returns an item object with institution_id, item_id, webhook, available_products, billed_products, consent_expiration_time, and an error field (null if healthy). It also returns an institution object with institution_name. In your app, add a Status Badge or Text component bound to getItemStatus data. Use a conditional expression to show a green 'Healthy' badge when error is null, and a red 'Error' badge with the error code when error is not null. For complex support panels managing many customer accounts, consider storing access_tokens in a Retool Database or PostgreSQL table and building a list view that runs getItemStatus for each selected account. This gives your support team a health overview across all connected accounts. For complex integrations involving multiple Plaid endpoints, custom transformers, and account health monitoring workflows, RapidDev's team can help architect and build your Retool solution.
1// JavaScript transformer — formats item status for display2const item = data.item || {};3const institution = data.institution || {};4const error = item.error;56return {7 item_id: item.item_id,8 institution_name: institution.name || 'Unknown',9 institution_id: item.institution_id,10 available_products: (item.available_products || []).join(', '),11 billed_products: (item.billed_products || []).join(', '),12 status: error ? 'ERROR' : 'HEALTHY',13 error_code: error ? error.error_code : null,14 error_message: error ? error.error_message : null,15 consent_expiration: item.consent_expiration_time16 ? new Date(item.consent_expiration_time).toLocaleDateString()17 : 'No expiration'18};Pro tip: ITEM_LOGIN_REQUIRED is the most common item error. Build a workflow that periodically checks all items for this error and notifies your team via Slack so users can be prompted to re-link their accounts before they notice data is stale.
Expected result: The item status query returns structured health data. The Status Badge component shows green for healthy items and red with an error code for items requiring attention.
Assemble the financial dashboard layout
Now bring the three queries together into a cohesive financial admin panel. The layout should have a top section for account lookup controls, a middle section for account balances, and a bottom section for transaction history. At the top, place a Text Input component named textInput_accessToken with a placeholder like 'Enter Plaid access_token' and a Date Range Picker named dateRange defaulting to the last 30 days. Add a Query Button that triggers all three queries simultaneously on click. In the middle section, add a Container component and place three Statistic components inside for Available Balance (total across all accounts), Current Balance, and Account Count. Bind these to JavaScript queries that sum the values from getAccountBalances.data. Below that, place a Table component named table_accounts with data source {{ getAccountBalances.data }}. Add a second Table component named table_transactions with data source {{ getTransactions.data }}. Enable server-side pagination on table_transactions. On the right side or in a panel, add a Text or JSON component bound to getItemStatus.data to show the item health summary. Add an Alert component that appears conditionally (visible when {{ getItemStatus.data.status === 'ERROR' }}) with a message showing the error code and a link to Plaid's error documentation. Test the complete flow in Plaid's sandbox environment using test credentials before connecting any production access tokens. Verify that switching the Date Range Picker automatically refreshes the transactions table.
1// JavaScript query — calculate balance totals across all accounts2const accounts = getAccountBalances.data || [];3const totals = accounts.reduce((acc, account) => {4 const available = parseFloat(5 account.available_balance?.replace(/[$,]/g, '') || 06 );7 const current = parseFloat(8 account.current_balance?.replace(/[$,]/g, '') || 09 );10 return {11 available: acc.available + available,12 current: acc.current + current,13 count: acc.count + 114 };15}, { available: 0, current: 0, count: 0 });1617return {18 total_available: `$${totals.available.toLocaleString('en-US', { minimumFractionDigits: 2 })}`,19 total_current: `$${totals.current.toLocaleString('en-US', { minimumFractionDigits: 2 })}`,20 account_count: totals.count21};Pro tip: Use Retool's built-in loading states to show spinners while Plaid queries are running — Plaid's API can take 2-5 seconds for transaction queries, especially in production with large transaction histories.
Expected result: The complete financial dashboard loads and displays account balances, transaction history, and item health status for any Plaid access_token entered by the user. Date range changes automatically refresh the transaction table.
Common use cases
Build a customer account health dashboard
Create a Retool panel where support agents can look up any customer by user ID, see their linked bank accounts, current balances, and recent transaction history. The panel includes a search input, account balance cards, and a paginated transaction table with category filters.
Build an admin panel that shows linked bank accounts for a given access_token, displays current balances from /accounts/get, and lists the last 90 days of transactions from /transactions/get with filters for amount range and transaction category.
Copy this prompt to try it in Retool
Transaction monitoring and fraud detection panel
Build a Retool dashboard that pulls transaction data across multiple accounts and flags transactions above a threshold or in unusual categories. A Chart component visualizes spending patterns over time, and a Table highlights suspicious activity for compliance review.
Create a fraud monitoring dashboard that fetches transactions for multiple access_tokens stored in a database, calculates daily spend totals using a JavaScript transformer, and highlights any single transaction over $500 or transactions in blocked merchant categories.
Copy this prompt to try it in Retool
Financial institution coverage monitor
Build an operational dashboard that tracks which Plaid Items (bank connections) are active, which have errors, and which need re-authentication. Use /item/get to check item status and display error codes with recommended actions for the support team.
Build an item health monitor that calls /item/get for a list of access_tokens stored in a PostgreSQL table, displays institution name, item status, and any error codes, with a button to trigger a re-link flow for items with LOGIN_REQUIRED status.
Copy this prompt to try it in Retool
Troubleshooting
Query returns 'INVALID_API_KEYS' error with status 400
Cause: The client_id or secret in your query body doesn't match the credentials for the environment you're targeting. Sandbox credentials only work against sandbox.plaid.com, not development or production URLs.
Solution: Verify that your PLAID_CLIENT_ID and PLAID_SECRET configuration variables match your Plaid dashboard credentials. Check that the environment in your resource base URL matches the environment of your credentials. Create separate Retool resources for each Plaid environment to prevent credential/URL mismatches.
Query returns 'INVALID_ACCESS_TOKEN' with a valid-looking token
Cause: The access_token belongs to a different Plaid environment than the resource base URL you're using. A sandbox access_token like access-sandbox-xxx will not work against sandbox.plaid.com if you're using a production or development base URL. Tokens are environment-specific.
Solution: Ensure the access_token prefix matches the environment. Sandbox tokens start with 'access-sandbox-', development tokens with 'access-development-', and production tokens with 'access-production-'. Create a Text component that shows the detected environment based on the token prefix to help operators identify mismatches.
1// Add this transformer to detect token environment2const token = textInput_accessToken.value || '';3if (token.startsWith('access-sandbox-')) return 'sandbox';4if (token.startsWith('access-development-')) return 'development';5if (token.startsWith('access-production-')) return 'production';6return 'unknown';Transactions query returns empty array even for a linked account with transactions
Cause: Plaid requires transactions to be explicitly requested as a product when the Item is created. If the access_token was generated without the 'transactions' product in the products array, transaction data will not be available.
Solution: Check the available_products array returned by /item/get. If 'transactions' is not listed, the Item needs to be re-created with the transactions product. In sandbox mode, use Plaid Link with products: ['transactions'] in the initialization. For existing production items, the user may need to re-link their account.
Query body shows 'client_id' and 'secret' in network requests visible to users
Cause: You hardcoded the credential values directly in the query body instead of referencing configuration variables. Retool only protects secret config vars at the proxy level — if you paste the actual key values into query body fields, they may be visible in query responses.
Solution: Replace hardcoded values with configuration variable references using {{ retoolContext.configVars.PLAID_CLIENT_ID }} and {{ retoolContext.configVars.PLAID_SECRET }}. Mark both variables as 'Secret' in Settings → Configuration Variables. Secret variables never reach the frontend.
Best practices
- Create separate Retool resources for each Plaid environment (sandbox, development, production) and use clear naming to prevent environment confusion.
- Always store Plaid client_id and secret as Secret configuration variables in Retool — never paste them directly into query body fields.
- Use Plaid's /item/get endpoint to check item health before running data queries, and surface error codes prominently in your UI so support agents can take action.
- Implement server-side pagination for transaction queries — requesting all transactions at once can timeout for accounts with years of history. Use the count and offset parameters.
- Add a transformer to every Plaid query to reshape the nested response structure into flat objects before binding to Table components — Retool Tables work best with flat data.
- For fintech products requiring HIPAA or SOC 2 compliance, consider Retool's self-hosted deployment to keep all API calls within your VPC and ensure Plaid data never transits Retool's cloud infrastructure.
- Use Plaid's webhook system for production — set up a Retool Workflow with a webhook trigger to receive item status updates and store them in your database rather than polling /item/get on demand.
- Test all queries in sandbox mode using Plaid's test credentials and simulated institution data before switching to development or production access tokens.
Alternatives
Stripe is better suited when you need to process online payments and manage subscriptions rather than read existing bank account data — use Plaid when you need to access users' bank account information and transaction history.
Yodlee offers broader financial data aggregation with more global institution coverage, making it a better choice for international fintech apps, while Plaid has stronger developer tooling for US-focused applications.
Coinbase API is the right choice when you need to access cryptocurrency wallet data and transaction history rather than traditional bank account data covered by Plaid.
Frequently asked questions
Do I need to store access_tokens in Retool to use this integration?
You can store access_tokens in a database (PostgreSQL, Retool Database) or pass them in via a text input from your team. For a production support panel, storing them in a database linked to user records is the recommended approach — create a query that looks up the access_token by customer ID from your user table. Never store access_tokens as plain text without encryption at rest.
Can I use Plaid's Link flow inside a Retool app?
Plaid Link (the front-end SDK for connecting accounts) is not natively supported in Retool. You would need to embed it in a Custom Component using Retool's custom component system. For most internal tool use cases, Retool is used to query existing Items (already linked via Link in your customer-facing app) rather than to initiate new link flows.
Is there a Plaid native connector in Retool, or does it always require a REST API resource?
As of 2026, Plaid does not have a native connector in Retool like Stripe or Twilio do. You need to configure it as a REST API resource. This means you must manually handle authentication by including client_id and secret in each query body, and you won't get dropdown-based action selection — you need to know the endpoint paths.
What Plaid environments should I use for testing vs. production in Retool?
Use Plaid's sandbox environment (sandbox.plaid.com) for all development and testing. Plaid provides pre-built test users with simulated bank data in sandbox mode. Create a separate Retool resource pointing to sandbox.plaid.com and use sandbox credentials. Only switch to development or production resources when you need to test with real bank connections or go live.
How do I handle Plaid's rate limits in Retool?
Plaid enforces rate limits per Item and per application. For internal tools, this is rarely an issue since you're making queries on demand rather than bulk-fetching all accounts continuously. If you're building a workflow that checks many Items in sequence, add a delay between requests using Retool Workflow's Wait block. For high-volume use cases, implement a caching layer using Retool Database to store the latest balance and transaction data and refresh it on a schedule.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation