> ## Documentation Index
> Fetch the complete documentation index at: https://developers.dwolla.com/llms.txt
> Use this file to discover all available pages before exploring further.

# MX: Instant Account Verification

> Step-by-step guide to integrating Dwolla's Open Banking solution with MX. Learn how to enable instant account verification, streamline account-to-account (A2A) payments, and enhance security for enterprise payment workflows.

## Overview

This guide dives into leveraging Dwolla's Open Banking Services in collaboration with **MX** to streamline bank account verification within your Dwolla-powered application. Open banking empowers your users to more securely share their financial data with Dwolla and your application, eliminating the need for manual data entry and improving the overall user experience.

We'll walk you through integrating [MX Connect Widget](https://docs.mx.com/connect/) to enable seamless bank account verification within your Dwolla integration. Dwolla's powerful [Exchange Sessions API](/docs/api-reference/exchange-sessions) acts as the intermediary, orchestrating a secure connection between your application and MX. This established connection facilitates real-time verification of your user's bank account details.

To gain hands-on experience, we recommend following along with the provided [integration-examples](https://github.com/Dwolla/integration-examples/tree/main/packages/open-banking/mx) sample app, which provides a practical understanding of the integration process for your own application.

## Instant Account Verification (IAV)

Instant Account Verification (IAV) is a one-time process that verifies the bank account being added by an end user is open and active. At the end of this guide, you'll obtain a **Funding Source URL**, which is a unique identifier that represents a bank account being used for account-to-account (A2A) payments.

## Prerequisites

Before starting the integration, make sure you have completed the following prerequisites:

1. **Dwolla Account**: Set up a Dwolla [production](https://accounts.dwolla.com/) or [sandbox](https://accounts-sandbox.dwolla.com/) account.
2. **Create a Customer**: Before creating a funding source, your application will need to create a Customer. A Customer is a user type, either business or personal, that identifies the end-user that is sending/receiving payments. Check out the Dwolla [Create a Customer](/docs/api-reference/customers/create-a-customer) API documentation for guidance.

<Note>
  You do not need an MX account or contract with MX to leverage MX via Dwolla's Open Banking Services. Dwolla handles the integration with MX for you, simplifying the process.
</Note>

## Sandbox Testing

Testing within the sandbox environment is an essential step before deploying your account verification solution to a production environment. The sandbox acts as a safe, isolated testing ground that mirrors real-world scenarios with test data. This allows you to validate the functionality of your integration without using actual user accounts or financial information. By thoroughly testing in the sandbox, you can identify and correct any potential issues before they impact your live users.

## MX Test Credentials

When using MX Connect in the Sandbox environment, you can use the following test credentials to simulate various scenarios. These credentials are provided by MX for sandbox testing purposes.

| Username | Password                      | Connection Status | Description                                                                                                                                                                                                                                                                                                             |
| -------- | ----------------------------- | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| mxuser   | Any value not described below | ✅ CONNECTED       | Successful aggregation with no MFA.                                                                                                                                                                                                                                                                                     |
| mxuser   | challenge                     | ❌ CHALLENGED      | Issues an MFA challenge. Answer with `correct` to simulate a correct MFA response, or use one of the passwords below that simulate a server error. Use anything else to simulate an incorrect answer.                                                                                                                   |
| mxuser   | options                       | ❌ CHALLENGED      | Issues an MFA challenge of the type OPTIONS. Answer with `correct` to simulate a correct MFA response, or use one of the passwords below that simulate a server error. Use anything else to simulate an incorrect answer.                                                                                               |
| mxuser   | image                         | ❌ CHALLENGED      | Issues an MFA challenge of type IMAGE. Answer with `correct` to simulate a correct MFA response, or use one of the passwords below that simulate a server error. Use anything else to simulate an incorrect answer.                                                                                                     |
| mxuser   | BAD\_REQUEST                  | ❌ FAILED          | External server returns a 400 error with the message, "You must fill out the username and password fields." If using the Connect Widget, this will display the message: "There was a problem validating your credentials with MX Bank. Please try again later."                                                         |
| mxuser   | UNAUTHORIZED                  | ❌ DENIED          | External server returns a 401 error with the message, "Invalid credentials." If using the Connect Widget, this will display the message: "The credentials entered do not match those at MX Bank. Please correct them below to continue."                                                                                |
| mxuser   | INVALID                       | ❌ DENIED          | External server returns a 401 error with the message, "The login and/or password are invalid." If using the Connect Widget, this will display the message: "The credentials entered do not match those at MX Bank. Please correct them below to continue."                                                              |
| mxuser   | LOCKED                        | ❌ LOCKED          | External server returns a 401 error with the message, "The credentials are valid, but the user is locked." If using the Connect Widget, this will display the message: "Your account is locked. Please log in to the appropriate website for MX Bank and follow the steps to resolve the issue."                        |
| mxuser   | DISABLED                      | ❌ DENIED          | External server returns a 401 error with the message, "The credentials are valid, but the user is locked." This password may also be used as an MFA answer. If using the Connect Widget, this will display the message: "The credentials entered do not match those at MX Bank. Please correct them below to continue." |
| mxuser   | SERVER\_ERROR                 | ❌ FAILED          | External server returns a 500 error with the message, "Internal server error." This password may also be used as an MFA answer. If using the Connect Widget, this will display the message: "There was a problem validating your credentials with MX Bank. Please try again later."                                     |
| mxuser   | UNAVAILABLE                   | ❌ FAILED          | External server returns a 503 error with the message, "Service is Unavailable." This password may also be used as an MFA answer. If using the Connect Widget, this will display the message: "There was a problem validating your credentials with MX Bank. Please try again later."                                    |

These credentials allow you to test different authentication and connection scenarios with MX in the sandbox environment.

### SDK Usage

#### MX Connect Widget for Seamless Verification

The MX Connect Widget is a pre-built user interface which simplifies the bank account verification process. It is designed to be embedded into your application using one of MX's SDKs. This guide will focus on using the Web Widget SDK. You have the option to integrate it in a webview for mobile applications.

#### Dwolla Node SDK for API Interactions

Throughout this guide, we'll utilize the Dwolla Node SDK to interact with Dwolla's API endpoints. This SDK simplifies making requests and handling responses for various Dwolla functionalities.

<Info>
  While we are using Dwolla's Node SDK for the sake of demonstration in this guide, [Dwolla also offers additional libraries](/docs/sdks-tools) for other server-side programming languages.
</Info>

## Integration Steps

### Step 1 - Initiate Exchange Session with MX

To begin, you will [create an exchange session](/docs/api-reference/exchange-sessions/create-customer-exchange-session) for your Customer in Dwolla using the **Exchange Sessions API**. This session will specify MX as the **exchange partner**. The Exchange Partner ID for MX can be found by calling the [List Exchange Partners](/docs/api-reference/exchanges/list-exchange-partners) API endpoint.

<Info>
  Exchange Sessions are single-use. Once a user starts the IAV flow initiated by creation of a session, it becomes invalid and cannot be reused.
</Info>

#### Example: Initiating an Exchange Session via Dwolla API

<CodeGroup>
  ```javascript dwolla-v2-node theme={"dark"}
  import { Client } from "dwolla-v2";

  const dwolla = new Client({
    key: "YOUR_KEY",
    secret: "YOUR_SECRET",
    environment: "sandbox", // or 'production'
  });

  // Retrieve MX's exchange partner href
  export async function getExchangePartnerHref(): Promise<string> {
    const response = await dwolla.get("/exchange-partners");
    const partnersList = response.body._embedded["exchange-partners"];
    const mxPartner = partnersList.find(partner => partner.name.toLowerCase() === "mx");
    console.log("MX Exchange partner retrieved successfully:", mxPartner._links.self.href);
    return mxPartner._links.self.href;
  }

  // Create an exchange session for a Customer
  export async function createExchangeSession(customerId: string): Promise<string> {
    const exchangePartnerHref = await getExchangePartnerHref();

    const requestBody = {
      _links: {
        'exchange-partner': {
          href: exchangePartnerHref
        }
      }
    };

    const response = await dwolla.post(`customers/${customerId}/exchange-sessions`, requestBody);
    const location = response.headers.get("location");
    console.log(location);
    return location;
  }

  // Example usage
  const exchangeSessionUrl = await createExchangeSession("your-customer-id");
  console.log("Exchange session URL:", exchangeSessionUrl); // => https://api.dwolla.com/exchange-sessions/fcd15e5f-8d13-4570-a9b7-7fb49e55941d
  ```

  ```bash HTTP theme={"dark"}
  POST https://api.dwolla.com/customers/74a207b2-b7b7-4efa-8bf8-582148e7b980/exchange-sessions
  Accept: application/vnd.dwolla.v1.hal+json
  Content-Type: application/vnd.dwolla.v1.hal+json
  Authorization: Bearer pBA9fVDBEyYZCEsLf/wKehyh1RTpzjUj5KzIRfDi0wKTii7DqY

  {
    "_links": {
      "exchange-partner": {
        "href": "https://api.dwolla.com/exchange-partners/bca8d065-49a5-475b-a6b4-509bc8504d22"
      }
    }
  }

  HTTP/1.1 201 Created
  Location: https://api.dwolla.com/exchange-sessions/fcd15e5f-8d13-4570-a9b7-7fb49e55941d
  ```
</CodeGroup>

### Step 2 - Retrieve Exchange Session and Initialize MX Connect Widget

After creating the exchange session, retrieve the session details from Dwolla to obtain the **external-provider-session** URL, which you will use to initialize the MX Connect Widget.

#### Backend: Retrieve MX Connect Widget URL

<CodeGroup>
  ```javascript dwolla-v2-node theme={"dark"}
  // Retrieve exchange session by ID
  export async function getExchangeSessionUrl(exchangeSessionId: string): Promise<string> {
    const response = await dwolla.get(`/exchange-sessions/${exchangeSessionId}`);
    const externalProviderSessionUrl = response.body._links["external-provider-session"].href;
    console.log(externalProviderSessionUrl);
    return externalProviderSessionUrl;
  }

  // Example usage:
  const mxWidgetUrl = await getExchangeSessionUrl("your-exchange-session-id");
  console.log("MX Widget URL:", mxWidgetUrl);
  ```

  ```bash HTTP theme={"dark"}
  GET https://api.dwolla.com/exchange-sessions/fcd15e5f-8d13-4570-a9b7-7fb49e55941d
  Accept: application/vnd.dwolla.v1.hal+json
  Authorization: Bearer pBA9fVDBEyYZCEsLf/wKehyh1RTpzjUj5KzIRfDi0wKTii7DqY

  {
    "created": "2024-11-18T18:48:11.357Z",
    "_links": {
      "self": {
        "href": "https://api.dwolla.com/exchange-sessions/ca80ecc6-9204-4e1c-9a2d-a78fa3c08933",
        "type": "application/vnd.dwolla.v1.hal+json",
        "resource-type": "exchange-sessions"
      },
      "exchange-partner": {
        "href": "https://api.dwolla.com/exchange-partners/bca8d065-49a5-475b-a6b4-509bc8504d22",
        "type": "application/vnd.dwolla.v1.hal+json",
        "resource-type": "exchange-partner"
      },
      "external-provider-session": {
        "href": "https://int-widgets.moneydesktop.com/md/connect/lAfkc7m897s3t1ks9mmwyj4ry7Zq0xql4grzAg1kz77x7c9jrwls1t22w6xt8d2lsxx9zpqv30js3wswfdwcrpAsqgbAfkqwpksp7c2chsx167xy90Asfc67dkj9y48y8p142xw3yp4x5l9t9gkk6m3yk5vwsvyq2qq7w9trszxwdl14lmkg7l6949bn5n41chdkbnxycy40n9b6fkbdwl6qt5wl107k1x8srvlkpz325p412x9tkyA5clf39109lsfrgz2lkgsvntqf7l0zzwb5hl658gdjbxwhb52krwybnbdAqfq69cdy54l05jkvfwyf01q89x48jtgtx290lzjdfcty1lwb8d648wns/eyJ1aV9tZXNzYWdlX3ZlcnNpb24iOjQsInVpX21lc3NhZ2Vfd2Vidmlld191cmxfc2NoZW1lIjoibXgiLCJtb2RlIjoidmVyaWZpY2F0aW9uIn0%3D"
      }
    }
  }
  ```
</CodeGroup>

#### Frontend: Initialize MX Connect Widget

Install the `@mxenabled/web-widget-sdk` package using your preferred package manager.

**Using npm:**

```bash theme={"dark"}
npm install --save @mxenabled/web-widget-sdk
```

**Using yarn:**

```bash theme={"dark"}
yarn add @mxenabled/web-widget-sdk
```

**Initialize MX Connect Widget**

```javascript theme={"dark"}
import { ConnectWidget } from "@mxenabled/web-widget-sdk";
import { useEffect, useRef } from "react";

export default function ConnectMXPage() {
  const widgetUrl = "your-external-provider-session-url"; // Retrieved from the previous step
  const widgetRef = useRef<HTMLDivElement>(null);
  const widgetInstance = useRef<ConnectWidget | null>(null);

  useEffect(() => {
    if (widgetRef.current && widgetUrl) {
      const options = {
        container: widgetRef.current,
        url: widgetUrl,
        onConnectedPrimaryAction: handleConnectedPrimaryAction
        // Add other event handlers as needed
      };

      // Mount the widget
      widgetInstance.current = new ConnectWidget(options);
    }

    // Unmount the widget when the component unmounts
    return () => {
      if (widgetInstance.current) {
        widgetInstance.current.unmount();
        widgetInstance.current = null;
      }
    };
  }, [widgetUrl]);

  /**
   * Handles the mx/connect/connected/primaryAction event.
   * This event indicates that the connection process is complete.
   */
  const handleConnectedPrimaryAction = () => {
    if (widgetInstance.current) {
      widgetInstance.current.unmount();
      widgetInstance.current = null;
    }
    // Redirect to account selection page
    router.push("/account-selection");
  };

  return (
    <div>
      <div id="connect-widget" ref={widgetRef}></div>
    </div>
  );
}
```

<Info>
  The MX Connect Widget provides a secure interface where users can select their financial institution and authenticate using their online banking credentials. The `onConnectedPrimaryAction` callback is triggered when the user successfully completes the connection process.
</Info>

### Step 3 - List Available Exchange Connections

After the user successfully completes the MX Connect flow, use the [list available exchange connections endpoint](/docs/api-reference/exchange-sessions/list-available-exchange-connections) to retrieve a list of available exchange connections associated with the customer. These connections represent the external bank accounts that the user has authorized through MX Connect that can be used to create funding sources.

#### Backend: Retrieve Available Exchange Connections

<CodeGroup>
  ```javascript dwolla-v2-node theme={"dark"}
  // List available exchange connections for a customer
  export async function getAvailableExchangeConnections(customerId: string): Promise<any[]> {
    try {
      const response = await dwolla.get(`/customers/${customerId}/available-exchange-connections`);
      return response.body._embedded["available-exchange-connections"];
    } catch (error) {
      console.error("Error retrieving available exchange connections:", error);
      return [];
    }
  }

  // Example usage
  const availableConnections = await getAvailableExchangeConnections("your-customer-id");
  console.log("Available connections:", availableConnections);
  ```

  ```bash HTTP theme={"dark"}
  GET https://api.dwolla.com/customers/74a207b2-b7b7-4efa-8bf8-582148e7b980/available-exchange-connections
  Accept: application/vnd.dwolla.v1.hal+json
  Authorization: Bearer pBA9fVDBEyYZCEsLf/wKehyh1RTpzjUj5KzIRfDi0wKTii7DqY

  {
    "_embedded": {
      "available-exchange-connections": [
        {
          "availableConnectionToken": "available-connection-123",
          "name": "Chase Checking"
        }
      ]
    }
  }
  ```
</CodeGroup>

#### Frontend: Account Selection Interface

Create an interface for users to select from their available bank accounts:

```javascript theme={"dark"}
import React, { useState, useEffect } from "react";
import { RadioGroup, FormControlLabel, Radio } from "@mui/material";

export default function AccountSelectionPage() {
  const [availableAccounts, setAvailableAccounts] = useState([]);
  const [selectedBank, setSelectedBank] = useState("");

  // Fetch available exchange connections when the component mounts
  useEffect(() => {
    async function fetchAvailableAccounts() {
      const customerId = sessionStorage.getItem("customerId");
      if (customerId) {
        try {
          const connections = await getAvailableExchangeConnections(customerId);
          setAvailableAccounts(connections);
        } catch (error) {
          console.error("Error fetching available exchange connections:", error);
        }
      }
    }

    fetchAvailableAccounts();
  }, []);

  const handleSubmit = async (event) => {
    event.preventDefault();
    
    const selectedAccount = availableAccounts.find(
      (account) => account.name === selectedBank
    );
    
    if (selectedAccount) {
      // Navigate to create exchange with the selected account
      router.push(`/create-exchange?token=${selectedAccount.availableConnectionToken}`);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <RadioGroup 
        value={selectedBank} 
        onChange={(e) => setSelectedBank(e.target.value)}
      >
        {availableAccounts.map((account) => (
          <FormControlLabel
            key={account.availableConnectionToken}
            value={account.name}
            control={<Radio />}
            label={account.name}
          />
        ))}
      </RadioGroup>
      <button type="submit">Continue</button>
    </form>
  );
}
```

### Step 4 - Create Exchange

Once the user selects an account, you will make a request to the [create an exchange](/docs/api-reference/exchanges/create-an-exchange-for-a-customer) endpoint, providing the `availableConnectionToken` of the chosen available exchange connection. This creates an "exchange" representing the link between the Dwolla customer and their external bank account.

<CodeGroup>
  ```javascript dwolla-v2-node theme={"dark"}
  // Create an exchange for a Dwolla customer using available connection token
  export async function createExchange(
    customerId: string,
    availableConnectionToken: string
  ): Promise<string> {
    const exchangePartnerHref = await getExchangePartnerHref(); // Retrieve MX's exchange partner href

    const requestBody = {
      _links: {
        "exchange-partner": {
          href: exchangePartnerHref,
        },
      },
      mx: {
        availableConnectionToken: availableConnectionToken,
      },
    };

    const response = await dwolla.post(
      `customers/${customerId}/exchanges`,
      requestBody
    );

    return response.headers.get("location"); // URL of the created exchange
  }

  // Example usage
  const exchangeUrl = await createExchange(
    "your-customer-id",
    "available-connection-123"
  );

  console.log("Exchange URL:", exchangeUrl); // => https://api.dwolla.com/exchanges/fcd15e5f-8d13-4570-a9b7-7fb49e55941d
  ```

  ```bash HTTP theme={"dark"}
  POST https://api.dwolla.com/customers/74a207b2-b7b7-4efa-8bf8-582148e7b980/exchanges
  Accept: application/vnd.dwolla.v1.hal+json
  Content-Type: application/vnd.dwolla.v1.hal+json
  Authorization: Bearer pBA9fVDBEyYZCEsLf/wKehyh1RTpzjUj5KzIRfDi0wKTii7DqY

  {
    "_links": {
      "exchange-partner": {
        "href": "https://api.dwolla.com/exchange-partners/bca8d065-49a5-475b-a6b4-509bc8504d22"
      }
    },
    "mx": {
      "availableConnectionToken": "available-connection-123"
    }
  }

  HTTP/1.1 201 Created
  Location: https://api.dwolla.com/exchanges/fcd15e5f-8d13-4570-a9b7-7fb49e55941d
  ```
</CodeGroup>

### Step 5 - Create Funding Source

After successfully creating the exchange, create a **funding source** for the Customer. Before creating a funding source, your application will need to create a Customer. A Customer is a user type, either business or personal, that identifies the end-user that is sending/receiving payments. This involves calling Dwolla's [Create a Funding Source](/docs/api-reference/funding-sources/create-customer-funding-source) endpoint, where you'll provide the **exchange** resource obtained from the previous step.

In the following function, once a response is received, it will extract the `Location` header value, which is the fully-qualified URL specifying the resource location of the Customer's funding source.

<CodeGroup>
  ```javascript dwolla-v2-node theme={"dark"}
  // Creates a funding source for a customer
  export async function createFundingSource(
    customerId: string,
    exchangeId: string,
    name: string,
    type: string
  ): Promise<string> {
    const exchangeUrl = `https://api.dwolla.com/exchanges/${exchangeId}`;

    const requestBody = {
      _links: {
        exchange: {
          href: exchangeUrl,
        },
      },
      bankAccountType: type,
      name: name,
    };

    const response = await dwolla.post(
      `customers/${customerId}/funding-sources`,
      requestBody
    );

    const location = response.headers.get("location");
    return location; // URL of the created funding source
  }

  // Example usage:
  const fundingSourceUrl = await createFundingSource(
    "your-customer-id",
    "your-exchange-id",
    "Jane Doe's Checking",
    "checking"
  );

  console.log("Funding Source URL:", fundingSourceUrl); // => https://api.dwolla.com/funding-sources/f41ab99c-7748-4f84-a3ed-3f669c002f4f
  ```

  ```bash HTTP theme={"dark"}
  POST https://api.dwolla.com/customers/99bfb139-eadd-4cdf-b346-7504f0c16c60/funding-sources
  Content-Type: application/vnd.dwolla.v1.hal+json
  Accept: application/vnd.dwolla.v1.hal+json
  Authorization: Bearer pBA9fVDBEyYZCEsLf/wKehyh1RTpzjUj5KzIRfDi0wKTii7DqY

  {
    "_links": {
      "exchange": {
        "href": "https://api.dwolla.com/exchanges/6bc9109a-04fd-49b6-ace6-ca06fd282d65"
      }
    },
    "bankAccountType": "checking",
    "name": "Jane Doe - Checking"
  }

  HTTP/1.1 201 Created
  Location: https://api.dwolla.com/funding-sources/AB443D36-3757-44C1-A1B4-29727FB3111C
  ```
</CodeGroup>

#### Verification Complete

Dwolla responds with a 201 code and triggers the `customer_funding_source_added` and `customer_funding_source_verified` webhooks indicating successful funding source creation and verification.

## Resources

* [Open Banking API Reference](/docs/api-reference/exchange-sessions)
* [MX Connect Documentation](https://docs.mx.com/connect/)
* [Integration Example App](https://github.com/Dwolla/integration-examples/tree/main/packages/open-banking/mx)
