Plaid integration allows your customers to securely connect their bank accounts without manually entering account numbers and routing information. Grid handles the complete Plaid Link flow, automatically creating external accounts when customers authenticate their banks.
Plaid integration requires Grid to manage your Plaid configuration. Contact
support to enable Plaid for your platform.
Overview
The Plaid flow involves collaboration between your platform, Grid, Plaid, and the customer’s bank:
- Request link token: Your platform requests a Plaid Link token from Grid for a specific customer
- Initialize Plaid Link: Display Plaid Link UI to your customer using the link token
- Customer authenticates: Customer selects their bank and authenticates using Plaid Link
- Exchange tokens: Plaid returns a public token; your platform sends it to Grid’s callback URL
- Async processing: Grid exchanges the public token with Plaid and retrieves account details
- External account created: Grid creates the external account and sends a webhook notification. The external account is available for transfers and payments
Request a Plaid Link token
To initiate the Plaid flow, request a link token from Grid:
curl -X POST 'https://api.lightspark.com/grid/2025-10-13/plaid/link-tokens' \
-H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' \
-H 'Content-Type: application/json' \
-d '{
"customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001"
}'
Response:
{
"linkToken": "link-sandbox-af1a0311-da53-4636-b754-dd15cc058176",
"expiration": "2025-10-05T18:30:00Z",
"callbackUrl": "https://api.lightspark.com/grid/2025-10-13/plaid/callback/link-sandbox-af1a0311-da53-4636-b754-dd15cc058176",
"requestId": "req_abc123def456"
}
Store the callbackUrl
when you request the link token so you can retrieve it later when exchanging the public token.
Key response fields:
linkToken
: Use this to initialize Plaid Link in your frontend
callbackUrl
: Where to POST the public token after Plaid authentication completes. The URL follows the pattern https://api.lightspark.com/grid/{version}/plaid/callback/{linkToken}
. While you can construct this manually, we recommend using the provided URL for forward compatibility.
expiration
: Link tokens typically expire after 4 hours
requestId
: Unique identifier for debugging purposes
Link tokens are single-use and will expire. If the customer doesn’t complete
the flow, you’ll need to request a new link token.
Initialize Plaid Link
Display the Plaid Link UI to your customer using the link token. The implementation varies by platform:
Install the appropriate Plaid SDK for your platform:
- React:
npm install react-plaid-link
- React Native:
npm install react-native-plaid-link-sdk
- Vanilla JS: Include the Plaid script tag as shown above
React
React Native
Vanilla JavaScript
import { usePlaidLink } from 'react-plaid-link';
function BankAccountConnector({ linkToken, onSuccess }) {
const { open, ready } = usePlaidLink({
token: linkToken,
onSuccess: async (publicToken, metadata) => {
console.log('Plaid authentication successful');
// Send public token to YOUR backend endpoint
await fetch('/api/plaid/exchange-token', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
publicToken: publicToken,
accountId: metadata.account_id, // Optional
}),
});
onSuccess();
},
onExit: (error, metadata) => {
if (error) {
console.error('Plaid Link error:', error);
}
console.log('User exited Plaid Link');
},
});
return (
<button onClick={() => open()} disabled={!ready}>
Connect your bank account
</button>
); }
Exchange the public token on your backend
Create a backend endpoint that receives the public token from your frontend and forwards it to Grid’s callback URL:
// Backend endpoint: POST /api/plaid/exchange-token
app.post('/api/plaid/exchange-token', async (req, res) => {
const { publicToken, accountId } = req.body;
const customerId = req.user.gridCustomerId; // From your auth
try {
// Get the callback URL (you stored this when requesting the link token)
const callbackUrl = await getStoredCallbackUrl(customerId);
// Forward to Grid's callback URL with proper authentication
const response = await fetch(callbackUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
publicToken: publicToken,
accountId: accountId,
}),
});
if (!response.ok) {
throw new Error(`Grid API error: ${response.status}`);
}
const result = await response.json();
res.json({ success: true, message: result.message });
} catch (error) {
console.error('Error exchanging token:', error);
res.status(500).json({ error: 'Failed to process bank account' });
}
});
Response from Grid (HTTP 202 Accepted):
{
"message": "External account creation initiated. You will receive a webhook notification when complete.",
"requestId": "req_def456ghi789"
}
A 202 Accepted
response indicates Grid has received the token and is
processing it asynchronously. The external account will be created in the
background.
Handle webhook notification
After Grid creates the external account, you’ll receive an ACCOUNT_STATUS
webhook.
{
"type": "ACCOUNT_STATUS",
"timestamp": "2025-01-15T14:32:10Z",
"webhookId": "Webhook:019542f5-b3e7-1d02-0000-0000000000ac",
"customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001",
"account": {
"accountId": "ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123",
"status": "ACTIVE",
"currency": "USD",
"platformAccountId": "user_123_primary_bank",
"accountInfo": {
"accountType": "US_ACCOUNT",
"accountNumber": "123456789",
"routingNumber": "021000021",
"accountCategory": "CHECKING",
"bankName": "Chase Bank",
"beneficiary": {
"beneficiaryType": "INDIVIDUAL",
"fullName": "John Doe",
"birthDate": "1990-01-15",
"nationality": "US",
"address": {
"line1": "123 Main Street",
"city": "San Francisco",
"state": "CA",
"postalCode": "94105",
"country": "US"
}
}
}
}
}
Error handling
Handle common error scenarios:
User exits Plaid Link
const { open } = usePlaidLink({
token: linkToken,
onExit: (error, metadata) => {
if (error) {
console.error("Plaid error:", error);
// Show user-friendly error message
setError("Unable to connect to your bank. Please try again.");
} else {
// User closed the modal without completing
console.log("User exited without connecting");
}
},
});
Next steps