plaid

Plaid banking API expert for financial data integration. Covers Plaid Link, Auth (account/routing numbers), Transactions, Identity verification, Balance checking, and webhooks. Build fintech apps with bank connections, ACH transfers, and transaction history. Triggers on Plaid, banking API, Plaid Link, bank connection, ACH, financial data, transaction history.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "plaid" with this command: npx skills add raintree-technology/claude-starter/raintree-technology-claude-starter-plaid

Plaid Banking API Expert

Plaid connects applications to users' bank accounts for financial data access, payments, and identity verification.

When to Use

  • Connecting bank accounts in fintech apps
  • Implementing Plaid Link flow
  • Retrieving transactions, balances, or account info
  • Setting up ACH transfers
  • Identity/income verification
  • Handling Plaid webhooks

Core Products

ProductPurpose
AuthBank account/routing numbers for ACH
TransactionsTransaction history (up to 24 months)
IdentityVerify user via bank account ownership
BalanceReal-time account balances
InvestmentsHoldings from investment accounts
LiabilitiesLoan and credit card data

Quick Start

1. Install SDK

npm install plaid react-plaid-link

2. Create Plaid Client

import { Configuration, PlaidApi, PlaidEnvironments } from "plaid";

const client = new PlaidApi(
  new Configuration({
    basePath: PlaidEnvironments.sandbox,
    baseOptions: {
      headers: {
        "PLAID-CLIENT-ID": process.env.PLAID_CLIENT_ID,
        "PLAID-SECRET": process.env.PLAID_SECRET,
      },
    },
  })
);

3. Create Link Token (Server)

// POST /api/plaid/create-link-token
export async function POST(req: Request) {
  const response = await client.linkTokenCreate({
    user: { client_user_id: userId },
    client_name: "Your App",
    products: ["auth", "transactions"],
    country_codes: ["US"],
    language: "en",
  });

  return Response.json({ link_token: response.data.link_token });
}

4. Plaid Link (Client)

import { usePlaidLink } from "react-plaid-link";

function ConnectBank({ linkToken }) {
  const { open, ready } = usePlaidLink({
    token: linkToken,
    onSuccess: async (public_token, metadata) => {
      // Exchange for access_token on server
      await fetch("/api/plaid/exchange-token", {
        method: "POST",
        body: JSON.stringify({ public_token }),
      });
    },
  });

  return (
    <button onClick={() => open()} disabled={!ready}>
      Connect Bank Account
    </button>
  );
}

5. Exchange Token (Server)

// POST /api/plaid/exchange-token
export async function POST(req: Request) {
  const { public_token } = await req.json();

  const response = await client.itemPublicTokenExchange({
    public_token,
  });

  // Store access_token securely (encrypted in database)
  await db.users.update(userId, {
    plaid_access_token: response.data.access_token,
    plaid_item_id: response.data.item_id,
  });

  return Response.json({ success: true });
}

Data Retrieval

Get Auth (Account/Routing Numbers)

const response = await client.authGet({ access_token });

const ach = response.data.numbers.ach[0];
console.log("Account:", ach.account);
console.log("Routing:", ach.routing);

Get Transactions

const response = await client.transactionsGet({
  access_token,
  start_date: "2024-01-01",
  end_date: "2024-12-31",
});

let transactions = response.data.transactions;

// Handle pagination
while (transactions.length < response.data.total_transactions) {
  const more = await client.transactionsGet({
    access_token,
    start_date: "2024-01-01",
    end_date: "2024-12-31",
    offset: transactions.length,
  });
  transactions = transactions.concat(more.data.transactions);
}

Transaction object:

{
  transaction_id: "abc123",
  amount: 12.34,           // Positive = outflow
  date: "2024-11-16",
  name: "Starbucks",
  merchant_name: "Starbucks",
  category: ["Food and Drink", "Coffee Shop"],
  pending: false,
}

Get Balance

const response = await client.accountsBalanceGet({ access_token });

response.data.accounts.forEach((account) => {
  console.log(`${account.name}: $${account.balances.current}`);
});

Get Identity

const response = await client.identityGet({ access_token });

const owner = response.data.accounts[0].owners[0];
console.log("Name:", owner.names[0]);
console.log("Email:", owner.emails[0].data);
console.log("Phone:", owner.phone_numbers[0].data);

Webhooks

Setup Endpoint

// POST /api/plaid/webhook
export async function POST(req: Request) {
  const { webhook_type, webhook_code, item_id } = await req.json();

  switch (webhook_type) {
    case "TRANSACTIONS":
      if (webhook_code === "DEFAULT_UPDATE") {
        // New transactions available - fetch them
        await syncTransactions(item_id);
      }
      break;

    case "ITEM":
      if (webhook_code === "ERROR") {
        // Connection issue - prompt user to re-authenticate
        await notifyUserReauth(item_id);
      }
      break;
  }

  return Response.json({ received: true });
}

Key webhook events:

EventMeaning
TRANSACTIONS: INITIAL_UPDATEFirst batch ready
TRANSACTIONS: DEFAULT_UPDATENew transactions
ITEM: ERRORConnection issue
ITEM: PENDING_EXPIRATIONCredentials expiring

Environments

EnvironmentUse CaseBase Path
SandboxTestingPlaidEnvironments.sandbox
DevelopmentLimited live (100 connections)PlaidEnvironments.development
ProductionLivePlaidEnvironments.production

Sandbox Test Credentials

  • Username: user_good
  • Password: pass_good
  • MFA: 1234

Error Handling

Re-authentication (Update Mode)

When credentials expire:

// Create link token for update mode
const response = await client.linkTokenCreate({
  user: { client_user_id: userId },
  client_name: "Your App",
  access_token: existingAccessToken, // Triggers update mode
  country_codes: ["US"],
  language: "en",
});

Common Errors

ErrorSolution
ITEM_LOGIN_REQUIREDRe-authenticate via Link update mode
RATE_LIMIT_EXCEEDEDImplement exponential backoff
PRODUCT_NOT_READYWait for webhook or retry

Security Best Practices

DO:

  • Store access tokens encrypted in database
  • Use environment variables for credentials
  • Verify webhook signatures
  • Use HTTPS for all endpoints

DON'T:

  • Expose secret keys client-side
  • Log access tokens
  • Store credentials in code

Next.js App Router Example

// app/api/plaid/create-link-token/route.ts
import { NextResponse } from "next/server";
import { Configuration, PlaidApi, PlaidEnvironments } from "plaid";

const client = new PlaidApi(
  new Configuration({
    basePath: PlaidEnvironments.sandbox,
    baseOptions: {
      headers: {
        "PLAID-CLIENT-ID": process.env.PLAID_CLIENT_ID!,
        "PLAID-SECRET": process.env.PLAID_SECRET!,
      },
    },
  })
);

export async function POST(req: Request) {
  const session = await getSession();

  const response = await client.linkTokenCreate({
    user: { client_user_id: session.user.id },
    client_name: "Your App",
    products: ["auth", "transactions"],
    country_codes: ["US"],
    language: "en",
    webhook: `${process.env.NEXT_PUBLIC_URL}/api/plaid/webhook`,
  });

  return NextResponse.json({ link_token: response.data.link_token });
}

Implementation Checklist

  • Sign up for Plaid account
  • Get client ID and secret
  • Install plaid and react-plaid-link
  • Set environment variables
  • Create link token endpoint
  • Implement token exchange endpoint
  • Integrate Plaid Link on frontend
  • Store access tokens securely
  • Set up webhook endpoint
  • Handle re-authentication errors
  • Test with sandbox credentials

Resources

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

toon-formatter

No summary provided by upstream source.

Repository SourceNeeds Review
General

helius

No summary provided by upstream source.

Repository SourceNeeds Review
General

supabase-expert

No summary provided by upstream source.

Repository SourceNeeds Review