Hosted Link
Integrate with Link using a Plaid-hosted frontend
Overview
Hosted Link is the easiest and fastest way to integrate with Plaid. With Hosted Link, Plaid hosts the Link experience. Customers can use this link in web browsers or open it in a secure web context within a mobile app, eliminating the need for front-end implementation work. Hosted Link is especially recommended for any mobile integration that is not using the official Plaid SDK. If your Plaid integration uses webviews, we strongly recommend switching to Hosted Link. Hosted Link can also be used to complement in-person interactions; for example, you can send a Hosted Link session to customers who are in-person at a retail location.
Hosted Link is fully compatible with all Plaid products and Link flows (including update mode), except for Plaid Layer.
With Link Delivery (beta), Hosted Link can also deliver the URL for the Link session to your users via text or email.
Hosted Link is the preferred frontend integration method for all platforms in which Plaid's native mobile or web SDKs can't be used, including webview-based mobile apps and integrations by embedded clients such as PSPs. For details on using Hosted Link as an embedded client, See Hosted Link for embedded clients.
Benefits of using Hosted Link
- Simplicity: Hosted Link is the easiest way to integrate with Plaid if you are unable to use Plaid's native mobile SDKs. You don't need to build or maintain a front-end component.
- Integration complexity: Since Hosted Link controls the redirections that are required to support OAuth, it removes one complex step to supporting embedded integrations.
- SDK size: Since the flow will be hosted by Plaid, there is no added SDK size to your application.
- Link Recovery: Hosted Link is required to enable Link Recovery (beta). With Link Recovery, users whose Link sessions failed due to temporary institution outages can be automatically notified when the issue has been healed, allowing you to increase Link conversion. For more details, see Link Recovery.
To request access to Link Delivery, contact your Account Manager.
Integration process
Creating a Link token
To start with Hosted Link, call /link/token/create
and include a hosted_link
object in the request. The hosted_link
object can be an empty object {}
, or it can contain any of the hosted_link
configuration fields. As long as you include the hosted_link
object in your request, the hosted_link_url
will be present in the response.
Previously, Hosted Link was enabled via Plaid Account Managers; it is no longer necessary to request that your Account Manager enable Hosted Link. If your account was enabled for Hosted Link via your Account Manager, the hosted_link_url
will be present in the response for all /link/token/create
requests, regardless of whether you include a hosted_link
object.
You can provide the following Hosted Link parameters to /link/token/create
:
- Specify a
webhook
endpoint where thepublic_token
will be delivered. Webhooks are strongly recommended when using Hosted Link, as the webhook is the primary means of delivering thepublic_token
, as well as of informing you when your user has completed the Link session. - To have Plaid deliver the link to your user with Link Delivery (beta), set
hosted_link.delivery_method
to eithersms
oremail
. Plaid will contact the user via the information you provide in theuser
object of the request. For example, if you selectedemail
, theuser
object in the request must contain anemail_address
. - To customize the duration of the Hosted Link token lifetime, set
hosted_link.url_lifetime_seconds
. If you don't set this field, the default lifetime for Hosted Links sent by Plaid via SMS is 24 hours, and for links sent via email, it is 7 days. If Plaid isn't delivering the url, the default lifetime is 30 minutes. - To have Plaid redirect the user after they complete the Link flow, set
hosted_link.completion_redirect_uri
to the destination URI. - To indicate that the user will be opening the link in a mobile app in an out-of-process webview (i.e., an
AsWebAuthenticationSession
/SFSafariViewController
or Android Custom Tab), sethosted_link.is_mobile_app
totrue
.
The response from /link/token/create
will include a hosted_link_url
field. You can send a user to this URL to start the Hosted Link session. Alternatively, if your integration uses both Hosted Link and non-Hosted Link sessions, you can start a non-Hosted Link session using the link_token
instead.
In a Hosted Link session, you should also store the link_token
value that gets returned, and make sure to associate it with the internal user ID your application is using for this user. This is the only way to make sure the data that gets returned from the Hosted Link session is associated with the right end-user.
1curl -X POST https://production.plaid.com/link/token/create \2-H 'Content-Type: application/json' \3-d '{4 "client_id": "${PLAID_CLIENT_ID}",5 "secret": "${PLAID_SECRET}",6 "client_name": "Wonderwallet",7 "country_codes": ["US"],8 "redirect_uri": "{{UNIVERSAL_OR_APP_LINK}}",9 "webhook": "https://wonderwallet.com/webhook_receiver",10 "language": "en",11 "user": {12 "client_user_id": "a57d3304",13 "phone_number": "+19162255887"14 },15 "products": ["auth"],16 "hosted_link": {17 "delivery_method": "sms",18 "completion_redirect_uri": "https://wonderwallet.com/redirect",19 "is_mobile_app": false,20 "url_lifetime_seconds": 90021 }22}
1{2 "expiration": "2023-08-18T02:08:03Z",3 "hosted_link_url": "https://secure.plaid.com/link/lp9o97618r1r6p93oon62nr025950ro4s3",4 "link_token": "link-production-4b42163e-6e1c-48bb-a17a-e570405eb9f8",5 "request_id": "kNbPMmLxzBObzpt"6}
Obtaining a public token
In most other Plaid integration methods, upon completion of the Link flow, your frontend code would receive either an onSuccess
or onExit
callback. In Hosted Link, there is no frontend integration required (or possible). You will instead receive information about the Link flow (including the public_token
) via the SESSION_FINISHED
webhook and the /link/token/get
endpoint.
Each time a user completes a Link flow, Plaid will fire a SESSION_FINISHED
webhook that contains information about what caused the session to end, as well as the public token if the session completed successfully. In flows where Link is re-initialized after an OAuth redirect, this webhook will fire exactly once, after the user completes the entire Link flow.
1{2 "webhook_type": "LINK",3 "webhook_code": "SESSION_FINISHED",4 "status": "SUCCESS",5 "link_session_id": "356dbb28-7f98-44d1-8e6d-0cec580f3171",6 "link_token": "link-sandbox-af1a0311-da53-4636-b754-dd15cc058176",7 "public_tokens": [8 "public-sandbox-b0e2c4ee-a763-4df5-bfe9-46a46bce993d"9 ],10 "environment": "sandbox"11}
You can also call /link/token/get
with the relevant Link token to receive more detailed metadata on sessions started using this Link token. Each Link session includes the time it was started, and (if applicable) the time it finished. Each completed Link session also includes the metadata that would normally be passed to the onSuccess
or onExit
callbacks, including the public_token
for successful sessions.
Detailed session data, including the public token, will also be available from /link/token/get
for six hours after the session has completed. In general, it is recommended to use the webhook to obtain the public token, since it will allow you to get the public token more promptly, but you may want to use /link/token/get
if your integration does not use webhooks, or as a backup mechanism if your system missed webhooks due to an outage.
In /link/token/get
the public_token
is present in both the on_success
object and the results
object. It is recommended to use the results
object, as the older on_success
object does not support the newer Multi-Item Link option, which allows for multiple public tokens to be returned in a single Link session.
1{2 {3 "created_at": "2024-07-29T21:22:13Z",4 "expiration": "2024-07-29T21:52:13Z",5 "link_sessions": [6 {7 "events": [8 {9 "event_id": "c1ccb962-9de0-494c-b85b-e89cdf3b2478",10 "event_metadata": {11 "institution_id": "ins_10",12 "institution_name": "American Express",13 "request_id": "Z9MB4fuMB2TJqgL"14 },15 "event_name": "HANDOFF",16 "timestamp": "2024-07-29T21:23:03Z"17 },18 {19 "event_id": "ff03f1ff-313b-4c2e-997f-83791c7ec432",20 "event_metadata": {21 "institution_id": "ins_10",22 "institution_name": "American Express",23 "request_id": "vyPamzbGPPsQucO"24 },25 "event_name": "OPEN_OAUTH",26 "timestamp": "2024-07-29T21:22:34Z"27 },28 {29 "event_id": "0ea6d0df-1bb0-4e4d-bea5-b3731b7536bf",30 "event_metadata": {31 "institution_id": "ins_10",32 "institution_name": "American Express",33 "request_id": "vyPamzbGPPsQucO"34 },35 "event_name": "TRANSITION_VIEW",36 "timestamp": "2024-07-29T21:22:33Z"37 },38 {39 "event_id": "e14c7b49-82c4-4f0c-a88a-987a62241d34",40 "event_metadata": {41 "institution_id": "ins_10",42 "institution_name": "American Express",43 "request_id": "0jjsLDlMl1VaVxU"44 },45 "event_name": "SELECT_INSTITUTION",46 "timestamp": "2024-07-29T21:22:32Z"47 },48 {49 "event_id": "b9b9f7ce-52bc-470e-801b-b4fa1da98e59",50 "event_metadata": {51 "request_id": "0jjsLDlMl1VaVxU"52 },53 "event_name": "TRANSITION_VIEW",54 "timestamp": "2024-07-29T21:22:23Z"55 },56 {57 "event_id": "e057a92c-eff8-467c-92c2-620109207e87",58 "event_metadata": {59 "request_id": "aHnAMiUzk8HrsNn"60 },61 "event_name": "SKIP_SUBMIT_PHONE",62 "timestamp": "2024-07-29T21:22:22Z"63 },64 {65 "event_id": "f1feedc9-61be-4a42-885c-cb67511473d2",66 "event_metadata": {67 "request_id": "aHnAMiUzk8HrsNn"68 },69 "event_name": "TRANSITION_VIEW",70 "timestamp": "2024-07-29T21:22:21Z"71 },72 {73 "event_id": "f9b49b0b-4259-4a32-860b-653b55ff8736",74 "event_metadata": {75 "request_id": "CGTlWrGRd8WaGa2"76 },77 "event_name": "TRANSITION_VIEW",78 "timestamp": "2024-07-29T21:22:20Z"79 }80 ],81 "finished_at": "2024-07-29T21:23:03.326860787Z",82 "link_session_id": "f3c3b8fe-16c6-4670-8f2b-e8d797aa6e9f",83 "on_success": {84 "metadata": {85 "accounts": [86 {87 "class_type": null,88 "id": "d1rVJdbe1jCE9jE81wlduPlQQejKXeCJnDzyw",89 "mask": "3333",90 "name": "Plaid Credit Card",91 "subtype": "credit card",92 "type": "credit",93 "verification_status": null94 }95 ],96 "institution": {97 "institution_id": "ins_10",98 "name": "American Express"99 },100 "link_session_id": "f3c3b8fe-16c6-4670-8f2b-e8d797aa6e9f",101 "transfer_status": null102 },103 "public_token": "public-sandbox-d1e64436-f033-4302-a469-b90656edf8c7"104 },105 "results": {106 "item_add_results": [107 {108 "accounts": [109 {110 "class_type": null,111 "id": "d1rVJdbe1jCE9jE81wlduPlQQejKXeCJnDzyw",112 "mask": "3333",113 "name": "Plaid Credit Card",114 "subtype": "credit card",115 "type": "credit",116 "verification_status": null117 }118 ],119 "institution": {120 "institution_id": "ins_10",121 "name": "American Express"122 },123 "public_token": "public-sandbox-d1e64436-f033-4302-a469-b90656edf8c7"124 }125 ]126 },127 "started_at": "2024-07-29T21:22:17.0951134Z"128 }129 ],130 "link_token": "link-sandbox-9b48eb4c-7e1b-44e1-b80d-faa1d71cdf5e",131 "metadata": {132 "client_name": "Wonderwallet",133 "country_codes": [134 "US"135 ],136 "initial_products": [137 "transactions"138 ],139 "language": "en",140 "redirect_uri": null,141 "webhook": "https://webhook.site/dc9c138f-75de-4db1-883a-a4add4b7eb7e"142 },143 "request_id": "jBBxxNC842EJNqW"144}
Link session events
To obtain Link session events while using Hosted Link, listen for the EVENTS
webhook, or use /link/token/get
.
Integrating Hosted Link in native mobile applications
If you are unable to use the Plaid Link SDK in your native mobile app, creating and opening up a Hosted Link is a stable and reliable way of taking users through the Link process. We recommend using a Hosted Link instead of trying to use the Plaid Web SDK in a webview.
When you use a Hosted Link in a native mobile app, you will open the URL inside of an "out of process" webview, such as an ASWebAuthenticationSession
in iOS, or an Android Custom Tab. These sessions appear as part of your application, making the process feel more seamless than opening up the URL in a separate browser application.
When the Link session is complete, Plaid will attempt to open a completion_redirect_uri
that you specify. This is typically a URI with a custom scheme that automatically closes the web session and allows your application to continue from where you left off.
To use Hosted Link in a native mobile application:
Create a Link token
Call /link/token/create
with the following values:
hosted_link.is_mobile_app
should be set totrue
hosted_link.completion_redirect_uri
should be a URI with a custom scheme.- This scheme can be any value you'd like, as long as it isn't a reserved one such as
http
ortel
. By convention, it's often one associated with the name of your application. - For example, your app might have something like
wonderwallet://hosted-link-complete
as a completion redirect URI. - Unlike the
redirect_uri
value, this URI does not need to be registered with the Plaid Dashboard, nor does it need to point to a "real" location.
- This scheme can be any value you'd like, as long as it isn't a reserved one such as
redirect_uri
should ideally be a URL that opens up your native mobile application, such as a Universal Link on iOS or an Android App Link on Android.- The
redirect_uri
is used in situations where Plaid opens up another mobile app, like Chase, for app-to-app authentication. - When the user is done authenticating with the third party app, the app will attempt to open this URI, which should redirect users back into your app.
- There is nothing you need to do to handle this incoming
redirect_uri
. As long as the link opens your application, the Hosted Link session can take it from there.
- The
1curl -X POST https://production.plaid.com/link/token/create \2-H 'Content-Type: application/json' \3-d '{4 ...Other data removed...5 hosted_link: {6 completion_redirect_uri: "wonderwallet://hosted-link-complete",7 is_mobile_app: true8 },9 redirect_uri: "https://mysite.com/universal-link/jump-to-my-app.html",10}'
"Register" your custom URL scheme
You will need to make sure your operating system can handle the completion redirect URI that the Hosted Link session will open when it's complete.
On iOS, all you need to do is pass this URL scheme into the ASWebAuthenticationSession
when you initialize it (see Open the Hosted Link URL). There is no need to register this URL scheme in your Xcode project in any other way.
In Android, you should create an Intent Filter for the activity that you want to appear when the user is done with the session.
1<intent-filter>2 <action android:name="android.intent.action.VIEW" />3 <category android:name="android.intent.category.DEFAULT" />4 <category android:name="android.intent.category.BROWSABLE" />5 <data android:scheme="wonderwallet" android:host="hosted-link-complete" />6</intent-filter>
If this activity is the same activity that you used to launch Hosted Link in the first place, you may want to give this activity a singleTask
or singleTop
launch mode, so that the original activity "pops back" to the top of your application, and you're not creating multiple copies of the same activity.
Open the Hosted Link URL
On iOS, create an ASWebAuthenticationSession
, using the Hosted Link URL you received from the server and the same custom scheme you used in your completion_redirect_uri
.
1@IBAction func connectBankWasPressed(_ sender: Any) {2 guard let hostedLinkURL = hostedLinkURL else {return}3 let scheme = "wonderwallet" 4 let session = ASWebAuthenticationSession(url: URL(string: urlToOpen)!, callbackURLScheme: scheme) { callbackURL, error in5 guard error == nil, let callbackURL = callbackURL else { 6 // The user might have clicked "Cancel" on the session. This will be captured as a 'canceledLogin' error.7 return 8 }9 if (callbackURL.absoluteString == "\(scheme)://hosted-link-complete") {10 self.checkLinkStatus()11 }12 }13 session.presentationContextProvider = self14 session.start() 15}
For the above code to work, you will also need to make sure the appropriate view controller is set as a proper context provider:
1extension ConnectToBankViewController: ASWebAuthenticationPresentationContextProviding {2 func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {3 return view.window!4 }5}
On Android devices, create a Custom Tab that opens up the Hosted Link URL from within your application:
1import androidx.browser.customtabs.CustomTabsIntent2import android.net.Uri3
4private fun openHostedLink(hostedLinkUrl) {5 val customTabsIntent = CustomTabsIntent.Builder().build()6 customTabsIntent.launchUrl(this, Uri.parse(hostedLinkUrl))7}
See the Apple Developer Documentation or Chrome Developer Documentation for more details on these two methods.
Handle the incoming URI
When the user is done with the Hosted Link session, Plaid will attempt to open the completion_redirect_uri
you specified when creating the Link token. If your application is set up as described above, this will close the Hosted Link page and bring your application back to the foreground. You will then receive this URI in a callback handler, where you can perform tasks such as refreshing the user's Link Session status.
Note that Plaid will call this URI whether the user successfully completed the session, or asked Plaid to exit the connection process, so you cannot assume that receiving this URI means a successful connection. You should listen for the SESSION_FINISHED
webhook or call /link/token/get
to determine the status of the user's Hosted Link session.
On iOS, you define this handler when you create the ASWebAuthenticationSession
1let scheme = "wonderwallet" 2let session = ASWebAuthenticationSession(url: URL(string: urlToOpen)!, callbackURLScheme: scheme) { callbackURL, error in3 guard error == nil, let callbackURL = callbackURL else { 4 return 5 }6 if (callbackURL.absoluteString == "\(scheme)://hosted-link-complete") {7 self.checkLinkStatus()8 }9}
If the user dismisses the window by clicking the Cancel button, this handler will still be called, but with a canceledLogin
error instead of a callbackURL
.
On Android, you can retrieve this URL in the onNewIntent
callback if you are returning to the previous activity. (If you are opening a new activity, you can use the onCreate
callback.)
1override fun onNewIntent(intent: Intent?) {2 super.onNewIntent(intent)3 intent?.data?.let { uri ->4 if (uri.scheme == "wonderwallet" && uri.host == "hosted-link-complete") {5 checkLinkStatus()6 }7 }8 }
If the user dismisses the Custom Tab by tapping the close button, this callback will not be called, but an onResume
callback will still be called.
Migrating to Hosted Link from other integrations
First, review the Integration process to understand how to launch the Hosted Link flow and the configuration options available.
If you're currently capturing Link session events, migrate to using /link/token/get
to receive Link session events.
If your users will be opening Link sessions inside a native mobile application, launch Hosted Link in an out-of-process webview as described above instead of using an in-process webview.
iFrame based nested integrations
For nested integrations like iFrames in a customer web page within a merchant webview, shift from capturing events through redirects to using backend events via /link/token/get
. Your merchants should launch Hosted Link in an out-of-process webview or external browser.
Either you or the merchant must add an extra callback parameter to /link/token/create
, allowing Plaid to close the out-of-process webview as needed. You will likely make the /link/token/create
call. Merchants must either obtain a callback from the customer or ensure handling of a specified callback in their code.
Next steps
Once you have obtained a public token, you can integrate via the same process as non-Hosted Link integrations, including calling /item/public_token/exchange
to exchange the public token for an access token.
Testing Hosted Link
You can test Hosted Link in Production or Sandbox. In the Sandbox environment, Plaid will not provide email or SMS Hosted Link delivery, and update mode sessions will expire after 30 minutes.
Pricing
Hosted Link is free; there is no additional fee to use it. If using Link Delivery to deliver Hosted Link sessions, there is a fee for each link sent. For Link Delivery pricing details, contact Sales or your Account Manager.