Plaid logo
Docs
ALL DOCS

Transfer

  • Transfer Overview
  • Transfer Application
  • Creating transfers
  • Monitoring transfers
  • Plaid Ledger flow of funds
  • Transfer Dashboard
  • Refunds
  • Recurring transfers
  • Transfer UI
  • Platform Payments
  • Errors and troubleshooting
  • Testing in Sandbox
  • Legacy Flow of funds
Plaid logo
Docs
Close search modal
Ask Bill!
Ask Bill!
Hi! I'm Bill! You can ask me all about the Plaid API. Try asking questions like:
  • What's the difference between an Item and an access token?
  • Which countries does Investments support?
  • How do I set up a webhook for IDV?
Note: Bill isn't perfect. He's just a robot platypus that reads our docs for fun. You should treat his answers with the same healthy skepticism you might treat any other answer on the internet. This chat may be logged for quality and training purposes. Please don't send Bill any PII -- he's scared of intimacy. All chats with Bill are subject to Plaid's Privacy Policy.
Plaid.com
Log in
Get API Keys
Open nav

Receiving Funds Using Transfer UI

Facilitate transfers with an intuitive user interface

Plaid Transfer UI is a drop-in user interface that makes it easy for end users to authorize transfers. Transfer UI is compliant with Nacha WEB guidelines and automatically captures and manages Proof of Authorization on your behalf.

With Transfer UI, users are able to authorize two types of transfers: payments or disbursements. Before authorizing a transfer, users are able to review transfer details, such as amount, fund origination account, fund target account, and more.

Transfer UI does not currently support recurring transfers, Database Auth, or Platform Payments.

Example Transfer UI flow.
Example Transfer UI flow.
Prefer to learn by watching? Get an overview of how Transfer UI works in just 3 minutes!

Integration

To integrate with Transfer UI, follow the following steps:

  1. In the Plaid Dashboard, create a Link customization with "Account Select" set to the "Enabled for one account" selection.

  2. Call /transfer/intent/create to obtain a transfer_intent.id.

  3. Call /link/token/create, specifying ["transfer"] in the products parameter, the transfer_intent.id in the transfer.intent_id parameter, and the name of your Link customization in the link_customization_name parameter. If you already have an access_token for this user, you can provide it to /link/token/create to streamline the Link flow, otherwise, the user will authenticate their bank account within the same Transfer UI session.

  4. Initialize a Link instance using the link_token created in the previous step. For more details for your specific platform, see the Link documentation.

  5. The user will now go through the Link flow to perform their transfer. The onSuccess callback will indicate they have completed the Link flow.

  6. (Optional) You will receive a public_token from the onSuccess callback. If you do not already have an access token for this user's account, call /item/public_token/exchange to exchange this public token for an access_token in order to streamline future transfers for this user.

Transfer UI user flows

Depending on whether you provided an access token in the /link/token/create step, the Transfer UI experience will differ.

Bank on file flow (access_token provided):

Example Transfer UI flow if the access_token is provided.
Example Transfer UI flow if the access_token is provided.

Net new user flow (access_token not provided):

Example Transfer UI flow if the access_token is not provided.
Example Transfer UI flow if the access_token is not provided.

Tracking transfer creation

The instructions in this section correspond to the Web and Webview libraries for Link. If you're using a mobile SDK, information about the transfer intent status can be found in the metadataJson field in the SDK's onSuccess callback. As an example, see Android: metadataJson.

You can determine whether a transfer was successfully created by referring to the transfer_status field in the metadata object returned by onSuccess. A value of complete indicates that the transfer was successfully originated. A value of incomplete indicates that the transfer was not originated. Note that this field only indicates the status of a transfer creation. It does not indicate the status of a transfer (i.e., funds movement). For more information on transfer intents, see Retrieving additional information about transfer intents. For help troubleshooting incomplete transfers, see Troubleshooting Transfer UI.

When using Transfer UI, the onSuccess callback is called at a different point in time in the Link flow. Typically, the onSuccess callback is called after an account is successfully linked using Link. When using Transfer UI, however, the onSuccess callback is called only after both of the following conditions are met: an account is successfully linked using Link and a transfer is confirmed via the UI. Note that the onSuccess callback only indicates that an account was successfully linked. It does not indicate a successful transfer (i.e., funds movement).

1{
2 institution: {
3 name: 'Wells Fargo',
4 institution_id: 'ins_4'
5 },
6 accounts: [
7 {
8 id: 'ygPnJweommTWNr9doD6ZfGR6GGVQy7fyREmWy',
9 name: 'Plaid Checking',
10 mask: '0000',
11 type: 'depository',
12 subtype: 'checking',
13 verification_status: ''
14 },
15 {
16 id: '9ebEyJAl33FRrZNLBG8ECxD9xxpwWnuRNZ1V4',
17 name: 'Plaid Saving',
18 mask: '1111',
19 type: 'depository',
20 subtype: 'savings'
21 }
22 ...
23 ],
24 transfer_status: 'incomplete',
25 link_session_id: '79e772be-547d-4c9c-8b76-4ac4ed4c441a'
26}

Retrieving additional information about transfer intents

To retrieve more information about a transfer intent, call the /transfer/intent/get endpoint and pass in a transfer intent id (returned by the /transfer/intent/create endpoint).

Select Language
1const request: TransferIntentGetRequest = {
2 transfer_intent_id: '460cbe92-2dcc-8eae-5ad6-b37d0ec90fd9',
3};
4
5try {
6 const response = await client.transferIntentGet(request);
7} catch (error) {
8 // handle error
9}
1{
2 "transfer_intent": {
3 "account_id": "3gE5gnRzNyfXpBK5wEEKcymJ5albGVUqg77gr",
4 "ach_class": "ppd",
5 "amount": "15.75",
6 "authorization_decision": "APPROVED",
7 "authorization_decision_rationale": null,
8 "created": "2020-08-06T17:27:15Z",
9 "description": "Desc",
10 "failure_reason": null,
11 "id": "460cbe92-2dcc-8eae-5ad6-b37d0ec90fd9",
12 "metadata": {
13 "key1": "value1",
14 "key2": "value2"
15 },
16 "mode": "DISBURSEMENT",
17 "origination_account_id": "9853defc-e703-463d-86b1-dc0607a45359",
18 "status": "SUCCEEDED",
19 "transfer_id": "8945fedc-e703-463d-86b1-dc0607b55460",
20 "user": {
21 "address": {
22 "street": "100 Market Street",
23 "city": "San Francisco",
24 "region": "California",
25 "postal_code": "94103",
26 "country": "US"
27 },
28 "email_address": "lknope@email.com",
29 "legal_name": "Leslie Knope",
30 "phone_number": "123-456-7890"
31 }
32 },
33 "request_id": "saKrIBuEB9qJZno"
34}

The response is a transfer_intent object with more information about the transfer intent.

The status field in the response indicates whether the transfer intent was successfully captured. It can have one of the following values: FAILED, SUCCEEDED, or PENDING.

If the value of status is FAILED, the transfer intent was not captured. The transfer_id field will be "null" and the failure_reason object will contain information about why the transfer intent failed. Some possible reasons may include: the authorization was declined, the account is blocked, or the origination account was invalid.

If the value of status is SUCCEEDED, the transfer intent was successfully captured. The accompanying transfer_id field in the response will be set to the ID of the originated transfer. This ID can be used with other Transfer API endpoints.

A value of PENDING can mean one of the following:

  • The transfer intent has not yet been processed by the authorization engine (authorization_decision is "null"). This is the initial state of all transfer intents.

  • The transfer intent was processed and approved by the authorization engine (authorization_decision is "approved"), but has not yet been processed by the transfer creation processor.

  • The transfer intent was processed by the authorization engine and was declined (authorization_decision is "declined") due to insufficient funds (authorization_decision_rationale.code is "NSF"). If this is the case, the end user can retry the transfer intent up to three times. The transfer intent status will remain as "PENDING" throughout the retry intents. After three unsuccessful retries, the transfer intent status will be "FAILED".

Transfer UI best practices

While transfer UI provides a Nacha-compliant UI for your transfers, to maximize conversion, it is recommended to still provide context in your own app to your end users about the transfer. We recommend that your app implement a UI where the customer selects or confirms the transfer amount and, if applicable, the funds source or destination. To maximize user confidence, your app should also provide a post-Link success pane confirming the transfer details, including amount and effective date.

Sample implementation

For a real-life example of an app that incorporates both Transfer and Transfer UI, see the Node-based Plaid Pattern Transfer sample app. Pattern Transfer is a sample subscriptions payment app that enables ACH bank transfers.

Troubleshooting Transfer UI

The onExit callback is called

If the onExit callback is called, the Link session failed to link an account. The transfer intent could not be initiated.

onSuccess is called, but the transfer intent is incomplete

The onSuccess callback is not an indication of a successful transfer creation or a successful transfer (i.e., funds movement). It only indicates the successful linking of an account. To fully diagnose incomplete transfer intents (i.e., when the transfer_status field in the metadata object returned by onSuccess is incomplete), call the /transfer/intent/get endpoint and pass in the corresponding transfer intent ID as an argument. Information about the transfer intent can be found in the status, failure_reason, authorization_decision, and authorization_decision_rationale fields in the response.

Network errors or institution connection issues

Network errors or institution connection issues can also result in incomplete transfer intents. In such cases, generate a new Link token using the existing access_token and invoke Transfer UI to allow the end user to attempt the transfer intent again.

Was this helpful?
Developer community
GitHub
GitHub
Stack Overflow
Stack Overflow
YouTube
YouTube
Discord
Discord