Link Transition Guide

Overview

Transitioning

COPY

COPIED!

Plaid's legacy API will be deprecated on September 30, 2018. Because you’re already using Link, transitioning to Plaid’s current API is easy. With the updates to the API, the client-side Link integration hasn’t changed significantly. We’ve just reorganized and added to the server-side API to make it even easier to use Plaid.

This guide walks through what has changed and how to update your integration to take advantage of the current API.

Looking for legacy Link docs? Head here. Looking for the current API docs? Head here.

Not using Link yet? Check out this transition guide instead.

Notable changes

COPY

COPIED!

  • Product, endpoint, and API environment names and URLs have changed
  • Existing users can be transitioned to the current API without having them re-authenticate
  • Link can now be initialized with multiple products
  • public_tokens are now one-time use and expire after 30 minutes but can be regenerated via an API call
  • Client libraries and docs are updated with the new endpoints -- legacy libraries are also available
  • Stateful Sandbox - test MFA flows, errors, webhooks, and more

API Environments

COPY

COPIED!

New Environment Notes
Sandbox https://sandbox.plaid.com
Development https://development.plaid.com – analogous to the legacy tartan environment
Production https://production.plaid.com – analogous to the legacy api environment

Your existing API keys will work across all three current API environments as well as the legacy API.

If you want to disable legacy API access, please open a case from your Dashboard.

Client-side integration

There are a few Link configuration changes that you’ll want to make in order to take advantage of the current API endpoints.

Also, since public_tokens are now one-time use, you’ll now utilize Link’s update mode differently. Going forward, generate a public_token for an access_token server-side and then initialize Link with the generated public_token.

Parameter Notes
apiVersion Set to v2 to leverage Plaid’s updated API.
product You can specify multiple products to initialize Link (previously, only one was supported). Link will only display institutions that support all requested products.

Valid products: auth, transactions, identity, and income.

Example: ['auth', 'transactions']
env The API environment to use. There are now three environments:
- production
- development
- sandbox

To integrate with the new Plaid API in Link, the apiVersion key is required, to ensure all subsequent requests are sent to the current API:

var linkHandler = Plaid.create({
   ...,
   apiVersion: 'v2',
});
<script src="https://cdn.plaid.com/link/v2/stable/link-initialize.js"></script>
<button id="link-button"> Open Link </button>
<script>
var linkHandler = Plaid.create({
  env: 'production',
  apiVersion: 'v2',
  clientName: '<CLIENT_NAME>',
  // Replace '<PUBLIC_KEY>' with your own `public_key`
  key: '<PUBLIC_KEY>',
  product: ['auth'],
  // Use webhooks to get transaction and error updates
  webhook: '<WEBHOOK_URL>',
  onSuccess: function(public_token, metadata) {
    // Send the public_token to your app server here.
    // The metadata object contains info about the
    // institution selected and, if the Select Account
    // view is enabled, information about the account(s)
    // the user selected too.
  },
  onExit: function(err, metadata) {
    // The user exited the Link flow.
    if (err != null) {
      // The user encountered a Plaid API error
      // prior to exiting.
    }
    // metadata contains information about the
    // institution that the user selected and the
    // most recent API request IDs. Storing this
    // information can be helpful for support.
  },
  onEvent: function(eventName, metadata) {
    // Optionally capture Link flow events, streamed through
    // this callback as your users connect an Item to Plaid.
  },
});
// Trigger the standard institution select view
document.getElementById('link-button').onclick = function() {
  linkHandler.open();
};
</script>

You still use a public_token to update an Item when it goes into a bad state, such as when a user changes the password or MFA information.

Because public_tokens are now one-time use, you will need to generate a new public_token each time you use Link in update mode. The /item/public_token/create endpoint makes it easy to generate a public_token on-demand from your back-end server.

<button id='linkButton'>Open Link</button>
<script src="https://cdn.plaid.com/link/v2/stable/link-initialize.js"></script>
<script>
  var linkHandler = Plaid.create({
    env: 'production',
    apiVersion: 'v2',
    product: ['auth'],
    clientName: '<CLIENT_NAME>',
    key: '<PUBLIC_KEY>',
    token: '<GENERATED_PUBLIC_TOKEN>',
    onSuccess: function(public_token, metadata) {
      // The onSuccess callback has not changed!

      // Send the public_token to your app server here.
      // The metadata object contains info about the institution the
      // user selected and the account ID, if selectAccount is enabled.
    },
  });
  // Trigger the login view for the institution associated with the public_token
  document.getElementById('linkButton').onclick = function() {
    linkHandler.open();
  };
</script>

Server-side integration

Creating new items

COPY

COPIED!

Plaid’s API endpoint names have changed. We’ve reorganized the API to make it easier to use, provide more information about your user’s Items, and made it simpler to access multiple products. All of our official client libraries (Node, Python, Ruby, and Java) have been updated to support these new endpoints. This guide provides an overview and reference for the changes. All API endpoints have documented, consistent request and response schemas.

Previous endpoint Notable changes
/upgrade Removed, there is no need to use /upgrade before accessing a product endpoint.
/connect/get Now /transactions/get, The endpoint is now paginated by default but still suports searching by date ranges. Pending transactions are returned by default.
/auth/get No change
/info/get Now /identity/get
/exchange_token Now /item/public_token/exchange, public_tokens are now one-time use and expire after 30 minutes.
/institutions/all/search Now /institutions/search

We’ve also introduced a few new endpoints to make it easier to work with the API and your Items:

Endpoint Notes
/item/public_token/create Create a public_token for a given access_token; a public_token can be used to initialize Link in update mode for an Item.
/item/webhook/update Update the webhook associated with an Item. Triggers an acknowledgement webhook in response.
/item/get Retrieve information about an Item, including the institution type, error status, available and billed products, and webhook information.

Transitioning existing Items

COPY

COPIED!

You can transition your users’ existing Items via the /item/access_token/update_version endpoint. A new access_token will be generated for the Item that can be used with our updated API. The previous access_token will still be valid in our legacy API environment.

Note: You’ll begin receiving updated webhooks and stop receiving legacy API webhooks as soon as you generate an updated access_token for an Item.

This endpoint is only available in the Development and Production API environments.

Update access_token version request

curl -X POST https://production.plaid.com/item/access_token/update_version \
  -H 'Content-Type: application/json' \
  -d '{
    "client_id": String,
    "secret": String,
    "access_token_v1": String
  }'
# generate an updated access token for the legacy token
response = client.item.access_token.update_version("LEGACY_API_ACCESS_TOKEN")
# The response includes an access token compatible with Plaid's new API and
# the item ID for the access_token, which is used to identify the Item in 
# webhook payloads from Plaid.
# The old access token will still work with Plaid's legacy API endpoints.
# Store the new access_token and the item_id in a persistent, secure data store.
newAccessToken = response['access_token']
itemId = response['item_id']
# generate an updated access token for the legacy token
response = client.Item.access_token.update_version("LEGACY_API_ACCESS_TOKEN")
# The response includes an access token compatible with Plaid's new API and
# the item ID for the access_token, which is used to identify the Item in 
# webhook payloads from Plaid.
# The old access token will still work with Plaid's legacy API endpoints.
# Store the new access_token and the item_id in a persistent, secure data store.
newAccessToken = response.access_token
itemId = response.item_id
// Generate an updated access token for the legacy token
client.updateAccessTokenVersion("LEGACY_API_ACCESS_TOKEN", (err, result) => {
  // Handle err
  // The response includes an access token compatible with Plaid's new API and
  // the item ID for the access_token, which is used to identify the Item in 
  // webhook payloads from Plaid.
  // The old access token will still work with Plaid's legacy API endpoints.
  // Store the new access_token and the item_id in a persistent, secure data store.
  const newAccessToken = result.access_token;
  const itemId = result.item_id;
});
// Generate an updated access token for the legacy token
Response<ItemAccessTokenUpdateVersionResponse> response = client().service().itemAccessTokenUpdateVersion(
  new ItemAccessTokenUpdateVersionRequest("LEGACY_API_ACCESS_TOKEN"))
  .execute();

String newAccessToken;
String itemId;
if (response.isSuccessful()) {
  // The response includes an access token compatible with Plaid's new API and
  // the item ID for the access_token, which is used to identify the Item in 
  // webhook payloads from Plaid.
  // The old access token will still work with Plaid's legacy API endpoints.
  // Store the new access_token and the item_id in a persistent, secure data store.
  newAccessToken = response.body().getAccessToken();
  itemId = response.body().getItemId();
}

Update access_token version response

http code 200
{
  "access_token": "access-production-8acd3678-3aa8-411e-bf5a-ee7851e0c75d",
  "item_id": "XkW36J3gewh9wE69VkyzSxyXwEBewBio4wwJ3",
  "request_id": "c92aB"
}

Other updates

Institutions

COPY

COPIED!

In the current API, all institutions IDs observe a consistent schema: numeric identifiers prefixed with ins_. Short hand institution codes (such as chase, bofa, and wells) are no longer supported. We suggest using Link's Institution Select view, which is always up-to-date, or using our Institution API endpoints if using custom initializers.

Errors

COPY

COPIED!

The schema for errors have changed, and we’ve reduced the total number of error types.

Field Nullable? Description
error_type
String
No A broad categorization of the error. One of: INVALID_REQUEST, INVALID_INPUT, RATE_LIMIT_EXCEEDED, API_ERROR, or ITEM.
Safe for programmatic use.
error_code
String
No The particular error code. Each error_type has a specific set of error_codes.
Safe for programmatic use.
error_message
String
No A developer-friendly representation of the error message.
This may change over time and is not safe for programmatic use.
display_message
String
Yes A user-friendly representation of the error message. null if the error is not related to user action.
Not safe for programmatic use.

Below are some of the most common error codes:

Error code Explanation
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.

Webhooks

COPY

COPIED!

There are a few key changes with webhooks:

  • Schema updated
  • New field: webhook_type is a high-level categorization of the webhook
  • item_id replaces access_token in the payload as the identifier
Legacy Webhook Code New Webhook Code
0 INITIAL_UPDATE - Initial transaction pull completed
1 HISTORICAL_UPDATE - Historical transaction pull completed
2 DEFAULT_UPDATE - New transactions are available
3 REMOVED_TRANSACTIONS - Transactions have been removed
4 WEBHOOK_UPDATE_ACKNOWLEDGED - Webhook successfully updated for an Item after calling /item/webhook/update
120X ITEM_LOGIN_REQUIRED - The user needs to re-authenticate through Link in update mode

See the docs for more webhook payload examples.

Legacy format webhook
{
  "message": "Initial transaction pull finished",
  "access_token": "xxxxx",
  "total_transactions": 123,
  "code": 0
}
Default Transaction Webhook
{
  "webhook_type": "TRANSACTIONS",
  "webhook_code": "DEFAULT_UPDATE",
  "item_id": "wz666MBjYWTp2PDzzggYhM6oWWmBb",
  "error": null,
  "new_transactions": 3
}
Error Webhook Example
{
  "webhook_type": "ITEM",
  "webhook_code": "ERROR",
  "item_id": "wz666MBjYWTp2PDzzggYhM6oWWmBb",
  "error": {
    "display_message": "The provided credentials were not correct. Please try again.",
    "error_code": "ITEM_LOGIN_REQUIRED",
    "error_message": "the provided credentials were not correct",
    "error_type": "ITEM_ERROR",
    "status": 400
  }
}

Stripe

COPY

COPIED!

The Plaid Stripe integration has also been updated in the current API. Instead of retrieving the Stripe bank account token at the token exchange step, we introduce a new endpoint /processor/stripe/bank_account_token/create with which you'll be able to generate a Stripe bank account token using a Plaid access_token and user-selected account_id. Our full Stripe docs can be found here.

You can create Stripe bank account tokens in all three API environments:

# Exchange token
curl -X POST https://sandbox.plaid.com/item/public_token/exchange \
-H 'Content-Type: application/json'
-d '{
  "client_id": "[Plaid Client ID]",
  "secret": "[Plaid secret]",
  "public_token": "[Public token]"
}'

# Create bank account token
curl -X POST https://sandbox.plaid.com/processor/stripe/bank_account_token/create \
-H 'Content-Type: application/json'
-d '{
  "client_id": "[Plaid Client ID]",
  "secret": "[Plaid secret]",
  "access_token": "[Access token]",
  "account_id": "[Account ID]"
}'
// Change `sandbox` to `development` to test with live users and change
// to `production when you're ready to go live!`
var plaid = require('plaid');

var plaidClient = new plaid.Client('[Plaid client ID]',
                                   '[Plaid secret]',
                                   '[Plaid public key]'
                                   plaid.environments.sandbox);

plaidClient.exchangePublicToken('[Plaid Link public_token]', function(err, res) {
  var accessToken = res.access_token;
  // Generate a bank account token
  plaidClient.createStripeToken(accessToken, '[Account ID]', function(err, res) {
    var bankAccountToken = res.stripe_bank_account_token;
  });
});
# Change `sandbox` to `development` to test with live users and change
# to `production` when you're ready to go live!
client = Client('[Plaid client ID]',
                '[Plaid secret]',
                '[Plaid public key]'],
                'sandbox')

exchange_token_response = client.Item.public_token.exchange('[Plaid Link public_token]')
access_token = exchange_token_response['access_token']

stripe_response = client.Processor.stripeBankAccountTokenCreate(access_token, '[Account ID]'])
bank_account_token = stripe_response['stripe_bank_account_token']
# Change `sandbox` to `development` to test with live users and change
# to `production` when you're ready to go live!
client = Plaid::Client.new(env: :sandbox,
                           client_id: ENV['PLAID_CLIENT_ID'],
                           secret: ENV['PLAID_SECRET'],
                           public_key: ENV['PLAID_PUBLIC_KEY'])

exchange_token_response = client.item.public_token.exchange('[Plaid Link public_token]')
access_token = exchange_response['access_token']

stripe_response = client.processor.stripeBankAccountTokenCreate(access_token, '[Account ID'])
bank_account_token = stripe_response['stripe_bank_account_token']
// Use builder to create a client
PlaidClient plaidClient = PlaidClient.newBuilder()
  .clientIdAndSecret("[Plaid client ID]", "[Plaid secret]")
  .publicKey("[Plaid public key]")
  .sandboxBaseUrl() // Use the Sandbox. Can also be `developmentBaseUrl()` or `productionBaseUrl()`
  .build();

// Required request parameters are always Request object constructor arguments
Response<ItemPublicTokenExchangeResponse> exchangeResponse = plaidClient.service()
    .itemPublicTokenExchange(new ItemPublicTokenExchangeRequest("[Plaid Link public_token]")).execute();

if (exchangeResponse.isSuccessful()) {
  String accessToken = exchangeResponse.body().getAccessToken();
  Response<ItemStripeTokenCreateResponse> stripeResponse =
      client().service().itemStripeTokenCreate(new ItemStripeTokenCreateRequest(accessToken, "[Account ID]")).execute();

  if (stripeResponse.isSuccessful()) {
    String bankAccountToken = stripeResponse.body().getStripeBankAccountToken();
  }
}

For a valid request, the API will return a JSON response similar to:

{
  "stripe_bank_account_token": "btok_5oEetfLzPklE1fwJZ7SG",
  "request_id": "[Unique request ID]"
}

Note: The account_id parameter is required if you wish to receive a Stripe bank account token.

Sandbox

COPY

COPIED!

The API Sandbox environment is now a stateful testing environment, allowing you to test errors, webhooks, MFA, Link update mode, and more. Head to the docs for more info.