Hosted Link 
============

#### Integrate with Link using a Plaid-hosted frontend 

#### Overview 

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 the recommended Link mode when the official Plaid mobile or web SDKs cannot be used. This includes webview-based mobile apps, or integrations that do not fully own their frontend experience, such as [embedded payment service providers](https://plaid.com/docs/link/hosted-link/embedded-clients/index.html.md) . Hosted Link can also be used for applications where you do not have a frontend at all; for example, you can send a Hosted Link session to customers who are in-person at a retail location.

(An image of "image of Hosted Link flow")

Example Hosted Link flow: you send a link to your customer and they complete the Plaid-hosted Link flow. You do no front-end integration work.

With Link Delivery (beta), Hosted Link can also deliver the URL for the Link session to your users via text or email.

To request access to Link Delivery, contact your account manager.

Hosted Link is fully supported with all Plaid products and Link flows (including [update mode](https://plaid.com/docs/link/update-mode/index.html.md) ), except for Layer and Identity Verification.

For Identity Verification customers, Hosted Link can optionally be used instead of IDV-specific [Verification Links](https://plaid.com/docs/identity-verification/index.html.md#verification-links-hosted-flow) to take advantage of features such as Link Delivery (beta); however, when using Hosted Link with Identity Verification, the session will not redirect to the `completion_redirect_uri`.

##### Who should use Hosted Link 

Hosted Link is recommended when any of the following apply:

*   You are using Plaid within a webview-based mobile app.
*   You do not have an application frontend.
*   Your users are launching Link from outside an in-app context (e.g. by scanning a QR code or receiving an SMS triggered by a customer service representative).
*   You are launching Link from inside an iFrame or an embedded client experience where you do not control the frontend (e.g. PSP integrations).

For details on using Hosted Link as an embedded client, See [Hosted Link for embedded clients](https://plaid.com/docs/link/hosted-link/embedded-clients/index.html.md) .

#### Integration process 

[Prefer to learn by watching? Check out this quick video guide to enabling Hosted Link!](https://www.youtube.com/watch?v=7wETps5G04A)

##### Creating a Link token 

To start with Hosted Link, call [/link/token/create](https://plaid.com/docs/api/link/index.html.md#linktokencreate) 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](https://plaid.com/docs/api/link/index.html.md#linktokencreate) requests, regardless of whether you include a `hosted_link` object.

You can provide the following Hosted Link parameters to [/link/token/create](https://plaid.com/docs/api/link/index.html.md#linktokencreate) :

*   Specify a `webhook` endpoint where the `public_token` will be delivered. Webhooks are strongly recommended when using Hosted Link, as the webhook is the primary means of delivering the `public_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 either `sms` or `email`. Plaid will contact the user via the information you provide in the `user` object of the request. For example, if you selected `email`, the `user` object in the request must contain an `email_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 `ASWebAuthenticationSession` or Android Custom Tab, set `hosted_link.is_mobile_app` to `true`.

The response from [/link/token/create](https://plaid.com/docs/api/link/index.html.md#linktokencreate) 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.

Sample /link/token/create request for Hosted Link

```bash
curl -X POST https://production.plaid.com/link/token/create \
-H 'Content-Type: application/json' \
-d '{
    "client_id": "${PLAID_CLIENT_ID}",
    "secret": "${PLAID_SECRET}",
    "client_name": "Wonderwallet",
    "country_codes": ["US"],
    "redirect_uri": "{{UNIVERSAL_OR_APP_LINK}}",
    "webhook": "https://wonderwallet.com/webhook_receiver",
    "language": "en",
    "user": {
      "client_user_id": "a57d3304",
      "phone_number": "+19162255887"
    },
  "products": ["auth"],
  "hosted_link": {
    "delivery_method": "sms",
    "completion_redirect_uri": "https://wonderwallet.com/redirect",
    "is_mobile_app": false,
    "url_lifetime_seconds": 900
  }
}'
```

Sample /link/token/create response

```json
{
  "expiration": "2023-08-18T02:08:03Z",
  "hosted_link_url": "https://secure.plaid.com/link/lp9o97618r1r6p93oon62nr025950ro4s3",
  "link_token": "link-production-4b42163e-6e1c-48bb-a17a-e570405eb9f8",
  "request_id": "kNbPMmLxzBObzpt"
}
```

##### 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](https://plaid.com/docs/api/link/index.html.md#session_finished) webhook and the [/link/token/get](https://plaid.com/docs/api/link/index.html.md#linktokenget) endpoint.

Each time a user completes a Link flow, Plaid will fire a [SESSION\_FINISHED](https://plaid.com/docs/api/link/index.html.md#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.

Sample SESSION\_FINISHED webhook

```json
{
  "webhook_type": "LINK",
  "webhook_code": "SESSION_FINISHED",
  "status": "SUCCESS",
  "link_session_id": "356dbb28-7f98-44d1-8e6d-0cec580f3171",
  "link_token": "link-sandbox-af1a0311-da53-4636-b754-dd15cc058176",
  "public_tokens": [
    "public-sandbox-b0e2c4ee-a763-4df5-bfe9-46a46bce993d"
  ],
  "environment": "sandbox"
}
```

You can also call [/link/token/get](https://plaid.com/docs/api/link/index.html.md#linktokenget) 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](https://plaid.com/docs/api/link/index.html.md#linktokenget) 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](https://plaid.com/docs/api/link/index.html.md#linktokenget) 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](https://plaid.com/docs/api/link/index.html.md#linktokenget) 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](https://plaid.com/docs/link/multi-item-link/index.html.md) option, which allows for multiple public tokens to be returned in a single Link session.

Sample /link/token/get response

```json
{
  "created_at": "2024-07-29T21:22:13Z",
    "expiration": "2024-07-29T21:52:13Z",
    "link_sessions": [
        {
            "events": [
                {
                    "event_id": "c1ccb962-9de0-494c-b85b-e89cdf3b2478",
                    "event_metadata": {
                        "institution_id": "ins_10",
                        "institution_name": "American Express",
                        "request_id": "Z9MB4fuMB2TJqgL"
                    },
                    "event_name": "HANDOFF",
                    "timestamp": "2024-07-29T21:23:03Z"
                },
                {
                    "event_id": "ff03f1ff-313b-4c2e-997f-83791c7ec432",
                    "event_metadata": {
                        "institution_id": "ins_10",
                        "institution_name": "American Express",
                        "request_id": "vyPamzbGPPsQucO"
                    },
                    "event_name": "OPEN_OAUTH",
                    "timestamp": "2024-07-29T21:22:34Z"
                },
                {
                    "event_id": "0ea6d0df-1bb0-4e4d-bea5-b3731b7536bf",
                    "event_metadata": {
                        "institution_id": "ins_10",
                        "institution_name": "American Express",
                        "request_id": "vyPamzbGPPsQucO"
                    },
                    "event_name": "TRANSITION_VIEW",
                    "timestamp": "2024-07-29T21:22:33Z"
                },
                {
                    "event_id": "e14c7b49-82c4-4f0c-a88a-987a62241d34",
                    "event_metadata": {
                        "institution_id": "ins_10",
                        "institution_name": "American Express",
                        "request_id": "0jjsLDlMl1VaVxU"
                    },
                    "event_name": "SELECT_INSTITUTION",
                    "timestamp": "2024-07-29T21:22:32Z"
                },
                {
                    "event_id": "b9b9f7ce-52bc-470e-801b-b4fa1da98e59",
                    "event_metadata": {
                        "request_id": "0jjsLDlMl1VaVxU"
                    },
                    "event_name": "TRANSITION_VIEW",
                    "timestamp": "2024-07-29T21:22:23Z"
                },
                {
                    "event_id": "e057a92c-eff8-467c-92c2-620109207e87",
                    "event_metadata": {
                        "request_id": "aHnAMiUzk8HrsNn"
                    },
                    "event_name": "SKIP_SUBMIT_PHONE",
                    "timestamp": "2024-07-29T21:22:22Z"
                },
                {
                    "event_id": "f1feedc9-61be-4a42-885c-cb67511473d2",
                    "event_metadata": {
                        "request_id": "aHnAMiUzk8HrsNn"
                    },
                    "event_name": "TRANSITION_VIEW",
                    "timestamp": "2024-07-29T21:22:21Z"
                },
                {
                    "event_id": "f9b49b0b-4259-4a32-860b-653b55ff8736",
                    "event_metadata": {
                        "request_id": "CGTlWrGRd8WaGa2"
                    },
                    "event_name": "TRANSITION_VIEW",
                    "timestamp": "2024-07-29T21:22:20Z"
                }
            ],
            "finished_at": "2024-07-29T21:23:03.326860787Z",
            "link_session_id": "f3c3b8fe-16c6-4670-8f2b-e8d797aa6e9f",
            "on_success": {
                "metadata": {
                    "accounts": [
                        {
                            "class_type": null,
                            "id": "d1rVJdbe1jCE9jE81wlduPlQQejKXeCJnDzyw",
                            "mask": "3333",
                            "name": "Plaid Credit Card",
                            "subtype": "credit card",
                            "type": "credit",
                            "verification_status": null
                        }
                    ],
                    "institution": {
                        "institution_id": "ins_10",
                        "name": "American Express"
                    },
                    "link_session_id": "f3c3b8fe-16c6-4670-8f2b-e8d797aa6e9f",
                    "transfer_status": null
                },
                "public_token": "public-sandbox-d1e64436-f033-4302-a469-b90656edf8c7"
            },
            "results": {
                "item_add_results": [
                    {
                        "accounts": [
                            {
                                "class_type": null,
                                "id": "d1rVJdbe1jCE9jE81wlduPlQQejKXeCJnDzyw",
                                "mask": "3333",
                                "name": "Plaid Credit Card",
                                "subtype": "credit card",
                                "type": "credit",
                                "verification_status": null
                            }
                        ],
                        "institution": {
                            "institution_id": "ins_10",
                            "name": "American Express"
                        },
                        "public_token": "public-sandbox-d1e64436-f033-4302-a469-b90656edf8c7"
                    }
                ]
            },
            "started_at": "2024-07-29T21:22:17.0951134Z"
        }
    ],
    "link_token": "link-sandbox-9b48eb4c-7e1b-44e1-b80d-faa1d71cdf5e",
    "metadata": {
        "client_name": "Wonderwallet",
        "country_codes": [
            "US"
        ],
        "initial_products": [
            "auth"
        ],
        "language": "en",
        "redirect_uri": "{{UNIVERSAL_OR_APP_LINK}}",
        "webhook": "https://wonderwallet.com/webhook_receiver"
    },
    "request_id": "jBBxxNC842EJNqW"
}
```

#### Link session events 

To obtain Link session events while using Hosted Link, listen for the [EVENTS](https://plaid.com/docs/api/link/index.html.md#events) webhook, or use [/link/token/get](https://plaid.com/docs/api/link/index.html.md#linktokenget) .

#### 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](https://plaid.com/docs/api/link/index.html.md#linktokencreate) with the following values:

*   `hosted_link.is_mobile_app` should be set to `true`
*   `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` or `tel`. 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.
*   `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.
    *   Plaid cannot use `android_package_name` to perform the OAuth redirect when using Hosted Link. You must use `redirect_uri`, even if using an Android 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.

Sample /link/token/create request for mobile apps

```bash
curl -X POST https://production.plaid.com/link/token/create \
-H 'Content-Type: application/json' \
-d '{
     ...Other data removed...
      "hosted_link": {
        "completion_redirect_uri": "wonderwallet://hosted-link-complete",
        "is_mobile_app": true
      },
      "redirect_uri": "https://mysite.com/universal-link/jump-to-my-app.html"
}'
```

##### "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](https://plaid.com/docs/link/hosted-link/index.html.md#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.

Sample Android Manifest snippet

```html
<intent-filter>
  <action android:name="android.intent.action.VIEW" />
  <category android:name="android.intent.category.DEFAULT" />
  <category android:name="android.intent.category.BROWSABLE" />
  <data android:scheme="wonderwallet" android:host="hosted-link-complete" />
</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`.

Sample call starting ASWebAuthenticationSession in Swift

```swift
@IBAction func connectBankWasPressed(_ sender: Any) {
    guard let hostedLinkURL = hostedLinkURL else {return}
    let scheme = "wonderwallet"
    let session = ASWebAuthenticationSession(url: URL(string: hostedLinkURL)!, callbackURLScheme: scheme) { callbackURL, error in
        guard error == nil, let callbackURL = callbackURL else {
            // The user might have clicked "Cancel" on the session. This will be captured as a 'canceledLogin' error.
            return
        }
        if (callbackURL.absoluteString == "\(scheme)://hosted-link-complete") {
            self.checkLinkStatus()
        }
    }
    session.presentationContextProvider = self
    session.start()
}
```

For the above code to work, you will also need to make sure the appropriate view controller is set as a proper context provider:

Setting a context provider in UIKit

```swift
extension ConnectToBankViewController: ASWebAuthenticationPresentationContextProviding {
    func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
        return view.window!
    }
}
```

On Android devices, create a Custom Tab that opens up the Hosted Link URL from within your application:

Creating a Custom Tab example in Kotlin

```kotlin
import androidx.browser.customtabs.CustomTabsIntent
import android.net.Uri

private fun openHostedLink(hostedLinkUrl: String) {
  val customTabsIntent = CustomTabsIntent.Builder().build()
  customTabsIntent.launchUrl(this, Uri.parse(hostedLinkUrl))
}
```

See the [Apple Developer Documentation](https://developer.apple.com/documentation/authenticationservices/aswebauthenticationsession) or [Chrome Developer Documentation](https://developer.chrome.com/docs/android/custom-tabs) 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.

The `completion_redirect_uri` is not supported in Identity Verification sessions.

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](https://plaid.com/docs/api/link/index.html.md#session_finished) webhook or call [/link/token/get](https://plaid.com/docs/api/link/index.html.md#linktokenget) to determine the status of the user's Hosted Link session.

On iOS, you define this handler when you create the `ASWebAuthenticationSession`

Sample callback handler in ASWebAuthenticationSession

```swift
let scheme = "wonderwallet"
let session = ASWebAuthenticationSession(url: URL(string: hostedLinkURL)!, callbackURLScheme: scheme) { callbackURL, error in
    guard error == nil, let callbackURL = callbackURL else {
        return
    }
    if (callbackURL.absoluteString == "\(scheme)://hosted-link-complete") {
        self.checkLinkStatus()
    }
}
```

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.)

Sample capturing the incoming URI in onNewIntent

```kotlin
override fun onNewIntent(intent: Intent?) {
    super.onNewIntent(intent)
    intent?.data?.let { uri ->
      if (uri.scheme == "wonderwallet" && uri.host == "hosted-link-complete") {
        checkLinkStatus()
      }
    }
  }
```

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.

#### Sample code 

[Tiny Quickstart: Hosted Link](https://github.com/plaid/tiny-quickstart/tree/main/hosted_link) contains a minimal Plaid integration showing the Hosted Link flow.

#### Migrating to Hosted Link from other integrations 

First, review the [Integration process](https://plaid.com/docs/link/hosted-link/index.html.md#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](https://plaid.com/docs/api/link/index.html.md#linktokenget) 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](https://plaid.com/docs/api/link/index.html.md#linktokenget) . 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](https://plaid.com/docs/api/link/index.html.md#linktokencreate) , allowing Plaid to close the out-of-process webview as needed. You will likely make the [/link/token/create](https://plaid.com/docs/api/link/index.html.md#linktokencreate) 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](https://plaid.com/docs/api/items/index.html.md#itempublic_tokenexchange) 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 

When using Link Delivery to deliver Hosted Link sessions, there is a fee for each link sent. For Link Delivery pricing details, [contact sales](https://plaid.com/contact/) or your account manager.