---
description: >-
  Link a US bank account through Plaid to obtain a tokenized account for ACH
  pulls — create a Plaid Link token via POST /providers/plaid, then verify and
  tokenize the account via POST /check-account.
---

# 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`](../../check-account.md) | 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](ach-bank-account.md), 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](ach-bank-account.md) 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. |

```bash
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.

```json
{
  "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](https://plaid.com/docs/link/) for the web, iOS, Android, and React Native SDKs.

```javascript
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](../../check-account.md). This verifies the account and returns the token you use in payments.

```
POST https://{FQDN}/check-account
```

```bash
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:

```json
{
  "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](../../check-account.md) 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.

```bash
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-bank-account.md#response) — ACH payments are auto-captured, so a successful result returns status `CAPTURED`.

## What's Next

- [ACH (Bank Account)](ach-bank-account.md) — Pull funds with raw routing/account numbers (no Plaid)
- [Payment](../README.md) — Core payment operations and payload structure
- [Webhooks](../../webhooks.md) — Receive payment status updates asynchronously
