Linking Bank Accounts with Plaid
Plaid lets a customer connect their US bank account by logging in with their bank credentials, instead of typing a routing and account number by hand. The gateway wraps the Plaid Link flow with:
| Method | Endpoint | Description |
|---|---|---|
POST | /providers/plaid | Create a Plaid Link token to initialize Plaid Link on the client |
POST | /check-account | Verify the selected account and exchange the Plaid public token for a reusable accountTokenId |
The result of a completed Plaid link is a tokenized bank account that you pass into a payment as paymentMethod.accountTokenId — the same PULL flow as ACH, but without handling raw bank numbers.
When to use Plaid vs. raw ACH: Use Plaid when you want instant account verification and a better conversion experience, and to avoid collecting/storing raw routing and account numbers. Use direct ACH when you already hold validated bank details.
Flow Overview
1. POST /providers/plaid 2. Open Plaid Link 3. POST /check-account
(server-side) (client-side) (server-side)
│ │ │
▼ ▼ ▼
Gateway returns a Customer logs in, selects Exchange the Plaid public
Plaid Link token → an account; Plaid returns → token for a verified,
(token) a public token + account id tokenized accountTokenId
4. POST /v2/payment
Use the accountTokenId in paymentMethod to pull funds via ACH
Step 1 — Create a Plaid Link Token
POST https://{FQDN}/providers/plaid
Headers:
| Header | Value |
|---|---|
Authorization | Bearer {accessToken} |
Content-Type | application/json |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
phoneNumber | string | ❌ | Customer phone number (E.164). Used by Plaid to pre-fill and accelerate the Link flow via Plaid Layer when available. |
androidPackageName | string | ❌ | Your Android app's package name. Required only when launching Plaid Link from an Android app, so Plaid can hand control back to the correct app. |
curl -X POST https://{FQDN}/providers/plaid \
-H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIs...' \
-H 'Content-Type: application/json' \
-d '{
"phoneNumber": "+15555550123",
"androidPackageName": "com.example.app"
}'
Response (200)
A successful call returns the Plaid Link token used to initialize Plaid Link on the client.
{
"token": "link-sandbox-7f8e9d0c-1a2b-3c4d-...",
"expiration": "2026-02-12T20:27:26Z",
"requestId": "JWk6bG8b0zsLXWZ"
}
| Field | Type | Description |
|---|---|---|
token | string | The Plaid Link token. Pass it to Plaid Link on the client (Step 2) to start the bank-selection flow. |
expiration | string | ISO 8601 timestamp after which the token is no longer valid — create a new one if it expires before the customer completes Link. |
requestId | string | Correlation ID for this request — include it when contacting support for tracing. |
Step 2 — Open Plaid Link (Client-Side)
Initialize Plaid Link with the token returned above. The customer authenticates with their bank and picks an account. See Plaid Link documentation for the web, iOS, Android, and React Native SDKs.
const handler = Plaid.create({
token: token, // the `token` from POST /providers/plaid
onSuccess: (publicToken, metadata) => {
// Link complete — Plaid returns a public token + the selected account id.
// Send both to your backend to verify and tokenize the account.
verifyAccount(publicToken, metadata.account_id);
},
onExit: (err, metadata) => { /* handle abandonment */ }
});
handler.open();
Plaid's onSuccess callback gives you a public token and the selected account id — these become accountCheckToken and accountCheckId in the next step.
Step 3 — Verify and Tokenize the Account
Exchange the Plaid public token for a reusable accountTokenId via Check Account. This verifies the account and returns the token you use in payments.
POST https://{FQDN}/check-account
curl -X POST https://{FQDN}/check-account \
-H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIs...' \
-H 'Content-Type: application/json' \
-d '{
"externalPaymentId": "CHECK_ACCOUNT_0a022f78-14b6-4f6b-8ddd-d1c9be7c4dcc",
"sender": {
"paymentMethod": {
"type": "BANK_DEPOSIT",
"countryCode": "US",
"accountCheckToken": "public-sandbox-57f6f60a-e637-4582-8858-b871e59f5f4e",
"accountCheckId": "X4943dk1W1CbvoJ6r3vPTQrLgWlmyet1vm1on"
}
}
}'
The response returns status: "VERIFIED" and the accountTokenId you'll use to pull funds:
{
"paymentId": "cf8600be-ec46-4ed9-a00b-ece348004ca7",
"externalPaymentId": "CHECK_ACCOUNT_0a022f78-14b6-4f6b-8ddd-d1c9be7c4dcc",
"accountTokenId": "62d37fd8-89d0-43ca-900f-a75a6e871fe5",
"approved": true,
"status": "VERIFIED",
"bankAccountName": "Chase",
"accountNumberMasked": "************0000",
"routingNumberMasked": "*****1533"
}
See Check Account for the full request and response field reference.
Step 4 — Pull Funds Using the Linked Account
Use the accountTokenId from Step 3 in a standard PULL payment. No routing/account numbers are sent — the token references the verified account.
curl -X POST https://{FQDN}/v2/payment \
-H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIs...' \
-H 'Content-Type: application/json' \
-d '{
"externalPaymentId": "plaid-order-001",
"ipAddress": "203.0.113.42",
"paymentType": "PULL",
"capture": true,
"amount": {
"total": 19.99,
"currency": "USD"
},
"sender": {
"firstName": "John",
"lastName": "Smith",
"paymentMethod": {
"type": "BANK_DEPOSIT",
"accountTokenId": "62d37fd8-89d0-43ca-900f-a75a6e871fe5"
}
}
}'
The payment response is identical to a direct ACH pull — ACH payments are auto-captured, so a successful result returns status CAPTURED.
What's Next
- ACH (Bank Account) — Pull funds with raw routing/account numbers (no Plaid)
- Payment — Core payment operations and payload structure
- Webhooks — Receive payment status updates asynchronously
