Database Insights and Match (legacy) 
=====================================

#### Evaluate a manually entered account using Plaid network data  

(An image of "Payment method selection page showing bank options with search. Car details, insurance info, and payment summary are displayed.")

The Database Insights end-user flow with Embedded Institution Search enabled.

#### Overview 

Database Insights and Database Match have been replaced with Database Auth. The documentation provided here is for the use of customers maintaining support of existing legacy Database Insights integrations.

Database Insights (legacy) can increase conversion by providing instant account verification without requiring users to link a bank account via credentials. End users choosing the manual Database Insights path will not be required to log in to their financial institution and instead can enter their account and routing number manually.

Database Insights verifies account and routing numbers by checking the information provided against Plaid's known account numbers, leveraging Plaid's database of over 200 million verified accounts. If no match is found, Plaid will check the account number format against known usages by the institution associated with the given routing number. Database Insights will provide a verification status of 'pass', 'pass with caution', or 'fail' and a set of attributes that contributed to that status, such as whether a match was found or whether Plaid fell back to checking account number formats.

Database Insights does not verify that the user has access to the bank account and Database Insights also does not fully guarantee that the account exists, especially in a 'pass with caution' scenario. For these reasons, Database Insights should only be enabled where there is a low risk of fraud or ACH returns. Examples of use cases where Database Insights may be appropriate include bill payment, rent collection, business to business payments, or subscription payments.

Approximately 30% of Items verified by Database Insights can also be verified by [/identity/match](https://plaid.com/docs/api/products/identity/index.html.md#identitymatch) or [/signal/evaluate](https://plaid.com/docs/api/products/signal/index.html.md#signalevaluate) . For more details, see [Identity Match](https://plaid.com/docs/identity/index.html.md#identity-match) and [Signal Transaction Scores](https://plaid.com/docs/signal/index.html.md) . All Items verified with Database Insights are also compatible with account ownership identity verification via [Identity Document Upload](https://plaid.com/docs/identity/identity-document-upload/index.html.md) .

Database Insights and [Embedded Institution Search](https://plaid.com/docs/link/embedded-institution-search/index.html.md) are both designed to increase adoption of ACH payment methods and are frequently used together. Database Insights is also fully compatible with the standard, non-Embedded Institution Search Link flow.

##### Database Insights flow 

1.  Starting on a page in your app, the user clicks an action that opens Plaid Link.
2.  Inside of Plaid Link, the user selects an option to enter the manual verification flow and provides their account and routing number.
3.  Once the user has submitted their information, Link closes and returns a `public_token` within the `onSuccess` callback.
4.  Call [/item/public\_token/exchange](https://plaid.com/docs/api/items/index.html.md#itempublic_tokenexchange) to exchange the `public_token` for an `access_token`, then call [/auth/get](https://plaid.com/docs/api/products/auth/index.html.md#authget) to obtain the account numbers and the verification results.
5.  Based on the values of the `verification_status` and `verification_insights` fields returned by [/auth/get](https://plaid.com/docs/api/products/auth/index.html.md#authget) , make a decision whether to proceed with the account information for ACH or to reject the account information as unverified.

When using other flows, customers using a [processor partner](https://plaid.com/docs/auth/partnerships/index.html.md) do not typically need to call [/auth/get](https://plaid.com/docs/api/products/auth/index.html.md#authget) , and can directly call [/processor/token/create](https://plaid.com/docs/api/processors/index.html.md#processortokencreate) instead. However, if you are using Database Insights with a processor partner, you must call [/auth/get](https://plaid.com/docs/api/products/auth/index.html.md#authget) and check the value of the `verification_status` and/or `verification_insights` fields before passing a processor token to the partner.

#### Implementation steps 

##### Create a link\_token 

Create a `link_token` with the following parameters:

*   The `products` array should include only `auth` or `transfer` as a product when using Database Insights. While in most cases additional products can be added to existing Plaid Items, Items created with Database Insights are an exception and cannot be used with any Plaid products other than Auth, Transfer, Signal, or Identity Match.

Approximately 30% of Items verified by Database Insights can also be verified by [/identity/match](https://plaid.com/docs/api/products/identity/index.html.md#identitymatch) or Signal. For more details, see [Identity Match](https://plaid.com/docs/identity/index.html.md#identity-match) and [Signal](https://plaid.com/docs/signal/signal-rules/index.html.md#data-availability-limitations) . If using Identity Match or Signal in this way, they should be added to the Item via the `required_if_supported_products`, `optional_products`, or `additional_consented_products` fields rather than the `products` array.

*   `country_codes` should be set to `['US']` or `['CA']`– Database Insights is currently only available in the United States or Canada.
*   The `auth` object should specify `"database_insights_enabled": true`.
*   (Optional) Within the `auth` object, specify `"auth_type_select_enabled": true` in order to enable [Auth Type Select](https://plaid.com/docs/auth/coverage/flow-options/index.html.md#adding-manual-verification-entry-points-with-auth-type-select) , which will surface the manual entry point for Database Insights on the default Link screen. If Auth Type Select is not enabled, credential-based flows will be the primary UI flow and the Database Insights entry point will only appear as a fallback, except when using [Embedded Institution Search](https://plaid.com/docs/link/embedded-institution-search/index.html.md) .

Database Insights cannot be used in the same session as Database Match, Same-Day Micro-deposits, or Instant Micro-deposits. If any of those flows are explicitly enabled in the `auth` object alongside Database Insights, an error will occur when calling [/link/token/create](https://plaid.com/docs/api/link/index.html.md#linktokencreate) . If they are implicitly enabled due to account default settings, they will be overridden by enabling Database Insights.

```bash
curl -X POST https://sandbox.plaid.com/link/token/create \
-H 'Content-Type: application/json' \
-d '{
  "client_id": "${PLAID_CLIENT_ID}",
  "secret": "${PLAID_SECRET}",
  "user": {"client_user_id": "${UNIQUE_USER_ID}"},
  "client_name": "Plaid App",
  "products": ["auth"],
  "country_codes": ["US"],
  "language": "en",
  "webhook": "https://sample-web-hook.com",
  "redirect_uri": "https://domainname.com/oauth-page.html",
  "auth": {
    "database_insights_enabled": true
  }
}'

```

```ruby
link_token_create_request = Plaid::LinkTokenCreateRequest.new(
  {
    user: {
      client_user_id: "user-id",
    },
    client_name: 'Plaid Test App',
    products: ['auth'],
    country_codes: ['US'],
    language: "en",
    webhook: 'https://webhook.sample.com',
    link_customization_name: "default",
    auth: {
        database_insights_enabled: true
    },
  }
)
response = client.link_token_create(
  link_token_create_request
)
link_token = response.link_token

```

```node
const request: LinkTokenCreateRequest = {
  user: { client_user_id: new Date().getTime().toString() },
  client_name: 'Plaid App',
  products: [Products.Auth],
  country_codes: [CountryCode.Us],
  webhook: 'https://webhook.sample.com',
  language: 'en',
  auth: {
    database_insights_enabled: true
  },
};
try {
  const response = await plaidClient.linkTokenCreate(request);
  const linkToken = response.data.link_token;
} catch (error) {
  // handle error
}

```

```java
LinkTokenCreateRequestAuth auth = new LinkTokenCreateRequestAuth()
    .databaseInsightsEnabled(true);

LinkTokenCreateRequest request = new LinkTokenCreateRequest()
    .user(user)
    .clientName("Test app")
    .products(Arrays.asList(Products.AUTH))
    .countryCodes(Arrays.asList(CountryCode.US))
    .language("en")
    .webhook("https://example.com/webhook")
    .linkCustomizationName("default")
    .accountFilters(accountFilters)
    .auth(auth);

```

```python
request = LinkTokenCreateRequest(
    products=[Products('auth')],
    client_name="Plaid Test App",
    country_codes=[CountryCode('US')],
    language='en',
    webhook='https://sample-webhook-uri.com',
    link_customization_name='default',
    user=LinkTokenCreateRequestUser(
        client_user_id='user-id',
    ),
    auth=LinkTokenCreateRequestAuth(
        database_insights_enabled=True,
    )
)

response = client.link_token_create(request)

```

```go
user := plaid.LinkTokenCreateRequestUser{
  ClientUserId: "user-id",
}

request := plaid.NewLinkTokenCreateRequest(
  "Plaid Test",
  "en",
  []plaid.CountryCode{plaid.COUNTRYCODE_US},
)
request.SetUser(user)
request.SetProducts([]plaid.Products{plaid.PRODUCTS_AUTH})
request.SetLinkCustomizationName("default")
request.SetWebhook("https://webhook-uri.com")
var databaseInsightsEnabled bool = true
request.SetAuth(plaid.LinkTokenCreateRequestAuth{
 DatabaseInsightsEnabled: &databaseInsightsEnabled,
})
resp, _, err := client.PlaidApi.LinkTokenCreate(ctx).LinkTokenCreateRequest(*request).Execute()
if err != nil {
  panic(err)
}

linkToken := resp.GetLinkToken()

```

##### Initialize Link with a link\_token 

After creating a `link_token` for the `auth` product, use it to initialize Plaid Link.

When the user successfully inputs their account and routing numbers, the `onSuccess()` callback function will return a `public_token`.

App.js

```javascript
const linkHandler = Plaid.create({
  // Fetch a link_token configured for 'auth' from your app server
  token: (await $.post('/create_link_token')).link_token,
  onSuccess: (public_token, metadata) => {
    // Send the public_token and connected accounts to your app server
    $.post('/exchange_public_token', {
      publicToken: public_token,
      accounts: metadata.accounts,
    });

    metadata = {
      ...,
      link_session_id: String,
      institution: {
        name: null, // name is always null for Database Insights
        institution_id: null // institution_id is always null for Database Insights
      },
      accounts: [{
        id: 'vzeNDwK7KQIm4yEog683uElbp9GRLEFXGK98D',
        mask: '1234',
        name: null,
        type: 'depository',
        subtype: 'checking',
        verification_status: ''
      }]
    }
  },
  // ...
});

// Open Link on user-action
linkHandler.open();
```

##### Exchange the public token 

In your own backend server, call the [/item/public\_token/exchange](https://plaid.com/docs/api/items/index.html.md#itempublic_tokenexchange) endpoint with the Link `public_token` received in the `onSuccess` callback to obtain an `access_token`. Persist the returned `access_token` and `item_id` in your database in relation to the user.

```bash
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-sandbox-b0e2c4ee-a763-4df5-bfe9-46a46bce993d"
  }'

```

```node
// publicToken and accountID are sent from your app to your backend-server
const accountID = 'vzeNDwK7KQIm4yEog683uElbp9GRLEFXGK98D';
const publicToken = 'public-sandbox-b0e2c4ee-a763-4df5-bfe9-46a46bce993d';

// Obtain an access_token from the Link public_token
try {
  const response = await client.itemPublicTokenExchange({
    public_token: publicToken,
  });
  const accessToken = response.data.access_token;
} catch (err) {
  // handle error
}

```

```python
# publicToken and accountID are sent from your app to your backend-server
accountID = 'vzeNDwK7KQIm4yEog683uElbp9GRLEFXGK98D'
publicToken = 'public-sandbox-b0e2c4ee-a763-4df5-bfe9-46a46bce993d'

# Obtain an access_token from the Link public_token
token_request = ItemPublicTokenExchangeRequest(
      public_token=publicToken)
token_response = client.item_public_token_exchange(token_request)
accessToken = token_response['access_token']

```

```ruby
# publicToken and accountID are sent from your app to your backend-server
accountID = 'vzeNDwK7KQIm4yEog683uElbp9GRLEFXGK98D'
publicToken = 'public-sandbox-b0e2c4ee-a763-4df5-bfe9-46a46bce993d'

# Obtain an access_token from the Link public_token
request = Plaid::ItemPublicTokenExchangeRequest.new(
  {
    public_token: publicToken
  }
)
token_response = client.item_public_token_exchange(request)
access_token = token_response.access_token

```

```java
// publicToken and accountID are sent from your app to your backend-servers
String accountID = "vzeNDwK7KQIm4yEog683uElbp9GRLEFXGK98D";
String publicToken = "public-sandbox-b0e2c4ee-a763-4df5-bfe9-46a46bce993d";

// Obtain an access_token from the Link public_token
ItemPublicTokenExchangeRequest tokenRequest = new ItemPublicTokenExchangeRequest()
  .publicToken(publicToken);

Response tokenResponse = plaidClient
  .itemPublicTokenExchange(tokenRequest)
  .execute();
String accessToken = tokenResponse.body().getAccessToken();

```

```go
// publicToken and accountID are sent from your app to your backend-server
accountID := "vzeNDwK7KQIm4yEog683uElbp9GRLEFXGK98D"
publicToken := "public-sandbox-b0e2c4ee-a763-4df5-bfe9-46a46bce993d"

// Obtain an access_token from the Link public_token
exchangePublicTokenResp, _, err := client.PlaidApi.ItemPublicTokenExchange(ctx).ItemPublicTokenExchangeRequest(
  *plaid.NewItemPublicTokenExchangeRequest(publicToken),
).Execute()
accessToken := exchangePublicTokenResp.GetAccessToken()

```

Exchange token response

```json
{
  "access_token": "access-sandbox-5cd6e1b1-1b5b-459d-9284-366e2da89755",
  "item_id": "M5eVJqLnv3tbzdngLDp9FL5OlDNxlNhlE55op",
  "request_id": "m8MDnv9okwxFNBV"
}
```

##### Fetch Auth data and verification results 

Next, we can retrieve Auth data, along with the results of the Database Insights verification check, by calling [/auth/get](https://plaid.com/docs/api/products/auth/index.html.md#authget) .

```bash
curl -X POST https://sandbox.plaid.com/auth/get \
  -H 'Content-Type: application/json' \
  -d '{
    "client_id": "${PLAID_CLIENT_ID}",
    "secret": "${PLAID_SECRET}",
    "access_token": "access-sandbox-5cd6e1b1-1b5b-459d-9284-366e2da89755"
  }'

```

```node
const accessToken = 'access-sandbox-5cd6e1b1-1b5b-459d-9284-366e2da89755';

// Instantly fetch Auth numbers
const request: AuthGetRequest = {
  access_token: accessToken,
};
try {
  const response = await client.authGet(request);
  const numbers = response.data.numbers;
} catch (err) {
  // handle error
}

```

```python
access_token = 'access-sandbox-5cd6e1b1-1b5b-459d-9284-366e2da89755'

# Instantly fetch Auth numbers
auth_request = AuthGetRequest(access_token=access_token)
auth_response = client.auth_get(auth_request)
numbers = auth_response['numbers']

```

```ruby
access_token = 'access-sandbox-5cd6e1b1-1b5b-459d-9284-366e2da89755';

# Instantly fetch Auth numbers
auth_get_request = Plaid::AuthGetRequest.new
auth_get_request.access_token = access_token

auth_response = client.auth_get(auth_get_request)
numbers = auth_response.numbers

```

```java
String accessToken = "access-sandbox-5cd6e1b1-1b5b-459d-9284-366e2da89755";

// Instantly fetch Auth numbers
AuthGetRequest authGetRequest = new AuthGetRequest()
  .accessToken(accessToken);

Response authResponse = client()
  .authGet(authGetRequest)
  .execute();

if (authResponse.isSuccessful()) {
  AuthGetResponse.Numbers numbers = authResponse.body().getNumbers();
}

```

```go
accessToken := "access-sandbox-5cd6e1b1-1b5b-459d-9284-366e2da89755"

// Instantly fetch Auth numbers
authGetResp, _, err := client.PlaidApi.AuthGet(ctx).AuthGetRequest(
  *plaid.NewAuthGetRequest(accessToken),
).Execute()
numbers := authGetResp.GetNumbers()

```

Auth response

```json
{
  "numbers": {
    "ach": [
      {
        "account_id": "vzeNDwK7KQIm4yEog683uElbp9GRLEFXGK98D",
        "account": "9900009606",
        "routing": "011401533",
        "wire_routing": "021000021"
      }
    ],
    "eft": [],
    "international": [],
    "bacs": []
  },
  "accounts": [
    {
      "account_id": "vzeNDwK7KQIm4yEog683uElbp9GRLEFXGK98D",
      "balances": { Object },
      "mask": "0000",
      "name": null,
      "official_name": null,
      "verification_status": "database_insights_pass",
      "verification_insights": { 
        "network_status": {
          "has_numbers_match": true,
          "is_numbers_match_verified": true
        },
        "previous_returns": {
          "previous_administrative_return": false
        },
        "account_number_format": "valid"
      },
      "subtype": "checking",
      "type": "depository"
    }
  ],
  "item": { Object },
  "request_id": "m8MDnv9okwxFNBV"
}
```

#### Making a risk determination 

The results of the Database Insights verification can be found in the `verification_status` and `verification_insights` fields returned by [/auth/get](https://plaid.com/docs/api/products/auth/index.html.md#authget) . Based on the values in these fields, you will make a business decision on whether to accept the account numbers as verified, take additional risk mitigation steps, or reject the account numbers as unverified.

#### Testing Database Insights in Sandbox 

For test credentials that can be used to test Database Insights in the Sandbox environment, see [Testing Database Auth or Database Insights](https://plaid.com/docs/auth/coverage/testing/index.html.md#testing-database-auth-or-database-insights) .

#### Database Match 

Database Insights and Database Match have been replaced with Database Auth. The documentation provided here is for the use of customers maintaining existing legacy Database Insights and Database Match integrations.

[Database Match](https://plaid.com/docs/auth/coverage/database/index.html.md#database-match) enables instant manual account verification without the need for micro-deposits, instead relying on Plaid's database of known account numbers. When provided as an alternative to Same-Day Micro-deposits, Database Match can increase conversion, as the user may be able to verify instantly, without having to return to Plaid to verify their micro-deposit codes.

Database Match will present the user with the option to manually add a bank account in Link by providing their name, account number, and routing number. Plaid will then check this information against its network of over 200 million known bank accounts. Approximately 30% of user bank accounts can be verified via Database Match. If a match is not found, Database Match will fall back to routing the user to a manual micro-deposit flow, either Instant Micro-deposits or Same-Day Micro-deposits.

Database Match can be used to verify the validity of the bank account being linked, but it does not verify that the end user has access to the bank account. To mitigate account takeover risk with Database Match, it can be paired with [Identity Match](https://plaid.com/docs/identity/index.html.md) to verify the user's information, such as phone number and address, on the linked account.

##### Database Match flow 

1.  Starting on a page in your app, the user clicks an action that opens Plaid Link.
2.  Inside of Plaid Link, the user selects an option to enter the manual verification flow and provides their legal name, account and routing number.
3.  Plaid will confirm the account number, routing number, and name match a previously verified account.
4.  If a match is confirmed, Link closes with a `public_token` and a verification status of `database_matched`.
5.  If there is no match, the user will be prompted to enter the Instant or Same-Day Micro-deposits flow.

When these steps are complete, you can call [/item/public\_token/exchange](https://plaid.com/docs/api/items/index.html.md#itempublic_tokenexchange) to obtain an `access_token` for calling endpoints such as [/auth/get](https://plaid.com/docs/api/products/auth/index.html.md#authget) or [/processor/token/create](https://plaid.com/docs/api/processors/index.html.md#processortokencreate) .

##### Database Match implementation steps 

To enable Database Match, use the same settings as Same-Day Micro-deposits. You can then enable the feature in one of two ways:

*   When calling [/link/token/create](https://plaid.com/docs/api/link/index.html.md#linktokencreate) , set `auth.database_match_enabled: true`.
*   [In the Dashboard Account Verification pane](https://dashboard.plaid.com/account-verification) , enable Database Match.

Database Match cannot be used in the same session as [Database Insights](https://plaid.com/docs/auth/coverage/database/index.html.md) . If Database Insights is explicitly enabled in the `auth` object alongside Database Match, an error will occur when calling [/link/token/create](https://plaid.com/docs/api/link/index.html.md#linktokencreate) .

```bash
curl -X POST https://sandbox.plaid.com/link/token/create \
-H 'Content-Type: application/json' \
-d '{
  "client_id": "${PLAID_CLIENT_ID}",
  "secret": "${PLAID_SECRET}",
  "user": {"client_user_id": "${UNIQUE_USER_ID}"},
  "client_name": "Plaid App",
  "products": ["auth"],
  "country_codes": ["US"],
  "language": "en",
  "webhook": "https://sample-web-hook.com",
  "redirect_uri": "https://domainname.com/oauth-page.html",
  "auth": {
    "database_match_enabled": true,
    "same_day_microdeposits_enabled": true,
  }
}'

```

```ruby
link_token_create_request = Plaid::LinkTokenCreateRequest.new(
  {
    user: {
      client_user_id: "user-id",
    },
    client_name: 'Plaid Test App',
    products: ['auth'],
    country_codes: ['US'],
    language: "en",
    webhook: 'https://webhook.sample.com',
    link_customization_name: "default",
    auth: {
        database_match_enabled: true,
        same_day_microdeposits_enabled: true
    },
  }
)
response = client.link_token_create(
  link_token_create_request
)
link_token = response.link_token

```

```node
const request: LinkTokenCreateRequest = {
  user: { client_user_id: new Date().getTime().toString() },
  client_name: 'Plaid App',
  products: [Products.Auth],
  country_codes: [CountryCode.Us],
  webhook: 'https://webhook.sample.com',
  language: 'en',
  auth: {
    database_match_enabled: true,
    same_day_microdeposits_enabled: true
  },
};
try {
  const response = await plaidClient.linkTokenCreate(request);
  const linkToken = response.data.link_token;
} catch (error) {
  // handle error
}

```

```java
LinkTokenCreateRequestAuth auth = new LinkTokenCreateRequestAuth()
    .databaseMatchEnabled(true);

LinkTokenCreateRequest request = new LinkTokenCreateRequest()
    .user(user)
    .clientName("WonderWallet")
    .products(Arrays.asList(Products.AUTH))
    .countryCodes(Arrays.asList(CountryCode.US))
    .language("en")
    .webhook("https://example.com/webhook")
    .linkCustomizationName("default")
    .accountFilters(accountFilters)
    .auth(auth);

```

```python
request = LinkTokenCreateRequest(
    products=[Products('auth')],
    client_name="Plaid Test App",
    country_codes=[CountryCode('US')],
    language='en',
    webhook='https://sample-webhook-uri.com',
    link_customization_name='default',
    user=LinkTokenCreateRequestUser(
        client_user_id='user-id',
    ),
    auth=LinkTokenCreateRequestAuth(
        database_match_enabled=True,
        same_day_microdeposits_enabled=True,

    )
)

response = client.link_token_create(request)

```

```go
user := plaid.LinkTokenCreateRequestUser{
  ClientUserId: "user-id",
}

request := plaid.NewLinkTokenCreateRequest(
  "Plaid Test",
  "en",
  []plaid.CountryCode{plaid.COUNTRYCODE_US},
)
request.SetUser(user)
request.SetProducts([]plaid.Products{plaid.PRODUCTS_AUTH})
request.SetLinkCustomizationName("default")
request.SetWebhook("https://webhook-uri.com")
var databaseMatchEnabled bool = true
var sameDayMicrodepositsEnabled bool = true
request.SetAuth(plaid.LinkTokenCreateRequestAuth{
 DatabaseMatchEnabled: &databaseMatchEnabled,
 SameDayMicrodepositsEnabled: &sameDayMicrodepositsEnabled,
})
resp, _, err := client.PlaidApi.LinkTokenCreate(ctx).LinkTokenCreateRequest(*request).Execute()
if err != nil {
  panic(err)
}

linkToken := resp.GetLinkToken()

```

When calling [/auth/get](https://plaid.com/docs/api/products/auth/index.html.md#authget) , the returned object will have a `verification_status` value of `database_matched` as an indication that the user's data was verified through Database Match.

Auth response for Database Match

```json
{
  "numbers": {
    "ach": [
      {
        "account": "1111222233330000",
        "routing": "011401533",
        "wire_routing": "021000021"
      }
    ],
    ...
  },
  "accounts": [
    {
      "account_id": "vzeNDwK7KQIm4yEog683uElbp9GRLEFXGK98D",
      "balances": { Object },
      "mask": "0000",
      "name": "Checking...0000",
      "official_name": null,
      "verification_status": "database_matched",
      "subtype": "checking",
      "type": "depository"
    }
  ],
  ...
}
```

##### Testing Database Match in Sandbox 

For test credentials that can be used to test Database Match in the Sandbox environment, see [Testing Database Match](https://plaid.com/docs/auth/coverage/testing/index.html.md#testing-database-match) .