Link token migration guide
How to migrate your application from the public key to a Link token
Support for the public-key based integration mode will end on January 31, 2025. All customers still using the public key-based method of integration must switch to using Link tokens by this date, as public keys will no longer work to launch Link sessions beginning in February 2025.
Introduction
Plaid has introduced a new link_token
, which replaces the static public_key
. This is an improvement that provides better error validation, more advanced security, and enables Link event logs to be surfaced in the Plaid Dashboard. While Plaid does not plan to discontinue support for existing public_key
integrations, it is recommended that you upgrade your integration to use a link_token
, as it enables enhanced capabilities, and future Plaid development and features will be based on the link_token
infrastructure. Link tokens are also required for new OAuth integrations.
If you are unable to migrate to Link tokens at this time, see maintaining a public key integration for instructions on working with legacy public keys.
This guide covers the client and server-side changes required to implement the new link_token
. Here's an overview of the updates before we dive into the details.
/link/token/create
to create a link_token
and pass the temporary token to your app's client.link_token
to open Link for your user. In the onSuccess
callback, Link will provide a temporary public_token
./item/public_token/exchange
to exchange the public_token
for a permanent access_token
and item_id
for the new Item
.access_token
and use it to make product requests for your user's Item
.What's new
- Link will now be initialized with a new
link_token
. Thelink_token
replaces all previous methods of initializing Link, including thepublic_key
(for initial Link), thepublic_token
(for update mode), and thepayment_token
(for Payment Initiation). - The new endpoint to create the
link_token
is/link/token/create
. - The
INVALID_LINK_TOKEN
error code is now available to gracefully handle invalidated tokens. - Link events from sessions created with the new
link_token
will be surfaced in the Logs section of the Dashboard. However, Link events from sessions created with thepublic_key
will not.
Link tokens
The link_token
is a new type of token that is created by your app's server and passed to your app's client to initialize Link. The Link configuration parameters that were previously set within Link itself are now set via parameters passed to /link/token/create
and conveyed to Link via the link_token
. If these configurations are still set client-side when using the link_token
, they will not have any effect on Link behavior.
Update your integration
The overall process for updating your integration is:
- Update your server to create a
link_token
. - Update your client to pass the
link_token
and handleINVALID_LINK_TOKEN
errors. - Ensure you have updated all Link entry points, including those for update mode.
- Test your integration.
- Update any callsites that use the
public_key
for authentication to use theclient_id
andsecret
instead, then re-test those callsites. - Disable the
public_key
.
Detailed instructions for each step can be found below.
Update your server
Add a new authenticated endpoint to your app's server to create a link_token
by calling /link/token/create
.
1app.post('/api/create_link_token', async function (request, response) {2 // Get the client_user_id by searching for the current user3 const user = await User.find(...);4 const clientUserId = user.id;5 const request = {6 user: {7 // This should correspond to a unique id for the current user.8 client_user_id: clientUserId,9 },10 client_name: 'Plaid Test App',11 products: ['transactions'],12 language: 'en',13 webhook: 'https://webhook.example.com',14 redirect_uri: 'https://domainname.com/oauth-page.html',15 country_codes: ['US'],16 };17 try {18 const createTokenResponse = await client.linkTokenCreate(request);19 response.json(createTokenResponse.data);20 } catch (error) {21 // handle error22 }23});
Many of the parameters to /link/token/create
are the same as parameters previously set in Link. Aside from the change to snake case from camelCase, there are a few substantive differences, summarized below. Note that these bullets are only a summary; for the full /link/token/create
endpoint definition, see the API Reference.
- A new required parameter,
user.id
, has been added. This should be a unique identifier, such as the user ID of the end user in your application. It should not contain personally identifiable information, such as a phone number or email address. language
andcountry_codes
, which were previously optional, are now required.accountSubtypes
has been replaced by theaccount_filters
parameter, and its syntax has changed.
In addition, there are a few differences relevant specifically to European integrations:
- The
oauthNonce
parameter is no longer used, since it is effectively replaced byuser.id
. - For the Payment Initiation product, The
paymentToken
is no longer used, and/payment_initiation/payment/token/create
has been deprecated. Instead, thepayment_id
should be provided to/link/token/create
via thepayment_initiation.payment_id
parameter. Initializing Link with the returnedlink_token
will launch the Payment Initiation flow.
Authenticate your app
The endpoint used to create a link_token
should only be available to users that are logged in to your app. Once your user is logged in, pass an identifier that uniquely identifies your user into the user.client_user_id
field. The value of this field should not be personally identifiable information such as an email address or phone number. Using user.client_user_id
will allow for easier debugging in the Dashboard logs. You will be able to search for Link logs that belong to one of your end users.
As this update involves an additional API call when adding an Item, create a link_token
when your user initially visits your app to avoid adding latency to your Link flow.
Update your client
For each of your web and mobile apps, use the new endpoint you created to fetch a link_token
, then pass it into one of Plaid's Link SDKs to initialize Link. You can then safely remove the public_key
and other client-side configs that are now configured in the /link/token/create
request.
If the token expires or the user enters too many invalid credentials, the link_token
can become invalidated. If it does get into an invalid state, Link will exit with an INVALID_LINK_TOKEN
error code. By recognizing when this error occurs in the onExit
callback, you can generate a fresh link_token
for the next time your user opens Link.
Update Link web
The code below demonstrates code that passes the new link_token
to Link. For more in-depth coverage on how to integrate with Link web, see the Link web docs.
Note that in the error handling section, in order to handle an invalid link_token
for Link in the browser, you will need to gracefully clean up the old iframe before reinitializing Link. To do this, use the destroy()
method and reinitialize Link with a new link_token
in the onExit
callback.
1<button id="link-button">Link Account</button>2<script src="https://cdn.plaid.com/link/v2/stable/link-initialize.js"></script>3<script type="text/javascript">4(async function() {5 const fetchLinkToken = async () => {6 const response = await fetch('/create_link_token', { method: 'POST' });7 const responseJSON = await response.json();8 return responseJSON.link_token;9 };10
11 const configs = {12 // 1. Pass a new link_token to Link.13 token: await fetchLinkToken(),14 onSuccess: async function(public_token, metadata) {15 // 2a. Send the public_token to your app server.16 // The onSuccess function is called when the user has successfully17 // authenticated and selected an account to use.18 await fetch('/exchange_public_token', {19 method: 'POST',20 body: JSON.stringify({ public_token: public_token }),21 });22 },23 onExit: async function(err, metadata) {24 // 2b. Gracefully handle the invalid link token error. A link token25 // can become invalidated if it expires, has already been used26 // for a link session, or is associated with too many invalid logins.27 if (err != null && err.error_code === 'INVALID_LINK_TOKEN') {28 linkHandler.destroy();29 linkHandler = Plaid.create({30 ...configs,31 token: await fetchLinkToken(),32 });33 }34 if (err != null) {35 // Handle any other types of errors.36 }37 // metadata contains information about the institution that the38 // user selected and the most recent API request IDs.39 // Storing this information can be helpful for support.40 },41 };42
43 var linkHandler = Plaid.create(configs);44
45 document.getElementById('link-button').onclick = function() {46 linkHandler.open();47 };48})();49</script>
Update Link iOS
The iOS SDK now provides an initWithLinkToken
method on both the PLKConfiguration
and the PLKPlaidLinkViewController
classes that should allow you to easily initialize Link with a link_token
.
The code below shows how to initialize Link with the link_token
in iOS. For more in-depth coverage on how to integrate with Link iOS, see the iOS docs.
1let linkConfiguration = PLKConfiguration(linkToken: "GENERATED_LINK_TOKEN")2let linkViewDelegate = self3let linkViewController = PLKPlaidLinkViewController(4 linkToken: "GENERATED_LINK_TOKEN",5 configuration: linkConfiguration,6 delegate: linkViewDelegate,7)8if (UI_USER_INTERFACE_IDIOM() == .pad) {9 linkViewController.modalPresentationStyle = .formSheet;10}11present(linkViewController, animated: true)
Update Link Android
The Android SDK exposes a new class called LinkTokenConfiguration
. This class accepts the link_token
and should be passed into the openPlaidLink
method.
The code below demonstrates how to use the LinkTokenConfiguration
class to open Link. For more in depth coverage on the Android SDK, see the Android docs.
1import android.os.Bundle2import android.util.Log3import androidx.appcompat.app.AppCompatActivity4
5import com.plaid.link.Plaid6import com.plaid.link.linkTokenConfiguration7import com.plaid.link.openPlaidLink8import com.plaid.link.configuration.AccountSubtype9import com.plaid.link.configuration.LinkLogLevel10import com.plaid.link.configuration.PlaidEnvironment11import com.plaid.link.configuration.PlaidProduct12import com.plaid.link.event.LinkEvent13import java.util.Locale14
15class MainActivity : AppCompatActivity() {16
17 override fun onCreate(savedInstanceState: Bundle?) {18 super.onCreate(savedInstanceState)19
20 // Optional21 Plaid.setLinkEventListener { event -> Log.i("Event", event.toString()) }22
23 // Open Link – put this inside of a Button / Fab click listener24 this@MAINACTIVITY.openPlaidLink(25 linkTokenConfiguration {26 // required27 token = "GENERATED_LINK_TOKEN"28
29 // optional30 logLevel = LinkLogLevel.WARN // Defaults to ASSERT31 extraParams = mapOf() // Map of additional configs32 }33 );34 }35}
Update Link update mode flows
With the introduction of the link_token
, /item/public_token/create
is deprecated, and Link's update mode is initialized by passing in a link_token
rather than a public_token
. You can obtain this link_token
by calling /link/token/create
and providing the user.id
of the user whose Item is being updated, along with the access_token
for the Item. Make sure to update any update mode flow entry points in addition to updating primary Link flows. For more details and complete sample code, see Updating Items via Link.
1// Initialize Link with the token parameter2// set to the generated link_token for the Item3const linkHandler = Plaid.create({4 token: 'GENERATED_LINK_TOKEN',5 onSuccess: (public_token, metadata) => {6 // You do not need to repeat the /item/public_token/exchange7 // process when a user uses Link in update mode.8 // The Item's access_token has not changed.9 },10 // ...11});
Test in Sandbox
Once you have updated both your app's client and server, it's time to test that your integration works. The best way to test is by using the test credentials in the Sandbox:
1username: user_good2password: pass_good
Test your error handling flow for INVALID_LINK_TOKEN
by using the Sandbox test credentials to force an error:
1username: user_custom2password: { "force_error": "INVALID_LINK_TOKEN" }
You can also verify that you have updated correctly by viewing Link event logs in the Plaid Dashboard.
To test your update mode implementation, use the /sandbox/item/reset_login
endpoint to force an Item into a state that requires an update, then walk through the test steps above.
Update API endpoints
In order to completely migrate off of the public_key
, there are a few Plaid API endpoints that should replace the public_key
with the client_id
and secret
: /institutions/search
, /institutions/get_by_id
, and /sandbox/public_token/create
.
Because the client_id
and secret
are now used to authenticate the endpoints above, they should only be called from your server. The ability to call them from the client has been removed in the latest client library updates.
Disable the public key
After completing all of the above steps, you can now confidently disable the public_key
via the Plaid Dashboard. This can be done on a per-environment basis, with different settings for Sandbox and Production, to help you test your migration. The public_key
can also be disabled separately for Link and for the API. Disabling it for Link means you will be required to use the link_token
to initialize Link. Disabling it for the API means that you will be required to use the client_id
and secret
to call /institutions/search
, /institutions/get_by_id
, and/sandbox/public_token/create
.
It is recommended you disable the public_key
as soon as possible to ensure that you do not accidentally add legacy public_key
-dependent code to your application.
Conclusion
Congratulations on upgrading to the new link_token
! If you require any help migrating to the link_token
integration, please contact Plaid Support for assistance.