Overview

Introduction

With this quickstart guide, we’ve tried to make it as easy as possible to get up and running with Plaid.

If you have any questions along the way, please head to the Help Center or refer to the API documentation.

We’ll start with an overview of Plaid and then jump start your integration with a step-by-step guide and code samples in Ruby, Python, and Node. If you’d like to follow along (and test it out!), clone the quickstart repository for the complete example apps. You’ll want to sign up for free API keys to get started.

Running the walkthrough app

Running the walkthrough app

git clone https://github.com/plaid/quickstart.git
cd quickstart/node
npm install

# Start the Quickstart with your API keys from the Dashboard
# https://dashboard.plaid.com/account/keys
#
# PLAID_PRODUCTS is a comma-separated list of products to use when
# initializing Link, see https://plaid.com/docs/#item-product-access
# for complete list.
#
# PLAID_COUNTRY_CODES is a comma-separated list of countries to use when
# initializing Link, see plaid.com/docs/faq/#does-plaid-support-international-bank-accounts-
# for a complete list

PLAID_CLIENT_ID='CLIENT_ID' \
PLAID_SECRET='SECRET' \
PLAID_ENV='sandbox' \
PLAID_PRODUCTS='transactions' \
PLAID_COUNTRY_CODES='US' \
node index.js
# Go to http://localhost:8000
          

git clone https://github.com/plaid/quickstart.git
cd quickstart/ruby

# Install dependencies
bundle

# Start the Quickstart with your API keys from the Dashboard
# https://dashboard.plaid.com/account/keys
#
# PLAID_PRODUCTS is a comma-separated list of products to use when
# initializing Link, see https://plaid.com/docs/#item-product-access
# for complete list.
#
# PLAID_COUNTRY_CODES is a comma-separated list of countries to use when
# initializing Link, see plaid.com/docs/faq/#does-plaid-support-international-bank-accounts-
# for a complete list

PLAID_CLIENT_ID='CLIENT_ID' \
PLAID_SECRET='SECRET' \
PLAID_ENV='sandbox' \
PLAID_PRODUCTS='transactions' \
PLAID_COUNTRY_CODES='US' \
ruby app.rb
# Go to http://localhost:4567
          

git clone https://github.com/plaid/quickstart.git
cd quickstart/python

# For virtualenv users
# virtualenv venv
# source venv/bin/activate

pip install -r requirements.txt

# Start the Quickstart with your API keys from the Dashboard
# https://dashboard.plaid.com/account/keys
#
# PLAID_PRODUCTS is a comma-separated list of products to use when
# initializing Link, see https://plaid.com/docs/#item-product-access
# for complete list.
#
# PLAID_COUNTRY_CODES is a comma-separated list of countries to use when
# initializing Link, see plaid.com/docs/faq/#does-plaid-support-international-bank-accounts-
# for a complete list

PLAID_CLIENT_ID='CLIENT_ID' \
PLAID_SECRET='SECRET' \
PLAID_ENV='sandbox' \
PLAID_PRODUCTS='transactions' \
PLAID_COUNTRY_CODES='US' \
python server.py
# Go to http://localhost:5000
          

Running the walkthrough app for the OAuth flow

Some institutions (especially European institutions) require an OAuth redirect authentication flow, where the end user is redirected to the bank's website or mobile app to authenticate. For this flow, you should provide one additional configuration parameter, PLAID_REDIRECT_URI.

Some European institutions require an OAuth redirect authentication flow, where the end user is redirected to the bank's website or mobile app to authenticate. For this flow, you should provide an additional configuration parameter, PLAID_REDIRECT_URI.

You will need to configure the PLAID_REDIRECT_URI for your client ID through the Plaid developer dashboard at https://dashboard.plaid.com/team/api. For more details, please refer to the OAuth docs.

Running the walkthrough app for the OAuth flow

git clone https://github.com/plaid/quickstart.git
cd quickstart/node
npm install

# You will need to configure the PLAID_REDIRECT_URI for
# your client ID through the Plaid developer dashboard at
# https://dashboard.plaid.com/team/api.
#
# Start the Quickstart with your API keys from the Dashboard
# https://dashboard.plaid.com/account/keys
#
# PLAID_PRODUCTS is a comma-separated list of products to use when
# initializing Link, see https://plaid.com/docs/#item-product-access
# for complete list.

PLAID_CLIENT_ID='CLIENT_ID' \
PLAID_SECRET='SECRET' \
PLAID_ENV='sandbox' \
PLAID_PRODUCTS='transactions' \
PLAID_COUNTRY_CODES='GB,FR,ES,IE,NL' \
PLAID_REDIRECT_URI='http://localhost:8000/oauth-response.html' \
node index.js

# Go to http://localhost:8000
          

git clone https://github.com/plaid/quickstart.git
cd quickstart/ruby

# Install dependencies
bundle

# You will need to configure the PLAID_REDIRECT_URI for
# your client ID through the Plaid developer dashboard at
# https://dashboard.plaid.com/team/api.
#
# Start the Quickstart with your API keys from the Dashboard
# https://dashboard.plaid.com/account/keys
#
# PLAID_PRODUCTS is a comma-separated list of products to use when
# initializing Link, see https://plaid.com/docs/#item-product-access
# for complete list.

PLAID_CLIENT_ID='CLIENT_ID' \
PLAID_SECRET='SECRET' \
PLAID_ENV='sandbox' \
PLAID_PRODUCTS='transactions' \
PLAID_COUNTRY_CODES='GB,FR,ES,IE,NL' \
PLAID_REDIRECT_URI='http://localhost:4567/oauth-response.html' \
ruby app.rb

# Go to http://localhost:4567
          

git clone https://github.com/plaid/quickstart.git
cd quickstart/python

# For virtualenv users
# virtualenv venv
# source venv/bin/activate

pip install -r requirements.txt

# You will need to configure the PLAID_REDIRECT_URI for
# your client ID through the Plaid developer dashboard at
# https://dashboard.plaid.com/team/api.
#
# Start the Quickstart with your API keys from the Dashboard
# https://dashboard.plaid.com/account/keys
#
# PLAID_PRODUCTS is a comma-separated list of products to use when
# initializing Link, see https://plaid.com/docs/#item-product-access
# for complete list.

PLAID_CLIENT_ID='CLIENT_ID' \
PLAID_SECRET='SECRET' \
PLAID_ENV='sandbox' \
PLAID_PRODUCTS='transactions' \
PLAID_COUNTRY_CODES='GB,FR,ES,IE,NL' \
PLAID_REDIRECT_URI='http://localhost:5000/oauth-response.html' \
python server.py

# Go to http://localhost:5000
          

Platform Overview

To help you get oriented with Plaid’s API and what it can help you do, let’s start by defining some basics:

API Keys

You have two different API keys

View in Dashboard

secret and client_id

private identifiers that are required for accessing any financial data

these should never be shared in client-side code

Though client_id is the same across all environments, you have a unique secret for each API environment. Use our Sandbox and Development environments to build out and test your integration with simulated and live users, respectively. You’ll move over to our Production environment once you’re ready to go live!

When using our Sandbox environment, use user_good as the username and pass_good for the password. See the docs for more.

API environments

  • sandbox: Stateful sandbox environment; use test credentials and build out and test your integration
  • development: Test your integration with live credentials; you will need to request access before you can access our Development environment
  • production: Production API environment; all requests are billed

Item overview

Most API requests interact with an Item, a set of credentials (map of key value pairs) associated with a financial institution. A single end-user of your application might have accounts at different financial institutions, which means they would have multiple different Items.

Your user can have multiple Items, or sets of credentials

Each Item can have many associated accounts, which hold information such as balance, name, and account type

Credit and depository accounts may also have transactions associated with them.

Your users create Items through Link, a drop-in module that handles the credential and MFA validation process. Once an Item is created, Link passes a public_token that you exchange for an access_token from your backend app server.

That access_token and item_id uniquely identify the Item. You use the access_token along with your client_id and secret to access products available for an Item and make changes to it over time.

Read on for more information about each of our products and how you create and access data for an Item.

Plaid Products

Accessing data

Once you create an Item, you can then access data—such as transaction data and account and routing numbers—using our API endpoints. You access data for an Item using the Item’s access_token, which is specific to your API keys and cannot be shared or used by any other API keys. By default, an access_token never expires, but you can rotate it.

You can use the /item/get endpoint to inspect the status of an Item and see a list of all available products as well as the ones you're actively using. You can then call different product endpoints to retrieve data. Due to the complexity of communicating with financial institutions, the initial product data request may take some time.

Note: The payment_initiation product works a bit differently than our data access products. See the docs for more details. Additionally, the Payment Initiation quickstart flow is described below.

To integrate an existing architecture with the Plaid API, you might try using a table to store access_token and item_id combinations that map to individual users in your system.

Initiating payments (in the UK)

You can run the Payment Initiation quickstart flow as follows:

Initialize Quickstart for Payment Initiation

PLAID_CLIENT_ID='CLIENT_ID' \
PLAID_SECRET='SECRET' \
PLAID_ENV='sandbox' \
PLAID_PRODUCTS='payment_initiation' \
PLAID_COUNTRY_CODES='GB' \
node index.js

# Go to http://localhost:8000
          

PLAID_CLIENT_ID='CLIENT_ID' \
PLAID_SECRET='SECRET' \
PLAID_ENV='sandbox' \
PLAID_PRODUCTS='payment_initiation' \
PLAID_COUNTRY_CODES='GB' \
ruby app.rb

# Go to http://localhost:4567
          

PLAID_CLIENT_ID='CLIENT_ID' \
PLAID_SECRET='SECRET' \
PLAID_ENV='sandbox' \
PLAID_PRODUCTS='payment_initiation' \
PLAID_COUNTRY_CODES='GB' \
python server.py

# Go to http://localhost:5000
          


The quickstart code takes care of generating a link_token configured for payment initiation for you in the sandbox environment. This entails sequentially creating a payment recipient, a payment, and finally a link token. Once the link_token is generated, the quickstart then initializes Link with this link_token.

You can use the /payment_initiation/payment/get endpoint to inspect the status of a Payment.

Products overview

AuthRetrieve account and routing numbers for ACH authentication. No micro-deposits required.
TransactionsClean transaction data going back as far as 24 months. Transaction data may include context such as geolocation, merchant, and category information.
IdentityIdentity information on file with the bank. Reduce fraud by comparing user-submitted data to validate identity.
This product has to be enabled separately in Development and Production. Contact us for more information
BalanceCheck balances in real time to prevent non-sufficient funds fees.
IncomeVerify employment and income information.
This product has to be enabled separately in Development and Production. Contact us for more information
AssetsStreamline borrower experiences by verifying assets, including account balances, transaction histories and account holder identity information.
This product has to be enabled separately in Development and Production. Contact us for more information
InvestmentsGain insight into a user's investment accounts, including account balances, holdings, and transactions.
This product has to be enabled separately in Development and Production. Contact us for more information
LiabilitiesAccess liabilities data for student loans and credit cards.
This product has to be enabled separately in Development and Production. Contact us for more information
Payment Initiation (Europe)The UK Payment Initiation API enables payment transfers within your app.
This product has to be enabled separately in Development and Production. Contact us for more information

Institution coverage

As we noted above, an Item represents accounts at a given financial institution, bank, or credit union. Plaid supports thousands of different institutions, and we’re constantly working to add more. Though integration information does change, Link stays up-to-date with Plaid’s latest institution coverage at all times and makes it easy for users to find their intended institution.

Because product coverage does vary by institution, we recommend initializing Link with all the products that your integration will need.


image of Link service

Now that you have API keys and know the basics of the Plaid API, it's time to integrate with Plaid Link, which will handle credential validation, multi-factor authentication, and error handling for each institution that we support. Link works across all modern browsers and platforms, including on iOS and Android.

You can also customize parts of Link's flow—including some copy elements, the institution select view, and the background color—and enable additional features like the Select Account view straight from the Dashboard. You can preview your changes in real-time and then publish them instantly once you're ready to go live.

Link allows your users to create Items and also update an Item if it goes into an error state, such as when they change their passwords or MFA information. You can initialize Link with multiple products, such as Transactions and Auth, making it easy to use multiple Plaid products right away.

Plaid is processor-agnostic, but if you need help actually processing ACH payments, Plaid and Stripe have partnered to offer frictionless, tokenized money transfers—no need to handle an account or routing number. Use Plaid Link to instantly authenticate your customer’s account and automatically generate a Stripe bank account token so that you can accept ACH payments via Stripe’s ACH API.

Item creation flow

An end-to-end integration involves client-side and server-side configurations. Here is a brief overview of how you'll be creating Items with Link.

The Plaid flow begins when your user wants to connect their bank account to your app.

1

Make a request to create a link_token and pass the temporary token to your app's client.

2

Use the link_token to open Link for your user. In the onSuccess callback, send the temporary public_token to your app's server.

3

Make a request to exchange the public_token for a permanent access_token and item_id for the new Item.

4

Store the access_token and use it to make product requests for your user's Item.

Link’s integration is a pure JavaScript approach that you trigger via your own client-side code, and specify callbacks to handle the public_token after the user has authenticated and created an Item.

Using Link requires an understanding of how Plaid access_tokens and public_tokens work and should be used. Read on for a breakdown of the differences.

access_tokens and public_tokens

A public_token (which is returned in your Link onSuccess() callback) should be passed to your server, which will exchange it for an access_token. public_tokens are one-time use tokens and expire after 30 minutes.

An access_token is used to access product data for an Item. This should be stored securely, and never in client-side code. This is used to make authenticated requests to the Plaid API for the user. By default, access_tokens do not expire, though you can rotate them; if it winds up in an error state, the access_token will work again if the Item’s error is resolved. Each access_token is unique to a specific Item, and cannot be used to access other Items.

Note: access_token and public_token are not applicable for the payment_initiation product. See the payment initiation docs for details.

To integrate an existing architecture with the Plaid API, you might try a one-to-many relationship between your user and Items. For each Item, we recommend storing the item_id and access_token.

User authentication, Item creation, and the public_token

Once you’ve got your client_id and secret from the Dashboard, the Link JavaScript integration is fairly straightforward.

Link allows you to create Items with multiple initial products. Link will only show the user institutions that support all of the products you specify. In order to specify these products, you will need to first create a link_token by calling the /link/token/create endpoint and passing in the products you want. Once this link_token is created, use it to initialize the Link module as shown below. For a full list of valid initial products, visit the link/token/create docs.

We'll use a client library to help us make the API call.

In our example app, we’ll go ahead and open the 'Institution Select' view, so the user can choose which financial institution to authenticate. The global Plaid object has a .create({}) method, which accepts our configured link_token.

We’ll use this to create an Item with transactions. You can still access other products for the Item later, even if you didn't initialize Link with them. Once the user has successfully authenticated, we’ll pass the public_token from our client-side code to the server.

Once a user goes through this flow, an Item will be created with Transactions information, and the onSuccess() callback will pass the public_token back to the server. The server handles the exchange of the public_token for the access_token.

Exchanging the public token for an access token

The next step is to exchange our public_token for an access_token and item_id. The access_token will allow us to make authenticated calls to the Plaid API. Doing so is as easy as calling the /item/public_token/exchange endpoint from our server-side handler. The item_id is used to identify Items in webhooks.

Save the access_token and item_id in a secure datastore, as they’re used to access Item data and identify webhooks, respectively. A public_token is a one-time use token, so there is no need to store it.


'use strict';

var util = require('util');

var envvar = require('envvar');
var express = require('express');
var bodyParser = require('body-parser');
var moment = require('moment');
var plaid = require('plaid');

var APP_PORT = envvar.number('APP_PORT', 8000);
var PLAID_CLIENT_ID = envvar.string('PLAID_CLIENT_ID');
var PLAID_SECRET = envvar.string('PLAID_SECRET');
var PLAID_ENV = envvar.string('PLAID_ENV', 'sandbox');

// We store the access_token in memory
// In production, store it in a secure persistent data store
var ACCESS_TOKEN = null;
var PUBLIC_TOKEN = null;
var ITEM_ID = null;

// Initialize the Plaid client
var client = new plaid.Client({
  clientID: PLAID_CLIENT_ID,
  secret: PLAID_SECRET,
  env: plaid.environments[PLAID_ENV],
  options: {version: '2018-05-22'}
});

var app = express();
app.use(express.static('public'));
app.set('view engine', 'ejs');
app.use(bodyParser.urlencoded({
  extended: false
}));
app.use(bodyParser.json());

app.get('/', function(request, response, next) {
  response.render('index.ejs');
});

app.post('/get_access_token', function(request, response, next) {
  PUBLIC_TOKEN = request.body.public_token;
  client.exchangePublicToken(PUBLIC_TOKEN, function(error, tokenResponse) {
    if (error != null) {
      var msg = 'Could not exchange public_token!';
      console.log(msg + '\n' + JSON.stringify(error));
      return response.json({
        error: msg
      });
    }
    ACCESS_TOKEN = tokenResponse.access_token;
    ITEM_ID = tokenResponse.item_id;
    prettyPrintResponse(tokenResponse);
    response.json({
      access_token: ACCESS_TOKEN,
      item_id: ITEM_ID,
      error: false
    });
  });
});
          

require 'date'
require 'json'
require 'plaid'
require 'sinatra'

set :public_folder, File.dirname(__FILE__) + '/static'

client = Plaid::Client.new(env: ENV['PLAID_ENV'],
                           client_id: ENV['PLAID_CLIENT_ID'],
                           secret: ENV['PLAID_SECRET'])

access_token = nil

get '/' do
  erb :index
end

post '/get_access_token' do
  exchange_token_response =
    client.item.public_token.exchange(params['public_token'])
  access_token = exchange_token_response['access_token']
  pretty_print_response(exchange_token_response)

  content_type :json
  exchange_token_response.to_json
end
          

import os
import datetime
import plaid
import json
from flask import Flask
from flask import render_template
from flask import request
from flask import jsonify

app = Flask(__name__)

PLAID_CLIENT_ID = os.getenv('PLAID_CLIENT_ID')
PLAID_SECRET = os.getenv('PLAID_SECRET')
# Use 'sandbox' to test with Plaid's Sandbox environment (username: user_good,
# password: pass_good)
# Use development to test with live users and credentials and production
# to go live
PLAID_ENV = os.getenv('PLAID_ENV', 'sandbox')


client = plaid.Client(
    client_id=PLAID_CLIENT_ID,
    secret=PLAID_SECRET,
    environment=PLAID_ENV,
    api_version='2018-05-22'
)

@app.route('/')
def index():
    return render_template('index.ejs')

access_token = None

@app.route('/get_access_token', methods=['POST'])
def get_access_token():
    global access_token
    public_token = request.form['public_token']
    exchange_response = \
        client.Item.public_token.exchange(public_token)
    access_token = exchange_response['access_token']

    pretty_print_response(exchange_response)

    return jsonify(exchange_response)

          

At this point, you’ve successfully walked a user through creating an Item, gotten the public_token from Link, and exchanged it for an access_token and item_id.

You can access other products available for the Item, but the initial request may take some time as Plaid must communicate with the financial institution to retrieve the data.

Note that we need to persist the access_token and item_id somewhere. The public_token does not need to be persisted as it is only one time use.

Now that we have the Item's access_token, we can access Transaction and Auth information. Below, we’ll show how to fetch this data.

Accessing Item Data

Pulling Auth data

By posting to the /auth/get endpoint, we’ll see high-level account information for all available accounts and account numbers for checking and savings accounts.

Let’s add another server-side handler to our example app that will retrieve Auth data for the Item by using its access_token:

/auth/get request

// Retrieve ACH or EFT Auth data for an Item's accounts
// https://plaid.com/docs/#auth
app.get('/auth', function(request, response, next) {
  client.getAuth(ACCESS_TOKEN, function(error, authResponse) {
    if (error != null) {
      prettyPrintResponse(error);
      return response.json({
        error: error,
      });
    }
    prettyPrintResponse(authResponse);
    response.json({error: null, auth: authResponse});
  });
});
          

# Retrieve ACH or EFT account numbers for an Item
# https://plaid.com/docs/#auth
get '/auth' do
  begin
    product_response = client.auth.get(access_token)
    pretty_print_response(product_response)
    content_type :json
    { auth: product_response }.to_json
  rescue Plaid::PlaidAPIError => e
    error_response = format_error(e)
    pretty_print_response(error_response)
    content_type :json
    error_response.to_json
  end
end
          

# Retrieve ACH or EFT account numbers for an Item
# https://plaid.com/docs/#auth
@app.route('/auth', methods=['GET'])
def get_auth():
    try:
        auth_response = client.Auth.get(access_token)
    except plaid.errors.PlaidError as e:
        return jsonify({'error': {'display_message': e.display_message, 'error_code': e.code, 'error_type': e.type } })
    pretty_print_response(auth_response)
    return jsonify({'error': None, 'auth': auth_response})
          

Auth data comes back like the following:


{
  "accounts": [
    {
      "account_id": "vzeNDwK7KQIm4yEog683uElbp9GRLEFXGK98D",
      "balances": {
        "available": 100,
        "current": 110,
        "limit": null,
        "iso_currency_code": "USD",
        "unofficial_currency_code": null,
      },
      "mask": "0000",
      "name": "Plaid Checking",
      "official_name": "Plaid Gold Standard 0% Interest Checking",
      "subtype": "checking",
      "type": "depository"
    },
    ...
  ],
  "numbers": {
    "ach": [{
      "account": "9900009606",
      "account_id": "vzeNDwK7KQIm4yEog683uElbp9GRLEFXGK98D",
      "routing": "011401533",
      "wire_routing": "021000021"
    }],
    "eft": [],
    ...
  }
}

One thing to note is that not all accounts calculate the available balance. For instance, mortgages or student loans may not necessarily store an available balance. In such cases, Plaid will return an available balance of null.

Pulling Transaction data

Once the Item is created with Transactions, Plaid automatically starts the transaction retrieval and cleansing process. You can make a request to the /transactions/get endpoint to retrieve this data. Transaction webhooks can notify you about new transactions as they become available.

Attempting to retrieve transaction data for an Item before the initial pull has completed triggers a PRODUCT_NOT_READY error. If you encounter this, simply wait and retry your request or use webhooks to learn when transaction data is available.

The first transaction pull, the initial pull, pulls in 30 days of transaction data and takes between 30 and 240 seconds. Once the initial pull is completed, Plaid will begin the historical pull, which retrieves as much data as is available. From there, we’ll update the Item 's data at set intervals throughout the day to collect all of the most recent transactions.

Note: We work hard to provide as much historical data as possible. However, there are limiting factors in the amount of information an institution holds and the length of time a user has had an account.

Transaction data with webhooks

Transaction data can be retrieved as it becomes available by calling the /transactions/get API endpoint—but you'll want to make sure Plaid actually has new data before doing so. Webhooks allow you to react when new data is available, rather than continuously polling for changes. With webhooks, you'll be notified of new transactions and can then retrieve the data as necessary.

Transaction notifications are posted as JSON to your webhook, and they might appear in a few different forms:

Initial transaction update

Always fired, usually within 30-240 seconds of the Item being created (if transactions is an initial product). The initial pull fetches 30 days of transaction data.


{
  "webhook_type": "TRANSACTIONS",
  "webhook_code": "INITIAL_UPDATE",
  "item_id": "wz666MBjYWTp2PDzzggYhM6oWWmBb",
  "error": null,
  "new_transactions": 19
}
  

Historical transaction update

Always fired, for historical transaction data beyond the initial pull.


{
  "webhook_type": "TRANSACTIONS",
  "webhook_code": "HISTORICAL_UPDATE",
  "item_id": "wz666MBjYWTp2PDzzggYhM6oWWmBb",
  "error": null,
  "new_transactions": 231
}
  

Default transaction pulls

Only fired when new transaction data is available as Plaid performs its regular updates of the Item.


{
  "webhook_type": "TRANSACTIONS",
  "webhook_code": "DEFAULT_UPDATE",
  "item_id": "wz666MBjYWTp2PDzzggYhM6oWWmBb",
  "error": null,
  "new_transactions": 3
}

Removed transactions

In some cases, an institution may remove a transaction (for instance, a temporary hold), and Plaid will react accordingly. You'll receive a webhook notification similar to the following:


  "webhook_type": "TRANSACTIONS",
  "webhook_code": "TRANSACTIONS_REMOVED",
  "removed_transactions": [
    "yBVBEwrPyJs8GvR77N7QTxnGg6wG74H7dEDN6",
    "kgygNvAVPzSX9KkddNdWHaVGRVex1MHm3k9no"
  ],
  "error": null,
  "item_id": "wz666MBjYWTp2PDzzggYhM6oWWmBb"
}

The item_id is passed with each webhook so you can match it with user records on your side. Once you've received a notification that transaction data is available, you can call the /transactions/get endpoint to retrieve it:


// Retrieve Transactions for an Item
// https://plaid.com/docs/#transactions
app.get('/transactions', function(request, response, next) {
  // Pull transactions for the Item for the last 30 days
  var startDate = moment().subtract(30, 'days').format('YYYY-MM-DD');
  var endDate = moment().format('YYYY-MM-DD');
  client.getTransactions(ACCESS_TOKEN, startDate, endDate, {
    count: 250,
    offset: 0,
  }, function(error, transactionsResponse) {
    if (error != null) {
      prettyPrintResponse(error);
      return response.json({
        error: error
      });
    } else {
      prettyPrintResponse(transactionsResponse);
      response.json({error: false, transactions: transactionsResponse});
    }
  });
});
          

# Retrieve Transactions for an Item
# https://plaid.com/docs/#transactions
get '/transactions' do
  now = Date.today
  thirty_days_ago = (now - 30)
  begin
    product_response =
      client.transactions.get(access_token, thirty_days_ago, now)
    pretty_print_response(product_response)
    content_type :json
    { transactions => product_response }.to_json
  rescue Plaid::PlaidAPIError => e
    error_response = format_error(e)
    pretty_print_response(error_response)
    content_type :json
    error_response.to_json
  end
end
          

# Retrieve Transactions for an Item
# https://plaid.com/docs/#transactions
@app.route('/transactions', methods=['GET'])
def get_transactions():
    # Pull transactions for the last 30 days
    start_date = '{:%Y-%m-%d}'.format(datetime.datetime.now() + datetime.timedelta(-30))
    end_date = '{:%Y-%m-%d}'.format(datetime.datetime.now())
    try:
        transactions_response = client.Transactions.get(access_token, start_date, end_date)
    except plaid.errors.PlaidError as e:
        return jsonify({'error': {'display_message': e.display_message, 'error_code': e.code, 'error_type': e.type } })
    pretty_print_response(transactions_response)
    return jsonify({'error': None, 'transactions': transactions_response})
          

Returned transaction data would look like the following. (For a full overview of what each field means, visit our transaction data overview.


{
  "transactions": [
    {
      "account_id": "XA96y1wW3xS7wKyEdbRzFkpZov6x1ohxMXwep",
      "amount": 78.5,
      "iso_currency_code": "USD",
      "unofficial_currency_code": null,
      "category": null,
      "category_id": null,
      "date": "2017-01-29",
      "location": {
        "address": "100 Market Street",
        "city": "San Francisco",
        "region": "CA",
        "postal_code": "94110",
        "country":
      },
      "name": "Touchstone Climbing",
      "payment_meta": {},
      "pending": false,
      "pending_transaction_id": null,
      "transaction_id": "4WPD9vV5A1cogJwyQ5kVFB3vPEmpXPS3qvjXQ",
      "transaction_code": null,
      "transaction_type": "unresolved"
    },
    ...
  ]
}

Handling Item Errors

Sometimes, a user might change a password with an institution, or they might lock themselves out of the account. When this happens, an Item enters an error state, at which point all data retrieval calls to the API for the Item will return an error code. You can still make requests to /item/get endpoint, even when an Item is in an error state.

The most common error is ITEM_LOGIN_REQUIRED, which means that the user needs to provide updated credentials and possibly MFA. It’s straightforward to flag and repair these scenarios using Link and the API.

Error webhooks

Webhooks go beyond transaction data: They can also be used to communicate certain errors, such as invalid credentials or a locked item. In the event this happens, you’ll receive a notification with an ITEM_LOGIN_REQUIRED error:


{
  "error": {
    "display_message": null,
    "error_code": "ITEM_LOGIN_REQUIRED",
    "error_message": "the login details of this item have changed
     and a user login is required to update this information.",
    "error_type": "ITEM_ERROR",
    "status": 400
  },
  "item_id": "wz666MBjYWTp2PDzzggYhM6oWWmBb",
  "webhook_code": "ERROR",
  "webhook_type": "ITEM"
}

Though the most common error is ITEM_LOGIN_REQUIRED, you may run into a few others. For all possible error_codes, see the docs, or read on for a description of the most common errors:

Error codeExplanation
ITEM_LOGIN_REQUIRED Indicates that the user must provide updated authentication in order for Plaid to continue updating the Item. This can happen when a user changes a password, MFA information, or if the account is locked. Use Link’s update mode to resolve this.
ITEM_NOT_SUPPORTED Indicates that Plaid is unable to support the Item. This is rare but can happen for some users with restrictions on their accounts at the institution.
MFA_NOT_SUPPORTED Indicates that the Item requires a type of MFA that Plaid does not support. In some cases, a user can disable the unsupported type of MFA.
NO_ACCOUNTS Indicates that there are no longer any accounts associated with the Item.
PRODUCT_NOT_READY Indicates that the requested product is not yet ready - Plaid is still working to pull the information from the financial institution. Retry the request later or use webhooks to determine when the products is ready.

Plaid Link makes it easy for your user to update their credentials as necessary, through what we call update mode. Update mode enables an end-user to re-authenticate an Item, at which point Plaid will resolve that Item into a functioning state. Once an Item has been updated, data access will be restored and transaction updates, if you're using them, will automatically resume. Check out an overivew of the process below.

1

Plaid sends an Item error webhook to your app server

2

Make a request with the Item's access_token to create a temporary link_token

3

Use the link_token to open Link and have your user re-authenticate with Plaid.

4

The onSuccess callback will indicate that the user has successfully re-authenticated

Attempting to initialize Link in update mode for an Item that is not in an error state will result in an ITEM_NO_ERROR error. You can use the /sandbox/item/reset_login endpoint to put an Item into an error state in the Sandbox.

To configure update mode, generate a link_token for the Item server-side and then use that link_token to initialize Link. The Item’s access_token will not change.

Resolve an Item error

// Create a one-time use link_token for the Item.
// This link_token can be used to initialize Link
// in update mode for the user
app.post('/create_link_token', function(request, response, next) {
  // 1. Grab the client_user_id by searching for the current user in your database
  const user = await User.find(...);
  const clientUserId = user.id;

  // 2. Create a link_token for the given user
  plaidClient.createLinkToken({ 
    user: { 
      client_user_id: clientUserId,
    },
    client_name: 'My App',
    country_codes: ['US'],
    language: 'en',
    access_token: ACCESS_TOKEN,
  }, (err, res) => {
    const link_token = res.link_token;

    // 3. Send the data to the client
    response.json({ link_token: link_token });
  });
});
          

# Create a one-time use access_token for the Item.
# This access_token can be used to initialize Link
# in update mode for the user.
post '/create_link_token' do
    # 1. Grab the client_user_id by searching for the current user in your database
    current_user = User.find(...)
    client_user_id = current_user.id

    # 2. Create a link_token for the given user
    response = client.link_token.create(
      user: {
        client_user_id: client_user_id
      },
      client_name: 'My App',
      country_codes: ['US'],
      language: 'en',
      access_token: ACCESS_TOKEN,
    )
    link_token = response['link_token']

    # 3. Send the data to the client
    content_type :json
    response.to_json
end
          

# Create a one-time use link_token for the Item.
# This public_token can be used to initialize Link
# in update mode for the user.
@app.route("/create_link_token", methods=['POST'])
def create_public_token():
    # 1. Grab the client_user_id by searching for the current user in your database
    user = User.find(...)
    client_user_id = user.id

    # 2. Create a link_token for the given user
    response = client.LinkToken.create({
      'user': {
        'client_user_id': client_user_id,
      },
      'client_name': "My App",
      'country_codes': ['US'],
      'language': 'en',
      'access_token': ACCESS_TOKEN,
    })
    link_token = response['link_token']

    # 3. Send the data to the client
    return jsonify(response)
          

Then initialize Link client-side with the newly generated public_token:


var handler = Plaid.create({
  token: 'GENERATED_LINK_TOKEN',
  onSuccess: function(public_token, metadata) {},
  onExit: function(err, metadata) {},
  onEvent: function(eventName, metadata) {}
});
          

var handler = Plaid.create({
  token: 'GENERATED_LINK_TOKEN',
  onSuccess: function(public_token, metadata) {},
  onExit: function(err, metadata) {},
  onEvent: function(eventName, metadata) {}
});
          

var handler = Plaid.create({
  token: 'CREATED_LINK_TOKEN',
  onSuccess: function(public_token, metadata) {},
  onExit: function(err, metadata) {},
  onEvent: function(eventName, metadata) {}
});
          

Link automatically handles the necessary API calls to update the Item. As we’ve noted, the Item's access_token does not change unless you explicitly rotate it. While you’re free to redo the storing and exchange token call, there’s no need to: The end-user has re-authenticated with the institution, and API product access calls should resume operating successfully.

To test Link’s update mode, you may want a to simulate putting an Item into an error state.

Note that Link will automatically configure itself to open with the user’s institution. Attempting to open Link with a specific institution in update mode will not work.

To do so, use the /sandbox/item/reset_login endpoint :


client.resetLogin(access_token,
  async function(err, reset_login_response) {
  // Handle err
  // create a link_token for the Item
  client.createLinkToken({
    user: {
      client_user_id: (await User.find(...)).id,
    },
    client_name: 'My App',
    country_codes: ['US'],
    language: 'en',
    access_token: access_token,
  }, function(err, link_token_response) {
    // Handle err
    // Use the generated link_token to initialize Link
    // in update mode
  });
});
          

# Force a Sandbox Item into an error state
client.sandbox.sandbox_item.reset_login(access_token)
# create a link_token for the Item and use it to initialize Link
# in update mode.
link_token_response = client.link_token.create(
  user: {
    client_user_id: User.find(...).id
  },
  client_name: 'My App',
  country_codes: ['US'],
  language: 'en',
  access_token: access_token
)
          

# Force a Sandbox Item into an error state
client.Sandbox.item.reset_login(access_token)
# create a link_token for the Item and use it to initialize Link
# in update mode.
link_token_response = client.LinkToken.create({
  'user': {
    'client_user_id': User.find(...).id
  },
  'client_name': 'My App',
  'country_codes': ['US'],
  'language': 'en',
  'access_token': access_token
})
          

Moving Forward

Migrating to production

Keep building your integration out in our Sandbox and Development API environments. When you're ready to go to Production, request access from the Dashboard. While you’re at it, be sure to also take a look at our Privacy Policy.

Getting help

Find answers to common integration and product questions at our Help Center. You can find all the code for this quickstart guide, including runnable sample apps, on GitHub.

We can’t wait to see what you build!