# Guides and Concepts Source: https://developers.dwolla.com/docs Start with foundational concepts, move into quickstarts and use cases, and keep API reference and SDKs close at hand when you need them.

build, and launch

Start with foundational concepts, move into quickstarts and use cases, and keep API reference and SDKs close at hand when you need them.

Search docs or ask a question... ⌘K
Quickstart Webhooks Funding sources
Start

What is Dwolla

Platform overview, core components, and how Dwolla fits into your payment architecture.

Concept

Customer Types

Unverified, verified, and receive-only customer models. Choose before you design onboarding.

Guide

Quickstart

Authenticate and make your first API call in under 5 minutes.

Testing

Sandbox Testing

Simulate transfers, trigger events, and test failure scenarios before going live.

Full content index

29 pages across 7 groups

Getting Started

4 pages

Funds Flows

4 pages

Customers

2 pages

Funding Sources

4 pages

Transfers

6 pages

Integrations

6 pages
Secure Exchange
Open Banking Services

Webhooks

3 pages

API Reference

Full API reference

Explore endpoints, request formats, and response examples for every Dwolla resource.

POST /customers
POST /transfers
GET
POST /webhook-subscriptions

Install an SDK

SDK documentation

Get started with an official Dwolla SDK in minutes.

```bash theme={"dark"} $ npm install dwolla ``` ```typescript theme={"dark"} import { Dwolla } from "dwolla"; const dwolla = new Dwolla({ security: { clientID: process.env.DWOLLA_CLIENT_ID, clientSecret: process.env.DWOLLA_CLIENT_SECRET, }, server: "sandbox", }); ``` ```bash theme={"dark"} composer require "dwolla/dwolla-php" ``` ```php theme={"dark"} use Dwolla; use Dwolla\Models\Components; $sdk = Dwolla\Dwolla::builder() ->setSecurity( new Components\Security( clientID: 'YOUR_CLIENT_ID', clientSecret: 'YOUR_CLIENT_SECRET', ) ) ->build(); ``` ```bash theme={"dark"} pip install dwollav2 ``` ```python theme={"dark"} import dwollav2 client = dwollav2.Client( key=os.environ["DWOLLA_APP_KEY"], secret=os.environ["DWOLLA_APP_SECRET"], environment="sandbox", ) ``` ```bash theme={"dark"} $ Install-Package Dwolla.Client -Version 6.0.1 ``` ```csharp theme={"dark"} var client = DwollaClient.Create(isSandbox: true); var tokenRes = await client.PostAuthAsync( new Uri($"{client.AuthBaseAddress}/token"), new AppTokenRequest {Key = "...", Secret = "..."}); ``` ```bash theme={"dark"} $ gem install dwolla_v2 ``` ```ruby theme={"dark"} require "dwolla_v2" client = DwollaV2::Client.new( key: ENV["DWOLLA_APP_KEY"], secret: ENV["DWOLLA_APP_SECRET"], environment: :sandbox ) ```
SDK documentation Postman collection
# Overview Source: https://developers.dwolla.com/docs/api-reference Comprehensive, structured, and up-to-date documentation for all Dwolla API endpoints, parameters, request/response schemas, and usage examples. # Introduction Welcome to the Dwolla API documentation, your gateway to seamlessly integrating your software with robust banking infrastructure. Our API empowers developers with the essential tools to facilitate account-to-account payments, digital wallet functionality, customer identity verification, and bank account verification in a secure and efficient manner. ## API Fundamentals Explore our detailed reference documentation, starting with the API Fundamentals. Here, you will find comprehensive information on how to interact with the API, as well as develop a better understanding of fundamental concepts that make up the design of the API. Learn how to interact with the Dwolla API, including making requests, setting headers, and handling authentication. Learn how to interpret Dwolla API responses including response types, status codes, headers, and body conventions. Understand the rate limits in the Dwolla API, including concurrency-based and volume-based limits, and learn how to handle HTTP 429 Too Many Requests status codes. Learn how to set up IP allowlisting to control access to the Dwolla API and prevent unauthorized actions on behalf of your application. Learn how to use the Idempotency-Key header to prevent duplicate operations and manage resource creation in the Dwolla API. Learn about JSON-HAL, a hypermedia format for APIs, and how Dwolla HAL-Forms extend the HAL spec to represent and dynamically generate forms within the API. Learn how to handle error responses in the Dwolla API, including standard HTTP status codes, top-level error codes, and embedded errors. Prioritize clear and concise debugging practices in the Dwolla API to ensure smooth integration and efficient troubleshooting. # Overview Source: https://developers.dwolla.com/docs/api-reference/accounts Endpoints for your Main Dwolla account that include funding source creation, mass payment and transfer listings, and retrieving account details. # Accounts The **Accounts** resource in the Dwolla API represents your organization's main account, which is created when you sign up on dwolla.com. This account serves as the central hub for managing your business's funds and is primarily used for pay-in and pay-out use cases. Through the Accounts resource, you can retrieve account details, link or remove funding sources (such as bank accounts), and track all transfers associated with your organization. Use these endpoints to programmatically manage your account profile and monitor payment activity at the business level. ### Account Links | Link | Description | | --------------- | ----------------------------------------------------------------------------------------------------- | | self | URL of the Account resource | | receive | Follow the link to create a transfer to this Account. | | funding-sources | GET this link to list the Account's funding sources. | | transfers | GET this link to list the Account's transfers. | | customers | (optional) If this link exists, this account is authorized to create and manage Dwolla API Customers. | | send | Follow the link to create a transfer to this Account. | ```bash theme={"dark"} { "_links": { "self": { "href": "https://api-sandbox.dwolla.com/accounts/ca32853c-48fa-40be-ae75-77b37504581b" }, "receive": { "href": "https://api-sandbox.dwolla.com/transfers" }, "funding-sources": { "href": "https://api-sandbox.dwolla.com/accounts/ca32853c-48fa-40be-ae75-77b37504581b/funding-sources" }, "transfers": { "href": "https://api-sandbox.dwolla.com/accounts/ca32853c-48fa-40be-ae75-77b37504581b/transfers" }, "customers": { "href": "https://api-sandbox.dwolla.com/customers" }, "send": { "href": "https://api-sandbox.dwolla.com/transfers" } }, "id": "ca32853c-48fa-40be-ae75-77b37504581b", "name": "Jane Doe" } ``` # Create a funding source for an account Source: https://developers.dwolla.com/docs/api-reference/accounts/create-a-funding-source-for-an-account post /funding-sources Create a funding source by adding a bank account to a Main Dwolla Account. This endpoint allows you to connect a checking or savings account using either manual bank account details or an exchange resource. For more information about funding sources, see the [Funding Sources API Reference](https://developers.dwolla.com/docs/api-reference/funding-sources). # List and search account transfers Source: https://developers.dwolla.com/docs/api-reference/accounts/list-and-search-transfers-for-an-account get /accounts/{id}/transfers Returns a paginated, searchable list of transfers associated with the specified Main Dwolla account. Supports advanced filtering by amount range, date range, transfer status, and correlation ID. Results are limited to 10,000 transfers per query; use date range filters for historical data beyond this limit. # List funding sources for an account Source: https://developers.dwolla.com/docs/api-reference/accounts/list-funding-sources-for-an-account get /accounts/{id}/funding-sources Get a list of all funding sources associated with a specific Main Dwolla Account. This endpoint returns both bank accounts and balance funding sources, with detailed information about each funding source's status, type, and available processing channels. # List account mass payments Source: https://developers.dwolla.com/docs/api-reference/accounts/list-mass-payments-for-an-account get /accounts/{id}/mass-payments Returns a paginated list of mass payments created by your Main Dwolla account. Results are sorted by creation date in descending order (newest first) and can be filtered by correlation ID. # Retrieve account details Source: https://developers.dwolla.com/docs/api-reference/accounts/retrieve-account-details get /accounts/{id} Returns basic account information for your authorized Main Dwolla Account, including account ID, name, and links to related resources such as funding sources, transfers, and customers. # Overview Source: https://developers.dwolla.com/docs/api-reference/api-fundamentals Streamline A2A Payments with Dwolla: Understand the API Fundamentals. Explore request/response structures, authentication, error handling, and more. # Introduction Welcome to the Dwolla API documentation, your gateway to seamlessly integrating your software with robust banking infrastructure. Our API empowers developers with the essential tools to facilitate account-to-account payments, digital wallet functionality, customer identity verification, and bank account verification in a secure and efficient manner. ## API Fundamentals Explore our detailed reference documentation, starting with the API Fundamentals. Here, you will find comprehensive information on how to interact with the API, as well as develop a better understanding of fundamental concepts that make up the design of the API. Learn how to interact with the Dwolla API, including making requests, setting headers, and handling authentication. Learn how to interpret Dwolla API responses including response types, status codes, headers, and body conventions. Understand the rate limits in the Dwolla API, including concurrency-based and volume-based limits, and learn how to handle HTTP 429 Too Many Requests status codes. Learn how to set up IP allowlisting to control access to the Dwolla API and prevent unauthorized actions on behalf of your application. Learn how to use the Idempotency-Key header to prevent duplicate operations and manage resource creation in the Dwolla API. Learn about JSON-HAL, a hypermedia format for APIs, and how Dwolla HAL-Forms extend the HAL spec to represent and dynamically generate forms within the API. Learn how to handle error responses in the Dwolla API, including standard HTTP status codes, top-level error codes, and embedded errors. Prioritize clear and concise debugging practices in the Dwolla API to ensure smooth integration and efficient troubleshooting. # Debugging with X-Request-ID Source: https://developers.dwolla.com/docs/api-reference/api-fundamentals/debugging Troubleshoot API Request Issues. Discover efficient methods to identify and resolve request errors. ## Overview As you embark on your journey with Dwolla's API, effective debugging is vital in ensuring smooth integration and troubleshooting potential issues. Prioritizing clear and concise debugging practices helps support teams quickly identify and address any concerns, leading to more robust and successful software applications. ## X-Request-Id To facilitate this debugging, Dwolla includes an `X-Request-ID header` in all API responses, providing a unique identifier for each API request. This ID allows you to track your API requests within your logs, enabling easier investigation of potential issues. Additionally, the `X-Request-ID` header assists Dwolla in tracking API requests initiated by your application. When you reach out to Dwolla's support team for debugging assistance, sharing the `X-Request-ID` value allows Dwolla to precisely locate the exact request in question within our logs. By leveraging this tool, developers enhance collaboration with Dwolla's support team, leading to more efficient issue resolution and a smoother development experience. ### Example X-Request-Id Header Value `X-Request-Id: 53cc2d47-09f7-42f2-ac9e-994fdc90ad14` # Handling Errors Source: https://developers.dwolla.com/docs/api-reference/api-fundamentals/errors Dwolla API Errors: Master Error Handling. Grasp standard HTTP status codes, Dwolla error codes, and embedded errors. ## Overview When interacting with the Dwolla API, it's essential to understand how error responses are handled. These error responses are communicated through standard HTTP status codes, which provide clear indications of the type of error encountered during an API request. In addition to the status code, the JSON response body will include a top-level error code, offering further insight into the nature of the error. To ensure consistency and easy integration, errors will have their own media type, closely aligned with the [vnd.error](https://github.com/blongden/vnd.error) spec. ### Example HTTP 401 error ```bash theme={"dark"} { "code": "InvalidAccessToken", "message": "Invalid access token." } ``` ## Embedded errors In cases where your API request encounters specific issues that can be corrected, the Dwolla API returns responses with a top-level error code of `ValidationError`. These responses serve as valuable feedback, indicating that there are validation errors present in your request. The response will include a message: "Validation error(s) present. See embedded errors list for more details." The embedded errors list may contain one or more detailed error objects, providing specific information about the issues found during the request. Each `_embedded` error object includes the following parameters: * `code`: A detailed error code indicating the nature of the problem. * `message`: A human-readable description of the error. * `path`: A JSON pointer to the specific field in the request that caused the issue. ### Possible Error codes | Code | Description | | --------------------- | ------------------------------------------------------------------------------------------ | | Required | `{field name}` is required. For example, null or empty string in required field. | | Invalid | `{field name}` invalid. | | InvalidFormat | `{field name}` is not in a valid format. For example, characters in the amount field. | | Duplicate | Duplicate resource error. For example, A customer with the specified email already exists. | | ReadOnly | this field is not allowed to be modified | | NotAllowed | value, while valid/exists, is not allowed to be used | | Restricted | account or customer restricted from this activity | | InsufficientFunds | used on source or destination fields of transfer endpoint | | RequiresFundingSource | used on destination field of transfer endpoint to indicate customer needs a bank | | FileTooLarge | used on document upload | ### Example HTTP 400 validation error ```bash theme={"dark"} { "code": "ValidationError", "message": "Validation error(s) present. See embedded errors list for more details.", "_embedded": { "errors": [ { "code": "Required", "message": "FirstName required.", "path": "/firstName", "_links": {} } ] } } ``` ## Common errors The table below outlines common errors across all API endpoints in Dwolla. | HTTP Status | Error Code | Description | | ----------- | ------------------------ | ------------------------------------------------------------------------------------------------------------------------- | | 400 | BadRequest | The request body contains bad syntax or is incomplete. | | 400 | ValidationError | Validation error(s) present. See embedded errors list for more details. ([See above](#example-http-400-validation-error)) | | 401 | InvalidCredentials | Missing or invalid Authorization header. | | 401 | InvalidAccessToken | Invalid access token. | | 401 | ExpiredAccessToken | Generate a new access token using your client credentials. | | 401 | InvalidAccountStatus | Invalid access token account status. | | 401 | InvalidApplicationStatus | Invalid application status. | | 401 | InvalidScopes | Missing or invalid scopes for requested endpoint. | | 403 | Forbidden | The supplied credentials are not authorized for this resource. | | 403 | InvalidResourceState | Resource cannot be modified. | | 404 | NotFound | The requested resource was not found. | | 405 | MethodNotAllowed | (varies) | | 406 | InvalidVersion | Missing or invalid API version. | | 500 | ServerError | A server error occurred. Error ID: | | 500 | RequestTimeout | The request timed out. | # Handling Responses Source: https://developers.dwolla.com/docs/api-reference/api-fundamentals/handling-responses Learn how to interpret Dwolla API responses including response types, status codes, headers, and body conventions. ## Overview When you make a request to the Dwolla API, your request is processed and a response is returned. This response, in JSON format, contains information about the success or failure of your request, along with any relevant data. This section will guide you through the structure and components of Dwolla API responses, helping you interpret the information they provide. By understanding how to read and interpret API responses, you can effectively troubleshoot issues, extract valuable data, and build robust integrations with the Dwolla platform. ### Status Codes Status codes are numerical codes that indicate the outcome of an API request. They provide essential information about the success or failure of the request. ##### Common Status Codes: * **200 OK:** The request was successful. * **201 Created:** The request was successful, and a new resource was created. * **400 Bad Request:** The request was malformed or cannot be understood. * **401 Unauthorized:** The request requires authentication. * **403 Forbidden:** The authenticated user/application is not authorized to perform the request. * **404 Not Found:** The requested resource could not be found. * **500 Internal Server Error:** An unexpected error occurred on the server. ##### Example: ```json theme={"dark"} { "code": "BadRequest", "message": "The request body contains bad syntax or is incomplete." } ``` This response indicates a top-level error code of BadRequest with a descriptive error message explaining the issue. ### Response Headers Response headers provide supplementary details about the response. They can contain information about the content type, date, and other relevant data. **HTTP headers are case-insensitive by definition**. This means that the Dwolla API, like most HTTP servers, might return headers in various capitalization formats. For instance, you could receive a Location header as `location` or `Location`. While HTTP/2 mandates lowercase header names, older HTTP/1.1 connections might still use mixed casing. To ensure consistent handling, it's recommended to normalize header names to lowercase within your application. This involves converting all header names to lowercase before accessing their values. This approach provides a reliable way to reference headers without worrying about inconsistent capitalization. For example, if you're expecting a `Location` header, you should check for `location` as well to ensure you capture all possible variations. ##### Common Headers Here are some common headers you might encounter in Dwolla API responses: * **Date:** The date and time at which the message was generated. * **Content-Type:** Specifies the format of the response body (e.g., application/json). * **Content-Length:** : Indicates the size of the response body in bytes. * **Location:** : Indicates the URL of the newly created resource (used in 201 Created responses). **Custom Headers:** The `X-Request-ID` and `CF-RAY` headers provide additional information that can be used for debugging purposes. # Idempotency Key Source: https://developers.dwolla.com/docs/api-reference/api-fundamentals/idempotency-key Generate unique keys to prevent accidental duplicate transactions and ensure predictable API behavior. Build robust and reliable financial applications with confidence. ## Overview To prevent an operation from being performed more than once, Dwolla supports passing in an `Idempotency-Key` header with a unique key as the value. Multiple `POST`s with the same idempotency key and request body won't result in multiple resources being created. It is recommended to use a random value for the idempotency key, like a UUID (i.e. - `Idempotency-Key: d2adcbab-4e4e-430b-9181-ac9346be723a`). For example, if a request to [initiate a transfer](/docs/api-reference/transfers/initiate-a-transfer) fails due to a network connection issue, you can reattempt the request with the same idempotency key to guarantee that only a single transfer is created. If you reattempt a `POST` request with the same value for the `Idempotency-Key`, rather than creating new or potentially duplicate resources, you will receive a `201 Created`, with the original response of the created resource. If the Dwolla server is still processing the original `POST` request, you will receive a `409 Conflict` error response on the subsequent request. Multiple `POST`s with the same idempotency key including an **exact match** request body won't result in multiple resources being created. Idempotency keys are intended to prevent conflicts over a short period of time, therefore keys which are paired with a request body expire after 24 hours. To prevent resources from being created more than once, we highly recommend making all requests idempotent. ### Example transfer using an Idempotency Key ```bash theme={"dark"} POST https://api-sandbox.dwolla.com/transfers Accept: application/vnd.dwolla.v1.hal+json Content-Type: application/vnd.dwolla.v1.hal+json Authorization: Bearer pBA9fVDBEyYZCEsLf/wKehyh1RTpzjUj5KzIRfDi0wKTii7DqY Idempotency-Key: 19051a62-3403-11e6-ac61-9e71128cae77 { "_links": { "destination": { "href": "https://api-sandbox.dwolla.com/funding-sources/04173e17-6398-4d36-a167-9d98c4b1f1c3" }, "source": { "href": "http://api-sandbox.dwolla.com/funding-sources/707177c3-bf15-4e7e-b37c-55c3898d9bf4" } }, "amount": { "currency": "USD", "value": "1337.00" } } ... HTTP/1.1 201 Created Location: https://api-sandbox.dwolla.com/transfers/74c9129b-d14a-e511-80da-0aa34a9b2388 ``` # IP Allowlist Source: https://developers.dwolla.com/docs/api-reference/api-fundamentals/ip-allowlist Understand IP Allowlisting. Explore how to define authorized IP addresses, prevent unauthorized activity, and troubleshoot Forbidden errors. ## Overview IP allowlisting enables you to define IP addresses from which you want to allow access to the Dwolla API. This helps to prevent unauthorized networks from performing actions on behalf of your application. **Things to remember:** * The IP Allowlist option is made available once an application has completed the application approval process. * If you are logged in as a sub-user, you will need Edit permissions for Applications to be able to edit the IP allowlist. To set up IP allowlisting navigate to the Applications tab in the Dashboard. Locate your application for which you want to specify a list of IP addresses, then click on Allow List. You can then specify an IP address (e.g., `192.0.2.1`) or a CIDR IP range (e.g., `192.0.2.1/24`) and add a description for that entry. **Production** - [https://dashboard.dwolla.com/applications](https://dashboard.dwolla.com/applications)
**Sandbox** - [https://dashboard-sandbox.dwolla.com/applications](https://dashboard-sandbox.dwolla.com/applications) Immediately after adding your first IP address, any attempt to make a call to the API from an IP address not in the list will result in a `403 Forbidden` HTTP error code with the following response body: ```bash theme={"dark"} { "code": "InvalidIpAddress", "message": "Access to this resource is forbidden from this network location." } ``` # JSON-HAL and Dwolla HAL-Forms Source: https://developers.dwolla.com/docs/api-reference/api-fundamentals/json-hal-hypermedia Uncover the power of Dwolla HAL-Forms for intuitive API interactions. Grasp JSON-HAL and how Dwolla expands it for dynamic forms. Effortlessly transition between resource states and gain valuable insights for a streamlined integration. ## Overview JSON-HAL is a popular format for creating hypermedia APIs. Hypermedia APIs provide links along with the data to guide consumers on how to interact with the API dynamically. In JSON-HAL, resources are represented in JSON format, and hypermedia controls are embedded within the response. These controls contain links that allow clients to discover related resources and available actions they can perform based on the current context using their permissioned [access token](/docs/api-reference/tokens/create-an-application-access-token). ## Dwolla HAL-Forms [Dwolla HAL-Forms](https://github.com/Dwolla/hal-forms) is an extension of the [HAL spec](http://stateless.co/hal_specification.html) and designed to describe how Dwolla represents forms within the API. The extension starts with the media type. The media type should be used as a profile link as part of the `Accept` header of the request in conjunction with the Dwolla HAL style media type. By including these two media-type identifiers in the Accept header, the API knows that you’re looking for a form for the given resource. ##### Example `Accept` Header Value for Dwolla HAL-forms `Accept: application/vnd.dwolla.v1.hal+json; profile="https://github.com/dwolla/hal-forms"` ### Dynamic UI Generation One of the primary benefits of Dwolla HAL-Forms is the ability to dynamically generate your UI based on the state of a particular resource. This enables your application to seamlessly transition between states without intricate knowledge of Dwolla's business rules or the specific information required for the transition. When a resource returns an `"edit-form"` link relation, your application can follow this link by making a GET request, including the Dwolla HAL-Forms `Accept` header. The response will provide a simple JSON body containing essential information such as the HTTP method, message content-type, and request parameters for communication with the Dwolla API. Note: At present, Dwolla HAL-Forms are available for creating and editing customers. However, we are looking forward to expanding the availability to other endpoints in the future, unlocking even more possibilities for developers. Reference [the spec](https://github.com/Dwolla/hal-forms) to gain a more comprehensive understanding of the properties that can be returned in the Dwolla HAL-Forms response. # Making Requests and Authentication Source: https://developers.dwolla.com/docs/api-reference/api-fundamentals/making-requests-and-authentication Learn how to construct well-formed API requests, set essential headers, and implement robust authentication. ## Making Requests To interact with the Dwolla API, all requests must include the `Accept` header: * `Accept: application/vnd.dwolla.v1.hal+json` For POST requests, specify either of the following `Content-Type`: * `Content-Type: application/vnd.dwolla.v1.hal+json` * `Content-Type: application/json` All request and response bodies are JSON encoded. Requests must be made over HTTPS. Any non-secure requests will be redirected (HTTP 302) to the HTTPS equivalent URI. ```bash theme={"dark"} POST https://api.dwolla.com/customers Content-Type: application/json Accept: application/vnd.dwolla.v1.hal+json Authorization: Bearer myOAuthAccessToken123 { "foo": "bar" } ... or ... GET https://api.dwolla.com/accounts/a84222d5-31d2-4290-9a96-089813ef96b3/transfers ``` ## API Host | Environment | Base URL | | ----------- | -------------------------------- | | Production | `https://api.dwolla.com` | | Sandbox | `https://api-sandbox.dwolla.com` | ## Authentication Dwolla uses the [OAuth 2 protocol](https://oauth.net/2/) to authorize API requests. Every call to the Dwolla API must include a valid access token in the `Authorization` header: `Authorization: Bearer {access_token}` Want to get up and running fast? The [Quickstart](/docs/quickstart) walks through token generation and your first API call in under 5 minutes. This page covers the concepts in depth. ### Creating an application Before requesting an access token, register an application with Dwolla by logging in to the [Dashboard](https://dashboard-sandbox.dwolla.com/) and navigating to the applications page. Each application has a `client_id` and `client_secret` (together, your client credentials) that identify it to the Dwolla API. The Sandbox creates an application automatically when you sign up — see the [Sandbox guide](/docs/testing) for details. Your client\_secret should be kept a secret. Store client credentials securely and never expose them on the client side of your application. ### Dwolla's authorization flow OAuth 2 defines four main authorization grant types. Dwolla implements one: **Application authorization** — using the [client credentials grant](https://tools.ietf.org/html/rfc6749#section-4.4), your application obtains authorization to interact with the API on its own behalf. This is a server-to-server flow, also known as 2-legged OAuth. ### Requesting an access token To obtain an access token, send a `POST` to `/token` with an HTTP Basic `Authorization` header containing your Base64-encoded client credentials. `Authorization: Basic Base64(client_id:client_secret)` The request body must be form-encoded and include `grant_type=client_credentials`: | Parameter | Required | Type | Description | | -------------- | -------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | | client\_id | yes | string | Application key. Find it on the [applications page](https://dashboard-sandbox.dwolla.com/applications-legacy) of the Sandbox dashboard (or production dashboard). | | client\_secret | yes | string | Application secret. Available alongside the `client_id` on the applications page. | | grant\_type | yes | string | Must be set to `client_credentials`. | For the full endpoint specification — request/response schema, error codes, and an interactive playground — see the [Create an application access token](/docs/api-reference/tokens/create-an-application-access-token) reference. ```bash curl theme={"dark"} curl -X POST 'https://api-sandbox.dwolla.com/token' \ -H "Authorization: Basic $(echo -n 'YOUR_CLIENT_ID:YOUR_CLIENT_SECRET' | base64)" \ -H 'Content-Type: application/x-www-form-urlencoded' \ -d 'grant_type=client_credentials' ``` ```typescript TypeScript theme={"dark"} // Using dwolla — https://github.com/Dwolla/dwolla-typescript import { Dwolla } from "dwolla"; const dwolla = new Dwolla({ security: { clientID: process.env.DWOLLA_CLIENT_ID ?? "", clientSecret: process.env.DWOLLA_CLIENT_SECRET ?? "", }, server: "sandbox", }); // The SDK requests and caches tokens for you on the first API call. ``` ```python Python theme={"dark"} # Using dwollav2 — https://github.com/Dwolla/dwolla-v2-python import dwollav2 client = dwollav2.Client( key=os.environ["DWOLLA_APP_KEY"], secret=os.environ["DWOLLA_APP_SECRET"], environment="sandbox", ) app_token = client.Auth.client() ``` ```ruby Ruby theme={"dark"} # Using dwolla-v2-ruby — https://github.com/Dwolla/dwolla-v2-ruby $dwolla = DwollaV2::Client.new( key: ENV['DWOLLA_APP_KEY'], secret: ENV['DWOLLA_APP_SECRET'] ) do |config| config.environment = :sandbox end app_token = $dwolla.auths.client ``` ```php PHP theme={"dark"} setSecurity( new Components\Security( clientID: getenv('DWOLLA_CLIENT_ID'), clientSecret: getenv('DWOLLA_CLIENT_SECRET'), ) ) ->setServer('sandbox') ->build(); // The SDK requests and caches tokens for you on the first API call. ?> ``` ### Token lifetime and refresh Application access tokens expire one hour after they're issued and are not paired with a refresh token. To continue making API calls, exchange your client credentials for a new token using the same request shown above. Official [Dwolla SDKs](/docs/sdks-tools) handle token acquisition, caching, and renewal automatically — you only need to supply your `client_id` and `client_secret` at client initialization. ### Using an access token Pass the token as a Bearer credential on every authenticated request: ```bash theme={"dark"} POST https://api.dwolla.com/webhook-subscriptions Content-Type: application/json Accept: application/vnd.dwolla.v1.hal+json Authorization: Bearer myApplicationAccessToken { "url": "https://myapplication.com/webhooks", "secret": "sshhhhhh" } ... or ... GET https://api.dwolla.com/accounts/a84222d5-31d2-4290-9a96-089813ef96b3/transfers Accept: application/vnd.dwolla.v1.hal+json Authorization: Bearer myApplicationAccessToken ``` If the token is expired or invalid, Dwolla returns HTTP `401` with an `InvalidAccessToken` or `ExpiredAccessToken` error code. Catch these responses, request a fresh token, and retry the original call. # Rate Limits Source: https://developers.dwolla.com/docs/api-reference/api-fundamentals/rate-limits Ensure smooth integration by understanding Dwolla's API rate limits. Grasp concurrency and volume-based limits, and learn effective strategies to handle 429 Too Many Requests errors. ## Overview Dwolla imposes a rate limit for any applications that make requests to our API. Currently, we have the following rate limit defined: * Concurrency-based: Quick successive transfers sourced from the same [Dwolla Wallet](/docs/balance-funding-source) associated with a Dwolla [Account](/docs/api-reference/accounts) or [Customer](/docs/api-reference/customers) may receive an HTTP 429 Too Many Requests status code. * Volume-based: If the rate limit is reached for an endpoint, subsequent requests will result in an HTTP 429 Too Many Requests status code, which will persist for 5 minutes. At this time, these thresholds are defined as outside the range of what we consider normal API usage. Dwolla recommends building a mechanism into your application to handle 429 Too Many Requests responses, which would retry the request following an exponential backoff schedule to reduce request volume, and utilizes [idempotency keys](/docs/api-reference/api-fundamentals/idempotency-key), where applicable. This ensures actions only perform once and prevents creation of duplicate records. If you believe that your application will require a high rate of API calls, please contact [support@dwolla.com](mailto:support@dwolla.com) for further assistance. # Overview Source: https://developers.dwolla.com/docs/api-reference/beneficial-owners Create, certify, remove, update and list Beneficial Owners for a Business Verified Customer. # Beneficial Owners Verified Customers of type `business` are required to verify the identity of beneficial owners in addition to the controller of the business if they are one of the following business types: * Corporation * LLC * Partnership For more information on how to add beneficial owners, or to learn more on whether certain business types are exempt, reference our step by step guide. ### Beneficial owners resource | Parameter | Description | | ------------------ | ----------------------------------------------------------- | | id | The beneficial owner unique identifier. | | firstName | The legal first name of the beneficial owner. | | lastName | The legal last name of the beneficial owner. | | address | The beneficial owner's physical address. | | verificationStatus | Possible values of `verified`, `document`, or `incomplete`. | ```bash theme={"dark"} { "_links": { "self": { "href": "https://api-sandbox.dwolla.com/beneficial-owners/caa81a5f-ec1e-4559-8b32-d90655bfd03c", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "beneficial-owner" } }, "id": "caa81a5f-ec1e-4559-8b32-d90655bfd03c", "firstName": "Joe", "lastName": "owner", "address": { "address1": "12345 18th st", "address2": "Apt 12", "address3": "", "city": "Des Moines", "stateProvinceRegion": "IA", "country": "US", "postalCode": "50265" }, "verificationStatus": "verified" } ``` # Certify beneficial ownership Source: https://developers.dwolla.com/docs/api-reference/beneficial-owners/certify-beneficial-ownership-status post /customers/{id}/beneficial-ownership Updates the beneficial ownership certification status to "certified", confirming that all beneficial owner information is accurate and complete. This action enables the business customer to send funds and is required to complete the verification process. # Create customer beneficial owner Source: https://developers.dwolla.com/docs/api-reference/beneficial-owners/create-beneficial-owner post /customers/{id}/beneficial-owners Creates a new beneficial owner for a business verified customer. Beneficial owners are individuals who own 25% or more of the company's equity. Requires personal information, address, and SSN or passport for identity verification. # Remove beneficial owner Source: https://developers.dwolla.com/docs/api-reference/beneficial-owners/delete-beneficial-owner delete /beneficial-owners/{id} Permanently removes a beneficial owner from a business customer. This action is irreversible and the beneficial owner cannot be retrieved after removal. Removing a beneficial owner will change the customer's certification status to "recertify". # List customer beneficial owners Source: https://developers.dwolla.com/docs/api-reference/beneficial-owners/list-beneficial-owners get /customers/{id}/beneficial-owners Returns all beneficial owners associated with a business verified customer. Beneficial owners are individuals who directly or indirectly own 25% or more of the company's equity. Includes personal information, verification status, and address details for each owner. # Retrieve beneficial owner Source: https://developers.dwolla.com/docs/api-reference/beneficial-owners/retrieve-beneficial-owner get /beneficial-owners/{id} Returns detailed information for a specific beneficial owner, including personal information, address, and verification status. The verification status indicates the owner's identity verification progress and affects the business customer's transaction capabilities. # Retrieve beneficial ownership status Source: https://developers.dwolla.com/docs/api-reference/beneficial-owners/retrieve-beneficial-ownership-status get /customers/{id}/beneficial-ownership Returns the certification status of beneficial ownership for a business verified customer. Status indicates whether beneficial owner information has been certified and affects the customer's ability to send funds. Possible values include uncertified, certified, and recertify. # Update beneficial owner Source: https://developers.dwolla.com/docs/api-reference/beneficial-owners/update-beneficial-owner post /beneficial-owners/{id} Updates a beneficial owner's information to retry verification when their status is "incomplete". Only beneficial owners with incomplete verification status can be updated. Used to correct information that caused initial verification to fail. # Create a client token Source: https://developers.dwolla.com/docs/api-reference/client-tokens/create-a-client-token post /client-tokens Create a client token for secure authentication within Dwolla Drop-in components. Requires a granular permission action and a Customer link to define what operations the end user can perform within the component. Returns a short-lived token for configuring client-side Drop-in components including customer creation, verification, funding source management, and payment processing. Essential for implementing secure, embeddable UI components without exposing application credentials to the frontend. # Overview Source: https://developers.dwolla.com/docs/api-reference/customers Create, search and update Customers, and search for business-classification. # Customers A Customer represents an individual or business with whom you intend to transact with and is programmatically created and managed by a Dwolla [Main account](/docs/api-reference/accounts) via the API. In order for a Dwolla `Account` to create and manage Customers, an application must obtain permission from Dwolla before being enabled in production. **Note:** Customers can only be US persons. Business Verified Customers may have non-US Controllers or [Beneficial Owners](/docs/api-reference/beneficial-owners). ### Customer types With a transfer of money, *at least one party must complete the identity verification process*, either the sender or the receiver. This can be either the Dwolla Main Account itself or a `verified` Customer type. Based on your business model and funds flow, it's your decision about which party completes this process-- you may even want to have both parties complete the identity verification process. A brief description of each Customer type is below, but for a more in-depth overview of each Customer type and what their capabilities are, check out our [developer resource article](/docs/customer-types). A brief description of each Customer type is below, but for a more in-depth overview of each Customer type and what their capabilities are, reference our concept article. ##### Receive-only Users Receive-only users are restricted to a "payouts only" funds flow. A receive-only user maintains limited functionality in the API and is only eligible to receive transfers to an attached bank account. This Customer type can only interact with verified Customers and a Dwolla Main Account. ##### Unverified Customers Unverified Customers have a default sending transaction limit of \$5,000 per week. A week is defined as Monday to Sunday UTC time. As this Customer is not identity verified, they will only be able to transact with verified Customers or your Dwolla Main Account. ##### Verified Customers Verified Customers are defined by their ability to both send and receive money, thus, being able to fit all funds flows. They can also interact with any Customer type and hold a `balance` funding source within the Dwolla network. Think of the Dwolla `balance` as a "wallet" which a Customer can send, receive, or hold funds to within the Dwolla network. With no weekly transfer limits, this Customer type is flexible for high transaction volumes. A verified Customer can be created as a type of either `Personal` or `Business`. ### Customer links | Link | Description | | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | self | URL of the Customer resource | | receive | Follow the link to create a transfer to this Customer. | | funding-sources | GET this link to list the Customer's funding sources. | | transfers | GET this link to list the Customer's transfers | | send | (optional) If this link exists, this Customer can send funds. POST to this URL to create a transfer. | | retry-verification | If the Customer has a `status` of `retry`, POST to this link to attempt to correct their identity verification information. | | verify-with-document | If the Verified Customer of type `personal` or `business` has a `status` of `document`, POST to this link to upload a new color photo document to verify the Customer's identity. If type `business`, the controller of the business. Read about [Documents](/docs/api-reference/documents). | | verify-business-with-document | If the Verified Customer of type `business` has a `status` of `document`, POST to this link to upload a new color photo document to verify the identity of the business itself. Read about [Documents](/docs/api-reference/documents). | | verify-controller-and-business-with-document | If the Verified Customer of type `business` has a `status` of `document`, POST to this link to upload new color photo documents to verify the identity of the controller of the business as well as the business itself. Read about [Documents](/docs/api-reference/documents). | | upload-dba-document | If the Verified Customer of type `business` with a `doingBusinessAs` name has a `status` of `document`, POST to this link to upload a new color photo document containing the DBA name along with the state registered business name to verify the identity of the business itself. Read about [Documents](/docs/api-reference/documents). | ### Customer resource | Parameter | Description | | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | id | Customer's unique identifier. | | firstName | Customer's first name. | | lastName | Customer's last name. | | email | Customer's email address. | | type | Either `unverified`, `personal`, `business`, or `receive-only`. | | status | If type is **unverified** or **receive-only**: status can be `unverified`, `deactivated`, or `suspended`.
If type is **personal**: status can be `retry`, `kba`, `document`, `verified`, `deactivated`, or `suspended`.
If type is **business**: status can be `retry`, `document`, `verified`,`deactivated`, or `suspended`. | | created | ISO-8601 timestamp. | ### Customer statuses | Status | Description | | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | unverified | Customers of type `unverified` or `receive-only` always have this status. | | retry | Verified Customers of type `personal` or `business` can have this status. The initial verification attempt failed because the information provided did not satisfy our verification check. You can make one additional attempt by changing some or all the attributes of the existing Customer with a POST request. If the additional attempt fails, the resulting status will be either `document` or `suspended`. | | document | Verified Customers of type `personal` or `business` can have this status. Dwolla requires additional documentation to identify the Customer in the `document` status. Read about [Documents](/docs/api-reference/documents). | | verified | Verified Customers of type `personal` or `business` can have this status. The Customer is currently verified. | | suspended | All Customer types can have a status of `suspended`. The Customer is suspended and may neither send nor receive funds. Contact Dwolla support for more information. | | deactivated | All Customer types can have a status of `deactivated`. A deactivated Customer may neither send nor receive funds. A deactivated Customer can be [reactivated](/docs/api-reference/customers/update-a-customer#reactivate-customer) which moves the Customer to the status they were in prior to being deactivated. | ```bash theme={"dark"} { "_links": { "self": { "href": "https://api-sandbox.dwolla.com/customers/9da3aa7c-2524-430b-a751-6dc722735fce", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "customer" }, "receive": { "href": "https://api-sandbox.dwolla.com/transfers", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "transfer" }, "edit-form": { "href": "https://api-sandbox.dwolla.com/customers/9da3aa7c-2524-430b-a751-6dc722735fce", "type": "application/vnd.dwolla.v1.hal+json; profile=\"https://github.com/dwolla/hal-forms\"", "resource-type": "customer" }, "edit": { "href": "https://api-sandbox.dwolla.com/customers/9da3aa7c-2524-430b-a751-6dc722735fce", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "customer" }, "funding-sources": { "href": "https://api-sandbox.dwolla.com/customers/9da3aa7c-2524-430b-a751-6dc722735fce/funding-sources", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "funding-source" }, "transfers": { "href": "https://api-sandbox.dwolla.com/customers/9da3aa7c-2524-430b-a751-6dc722735fce/transfers", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "transfer" }, "send": { "href": "https://api-sandbox.dwolla.com/transfers", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "transfer" } }, "id": "9da3aa7c-2524-430b-a751-6dc722735fce", "firstName": "Jane", "lastName": "Doe", "email": "janedoe@email.com", "type": "personal", "status": "verified", "created": "2016-08-17T18:58:47.630Z", "address1": "99-99 33rd St", "address2": "Apt 8", "city": "Some City", "state": "NY", "postalCode": "11101", "phone": "5554321234" } ``` # Create a customer Source: https://developers.dwolla.com/docs/api-reference/customers/create-a-customer post /customers Creates a new customer with different verification levels and capabilities. Supports personal verified customers (individuals), business verified customers (businesses), unverified customers, and receive-only users. Customer type determines transaction limits, verification requirements, and available features. # List and search customers Source: https://developers.dwolla.com/docs/api-reference/customers/list-and-search-customers get /customers Returns a paginated list of customers sorted by creation date. Supports fuzzy search across customer names, business names, and email addresses, plus exact filtering by email and verification status. Default limit is 25 customers per page, maximum 200. # List business classifications Source: https://developers.dwolla.com/docs/api-reference/customers/list-business-classifications get /business-classifications Returns a directory of business and industry classifications required for creating business verified customers. Each business classification contains multiple industry classifications. The industry classification ID must be provided in the businessClassification parameter during business customer creation for verification. # Retrieve a business classification Source: https://developers.dwolla.com/docs/api-reference/customers/retrieve-a-business-classification get /business-classifications/{id} Returns a specific business classification with its embedded industry classifications. Use this endpoint to browse available industry options within a business category and obtain the industry classification ID required for the businessClassification parameter when creating business verified customers. # Retrieve a customer Source: https://developers.dwolla.com/docs/api-reference/customers/retrieve-a-customer get /customers/{id} Retrieve identifying information for a specific customer. The returned data varies by customer type - verified customers include contact details, address information, and verification status, while unverified customers and receive-only users contain basic contact information only. # Update a customer Source: https://developers.dwolla.com/docs/api-reference/customers/update-a-customer post /customers/{id} Update Customer information, upgrade an unverified Customer to a verified Customer, suspend a Customer, deactivate a Customer, reactivate a Customer, and update a verified Customer's information to retry verification. # Overview Source: https://developers.dwolla.com/docs/api-reference/documents Create, list, and retrieve documents from Customers and Beneficial Owners. # Documents Verified Customers of type `personal` or `business` and of status `document` require color photos of identifying documents to be uploaded for manual review in order to be verified. Currently, SDK support for document upload only exists for Ruby, Node.js, and Python. To upload a document using other languages, you must use an external HTTP library. For more information on handling the Customer verification status of `document`, reference our [Business Customer](/docs/business-verified-customer#handling-document-status) or [Personal Customer](/docs/personal-verified-customer#handling-status-document) guides. ### Document resource | Parameter | Description | | -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | id | Document unique identifier | | type | Either `passport`, `license`, `idCard`, or `other`. Must be a colored camera captured image of a U.S. government issued identification. | | status | Either `pending` or `reviewed`. When a document has been manually reviewed by Dwolla, its status will be `reviewed`. A reviewed document does not necessarily indicate that the customer has completed the identity verification process. | | documentVerificationStatus | The value of this field indicates the status of the document after being reviewed by Dwolla. Values can be either `pending`, `accepted`, or `rejected`. | | created | ISO 8601 Timestamp of document upload time and date. | | failureReason | The reason an uploaded document was rejected. Can be: `BusinessDocNotSupported`, `BusinessNameMismatch`, `BusinessTypeMismatch`, `ForeignPassportNotAllowed`, `ScanDobMismatch`, `ScanFailedOther`, `ScanIdExpired`, `ScanIdTypeNotSupported`, `ScanIdUnrecognized`, `ScanNameMismatch`, `ScanNotReadable` or `ScanNotUploaded`. | | allFailureReasons | An array of `reason`s and `description`s for when an uploaded document is rejected for multiple reasons. | ```bash theme={"dark"} { "_links": { "self": { "href": "https://api.dwolla.com/documents/56502f7a-fa59-4a2f-8579-0f8bc9d7b9cc" } }, "id": "56502f7a-fa59-4a2f-8579-0f8bc9d7b9cc", "status": "reviewed", "type": "passport", "created": "2015-09-29T21:42:16.000Z", "documentVerificationStatus": "rejected", "failureReason": "ScanDobMismatch", "allFailureReasons": [ { "reason": "ScanDobMismatch", "description": "Scan DOB does not match DOB on account" }, { "reason": "ScanIdExpired", "description": "ID is expired or missing expiration date" } ] } ``` # Create a document for beneficial owner Source: https://developers.dwolla.com/docs/api-reference/documents/create-a-document-for-beneficial-owner post /beneficial-owners/{id}/documents Uploads an identity verification document for a beneficial owner using multipart form-data. Required when a beneficial owner has "document" status during the business verification process. # Create a document for customer Source: https://developers.dwolla.com/docs/api-reference/documents/create-a-document-for-customer post /customers/{id}/documents Uploads an identity verification document for a customer using multipart form-data. Required when a customer has "document" status during the verification process. # List documents for beneficial owner Source: https://developers.dwolla.com/docs/api-reference/documents/list-document-for-beneficial-owner get /beneficial-owners/{id}/documents Returns all identity verification documents submitted for a beneficial owner. Includes document status, verification results, document type (passport, driver's license, etc.), and failure reasons if verification was rejected. Used to track document submission and verification progress during the business verification process. # List documents for customer Source: https://developers.dwolla.com/docs/api-reference/documents/list-documents-for-customer get /customers/{id}/documents Returns all identity verification documents submitted for a customer. Includes document status, verification results, document type (passport, driver's license, etc.), and failure reasons if verification was rejected. Used to track document submission and verification progress during the business verification process. # Retrieve a document Source: https://developers.dwolla.com/docs/api-reference/documents/retrieve-a-document get /documents/{id} Returns detailed information about a specific identity verification document, including its status, type, and verification results. Used to track document submission and verification progress during the business verification process. # Overview Source: https://developers.dwolla.com/docs/api-reference/events List or retrieve an event using the Dwolla API. # Events When the state of a resource changes, Dwolla creates a new event resource to record the change. When an Event is created, a [Webhook](/docs/api-reference/webhooks) will be created to deliver the Event to any URLs specified by your active [Webhook Subscriptions](/docs/api-reference/webhook-subscriptions). To view example payloads for Customer related events, refer to the [Webhooks Events](/docs/webhook-events) resource within the Developer Docs. ### Events resource | Parameter | Description | | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | \_links | Contains links to the event, associated resource, and the Account associated with the event. | | id | Event ID | | created | ISO-8601 timestamp when event was created | | topic | Type of event | | resourceId | ID of the resource associated with the event. | | correlationId | Unique ID that was specified, if any, when a [transfer was created](/docs/api-reference/transfers/initiate-a-transfer).
**This value is only present for [Account transfer](#transfers) and [Customer transfer](#transfers-1) events.** | ```bash theme={"dark"} { "_links": { "self": { "href": "https://api.dwolla.com/events/f8e70f48-b7ff-47d0-9d3d-62a099363a76" }, "resource": { "href": "https://api.dwolla.com/transfers/48CFDDB4-1E74-E511-80DB-0AA34A9B2388" }, "account": { "href": "https://api.dwolla.com/accounts/ca32853c-48fa-40be-ae75-77b37504581b" } }, "id": "f8e70f48-b7ff-47d0-9d3d-62a099363a76", "created": "2015-10-16T15:58:15.000Z", "topic": "transfer_created", "resourceId": "48CFDDB4-1E74-E511-80DB-0AA34A9B2388" } ``` ## Dwolla Master Account Event topics ### Accounts | Topic | Description | | ------------------ | ------------------------------------------------------------------------------------------------------------- | | account\_suspended | **Description**: A Dwolla Master Account was suspended. | | account\_activated | **Description**: A Dwolla Master Account moves from deactivated or suspended to active state of verification. | ### Funding Sources | Topic | Description | | --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | funding\_source\_added | **Description**: A funding source was added to a Dwolla account.
**Timing**: Occurs after a bank funding source is created for the Master account. | | funding\_source\_removed | **Description**: A funding source was removed from a Dwolla account.
**Timing**: Occurs upon a POST request to the [Remove a funding source](/docs/api-reference/funding-sources/update-or-remove-a-funding-source) endpoint, or when Dwolla systematically removes a funding source upon receiving certain ACH return codes when a transfer fails. | | funding\_source\_verified | **Description**: A funding source was marked as `verified`.
**Timing**: Occurs when the Master Account Admin Verifies their funding source through micro-deposits. | | funding\_source\_unverified | **Description**: A funding source has been systematically `unverified`. This is generally a result of a transfer failure. [View our developer resource article](/docs/transfer-failures) to learn more. | | funding\_source\_negative | **Description**: A Dwolla Master Account `balance` has gone negative. You are responsible for ensuring a zero or positive Dwolla balance for your account. If your balance funding source has gone negative, you are responsible for making the Dwolla account whole. Dwolla will notify you via a webhook and separate email of the negative balance. If no action is taken, Dwolla will debit your attached billing source.
**Timing**: Occurs immediately after a Master account balance becomes negative. | | funding\_source\_updated | **Description**: A funding source has been updated. This can also be fired as a result of a correction after a bank transfer process. For example, a financial institution can issue a correction to change the bank account `type` from `checking` to `savings`.
**Timing**: Occurs after the Master Account Admin or the bank makes a correction to the funding source. | | microdeposits\_added | **Description**: Two `<=10¢` transfers to a Dwolla Master Account's linked bank account were initiated.
**Timing**: Occurs when Dwolla processes micro-deposits to the linked bank account. | | microdeposits\_failed | **Description**: The two `<=10¢` transfers to a Dwolla Master Account's linked bank account failed to clear successfully.
**Timing**: Occurs when the microdeposits are returned by the bank or if the destination bank is removed before the micro-deposits export out of Dwolla. | | microdeposits\_completed | **Description**: The two `<=10¢` transfers to a Dwolla Master Account's linked bank account have cleared successfully.
**Timing**: Occurs when micro-deposits are successfully verified. | | microdeposits\_maxattempts | **Description**: The funding source has reached its max verification attempts limit of three. The funding source can no longer be verified with the completed micro-deposit amounts.
**Timing**: Occurs with the fourth attempt to verify micro-deposits. | ### Transfers For [Account](/docs/api-reference/accounts) transfer events, in addition to the [default payload keys](#events-resource), a `correlationId` key-value pair *may* be present if a value was specified when the [transfer was created](/docs/api-reference/transfers/initiate-a-transfer). | Topic | Description | | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | bank\_transfer\_created | **Description**: A bank transfer was created. Represents funds moving either from a Dwolla Master Account's bank to the Dwolla network or from the Dwolla network to a Dwolla Master Account's bank.
**Timing**: Occurs upon a POST request to the [Initiate a transfer](/docs/api-reference/transfers/initiate-a-transfer) endpoint when sending funds from a Dwolla Master Account's bank, or when funds move from the Dwolla Master Account's balance to their bank. | | bank\_transfer\_creation\_failed | **Description**: An attempt to initiate a transfer to a Master Account's bank was made, but failed. Transfers initiated to a Master Account's bank must pass through the Master Account's balance before being sent to a receiving bank. Dwolla will fail to create a transaction intended for a Master Account's bank if the funds available in the balance are less than the transfer amount.
**Timing**: Occurs when a transfer to a Dwolla Master Account's bank fails to be created. | | bank\_transfer\_cancelled | **Description**: A pending bank transfer has been cancelled, and will not proceed further. Represents a cancellation of funds either transferring from a Dwolla Master Account's bank to the Dwolla network or from the Dwolla network to a Dwolla Master Account's bank.
**Timing**: Occurs upon a POST request to the [Cancel a Transfer](/docs/api-reference/transfers/cancel-a-transfer) endpoint, or when Dwolla manually cancels a transfer. | | bank\_transfer\_failed | **Description**: A transfer failed to clear successfully. Usually, this is a result of an ACH failure (insufficient funds, etc.). Represents funds failing to clear either from a Dwolla Master Account's bank to the Dwolla network or from the Dwolla network to a Dwolla Master Account's bank.
**Timing**: Occurs when a Dwolla Master Account's bank issues an ACH failure on an incoming or outgoing transfer. | | bank\_transfer\_completed | **Description**: A bank transfer has cleared successfully. Represents funds clearing either from a Dwolla Master Account's bank to the Dwolla network or from the Dwolla network to a Dwolla Master Account's bank.
**Timing**: Occurs when Dwolla has processed a transfer to or from a Dwolla Master Account's bank. | | transfer\_created | **Description**: A transfer was created. Represents funds moving either to or from a Dwolla Master Account's `balance` or `bank`.
**Timing**: Occurs upon a POST request to the [Initiate a transfer](/docs/api-reference/transfers/initiate-a-transfer) endpoint when sending funds from a Dwolla Master Account's `bank` or `balance`. | | transfer\_cancelled | **Description**: A pending transfer has been cancelled, and will not proceed further. Represents a cancellation of funds transferring either to or from a Dwolla Master Account's `balance` or `bank`.
**Timing**: Occurs upon a POST request to the [Cancel a transfer](/docs/api-reference/transfers/cancel-a-transfer) endpoint, or when a transfer is systemically canceled by Dwolla in the event that the funding source is removed after the initiation of the transfer and before it exports out of Dwolla | | transfer\_failed | **Description**: A transfer failed to clear successfully. Represents funds failing to clear either to or from a Dwolla Master Account's `balance` or `bank`.
**Timing**: Occurs after Dwolla receives an ACH return from the bank. | | transfer\_completed | **Description**: A transfer has cleared successfully. Represents funds clearing either to or from a Dwolla Master Account's `balance` or `bank`.
**Timing**: Occurs after the transfer has successfully been processed in or out of the Dwolla Master Account's balance or bank based on the transfer processing timing used. | ### Mass Payments | Topic | Description | | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | mass\_payment\_created | **Description**: A mass payment was created. ​​
**Timing**: Occurs upon a POST request to the [Initiate a mass-payment](/docs/api-reference/mass-payments/initiate-a-mass-payment) endpoint. | | mass\_payment\_completed | **Description**: A mass payment was completed. However, this doesn't mean that each mass payment item's transfer was successful.
**Timing**: Occurs when a mass payment job completes. | | mass\_payment\_cancelled | **Description**: A created and deferred mass payment was cancelled.
**Timing**: Occurs upon a POST request to the [Update a mass-payment](/docs/api-reference/mass-payments/update-a-mass-payment) endpoint when cancelling a mass payment job. | ### Statements | Topic | Description | | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | statement\_created | **Description**: A monthly balance summary and a detailed transaction record for the previous month was created for a Dwolla Master Account.
**Timing**: Occurs at the beginning of each month. | ## Customer Account Event topics ### Customers | Topic | Description | | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | customer\_created | **Description**: A Customer was created.
**Timing**: Occurs upon a POST request to the [Create a Customer](/docs/api-reference/customers/create-a-customer) endpoint. | | customer\_kba\_verification\_needed | **Description**: The retry identity verification attempt failed due insufficient scores on the submitted data. The end user will have a single kba attempt to answer a set of "out of wallet" questions about themselves for identity verification.
**Timing**: Occurs after a failed attempt to verify a Verified Customer Record. | | customer\_kba\_verification\_failed | **Description**: The end user failed KBA verification and was unable to correctly answer at least three KBA questions.
**Timing**: Triggered after a single attempt at verifying a Verified Customer Record using KBA. | | customer\_kba\_verification\_passed | **Description**: The end user was able to correctly answer at least three KBA questions.
**Timing**: Triggered after a Verified Customer Record successfully passes KBA requirements. | | customer\_verification\_document\_needed | **Description**: Additional documentation is needed to verify a Customer.
**Timing**: Occurs when a second attempt to re-verify a Customer fails, which systematically places the Customer in document status immediately after a POST request to the [Update a Customer](/docs/api-reference/customers/update-a-customer) endpoint. | | customer\_verification\_document\_uploaded | **Description**: A verification document was uploaded for a Customer.
**Timing**: Occurs upon a POST request to the [Create a Document](/docs/api-reference/#create-a-document-for-a-customer) endpoint. | | customer\_verification\_document\_failed | **Description**: A verification document has been rejected for a Customer.
**Timing**: Occurs when a document uploaded for a Customer is reviewed by Dwolla, and rejected with a document failure reason, usually within 1-2 business days of uploading a document. | | customer\_verification\_document\_approved | **Description**: A verification document was approved for a Customer.
**Timing**: Occurs when a document uploaded for a Customer is reviewed by Dwolla, and approved, usually within 1-2 business days of uploading a document. | | customer\_reverification\_needed | **Description**: Incomplete information was received for a Customer; updated information is needed to verify the Customer.
**Timing**: Occurs upon a POST request to the [Update a Customer](/docs/api-reference/customers/update-a-customer) endpoint, or when Dwolla places a Customer into retry status. | | customer\_verification\_pending\_review | **Description**: Sent when a Customer enters manual review (AdditionalReviewRequired). No further action is required until Dwolla completes review. This webhook could accompany the customer\_suspended webhook.
**Timing**: Occurs when Dwolla places a Customer into suspended status for manual review; can be triggered on the initial verification attempt, retry attempts, and/or manually. | | customer\_address\_verification\_failed | **Description**: Sent when verification returns an address-related directive (POBoxNotAllowed, AddressNotAssociatedWithBusiness, ResidentialAddressRequired, CoWorkingAddress, or RegisteredAgentAddressNotAllowed). The address needs to be updated and/or proof of address documentation is required. This webhook could accompany the customer\_reverification\_needed webhook or be sent after a document is uploaded while in the document status.
**Timing**: Occurs on the initial verification attempt, during retry attempts, after document review, and/or manually. | | customer\_verified | **Description**: A Customer was verified.
**Timing**: Occurs when a Customer is verified by Dwolla upon a POST request to the [Create a Customer endpoint](/docs/api-reference/customers/create-a-customer). In a case where the Customer isn't instantly verified upon creation, this event occurs when the Customer is verified after a retry attempt, or after a document is approved. | | customer\_suspended | **Description**: A Customer was suspended.
**Timing**: Occurs when Dwolla systematically places a Customer in suspended status as a result of uploading fraudulent documents, or upon receiving certain ACH return codes when a transfer fails. | | customer\_activated | **Description**: A Customer moves from deactivated or suspended to an active status.
**Timing**: Occurs upon reactivating a Customer that has a deactivated status by making a POST request to the [Update a Customer](/docs/api-reference/customers/update-a-customer) endpoint, or when Dwolla reactivates a Customer that has a suspended status. | | customer\_deactivated | **Description**: A Customer was deactivated.
**Timing**: Occurs upon deactivation of a Customer by making a POST request to the [Update a Customer](/docs/api-reference/customers/update-a-customer) endpoint, or when Dwolla systematically deactivates a Customer upon receiving certain ACH return codes when a transfer fails. | ### Beneficial Owners | Topic | Description | | ------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | customer\_beneficial\_owner\_created | **Description**: Beneficial owner successfully created.
**Timing**: Occurs upon a POST request to the [Create a beneficial owner](/docs/api-reference/beneficial-owners/create-beneficial-owner) endpoint. | | customer\_beneficial\_owner\_removed | **Description**: An individual beneficial owner has been successfully removed from the Customer.
**Timing**: Occurs upon a POST request to the [Remove a beneficial owner](/docs/api-reference/beneficial-owners/delete-beneficial-owner) endpoint. | | customer\_beneficial\_owner\_verification\_document\_needed | **Description**: Additional documentation is needed to verify an individual beneficial owner.
**Timing**: Occurs when a second attempt to re-verify a beneficial owner fails, which systematically places the beneficial owner in document status immediately after a POST request to the [Update a beneficial owner](/docs/api-reference/beneficial-owners/update-beneficial-owner) endpoint. | | customer\_beneficial\_owner\_verification\_document\_uploaded | **Description**: A verification document was uploaded for the beneficial owner.
**Timing**: Occurs upon a POST request to the [Create a document for a beneficial owner](/docs/api-reference/documents/create-a-document-for-beneficial-owner) endpoint. | | customer\_beneficial\_owner\_verification\_document\_failed | **Description**: A verification document has been rejected for a beneficial owner.
**Timing**: Occurs when a document uploaded for a beneficial owner is reviewed by Dwolla, and rejected with a document failure reason, usually within 1-2 business of uploading a document. | | customer\_beneficial\_owner\_verification\_document\_approved | **Description**: A verification document was approved for a beneficial owner.
**Timing**: Occurs when a document uploaded for a Customer is reviewed by Dwolla, and approved, usually within 1-2 business days of uploading a document. | | customer\_beneficial\_owner\_reverification\_needed | **Description**: A previously `verified` beneficial owner status has changed due to either a change in the beneficial owner's information or at request for more information from Dwolla. The individual will need to verify their identity within 30 days.
**Timing**: Occurs upon a POST request to the [Update a beneficial owner](/docs/api-reference/beneficial-owners/update-beneficial-owner) endpoint, or when Dwolla places the beneficial owner into incomplete status. | | customer\_beneficial\_owner\_verified | **Description**: A beneficial owner has been verified.
**Timing**: Occurs when a Beneficial Owner is verified by Dwolla upon a POST request to the [Create a beneficial owner](/docs/api-reference/beneficial-owners/create-beneficial-owner) endpoint. In a case where the Beneficial Owner isn't instantly verified upon creation, this event occurs when the Beneficial Owner is verified after an update, or after a document is approved. | ### Exchanges | Event Topic Name | Description | | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | customer\_exchange\_reauth\_required | **Description:** An exchange has been deactivated (or is pending deactivation) and requires reauthentication.
**Timing:** Occurs when access to a user's connected bank account has been interrupted. This interruption could be due to changes on the bank's end, such as a password update, multi-factor authentication reset or revoked consent. This event signals that a user's bank connection needs to be refreshed by [creating a reauth exchange session](/docs/api-reference/exchange-sessions/create-re-authentication-exchange-session). | ### Funding Sources | Topic | Description | | ------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | customer\_funding\_source\_added | **Description**: A funding source was added to a Customer.
**Timing**: Occurs upon a POST request to the [Create a funding source for a customer](/docs/api-reference/funding-sources/create-customer-funding-source) endpoint, or when a funding source is added via [drop-in components](/docs/bank-funding-source#drop-in-components) or a third party bank verification method. | | customer\_funding\_source\_removed | **Description**: A funding source was removed from a Customer.
**Timing**: Occurs upon a POST request to the [Remove a funding source](/docs/api-reference/funding-sources/update-or-remove-a-funding-source) endpoint, or when Dwolla systematically removes a funding source upon receiving certain ACH return codes when a transfer fails. | | customer\_funding\_source\_verified | **Description**: A Customer's funding source was marked as verified.
**Timing**: Occurs upon a POST request to the [Verify micro-deposits](/docs/api-reference/funding-sources/initiate-or-verify-micro-deposits) endpoint with the correct amounts, or when a funding source is added + verified via a third-party bank verification method. Also occurs in cases where Dwolla manually marks a funding source as verified. | | customer\_funding\_source\_unverified | **Description**: A funding source has been systematically `unverified`. This is generally a result of a transfer failure. [View our developer resource article](https://developers.dwolla.com/concepts/transfer-failures) to learn more.
**Timing**: Occurs when Dwolla systematically marks a funding source unverified upon receiving certain ACH return codes when a transfer fails. | | customer\_funding\_source\_negative | **Description**: A Customer's `balance` has gone negative. You are responsible for ensuring a zero or positive Dwolla balance for Customer accounts created by your application. If a Customer balance funding source has gone negative, you are responsible for making the Dwolla Customer account whole. Dwolla will notify you via a webhook and separate email of the negative balance. If no action is taken, Dwolla will debit your attached billing source.
**Timing**: Occurs upon a POST request to the [Initiate a transfer](/docs/api-reference/transfers/initiate-a-transfer) endpoint that causes a funding source balance to go negative. | | customer\_funding\_source\_updated | **Description**: A Customer's funding source has been updated. This can also be fired as a result of a correction after a bank transfer process. For example, a financial institution can issue a correction to change the bank account `type` from `checking` to `savings`.
**Timing**: Occurs upon a POST request to the [Update a funding source](/docs/api-reference/funding-sources/update-or-remove-a-funding-source) endpoint. | | customer\_microdeposits\_added | **Description**: Two `<=10¢` transfers to a Customer's linked bank account were initiated.
**Timing**: Occurs upon a POST request to the Initiate micro-deposits endpoint. | | customer\_microdeposits\_failed | **Description**: The two `<=10¢` transfers to a Customer's linked bank account failed to clear successfully.
**Timing**: Occurs when micro-deposits fail to clear into a bank account, usually within 1-2 business days of initiating them. | | customer\_microdeposits\_completed | **Description**: The two `<=10¢` transfers to a Customer's linked bank account have cleared successfully.
**Timing**: Occurs when micro-deposit are successful, usually within 1-2 business days of initiating them. | | customer\_microdeposits\_maxattempts | **Description**: The Customer has reached their max verification attempts limit of three. The Customer can no longer verify their funding source with the completed micro-deposit amounts.
**Timing**: Occurs upon the third POST request to the [Verify micro-deposits](/docs/api-reference/funding-sources/initiate-or-verify-micro-deposits) endpoint with incorrect micro-deposit amounts. | ### Transfers For [Customer](/docs/api-reference/customers) transfer events, in addition to the [default payload keys](#events-resource), a `correlationId` key-value pair *may* be present if a value was specified when the [transfer was created](/docs/api-reference/transfers/initiate-a-transfer). | Topic | Description | | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | customer\_bank\_transfer\_created | **Description**: A bank transfer was created for a Customer. Represents funds moving either from a verified Customer's `bank` to the Dwolla network or from the Dwolla network to a verified Customer's `bank`.
**Timing**: Occurs upon a POST request to the [Initiate a transfer](/docs/api-reference/transfers/initiate-a-transfer) endpoint when sending funds from a Verified Customer's bank, or when funds move from a receiving Verified Customer's balance to their bank. | | customer\_bank\_transfer\_creation\_failed | **Description**: An attempt to initiate a transfer to a verified Customer's `bank` was made, but failed. Transfers initiated to a verified Customer's `bank` must pass through the verified Customer's `balance` before being sent to a receiving bank. Dwolla will fail to create a transaction intended for a verified Customer's `bank` if the funds available in the `balance` are less than the transfer amount.
**Timing**: Occurs when a transfer to a verified Customer's bank fails to be created. | | customer\_bank\_transfer\_cancelled | **Description**: A pending Customer bank transfer has been cancelled, and will not proceed further. Represents a cancellation of funds either transferring from a verified Customer's `bank` to the Dwolla network or from the Dwolla network to a verified Customer's `bank`.
**Timing**: Occurs upon a POST request to the [Cancel a transfer](/docs/api-reference/transfers/cancel-a-transfer) endpoint, or when Dwolla manually cancels a transfer. | | customer\_bank\_transfer\_failed | **Description**: A Customer bank transfer failed to clear successfully. Usually, this is a result of an ACH failure (insufficient funds, etc.). Represents funds failing to clear either from a verified Customer's `bank` to the Dwolla network or from the Dwolla network to a verified Customer's `bank`.
**Timing**: Occurs when Dwolla marks a transfer as failed. | | customer\_bank\_transfer\_completed | **Description**: A bank transfer that was created for a Customer has cleared successfully. Represents funds clearing either from a verified Customer's `bank` to the Dwolla network or from the Dwolla network to a verified Customer's `bank`.
**Timing**: Occurs when a funds transfer into the Dwolla Platform or a verified Customer's bank is successful, based on the transfer processing timing used. | | customer\_transfer\_created | **Description**: A transfer was created for a Customer. Represents funds transferring to an unverified Customer's `bank` or to a verified Customer's `balance`.
**Timing**: Occurs upon a POST request to the [Initiate a transfer](/docs/api-reference/transfers/initiate-a-transfer) endpoint when sending funds from a verified Customer's balance, or to/from an unverified Customer's bank. | | customer\_transfer\_cancelled | **Description**: A pending transfer has been cancelled, and will not process further. Represents a cancellation of funds transferring either to an unverified Customer's `bank` or to a verified Customer's `balance`.
**Timing**: Occurs upon a POST request to the [Cancel a transfer](/docs/api-reference/transfers/cancel-a-transfer) endpoint to cancel a transfer initiated from a verified Customer's balance, or to/from an unverified Customer's bank. | | customer\_transfer\_failed | **Description**: A Customer transfer failed to clear successfully. Represents funds failing to clear either to an unverified Customer's `bank` or to a verified Customer's `balance`.
**Timing**: Occurs when Dwolla marks a transfer as failed. | | customer\_transfer\_completed | **Description**: A Customer transfer has cleared successfully. Represents funds clearing either to an unverified Customer's `bank` or to a verified Customer's `balance`.
**Timing**: Occurs when a funds transfer into an unverified Customer's bank or a verified Customer's balance is successful, based on the transfer processing timing used. | ### Mass Payments | Topic | Description | | ------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | customer\_mass\_payment\_created | **Description**: A Verified Customer's mass payment was created.
**Timing**: Occurs upon a POST request to the [Update a mass-payment](/docs/api-reference/mass-payments/update-a-mass-payment) endpoint when cancelling a mass payment job. | | customer\_mass\_payment\_completed | **Description**: A Verified Customer's mass payment was completed. However, this doesn't mean that each mass payment item's transfer was successful.
**Timing**: Occurs when a mass payment job completes. | | customer\_mass\_payment\_cancelled | **Description**: A Verified Customer's created and deferred mass payment was cancelled.
**Timing**: Occurs upon a POST request to the [Update a mass-payment](/docs/api-reference/mass-payments/update-a-mass-payment) endpoint when cancelling a mass payment job. | | customer\_balance\_inquiry\_completed | **Description**: Upon checking a Customer's bank balance, Dwolla will immediately return an HTTP 202 with response body that includes a status of `processing`.
**Timing**: This event will be triggered when the bank balance check has completed processing. | ### Labels | Topic | Description | | --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | customer\_label\_created | **Description**: A Verified Customer's label was created.
**Timing**: Occurs upon a POST request to the [Create a label](/docs/api-reference/labels/create-a-label-for-a-customer) endpoint. | | customer\_label\_ledger\_entry\_created | **Description**: A ledger entry for a Verified Customer's label was created.
**Timing**: Occurs upon a POST request to the [Create a label ledger entry](/docs/api-reference/labels/create-a-label-for-a-customer) endpoint. | | customer\_label\_removed | **Description**: A Verified Customer's label was removed.
**Timing**: Occurs upon a POST request to the [Remove a label](/docs/api-reference/labels/remove-a-label) endpoint. | # List events Source: https://developers.dwolla.com/docs/api-reference/events/list-events get /events Returns a paginated list of events representing state changes to resources in your Dwolla application. Events track actions on customers, transfers, funding sources, and other resources, sorted by creation date (newest first). Events are retained for 30 days and are essential for webhook notifications and system activity monitoring. # Retrieve event Source: https://developers.dwolla.com/docs/api-reference/events/retrieve-event get /events/{id} Returns detailed information for a specific event representing a state change that occurred on a resource in your Dwolla application. Includes the event topic, timestamp, resource links, and correlation ID if applicable. # Overview Source: https://developers.dwolla.com/docs/api-reference/exchange-sessions Dwolla Exchange Sessions API connects your app, open banking providers and user banks. Streamline onboarding with instant account verification and leverage Dwolla's pre-integrated solutions. ## Overview This section dives into the process of leveraging Dwolla's Exchange Sessions API for Instant Account Verification (IAV) within your application. Dwolla's Exchange Sessions API enables you to leverage open banking providers to perform bank account verification in real-time, streamlining your onboarding process and improving user experience. An Exchange Session represents a generated IAV session that is established between Dwolla and integrated open banking service providers for the purposes of user bank account verification. New to Open Banking? Get a head start with our comprehensive overview article before diving into the API details. ### Exchange Sessions resource | Parameter | Description | | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | \_links | A \_links JSON object containing a collection of links to related resources including: exchange session, exchange-partner, and external-provider-session. The unique URL called "external-provider-session" is specifically designed to be embedded within your application, facilitating the Instant Account Verification flow. | | externalProviderSessionToken | A short-lived, one-time use token that is used to authenticate your application with the Plaid Link flow. The external provider session token grants access to Plaid Link for a specific customer Exchange Session. An `externalProviderSessionToken` is generated on your server by making a request to the [create exchange session for a customer](/docs/api-reference/exchange-sessions/create-customer-exchange-session) API endpoint. | | created | ISO 8601 timestamp of when the exchange-partner resource was created. | ```bash theme={"dark"} { "created": "2024-03-25T17:13:38.430Z", "_links": { "self": { "href": "https://api-sandbox.dwolla.com/exchange-sessions/9b7fb629-0fad-44f4-8c5e-44c25a0bfa8e", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "exchange-sessions" }, "exchange-partner": { "href": "https://api-sandbox.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", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "text/html" } } } ``` # Create customer exchange session Source: https://developers.dwolla.com/docs/api-reference/exchange-sessions/create-customer-exchange-session post /customers/{id}/exchange-sessions Creates an exchange session for a customer. Use cases include: - **Plaid / MX**: Instant bank account verification (open banking). For faster verification as compared to traditional micro-deposits. - **Checkout.com**: Debit card capture for Push to Card. Create a session, then retrieve it to get `externalProviderSessionData` (payment session) for the Checkout.com Flow component. # Create re-authentication exchange session Source: https://developers.dwolla.com/docs/api-reference/exchange-sessions/create-re-authentication-exchange-session post /exchanges/{id}/exchange-sessions Creates a re-authentication exchange session to refresh a user's bank account connection when their existing authorization is no longer valid. Required when receiving an UpdateCredentials error during bank balance checks or when user re-authentication is needed. # List available exchange connections Source: https://developers.dwolla.com/docs/api-reference/exchange-sessions/list-available-exchange-connections get /customers/{id}/available-exchange-connections Returns available exchange connections for a customer's bank accounts authorized through MX Connect. Each connection includes an account name and availableConnectionToken required to create exchanges and funding sources for transfers. # Retrieve exchange session Source: https://developers.dwolla.com/docs/api-reference/exchange-sessions/retrieve-exchange-session get /exchange-sessions/{id} Returns details of a previously created exchange session. Response varies by partner: - **MX**: `_links.external-provider-session.href` (redirect URL for verification). - **Plaid**: `externalProviderSessionToken` (token to initialize Plaid Link). - **Checkout.com**: `externalProviderSessionData` with `id`, `payment_session_secret`, and `payment_session_token` to initialize the Checkout.com Flow component for debit card capture (Push to Card). # Overview Source: https://developers.dwolla.com/docs/api-reference/exchanges Create, list, and retrieve exchanges and exchange partners. # Exchanges The Secure Exchange solution is a tokenized approach to creating an interconnected payment experience among third-party data providers, payment technologies and financial institutions. The Exchanges API generates provider tokens, providing a secure way to receive data from third parties. Today, Dwolla clients and reseller partners can utilize this API to perform functions such as creating funding-sources within the Dwolla ecosystem. As more functionality is released, the functions this endpoint can perform will continue to expand. ### Exchange Partners resource | Parameter | Description | | --------- | ------------------------------------------------------------------------------------------------- | | id | A unique string value which can be used to identify a third-party “partner” via the Exchange API. | | name | The name of the third-party exchange-partner | | status | Status of the exchange-partner. Possible values are: `active`, `deactivated`, or `removed`. | | created | ISO 8601 timestamp of when the exchange-partner resource was created. | ```bash theme={"dark"} { "_links": { "self": { "href": "https://api.dwolla.com/exchange-partners/e5e9f2d3-a96c-4abd-a097-8ec7ae28aa8a", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "exchange-partner" }, "funding-source": { "href": "https://api.dwolla.com/funding-sources", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "funding-source" } }, "id": "e5e9f2d3-a96c-4abd-a097-8ec7ae28aa8a", "name": "MX", "status": "active", "created": "2022-08-30T19:31:59.106Z" } ``` ### Exchanges resource | Parameter | Description | | --------- | ----------------------------------------------------------------------------------- | | id | A unique string value which can be used to identify an exchange resource. | | status | Status of the exchange. Possible values are: `active`, `deactivated`, or `removed`. | | created | ISO 8601 timestamp of when the exchange resource was created. | ```bash theme={"dark"} { "_links": { "self": { "href": "https://api.dwolla.com/exchanges/fcd15e5f-8d13-4570-a9b7-7fb49e55941d", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "exchange" }, "exchange-partner": { "href": "https://api.dwolla.com/exchange-partners/9b55a4b3-34ae-4607-b2d1-622f1eed77f9", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "exchange-partner" } }, "id": "fcd15e5f-8d13-4570-a9b7-7fb49e55941d", "status": "active", "created": "2022-10-21T21:41:03.283Z" } ``` # Create an exchange for a customer Source: https://developers.dwolla.com/docs/api-reference/exchanges/create-an-exchange-for-a-customer post /customers/{id}/exchanges Creates an exchange connection between a customer and Dwolla. Request body varies by partner (Plaid, MX, Flinks, Finicity, Checkout.com). For bank accounts, use Plaid, MX, Flinks, or Finicity to establish secure access to the customer's bank account data. For debit cards (Push to Card), use Checkout.com and pass the payment ID from Checkout.com Flow. # Create an exchange for an account Source: https://developers.dwolla.com/docs/api-reference/exchanges/create-an-exchange-for-an-account post /exchanges Create an exchange for an account. The request body will vary based on the exchange partner. For Finicity, the request body will include finicity-specific fields. For MX Secure Exchange, the request body will include a token. For Flinks Secure Exchange, the request body will include a token. For Plaid Secure Exchange, the request body will include a token. # List exchange partners Source: https://developers.dwolla.com/docs/api-reference/exchanges/list-exchange-partners get /exchange-partners Returns a list of all supported exchange partners. Each partner includes a unique ID, name, and status indicating whether they are active or inactive. # List exchanges for a customer Source: https://developers.dwolla.com/docs/api-reference/exchanges/list-exchanges-for-a-customer get /customers/{id}/exchanges Returns all exchanges for a specific customer. Exchanges represent connections between the customer's external bank accounts and open banking partners. Includes exchange status, creation date, and links to associated funding sources and partners. # List exchanges for an account Source: https://developers.dwolla.com/docs/api-reference/exchanges/list-exchanges-for-an-account get /exchanges Returns all exchanges for your Dwolla account. Exchanges represent connections between external bank accounts and your account through open banking partners. Includes exchange status, creation date, and associated partner information. # Retrieve exchange partner Source: https://developers.dwolla.com/docs/api-reference/exchanges/retrieve-exchange-partner get /exchange-partners/{id} Returns details for a specific open banking provider that integrates with Dwolla. Includes partner name, status, and creation date. Use this to verify partner availability before creating exchanges and funding sources. # Retrieve exchange resource Source: https://developers.dwolla.com/docs/api-reference/exchanges/retrieve-exchange-resource get /exchanges/{id} Returns details for a specific exchange connection between Dwolla and an open banking partner for a customer's bank account. Includes exchange status, creation date, and links to the associated customer and exchange partner. # Overview Source: https://developers.dwolla.com/docs/api-reference/funding-sources Create, update, remove or retrieve a funding source with the API. Use Finicity, MX, or Flinks via Secure Exchange, Plaid, or micro deposits to verify the funding source. # Funding Sources The Funding Sources resource represents payment accounts that can be used to send and/or receive funds. Funding sources are relational to either a [Dwolla Main Account](/docs/api-reference/accounts/create-a-funding-source-for-an-account) or [Customer](/docs/api-reference/funding-sources/create-customer-funding-source) and can be used to reference details on a payment account. ### Funding source types The three funding source types available with a Dwolla integration include a `bank`, and the Dwolla `balance` account. Type `bank` represents any bank account attached as a funding source to Account and Customer resources. Type `balance` represents the Dwolla Balance made available to Account and Verified Customer resources. ##### Bank funding source Funding sources of type `bank` include an additional attribute, `bankAccountType`, denoting the type of the bank account being attached. The bank account types currently supported by Dwolla include `checking`, `savings`, `general-ledger` and `loan`. * `checking`, `savings` - Checking and savings accounts can be attached to any Customer type. These account types are enabled for all Accounts and Customers, by default. * `general-ledger` - General ledger accounts can only be attached to exempt Business Verified Customers. **Note**: Enabling this account type requires additional Dwolla approvals before getting started. Please contact [Sales](https://www.dwolla.com/contact?b=apidocs) or your account manager for more information on enabling this account type. * `loan` - Loan accounts can only be attached to Verified Customers. These funding-sources can only be credited, meaning funds can only be sent to these accounts. **Note**: Enabling this account type requires additional Dwolla approvals before getting started. Please contact [Sales](https://www.dwolla.com/contact?b=apidocs) or your account manager for more information on enabling this account type. ##### Balance funding source The [Dwolla Balance](/docs/balance-funding-source) can be utilized as a digital "wallet", storing USD funds for the Customer or Account with Dwolla's financial institution partners. Additionally, the Dwolla Balance can be pre-loaded with funds for quicker outgoing ACH transfers to destination funding sources. To get a more in-depth overview of the Dwolla Balance, including functionality and other benefits, check out our [developer resource article](/docs/balance-funding-source) or view our [webinar](https://www.dwolla.com/resources/balance-webinar/). ### Funding source links | Link | Description | | ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | self | URL of the funding source resource. | | customer | GET this link to [retrieve details](/docs/api-reference/customers/retrieve-a-customer) of the Customer. | | remove | POST to this link to [remove the funding source](/docs/api-reference/funding-sources/update-or-remove-a-funding-source) from the Customer. | | balance | (Verified Customer only) GET this link to [retrieve the amount available in the balance](/docs/api-reference/funding-sources/retrieve-funding-source-balance) of the Customer's Balance funding source. | | transfer-from-balance | (Verified Customer only) if this link exists, the Customer can transfer funds from their balance. | | transfer-to-balance | (Verified Customer only) if this link exists, funds can be transferred to the Customer's balance. | | transfer-send | If this link exists, the Customer can send funds to another Customer. | | transfer-receive | The Customer can receive funds from another Customer. | | initiate-micro-deposits | POST to this link to [initiate micro-deposits](/docs/api-reference/funding-sources/initiate-or-verify-micro-deposits) on an unverified funding source. | | verify-micro-deposits | Micro-deposits have completed to this funding source and are eligible for verification. POST to this link with the [verify micro-deposit amounts](/docs/api-reference/funding-sources/initiate-or-verify-micro-deposits) and complete bank funding source verification. | | failed-verification-micro-deposits | Micro-deposits attempts have failed due to too many failed attempts. [Remove the bank and re-add to attempt verification again.](/docs/micro-deposit-verification) | ### Funding source resource | Parameter | Description | | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | id | The funding source unique identifier. | | status | Possible values are `unverified` or `verified`. Determines if the funding source has completed verification. | | type | Type of funding source. Possible values are `bank` or `balance`. | | bankAccountType | An attribute for `bank` funding sources that determines the type of account. Possible values are `checking`, `savings`, `general-ledger` or `loan`. | | name | Arbitrary nickname for the funding source. | | created | ISO-8601 timestamp for when the funding source was created. | | balance | An optional object that includes `value` and `currency` parameters. `value` is a string value for the amount available and `currency` is a string value currency code. Only returned for a Dwolla API Customer account balance. | | removed | Determines if the funding source has been [removed](/docs/api-reference/funding-sources/update-or-remove-a-funding-source). A boolean `true` if the funding source was removed or `false` if the funding source is not removed. | | channels | List of processing channels. ACH is the default processing channel for bank transfers. Possible values are `ach`, `real-time-payments` or `wire`. | | bankName | The financial institution name. This value is generated from its routing number by Dwolla when a funding source is created; it cannot be specified manually. | | iavAccountHolders | An optional object that includes optional `selected` and `other` parameters. `selected`, a string with the account holder name(s) on file with the financial institution for the IAV selected account. `other`, a list of strings with name(s) of other accounts on file. Only returned for a Customer that added a bank using Dwolla IAV, and if names are returned for the selected bank account. | | fingerprint | Fingerprint is an optional unique identifying string value returned for funding sources of type `bank`. This attribute can be used to check across all Dwolla API Customers if two bank accounts share the same account number and routing number. Removing a funding source does not remove the `fingerprint`. | #### Funding source resource example ```bash theme={"dark"} { "_links": { "self": { "href": "https://api-sandbox.dwolla.com/funding-sources/fc84223a-609f-42c9-866e-2c98f17ab4fb", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "funding-source" }, "customer": { "href": "https://api-sandbox.dwolla.com/customers/241ec287-8d7a-4b69-911e-ffbea98d75ce", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "customer" } }, "id": "fc84223a-609f-42c9-866e-2c98f17ab4fb", "status": "verified", "type": "bank", "bankAccountType": "checking", "name": "Your Account #1 - CHECKING", "created": "2017-08-16T20:06:34.000Z", "removed": false, "channels": [ "ach", "real-time-payments" ], "bankName": "SANDBOX TEST BANK", "iavAccountHolders": { "selected": "account holder", "other": [ "Jane Doe", "GeneriCompany LLC" ] }, "fingerprint": "4cf31392f678cb26c62b75096e1a09d4465a801798b3d5c3729de44a4f54c794" } ``` # Create customer funding source Source: https://developers.dwolla.com/docs/api-reference/funding-sources/create-customer-funding-source post /customers/{id}/funding-sources Creates a bank account or debit card funding source for a customer. Supports multiple methods including manual entry with routing/account numbers, instant verification using existing open banking connections, debit card addition via Exchange, and virtual account numbers. Bank funding sources require verification before transfers can be initiated. # Initiate or Verify micro-deposits Source: https://developers.dwolla.com/docs/api-reference/funding-sources/initiate-or-verify-micro-deposits post /funding-sources/{id}/micro-deposits Handles micro-deposit bank verification process. Make a request without a request body to initiate two small deposits to the customer's bank account. Include deposit amounts to verify the received values and complete verification. # List customer funding sources Source: https://developers.dwolla.com/docs/api-reference/funding-sources/list-customer-funding-sources get /customers/{id}/funding-sources Returns all funding sources for a customer, including bank accounts, debit card funding sources, and Dwolla balance (verified customers only). Shows verification status, limited account details, and creation dates. Card funding sources include masked card information. Supports filtering to exclude removed funding sources using the removed parameter. # Retrieve a funding source Source: https://developers.dwolla.com/docs/api-reference/funding-sources/retrieve-a-funding-source get /funding-sources/{id} Returns detailed information for a specific funding source, including its type, status, and verification details. Supports bank accounts (via Open Banking), debit card funding sources, and Dwolla balance (verified customers only). Debit card funding sources include masked card details such as brand, last four digits, expiration date, and cardholder name. # Retrieve funding source balance Source: https://developers.dwolla.com/docs/api-reference/funding-sources/retrieve-funding-source-balance get /funding-sources/{id}/balance Returns the current balance for a specific funding source. For bank accounts, includes available and closing balances; for Dwolla balance, includes balance and total amounts; for settlement accounts (bankUsageType = card-network), includes available balance only. Supports bank accounts (via Open Banking), Dwolla balance (verified customers only), and settlement accounts for card network processing. # Retrieve micro-deposits details Source: https://developers.dwolla.com/docs/api-reference/funding-sources/retrieve-micro-deposits-details get /funding-sources/{id}/micro-deposits Returns the status and details of micro-deposits for a funding source to check verification eligibility. Includes deposit status (pending, processed, failed), creation timestamp, and failure details with ACH return codes if deposits failed. Use this endpoint to determine when micro-deposits are ready for verification. # Retrieve VAN account and routing numbers Source: https://developers.dwolla.com/docs/api-reference/funding-sources/retrieve-van-account-and-routing-numbers get /funding-sources/{id}/ach-routing Returns the unique account and routing numbers for a Virtual Account Number (VAN) funding source. These numbers can be used by external systems to initiate ACH transactions that pull funds from or push funds to the associated Dwolla balance. # Update or remove a funding source Source: https://developers.dwolla.com/docs/api-reference/funding-sources/update-or-remove-a-funding-source post /funding-sources/{id} Updates a bank funding source's details or soft deletes it. When updating, you can change the name (any status) or modify routing/account numbers and account type (unverified status only). When removing, the funding source is soft deleted and can still be accessed but marked as removed. # Overview Source: https://developers.dwolla.com/docs/api-reference/kba Apply Knowledge Based Authentication (KBA) in your application to verify the identity of a Personal Verified Customer. # Knowledge-based Authentication (KBA) Knowledge-based authentication, commonly referred to as KBA, is a method of authentication which seeks to prove the identity of an individual. KBA requires the knowledge of private information of the individual to prove that the person providing the identity information is the owner of the identity. Questions are compiled from public and private data such as marketing data, credit reports or transaction history. KBA as a method of verifying an identity is only available to Personal Verified Customers at this time. This section outlines a premium feature for the Dwolla API. To learn more about pricing and enabling this functionality, please contact Sales. ### KBA Links | Links | Description | | ------ | ----------------------------- | | answer | Url of the correct KBA answer | # Initiate a KBA session Source: https://developers.dwolla.com/docs/api-reference/kba/initiate-a-kba-session post /customers/{id}/kba Creates a new KBA (Knowledge-Based Authentication) session for a personal Verified Customer. Returns a KBA identifier that represents the session and is used to retrieve authentication questions for customer verification. # Retrieve KBA Questions Source: https://developers.dwolla.com/docs/api-reference/kba/retrieve-kba-questions get /kba/{id} Returns the KBA questions for a specific KBA session. The questions are used to verify the customer's identity during the KBA process. # Verify KBA Questions Source: https://developers.dwolla.com/docs/api-reference/kba/verify-kba-questions post /kba/{id} Submits customer answers to KBA questions for identity verification. Requires four question-answer pairs with questionId and answerId values. Returns verification status indicating whether the customer passed or failed the KBA authentication. # Overview Source: https://developers.dwolla.com/docs/api-reference/labels Labels represent a designated portion of funds within a Verified Customer's balance. Create, remove, and list them using the Dwolla API. # Labels A **Label** represents a designated portion of funds within a [Verified Customer's](/docs/customer-types#verified-customer) balance. To create a label, you'll specify the ID of a Verified Customer and an amount. Your application will maintain any other Label information or associations. Labels can be created, updated, and deleted. You can also list all Labels for a Verified Customer Record and list all entries for a specified Label. Note that a Verified Customer's labeled amounts cannot exceed the balance available in such Verified Customer's account. This section outlines a premium feature for the Dwolla API. To learn more about pricing and enabling this functionality, please contact Sales. ### Label Links | Link | Description | | -------------- | -------------------------------------------------------------- | | self | URL of the Label resource. | | ledger-entries | GET this link to list the ledger entries for a Label. | | update | GET this link to update the ledger for this Verified Customer. | | remove | GET this link to remove the Label for this Verified Customer. | ### Label resource | Parameter | Description | | --------- | -------------------------------------------------------------------------------------------------------------------- | | \_links | A \_links JSON object that contains links to suggested resources and actions available based on the current context. | | id | A Label unique identifier. | | amount | An Amount JSON object that contains value and currency keys. Reference the amount object to learn more. | | created | ISO-8601 timestamp. | ### Amount object | Parameter | Description | | --------- | ------------------------ | | value | Amount of funds. | | currency | Acceptable values: `USD` | # Create a label for a customer Source: https://developers.dwolla.com/docs/api-reference/labels/create-a-label-for-a-customer post /customers/{id}/labels Creates a new label for a Verified Customer with a specified amount. Labels help organize and track funds within a customer's balance. Returns the location of the created label resource in the response header. # Create a label ledger entry Source: https://developers.dwolla.com/docs/api-reference/labels/create-a-label-ledger-entry post /labels/{id}/ledger-entries Create a new ledger entry to track fund adjustments on a Label by specifying a positive or negative amount value. Returns the location of the created ledger entry in the response header. Label amounts cannot go negative, so validation errors occur if the entry would result in a negative Label balance. # Create a label reallocation Source: https://developers.dwolla.com/docs/api-reference/labels/create-a-label-reallocation post /label-reallocations Reallocates funds between two labels belonging to the same Verified Customer. Moves the specified amount from the source label to the destination label, creating ledger entries for both. The reallocation only succeeds if the source label has sufficient funds. # List label ledger entries Source: https://developers.dwolla.com/docs/api-reference/labels/list-label-ledger-entries get /labels/{id}/ledger-entries Returns all ledger entries for a specific Label, sorted by creation date (newest first). Supports pagination with limit and offset parameters. Each ledger entry includes its amount, currency, and creation timestamp. # List labels for a customer Source: https://developers.dwolla.com/docs/api-reference/labels/list-labels-for-a-customer get /customers/{id}/labels Returns all labels for a specified Verified Customer, sorted by creation date (most recent first). Supports pagination with limit and offset parameters. Each label includes its current amount and creation timestamp. # Remove a label Source: https://developers.dwolla.com/docs/api-reference/labels/remove-a-label delete /labels/{id} Delete a Label to stop tracking funds and remove it from your account. Returns success status if the Label is successfully removed. Use this to streamline your account management and remove unused Labels from your system. # Retrieve a label Source: https://developers.dwolla.com/docs/api-reference/labels/retrieve-a-label get /labels/{id} Retrieve details for a specific Label used to categorize and track funds within your account. Returns Label information including unique identifier, current amount with currency, and creation timestamp. # Retrieve a label ledger entry Source: https://developers.dwolla.com/docs/api-reference/labels/retrieve-a-label-ledger-entry get /ledger-entries/{id} # Retrieve a label reallocation Source: https://developers.dwolla.com/docs/api-reference/labels/retrieve-a-label-reallocation get /label-reallocations/{id} # Overview Source: https://developers.dwolla.com/docs/api-reference/mass-payments Create, list, update, and retrieve mass payments using the Dwolla API. # Mass payments Dwolla mass payments allows you to easily send up to 5,000 payments in one API request. The payments are funded from a single user's specified funding source and processed asynchronously upon submission. Dwolla Mass Payments are meant for batches of multiple payments. If you are initiating a single payment to a singular Customer, we recommend using our transfers endpoint. Your mass payment will initially be pending and then processed. As the service processes your mass payment, each `item` is processed one after the other, at a rate between 0.5 sec. - 1 sec. / item. Therefore, you can expect a 1000-item mass payment to be completed between 8-16 minutes. A mass payment offers a significant advantage over repeatedly calling the [Transfers](/docs/api-reference/transfers) endpoint for each individual transaction. A key benefit is that a bank-funded mass payment only incurs a single ACH debit from the bank account to fund the entire batch of payments. The alternative approach will incur an ACH debit from the bank funding source for each individual payment. Those who used this approach have reported incurring fees from their financial institutions for excessive ACH transactions. ### Mass payments resource | Parameter | Description | | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | id | Mass payment unique identifier. | | status | Either `deferred`: A created mass payment that can be processed at a later time. `pending`: A mass payment that is pending and awaiting processing. A mass payment has a pending status for a brief period of time and cannot be cancelled. `processing`: A mass payment that is processing. `complete`: A mass payment successfully completed processing. | | created | ISO-8601 timestamp. | | metadata | A metadata JSON object. | | clearing | A clearing JSON object. | | total | The sum amount of all items in the mass payment. | | totalFees | The sum amount of all fees charged for the mass payment. | | correlationId | A string value attached to a mass payment resource which can be used for traceability between Dwolla and your application. | ```bash theme={"dark"} { "_links": { "self": { "href": "https://api-sandbox.dwolla.com/mass-payments/da835c07-1e12-4212-8b93-a7e0013dfd98", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "mass-payment" }, "source": { "href": "https://api-sandbox.dwolla.com/funding-sources/707177c3-bf15-4e7e-b37c-55c3898d9bf4", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "funding-source" }, "items": { "href": "https://api-sandbox.dwolla.com/mass-payments/da835c07-1e12-4212-8b93-a7e0013dfd98/items", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "mass-payment-item" } }, "id": "da835c07-1e12-4212-8b93-a7e0013dfd98", "status": "complete", "created": "2017-08-31T19:18:02.000Z", "metadata": { "batch": "batch1" }, "total": { "value": "0.02", "currency": "USD" }, "totalFees": { "value": "0.00", "currency": "USD" }, "correlationId": "d028beed-8152-481d-9427-21b6c4d99644" } ``` # Initiate a mass payment Source: https://developers.dwolla.com/docs/api-reference/mass-payments/initiate-a-mass-payment post /mass-payments Create a mass payment containing up to 5,000 individual payment items from a Dwolla Main Account or Verified Customer funding source. Supports optional metadata, correlation IDs for traceability, deferred processing, and expedited transfer options including same-day ACH clearing. Returns the location of the created mass payment resource with a unique identifier for tracking and management. # List items for a mass payment Source: https://developers.dwolla.com/docs/api-reference/mass-payments/list-items-for-a-mass-payment get /mass-payments/{id}/items Retrieve individual payment items within a mass payment with optional status filtering and pagination support. Each item represents a distinct payment with status indicators (failed, pending, success) showing whether a transfer was successfully created. Returns paginated item details including amount, destination, metadata, and error information for failed items. Supports filtering by status and standard pagination. # List mass payments for customer Source: https://developers.dwolla.com/docs/api-reference/mass-payments/list-mass-payments-for-customer get /customers/{id}/mass-payments Retrieve all previously created mass payments for a Verified Customer account with optional correlation ID filtering and pagination support. Mass payments are returned ordered by date created with most recent appearing first. Returns paginated results including mass payment status, metadata, source funding information, and item links. Supports standard pagination parameters and correlation ID search for enhanced traceability. # Retrieve a mass payment Source: https://developers.dwolla.com/docs/api-reference/mass-payments/retrieve-a-mass-payment get /mass-payments/{id} Retrieve detailed information for a mass payment by its unique identifier. Returns the current processing status (pending, processing, or complete), creation date, metadata, and links to the source funding source and payment items. Use this endpoint to monitor mass payment processing progress and determine when to check individual item results. # Retrieve mass payment item Source: https://developers.dwolla.com/docs/api-reference/mass-payments/retrieve-mass-payment-item get /mass-payment-items/{id} # Update a mass payment Source: https://developers.dwolla.com/docs/api-reference/mass-payments/update-a-mass-payment post /mass-payments/{id} Update the status of a deferred mass payment to control its processing lifecycle. Set status to `pending` to trigger processing and begin fund transfers, or `cancelled` to permanently cancel the mass payment before processing begins. Only applies to mass payments created with deferred status. Returns the updated mass payment resource with the new status. # root Source: https://developers.dwolla.com/docs/api-reference/root get / Retrieve the API root entry point to discover available resources and endpoints based on your OAuth access token permissions. Returns HAL+JSON with navigation links to accessible resources including accounts, customers, events, and webhook subscriptions depending on token scope. Essential for API exploration, dynamic resource discovery, and building adaptive client applications that respond to available permissions. # Create an application access token Source: https://developers.dwolla.com/docs/api-reference/tokens/create-an-application-access-token post /token Generate an application access token using OAuth 2.0 client credentials flow for server-to-server authentication. Requires client ID and secret sent via Basic authentication header with grant_type=client_credentials in the request body. Returns a bearer access token with expiration time for authenticating API requests scoped to your application. Essential for secure API access. # Overview Source: https://developers.dwolla.com/docs/api-reference/transfers Create, list, cancel, or retrieve transfer details. # Transfers The **Transfers** resource in the Dwolla API enables you to programmatically move funds between accounts. Transfers represent the movement of money from a source (such as a bank or Dwolla balance/wallet) to a destination, and support a variety of use cases—including: pay-ins, pay-outs, facilitating payments between users and enabling transfers between a user's own accounts (me-to-me). With the Transfers resource, you can initiate, track, and manage payments, handle cancellations, and view detailed transfer statuses and metadata. This resource is central to orchestrating payments, providing transparency and control over the flow of funds in your application. ### Transfer Links | Link | Description | | -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | self | URL of the transfer. | | source | GET this link to [retrieve the Customer](/docs/api-reference/customers/retrieve-a-customer) that was the `source` of the transfer. | | destination | GET this link to [retrieve the Customer](/docs/api-reference/customers/retrieve-a-customer) that was the `destination` of the transfer. | | source-funding-source | GET this link to [retrieve the funding source](/docs/api-reference/funding-sources/retrieve-a-funding-source) that was the `source` of the transfer. | | destination-funding-source | GET this link to [retrieve the funding source](/docs/api-reference/funding-sources/retrieve-a-funding-source) that was the `destination` of the transfer. | | cancel | POST to this link to [cancel the transfer](/docs/api-reference/transfers/cancel-a-transfer) (A bank transfer is cancellable up until 4pm CT on that same business day if the transfer was initiated prior to 4pm CT. If a transfer was initiated after 4pm CT, it can be cancelled before 4pm CT on the following business day.) | | fees | GET this link to [retrieve the facilitator fees](/docs/api-reference/transfers/list-fees-for-a-transfer) associated with the transfer. | ### Transfer resource | Parameter | Description | | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | id | Transfer unique identifier. | | status | Either `processed`, `pending`, `cancelled`, or `failed`. | | amount | An amount JSON object. See below. | | created | ISO-8601 timestamp. | | metadata | A metadata JSON object | | clearing | A clearing JSON object. | | achDetails | An achDetails JSON object. [See below](#achdetails-object). | | rtpDetails | An rtpDetails JSON object. Included when the transfer was sent via RTP Network. [See below](#rtpdetails-and-fednowdetails-objects). | | fedNowDetails | A fedNowDetails JSON object. Included when the transfer was sent via FedNow Service. [See below](#rtpdetails-and-fednowdetails-objects). | | correlationId | A string value attached to a transfer resource which can be used for traceability between Dwolla and your application. | | individualAchId | A unique string value matching the value on bank line related to the transfer. Appears when the debit entry clears out of the bank. The individual identifier for that ACH entry. | | processingChannel | A processingChannel JSON object that contains a key-value pair with a string key and string value of `destination` and `real-time-payments`. | ```bash theme={"dark"} { "_links": {}, "_embedded": {}, "id": "string", "status": "string", "amount": { "value": "string", "currency": "string" }, "created": "string", "metadata": { "key": "value" }, "clearing": { "source": "standard", "destination": "next-available" }, "achDetails": { "source": { "addenda": { "values": [ "string" ] }, "traceId": "string" }, "destination": { "addenda": { "values": [ "string" ] }, "traceId": "string" } }, "rtpDetails": { "destination": { "remittanceData": "string", "networkId": "string", "endToEndReferenceId": "string" } }, "fedNowDetails": { "destination": { "remittanceData": "string", "networkId": "string", "endToEndReferenceId": "string" } }, "correlationId": "string", "individualAchId": "string", "processingChannel": { "destination": "real-time-payments" } } ``` ### Source and destination types ##### Source types | Source Type | URI | Description | | -------------- | --------------------------------------------- | --------------------------------- | | Funding source | `https://api.dwolla.com/funding-sources/{id}` | A bank or balance funding source. | ##### Destination types | Destination Type | URI | Description | | ---------------- | --------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | | Funding source | `https://api.dwolla.com/funding-sources/{id}` | Destination of an Account or verified Customer's own bank or balance funding source. **OR** A Customer's bank funding source. | ### amount JSON object | Parameter | Required | Type | Description | | --------- | -------- | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | value | yes | string | Amount of money. If the entered amount has more than two decimal places, Dwolla will automatically round it to the nearest even integer using [Banker's Rounding](http://wiki.c2.com/?BankersRounding).
Maximum limit: Default transaction limits based on [Customer type](/docs/customer-types) or custom transaction limits as defined in the services agreement with Dwolla.
Minimum limit: \$0.01. | | currency | yes | string | Possible values: `USD` | ### Facilitator fee JSON object The facilitator fee is a feature allowing for a flat rate amount to be removed from a payment as a fee, and sent to the creator of the Dwolla application. The fee does not affect the original payment amount, and exists as a separate [Transfer resource](/docs/api-reference/transfers#transfer-resource) with a unique transfer ID. Within a transfer request you can specify an optional `fees` request parameter, which is an array of [fee objects](/docs/api-reference/transfers#facilitator-fee-json-object) that can represent many unique fee transfers. The `fees` array is supported on both ACH transfers and [Instant Payments](/docs/instant-payments) transfers (those using a `processingChannel.destination` of `instant` or `real-time-payments`). For more information on collecting fees on payments, reference the facilitator fee resource article.
| Parameter | Description | | --------- | ------------------------------------------------------------------------------------------------------------------------------------- | | \_links | Contains a `charge-to` JSON object with a link to the associated source or destination `Customer` resource. | | amount | Amount of fee to charge. An amount JSON object. [See above](https://developers.dwolla.com/api-reference/transfers#amount-json-object) | #### Facilitator fee example: ```bash theme={"dark"} "fees": [ { "_links": { "charge-to": { "href": "https://api-sandbox.dwolla.com/customers/d795f696-2cac-4662-8f16-95f1db9bddd8" } }, "amount": { "value": "4.00", "currency": "USD" } } ] ``` ### clearing JSON object The `clearing` object is used in tandem with our expedited transfer feature. This object does not need to be included if not using expedited transfers. Source specifies the clearing time for the source funding source involved in the transfer, and can be used to downgrade the clearing time from the default of Next-day ACH or to upgrade it to Same-day ACH debit. Destination specifies the clearing time for the destination funding source involved in the transfer, and can be used to upgrade the clearing time from the default of Standard ACH to Same-day ACH. The clearing request parameter is a premium feature available for Dwolla customers in the Scale pricing tier. Enabling Next-day ACH and Same-day ACH requires additional Dwolla approvals before getting started. Please contact sales or your account manager for more information on enabling this feature. | Parameter | Required | Type | Description | | ----------- | -------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | source | no | string | Represents a clearing object for debits into the Dwolla network.
Possible values: `standard`, `next-available`
`standard` - Used to downgrade the clearing time of debits from the default of Next-day ACH (if enabled) to Standard ACH.
`next-available` - Used to upgrade the clearing time of debits to Same-day ACH. | | destination | no | string | Represents a clearing object for credits out of the Dwolla network to a bank funding source.
Possible values: `next-available`
`next-available` - Used to upgrade the clearing time of credits to Same-day ACH. | #### Clearing examples: ##### Standard debit and Same-day credit (when Next-day is enabled) ```bash theme={"dark"} "clearing": { "source": "standard", "destination": "next-available" } ``` ##### Next-day debit and Same-day credit (when Next-day is enabled) ```bash theme={"dark"} "clearing": { "destination": "next-available" } ``` ##### Same-day debit and Same-day credit ```bash theme={"dark"} "clearing": { "source": "next-available", "destination": "next-available" } ``` ##### Same-day debit and Standard credit ```bash theme={"dark"} "clearing": { "source": "next-available" } ``` ### achDetails and addenda object **Note: This feature is only supported for business Customer records.**
The addendum record is used to provide additional information to the payment recipient about the payment. This value will be passed in on a transfer request and can be exposed on a Customer's bank statement. Addenda records provide a unique opportunity to supply your customers with more information about their transactions. Allowing businesses to include additional details about the transaction—such as invoice numbers—provides their end users with more information about the transaction in the comfort of their own banking application. ##### achDetails object | Parameter | Required | Type | Description | | ----------- | -------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | source | no | object | Represents information that is sent to a source/originating bank account along with a transfer. Include information within this JSON object for customizing details on ACH debit transfers. Can include an addenda JSON object. | | destination | no | object | Represents information that is sent to a destination/receiving bank account along with a transfer. Include information within this JSON object for customizing details on ACH credit transfers. Can include an addenda JSON object. | ##### addenda object | Parameter | Required | Type | Description | | --------- | -------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | addenda | no | object | An addenda object contains a `values` key where its value is an array containing a **single** string addenda value. Addenda record information is used for the purpose of transmitting transfer-related information from a business.
Addenda value must be less than or equal to 80 characters and can include spaces.
Acceptable characters are: a-Z, 0-9, and special characters `- _ . ~ ! * ' ( ) ; : @ & = + $ , / ? % # [ ]`.
*Transfers must be sent to/from a business entity's bank to guarantee addenda delivery.* | #### achDetails with addenda example: ```bash theme={"dark"} "achDetails": { "source": { "addenda": { "values": ["ABC123_AddendaValue"] } }, "destination": { "addenda": { "values": ["ZYX987_AddendaValue"] } } } ``` ### rtpDetails and fedNowDetails objects > Note: Instant Payments (RTP and FedNow) is a premium feature available for Dwolla customers. Enabling Instant Payments requires additional Dwolla approvals before getting started. Please [contact Sales](https://www.dwolla.com/contact?b=apidocs) or your account manager for more information on enabling this account feature. When retrieving a transfer from the API, the response will contain either an `rtpDetails` object or a `fedNowDetails` object depending on which payment network was used: * **`rtpDetails`** - Included when the transfer was sent via TCH's RTP Network * **`fedNowDetails`** - Included when the transfer was sent via FRB's FedNow Service Both objects have an identical structure and contain network-specific identifiers that appear once the credit entry clears into the destination bank account. Refer to our [Instant Payments developer concept article](/docs/instant-payments) to learn more about initiating Instant Payment credit transfers. When creating a transfer, use `instantDetails` (recommended) or `rtpDetails` in your request payload to pass remittance data. The `fedNowDetails` object only appears in API responses and cannot be used in transfer creation requests. ##### rtpDetails / fedNowDetails object | Parameter | Required | Type | Description | | ----------- | -------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | destination | no | object | Represents information that is sent to a destination/receiving bank account along with an Instant Payment credit transfer. Contains network-specific identifiers and optional remittance data. | ##### destination object | Parameter | Type | Description | | ------------------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | remittanceData | string | Contains a string value. Used for the purpose of transmitting Instant Payment transfer-related information to the recipient's bank account.
Remittance data value must be less than or equal to 140 characters and can include spaces. | | networkId | string | A unique identifier for the transfer within the payment network (RTP or FedNow). Appears when the credit entry clears into the destination bank account. | | endToEndReferenceId | string | An end-to-end reference identifier for the transfer. Appears when the credit entry clears into the destination bank account. | #### rtpDetails example (RTP Network response): ```json theme={"dark"} "rtpDetails": { "destination": { "remittanceData": "ABC_123 Remittance Data", "networkId": "20210617021214273T1BG27487110796028", "endToEndReferenceId": "E2E-RTP-20210617-001" } } ``` #### fedNowDetails example (FedNow Service response): ```json theme={"dark"} "fedNowDetails": { "destination": { "remittanceData": "ABC_123 Remittance Data", "networkId": "20240115123456789FEDNOW123456", "endToEndReferenceId": "E2E-FEDNOW-20240115-001" } } ``` ### instantDetails object (request only) The `instantDetails` object is the recommended way to provide remittance data when initiating an Instant Payment transfer. This object can be used in transfer creation requests regardless of whether the payment ultimately routes via RTP or FedNow. For backward compatibility, you can also use `rtpDetails` in transfer creation requests. Both `instantDetails` and `rtpDetails` are functionally equivalent for request payloads. However, we recommend using `instantDetails`. ##### instantDetails object | Parameter | Required | Type | Description | | ----------- | -------- | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | destination | no | object | Represents information that is sent to a destination/receiving bank account along with an Instant Payment credit transfer. Contains a key-value pair for `remittanceData`. | ##### destination object | Parameter | Required | Type | Description | | -------------- | -------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | remittanceData | no | string | Contains a string value. Used for the purpose of transmitting Instant Payment transfer-related information to the recipient's bank account.
Remittance data value must be less than or equal to 140 characters and can include spaces. | #### instantDetails example: ```json theme={"dark"} "instantDetails": { "destination": { "remittanceData": "ABC_123 Remittance Data" } } ``` # Cancel a transfer Source: https://developers.dwolla.com/docs/api-reference/transfers/cancel-a-transfer post /transfers/{id} Cancel a pending transfer by setting its status to cancelled. Only transfers in pending status can be cancelled before processing begins. Returns the updated transfer resource with cancelled status. Use this endpoint to stop a bank transfer from further processing. # Create an on-demand transfer authorization Source: https://developers.dwolla.com/docs/api-reference/transfers/create-an-on-demand-transfer-authorization post /on-demand-authorizations Create an on-demand transfer authorization that allows Customers to pre-authorize variable amount ACH transfers from their bank account for future payments. This authorization is used when creating Customer funding sources to enable flexible payment processing. Returns UI text elements including authorization body text and button text for display in your application's bank account addition flow. # Initiate a transfer Source: https://developers.dwolla.com/docs/api-reference/transfers/initiate-a-transfer post /transfers Initiate a transfer between funding sources from a Dwolla Account or API Customer resource. Supports ACH, Instant Payments (RTP/FedNow), Push-to-Debit Card, and wire transfers with optional expedited clearing, facilitator fees, metadata, and correlation IDs for enhanced traceability. Includes idempotency key support to prevent duplicate transfers and extensive customization options for addenda records and processing channels. Returns the location of the created transfer resource for tracking and management. # List and search transfers for a customer Source: https://developers.dwolla.com/docs/api-reference/transfers/list-and-search-transfers-for-a-customer get /customers/{id}/transfers Retrieve and search transfers for a specific Customer with comprehensive filtering and pagination support. Supports searching by customer details (name, email, business name), amount ranges, date ranges, transfer status, and correlation IDs for enhanced transaction discovery. Returns paginated transfer results including status, amounts, metadata, and links to source and destination funding sources. Use this endpoint for transaction history analysis and reconciliation purposes. # List fees for a transfer Source: https://developers.dwolla.com/docs/api-reference/transfers/list-fees-for-a-transfer get /transfers/{id}/fees Retrieve detailed fee information for a specific transfer by its unique identifier. Returns the total number of fees and individual fee transaction details including amounts, status, and links to source and destination accounts. # Retrieve a transfer Source: https://developers.dwolla.com/docs/api-reference/transfers/retrieve-a-transfer get /transfers/{id} Retrieve detailed information for a specific transfer by its unique identifier belonging to an Account or Customer. Returns transfer status, amount, creation date, clearing details, and links to source and destination funding sources for complete transaction tracking. Includes cancellation links when applicable and references to related funding transfers. Essential for monitoring transfer lifecycle and transaction reconciliation. # Retrieve a transfer failure reason Source: https://developers.dwolla.com/docs/api-reference/transfers/retrieve-a-transfer-failure-reason get /transfers/{id}/failure Retrieve detailed failure information for a failed bank or VAN transfer including the ACH return code, description, and explanation. Returns failure details with links to the failed funding source and associated Customer for comprehensive error analysis. Available only for transfers with failure status and accessed through the failure link from transfer retrieval. Critical for troubleshooting payment failures and understanding ACH return reasons. # Overview Source: https://developers.dwolla.com/docs/api-reference/webhook-subscriptions Webhook subscriptions help you follow the events in your application triggered with every POST request. Create, list, update, or remove them from your application using the Dwolla API. # Webhook subscriptions Create a webhook subscription to receive `POST` requests from Dwolla (called webhooks) when events associated with your application occur. [Webhooks](/docs/api-reference/webhooks) are sent to a URL which you provide when creating a webhook subscription. While we see most applications maintain one webhook subscription, you can have up to **ten** active webhook subscriptions in Sandbox, and up to **five** in Production at a time. Refer to the [events](/docs/api-reference/events) section for the complete list of events that trigger webhooks. To view example payloads for Customer related events, refer to the [Webhooks Events](/docs/webhook-events) resource within the Developer Docs. ### **Automatic pause of a webhook subscription** Dwolla will automatically pause subscribed webhook endpoints that are no longer reachable. The webhook subscription will be paused after **400 consecutive failures** and **24 hours since the last success**. This will help us ensure that unavailable endpoints don’t cause delays or issues in delivery of notifications for other API customers. Webhook subscriptions can be unpaused by calling [this endpoint](/docs/api-reference/webhook-subscriptions/update-a-webhook-subscription). ### Acknowledgement and retries When your application receives a [webhook](/docs/api-reference/webhooks), it should respond with a HTTP 2xx status code to indicate successful receipt. If Dwolla receives a status code greater than or equal to 3xx, or your application fails to respond within 10 seconds of the attempt, another attempt will be made. Dwolla will not follow redirects and will treat them as a failure. Dwolla will re-attempt delivery 8 times over the course of 72 hours according to the backoff schedule below. If a webhook was successfully received but you would like the information again, you can call [retrieve a webhook by its Id](/docs/api-reference/webhook-subscriptions/retrieve-a-webhook-subscription). ### Delivery rate Webhooks are delivered in near real-time as events tied to your application are created. If there is a large number of events created for your application within a short timeframe, Dwolla may deliver bursts of 10 concurrent webhook requests to your subscribed URL. We encourage applications to make their webhook handler do as little as possible and only perform a high level validation of the request. Once initial validation is performed, immediately acknowledge the webhook request and process it in the background later. If your subscribed webhook URL is unable to handle the volume of concurrent requests, please contact Dwolla developer support and the delivery rate can be adjusted. | Retry number | Interval (relative to last retry) | Interval (relative to original attempt) | | :----------: | :-------------------------------: | :-------------------------------------: | | 1 | 15 min | 15 min | | 2 | 45 min | 1 h | | 3 | 2 h | 3 h | | 4 | 3 h | 6 h | | 5 | 6 h | 12 h | | 6 | 12 h | 24 h | | 7 | 24 h | 48 h | | 8 | 24 h | 72 h | ### Webhook subscription resource | Parameter | Description | | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | id | Webhook subscription unique identifier. | | url | Subscribed url where Dwolla should deliver the webhook notification. | | paused | A boolean `true` or `false` value indicating if the webhook subscription is paused. A webhook subscription will be automatically paused after 400 consecutive failures. In addition, a subscription can be paused or unpaused by calling [this endpoint](/docs/api-reference/webhook-subscriptions/update-a-webhook-subscription) in the API. | | created | ISO-8601 timestamp | # Create a webhook subscription Source: https://developers.dwolla.com/docs/api-reference/webhook-subscriptions/create-a-webhook-subscription post /webhook-subscriptions Create a webhook subscription to deliver webhook notifications to a specified URL endpoint for your application. Requires a destination URL where Dwolla will send notifications and a secret key for webhook validation and security. Returns the location of the created subscription resource. Essential for establishing real-time event notifications and automated integrations with Dwolla's payment processing events. # Delete a webhook subscription Source: https://developers.dwolla.com/docs/api-reference/webhook-subscriptions/delete-a-webhook-subscription delete /webhook-subscriptions/{id} Delete a webhook subscription to permanently remove webhook notifications for your application. This action stops all future webhook deliveries and cannot be undone. Returns the deleted subscription resource for confirmation. Use this endpoint when webhook notifications are no longer needed or when cleaning up unused subscriptions. # List webhook subscriptions Source: https://developers.dwolla.com/docs/api-reference/webhook-subscriptions/list-webhook-subscriptions get /webhook-subscriptions Retrieve all webhook subscriptions that belong to an application including their configuration details and status. Returns subscription details including webhook endpoints, status, creation dates, and links to associated webhooks with total count. Essential for webhook management and monitoring subscription health. # List webhooks for a webhook subscription Source: https://developers.dwolla.com/docs/api-reference/webhook-subscriptions/list-webhooks-for-a-webhook-subscription get /webhook-subscriptions/{id}/webhooks Retrieve all fired webhooks for a specific webhook subscription with comprehensive filtering and pagination support. Returns webhook delivery history including topics, attempts, request/response details, and delivery status over a rolling 30-day period. Supports filtering by resource ID, date ranges, and pagination parameters for detailed webhook delivery analysis. Critical for debugging webhook delivery issues and monitoring event notification success rates. # Retrieve a webhook subscription Source: https://developers.dwolla.com/docs/api-reference/webhook-subscriptions/retrieve-a-webhook-subscription get /webhook-subscriptions/{id} Retrieve detailed information for a specific webhook subscription by its unique identifier. Returns subscription configuration including URL endpoint, creation date, and links to associated webhooks for comprehensive subscription management. Essential for monitoring webhook subscription status and accessing webhook delivery history. # Update a webhook subscription Source: https://developers.dwolla.com/docs/api-reference/webhook-subscriptions/update-a-webhook-subscription post /webhook-subscriptions/{id} Update a webhook subscription to pause or resume webhook delivery notifications. Allows toggling the paused status to temporarily stop webhook notifications without deleting the subscription. Returns the updated subscription resource with the new paused status. Use this endpoint to manage webhook delivery during maintenance or troubleshooting periods. # Overview Source: https://developers.dwolla.com/docs/api-reference/webhooks List, retrieve, or retry webhooks in your application using the Dwolla API. # Webhooks When a new [event](/docs/api-reference/events) is created, and there is an active [webhook subscription](/docs/api-reference/webhook-subscriptions), a new webhook is created in order to deliver that event. Attempted deliveries are recorded under the webhook's `attempts` property. Each attempt includes the recorded request and response of the delivery attempt. Webhooks are sent asynchronously and are not guaranteed to be delivered in order. We recommend that applications protect against duplicated events by [making event processing idempotent](/docs/working-with-webhooks#check-for-duplicate-events). ### Webhook resource | Parameter | Description | | -------------- | ----------------------------------------------------------------------- | | id | Webhook unique identifier | | topic | Webhook topic that denotes the type of action that occurred with Dwolla | | accountId | Account associated with the webhook notification | | eventId | Event id for this webhook | | subscriptionId | Webhook subscription id for this event | | attempts | Array of attempt JSON object | ### Attempts JSON object | Parameter | Description | | --------- | -------------------------------------- | | id | Unique id of webhook delivery attempt. | | request | Request JSON object | | response | Response JSON object | ### Request/response JSON object | Parameter | Description | | --------- | ---------------------------------------------------------------------------- | | created | ISO-8601 timestamp | | url | URL where data was sent to/received from | | headers | Array of objects with keys `name` and `value` representative of HTTP headers | | body | An Event for the webhook | # List retries for a webhook Source: https://developers.dwolla.com/docs/api-reference/webhooks/list-retries-for-a-webhook get /webhooks/{id}/retries Retrieve all retry attempts for a specific webhook including timestamps and delivery details. Returns a list of retry attempts with unique identifiers, timestamps, and links to the parent webhook with total count. Essential for tracking webhook delivery failures, analyzing retry patterns, and debugging webhook notification issues to ensure reliable event processing. # Retrieve a webhook Source: https://developers.dwolla.com/docs/api-reference/webhooks/retrieve-a-webhook get /webhooks/{id} Retrieve detailed information for a specific webhook by its unique identifier including delivery attempts and response data. Returns webhook details with topic, account information, delivery attempts containing request/response history, and links to subscription and retry resources. Essential for debugging webhook delivery issues, analyzing response data, and monitoring notification processing status. # Retry a webhook Source: https://developers.dwolla.com/docs/api-reference/webhooks/retry-a-webhook post /webhooks/{id}/retries Retry a webhook by its unique identifier to redeliver the notification to your endpoint. Creates a new retry attempt and returns the location of the new webhook resource. Essential for recovering from webhook delivery failures and ensuring reliable event notification processing in your application. # Balance Funding Source Source: https://developers.dwolla.com/docs/balance-funding-source Send to, receive from, or hold funds within the Dwolla network for client master accounts and Verified Customer accounts. ## Overview There are two types of [Funding Sources](/docs/api-reference/funding-sources) available within the Dwolla Platform: a bank account and a user’s Dwolla Balance. A bank account is commonly used as the source or destination for ACH transfers. The Dwolla `balance` is a Funding Source that can be utilized like a “wallet” for holding a stored value of USD funds. The Dwolla Balance is made available for account types that have completed [“KYC” requirements](https://www.dwolla.com/updates/guide-customer-identification-program-payments-api/), which includes clients of Dwolla and their end users that have been on-boarded as [Verified Customers](/docs/customer-types#verified-customer). All funds held in a Dwolla Balance are held by Dwolla’s [financial institution partner(s)](https://www.dwolla.com/legal/about-our-financial-institution-partner/) and not by Dwolla. What makes the Dwolla Balance useful in relation to the platform is that the funds are immediately available within the Dwolla network. This means the Dwolla Balance acts as a funding source associated directly with each Verified Customer within your application. Your Customer can only access their Dwolla Balance through your application. You must provide an easily accessible and accurate summary of the [total and available balance](#total-and-available-balance) as well as transaction history for the Customer’s Dwolla Balance. ## Functionality and benefits The Dwolla Balance allows for greater flexibility for your desired funds flow and delivers another efficient method to move funds. #### Transfers As a Funding Source, you and your end users can use the Dwolla Balance to: * Receive funds from a bank account into the Dwolla Balance * Send funds from the Dwolla Balance to a bank account * Make instant payment transfers between two Dwolla Balances * Keep funds accessible in a Dwolla Balance To learn more about how to initiate transfers with the Dwolla API, check out our [API Reference Docs](/docs/api-reference/transfers) #### Balance transfer timing In production, transfer timing will vary depending on whether the Dwolla Balance is specified as the `source` or `destination` of the bank transfer. If transferring between two Dwolla Balances, the funds will transfer instantly. | Bank to Balance | Balance to Bank | Balance to Balance | | ----------------- | ----------------- | ------------------ | | 3-4 Business Days | 1-2 Business Days | Instant | ## Viewing and displaying the balance The Dwolla Balance is a Funding Source that can be accessed when an account, your [Main Dwolla Account](/docs/api-reference/accounts) or a [Verified Customer account](/docs/customer-types#verified-customer), has successfully completed the identity verification process and has a status of `verified`. Because this funding source exists within the Dwolla Network, you can obtain the details by utilizing the Dwolla API to retrieve the account’s list of funding sources. #### Retrieve Dwolla Main Account Balance Funding Source To retrieve your Account ID, you will need to call the [root](/docs/api-reference/root) of the API. Check out our API Reference Docs to learn more about retrieving the Balance funding source for [your Dwolla Main Account](/docs/api-reference/accounts/list-funding-sources-for-an-account). ##### Example request and response (Main Dwolla Account) ```bash HTTP theme={"dark"} GET https://api.dwolla.com/accounts/ca32853c-48fa-40be-ae75-77b37504581b/funding-sources Accept: application/vnd.dwolla.v1.hal+json Authorization: Bearer pBA9fVDBEyYZCEsLf/wKehyh1RTpzjUj5KzIRfDi0wKTii7DqY ... { "_links": { "self": { "href": "https://api-sandbox.dwolla.com/accounts/ca32853c-48fa-40be-ae75-77b37504581b/funding-sources", "resource-type": "funding-source" } }, "_embedded": { "funding-sources": [ { "_links": { "self": { "href": "https://api-sandbox.dwolla.com/funding-sources/04173e17-6398-4d36-a167-9d98c4b1f1c3", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "funding-source" }, "account": { "href": "https://api-sandbox.dwolla.com/accounts/ca32853c-48fa-40be-ae75-77b37504581b", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "account" } }, "id": "04173e17-6398-4d36-a167-9d98c4b1f1c3", "status": "verified", "type": "bank", "bankAccountType": "checking", "name": "My Account - Checking", "created": "2017-09-25T20:03:41.000Z", "removed": false, "channels": [ "ach" ], "bankName": "First Midwestern Bank" }, { "_links": { "self": { "href": "https://api-sandbox.dwolla.com/funding-sources/b268f6b9-db3b-4ecc-83a2-8823a53ec8b7", }, "account": { "href": "https://api-sandbox.dwolla.com/accounts/ca32853c-48fa-40be-ae75-77b37504581b", }, "with-available-balance": { "href": "https://api-sandbox.dwolla.com/funding-sources/b268f6b9-db3b-4ecc-83a2-8823a53ec8b7", }, "balance": { "href": "https://api-sandbox.dwolla.com/funding-sources/b268f6b9-db3b-4ecc-83a2-8823a53ec8b7/balance", } }, "id": "b268f6b9-db3b-4ecc-83a2-8823a53ec8b7", "status": "verified", "type": "balance", "name": "Balance", "created": "2017-08-22T18:21:51.000Z", "removed": false, "channels": [] } ] } } ``` ```ruby list_account_funding_sources.rb theme={"dark"} # Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby account_url = 'https://api.dwolla.com/accounts/ca32853c-48fa-40be-ae75-77b37504581b' funding_sources = app_token.get "#{account_url}/funding-sources" funding_sources._embedded['funding-sources'][1].name # => "Balance" ``` ```javascript listAccountFundingSources.js theme={"dark"} var accountUrl = "https://api-sandbox.dwolla.com/accounts/ca32853c-48fa-40be-ae75-77b37504581b"; dwolla .get(`${accountUrl}/funding-sources`) .then((res) => res.body._embedded["funding-sources"][1].name); // => 'Balance' ``` ```python list_account_funding_sources.py theme={"dark"} # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python account_url = 'https://api.dwolla.com/accounts/ca32853c-48fa-40be-ae75-77b37504581b' funding_sources = app_token.get('%s/funding-sources' % account_url) funding_sources.body['_embedded']['funding-sources'][1]['name'] # => 'Balance' ``` ```php list_account_funding_sources.php theme={"dark"} getAccountFundingSources($accountUrl); $fundingSources->_embedded->{'funding-sources'}[1]->name; # => "Balance" ?> ``` #### Retrieve a Customer’s Dwolla Balance Funding Source Check out our API Reference Docs to learn more about retrieving the Dwolla Balance funding source for [your Verified Customers](/docs/api-reference/funding-sources/list-customer-funding-sources). ##### Example request and response (Customer) ```bash HTTP theme={"dark"} GET https://api-sandbox.dwolla.com/customers/5b29279d-6359-4c87-a318-e09095532733/funding-sources Accept: application/vnd.dwolla.v1.hal+json Authorization: Bearer pBA9fVDBEyYZCEsLf/wKehyh1RTpzjUj5KzIRfDi0wKTii7DqY ... { "_links": { "self": { "href": "https://api-sandbox.dwolla.com/customers/5b29279d-6359-4c87-a318-e09095532733/funding-sources" }, "customer": { "href": "https://api-sandbox.dwolla.com/customers/5b29279d-6359-4c87-a318-e09095532733" } }, "_embedded": { "funding-sources": [ { "_links": { "self": { "href": "https://api-sandbox.dwolla.com/funding-sources/ab9cd5de-9435-47af-96fb-8d2fa5db51e8" }, "customer": { "href": "https://api-sandbox.dwolla.com/customers/5b29279d-6359-4c87-a318-e09095532733" }, "with-available-balance": { "href": "https://api-sandbox.dwolla.com/funding-sources/ab9cd5de-9435-47af-96fb-8d2fa5db51e8" } }, "id": "ab9cd5de-9435-47af-96fb-8d2fa5db51e8", "status": "verified", "type": "balance", "name": "Balance", "created": "2015-10-02T21:00:28.153Z", "removed": false, "channels": [] }, { "_links": { "self": { "href": "https://api-sandbox.dwolla.com/funding-sources/98c209d3-02d6-4bee-bc0f-61e18acf0e33" }, "customer": { "href": "https://api-sandbox.dwolla.com/customers/5b29279d-6359-4c87-a318-e09095532733" } }, "id": "98c209d3-02d6-4bee-bc0f-61e18acf0e33", "status": "verified", "type": "bank", "bankAccountType": "checking", "name": "Jane Doe’s Checking", "created": "2015-10-02T22:03:45.537Z", "removed": false, "channels": [ "ach" ], "fingerprint": "4cf31392f678cb26c62b75096e1a09d4465a801798b3d5c3729de44a4f54c794" } ] } } ``` ```ruby list_customer_funding_sources.rb theme={"dark"} # Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby customer_url = 'https://api-sandbox.dwolla.com/customers/5b29279d-6359-4c87-a318-e09095532733' funding_sources = app_token.get "#{customer_url}/funding-sources" funding_sources._embedded['funding-sources'][0].name # => "Balance" ``` ```javascript listCustomerFundingSources.js theme={"dark"} var customerUrl = "https://api-sandbox.dwolla.com/customers/5b29279d-6359-4c87-a318-e09095532733"; dwolla .get(`${customerUrl}/funding-sources`) .then((res) => res.body._embedded["funding-sources"][0].name); // => 'Balance' ``` ```python list_customer_funding_sources.py theme={"dark"} # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python customer_url = 'https://api-sandbox.dwolla.com/customers/5b29279d-6359-4c87-a318-e09095532733' funding_sources = app_token.get('%s/funding-sources' % customer_url) funding_sources.body['_embedded']['funding-sources'][0]['name'] # => 'Balance' ``` ```php list_customer_funding_sources.php theme={"dark"} getCustomerFundingSources($customerUrl); $fundingSources->_embedded->{'funding-sources'}[0]->name; # => "Balance" ?> ``` ## Create Transfers using the Dwolla Balance In order to create a transfer using the Dwolla Balance, you will first need the Dwolla Balance funding-source ID. Check out the above section on [Viewing and Displaying the Balance](#viewing-and-displaying-the-balance) in order to learn how to retrieve this ID. Once you have the Balance ID, it can be used as the `source` or the `destination` in a [transfer](/docs/api-reference/transfers) request depending on if you want to send funds out of the Dwolla Balance or into the Dwolla Balance, respectively. ### Add funds into the Dwolla Balance There are several reasons you may want to pre-load a Dwolla Balance. Commonly, a pre-loaded Dwolla Balance is used to speed up payouts to Customers. According to the [transfer processing timing](/docs/transfer-processing-times#standard-ach-transfers) at Dwolla, standard transfers from a bank account into the Dwolla network takes 3-4 business days to settle, and transfers out of the Dwolla Network to a bank account take 1-2 business days to settle. If your use case involves creating payouts to your Customers and you do not want them to wait for 4-6 business days from the time of transfer creation to receive the funds, you can add funds into your Dwolla Balance in advance so that payouts to Customers will only take 1-2 business days from the time of transfer creation. When [creating a transfer](/docs/api-reference/transfers/initiate-a-transfer) to add funds into a Dwolla Balance, you will have to make sure to use the Balance funding-source ID in the `destination` URL. The `source` can be a bank account funding-source or even another Balance funding-source from which you want to debit the funds. ### Transfer funds out of the Dwolla Balance You can also create transfers from a Dwolla Balance out to another Dwolla Balance or an attached bank account funding-source. In the example above, this may be part of your application design to push funds out to Customers from your Dwolla Balance. It’s also possible that funds will unintentionally accumulate in your or a Verified Customer’s Balance if funds transferred through the Dwolla Network do not succeed in reaching the final destination source due to technical errors in your application or ACH Returns or Reversals (see the [transfer failure example](#transfer-failures) below for more details). You or your Customer may want to withdraw those accumulated funds into a bank account or send them to another Dwolla Balance. When [creating a transfer](/docs/api-reference/transfers/initiate-a-transfer) to send funds out from a Dwolla Balance, you will have to make sure to use the Balance funding-source ID in the `source` URL. The `destination` can be a bank account funding-source or even another Dwolla Balance funding-source to which you want to sends funds. ## Retrieve the balance Amount You can check the amount in a Dwolla Balance at any given time. You’ll want to be sure you and your end users know the amount available in your respective Dwolla Balances prior to sending funds. You must also be able to show the available balance at all times within your application to your Verified Customers. Check out our [API Reference Docs](/docs/api-reference/funding-sources/retrieve-funding-source-balance) to learn more. #### Total and Available Balance There are two different amounts returned in the API response when [retrieving a balance](/docs/api-reference/funding-sources/retrieve-funding-source-balance) which correspond to a `total` and `available` balance. **Note:** Unless your application utilizes [Labels](/docs/api-reference/labels) functionality, the amounts that are returned in both the balance and total object will be the same. Available balance can be accessed via the `balance` attribute, whereas total balance can be accessed via the `total` attribute within the Balance object. Both `balance` and `total` are JSON objects that contain key value pairs for `value` and `currency`. ##### Available Balance Available Balance means the amount readily available in a Verified Customer’s Dwolla Balance that can be sent, withdrawn, or labeled. The “Available Balance” does not include labeled funds. The amount of funds for the following actions are limited to the amount of the Available Balance: * Creating and adding funds to a new Label * Increasing an existing Label * Withdrawing funds * Available Balance transfers, i.e. send or withdraw If a Return occurs on a Verified Customer’s transfer sourced from a Dwolla Balance, that Return will only impact the Available Balance, but not the labeled funds. This could result in a negative Available Balance. If a Dwolla Balance has a negative Available Balance, funds cannot be transferred out of the Dwolla Balance, even if the Total Balance is positive. To resume use of the Dwolla Balance, funds from labels will need to be “un-labeled” by creating a label ledger entry or additional funds will need to be added to the Available Balance. ##### Total Balance Represents the Verified Customer Record’s total balance held in the Dwolla network. This includes both labeled funds and the Available Balance, i.e. both labeled and unlabeled funds. ##### Example request and response ```bash HTTP theme={"dark"} GET https://api-sandbox.dwolla.com/funding-sources/e5b8223f-08f7-4a7e-b952-88a773c0df61/balance Accept: application/vnd.dwolla.v1.hal+json Authorization: Bearer pBA9fVDBEyYZCEsLf/wKehyh1RTpzjUj5KzIRfDi0wKTii7DqY { "_links": { "self": { "href": "https://api-sandbox.dwolla.com/funding-sources/e5b8223f-08f7-4a7e-b952-88a773c0df61/balance", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "balance" }, "funding-source": { "href": "https://api-sandbox.dwolla.com/funding-sources/e5b8223f-08f7-4a7e-b952-88a773c0df61", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "funding-source" } }, "balance": { "value": "142.50", "currency": "USD" }, "total": { "value": "142.50", "currency": "USD" }, "lastUpdated": "2019-06-03T14:28:12.679Z" } ``` ```ruby retrieve_balance.rb theme={"dark"} funding_source_url = 'https://api-sandbox.dwolla.com/funding-sources/e5b8223f-08f7-4a7e-b952-88a773c0df61' # Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby funding_source = app_token.get "#{funding_source_url}/balance" ``` ```javascript retrieveBalance.js theme={"dark"} var fundingSourceUrl = "https://api-sandbox.dwolla.com/funding-sources/e5b8223f-08f7-4a7e-b952-88a773c0df61"; dwolla .get(`${fundingSourceUrl}/balance`) .then((res) => res.body.balance.amount); ``` ```python retrieve_balance.py theme={"dark"} # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python funding_source_url = 'https://api-sandbox.dwolla.com/funding-sources/e5b8223f-08f7-4a7e-b952-88a773c0df61' funding_source = app_token.get('%s/balance' % funding_source_url) ``` ```php retrieve_balance.php theme={"dark"} getBalance($fundingSourceUrl); ?> ``` ## Transfer failures The amount in a Dwolla Balance can be adjusted when a bank transfer failure occurs. In a transaction, if funds fail to process to a destination bank, they will be sent back to either the sender’s or the receiver’s Dwolla Balance, depending on which of the parties is a Verified Customer with a Dwolla Balance funding source. Funds can also be pulled from a Balance funding source in a transfer failure. In the case of transfer failure, funds may be pulled out of a Dwolla Balance to fund the transfer. In this case, responsibility falls on you to bring the Dwolla Balance back to zero. #### Transfer failure example Let’s illustrate a transfer failure with an example: Say you, Dwolla’s Client, are using Dwolla to send payments to your vendors, who are established as Receive-only Users. You send a payment from your bank account to the vendor’s bank account. * Source - Dwolla Master Account’s Bank Funding Source * Destination - Receive-only User’s Bank Funding Source In this example, our Receive-only User closes down their bank account while the transaction has a `pending` status. The funds cannot settle in a closed bank account, and the receiving bank will send an ACH return. This will send these funds back to your Dwolla Master Account Balance funding source, not all the way back to your bank account. You can decide whether to build your application to prompt your Receive-only User to add another bank account, or if you want to withdraw that failed transfer amount to your bank account and attempt another method to make the payment. For more information on possible transfer failure scenarios and the ACH Return Codes associated with each, check out our [Transfer Failures](/docs/transfer-failures) developer resource article. # Bank Funding Source Source: https://developers.dwolla.com/docs/bank-funding-source Learn about the different methods of adding and verifying a bank funding source to a Customer account. ## Overview There are many different ways for a Customer to add a bank account on Dwolla's platform. Choosing the approach that is ideal for your application depends on a number of factors, such as speed and/or user experience. As you think about the different method(s) to attach a bank account, you will need to consider which one best suits the use case of your business' application. #### Bank Addition Adding a bank account to a Customer account. If a business wants to allow for adding but not require verifying the bank account, the funding source will remain in an `unverified` status and will only be allowed to receive funds. #### Bank Verification Verifying a bank account. This step is required before a Customer can send funds using their Customer account. This can be accomplished either (1) at the time the bank account is added via [Open Banking](/docs/open-banking), [Secure Exchange](/docs/secure-exchange) or a Third-party Provider, or (2) after attaching a bank by verifying it with microdeposits. ## Bank Account Types Dwolla supports traditional `checking` and `savings` accounts to be added and verified by default. Other supported account types include `loan` and `general-ledger`, the use of which require review and approval by Dwolla. The table below details a high level overview of the different methods supported by Dwolla for adding a bank funding source. | Bank Addition Method | Automatic Verified Status (i.e. Eligible to Send Funds) | Information Required | U.S Bank Coverage | Supported Bank Account Types | Other features | | -------------------------------------------------------------------------------- | ------------------------------------------------------- | ------------------------------------------ | ----------------- | ------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------- | | [Dwolla API](/docs/api-reference/funding-sources/create-customer-funding-source) | No | Bank Account Number,
Routing number | 100% | Checking,
Savings,
Loan,
General Ledger | Optional bank verification with [Microdeposits](/docs/micro-deposit-verification) | | [Dwolla + Open Banking](/docs/open-banking) | Yes | Online banking credentials | \~85% | Checking, Savings | Secure, streamlined user experience.
Real-time account verification | | [Drop-in components](/docs/drop-in-components#create-a-funding-source) | No | Bank Account Number,
Routing number | 100% | Checking,
Savings | Tokenized data,
Optional bank verification with [Microdeposits](/docs/drop-in-components#verify-micro-deposits) | | [Dwolla + Secure Exchange solution](/docs/api-reference/exchanges) | Yes | Online banking credentials | \~85% | Checking,
Savings | [Tokenized integration](/docs/secure-exchange) | | Other Approved Third-party Provider | Yes, if part of third party offering | Variable | Variable | Variable | Variable | As you decide on the various methods for adding a funding-source, a good thing to keep in mind is that a transfer between two parties requires the sending party to have a verified bank account. Bank account verification prior to sending funds is required by the ACH network. ## Bank Addition + Verification methods ### Dwolla + Open Banking **The use case:** I want my users to add a bank account using Open Banking, leveraging real-time, secure access to their financial data directly from their bank. I want to access the capabilities of leading providers like Plaid and MX, for their seamless integration and UI experience, without having to manage multiple API integrations. I need instant account verification without the need for microdeposits or manual entry of account details. **Tell me more** Dwolla's pre-built connections streamline development by tailoring Open Banking features and functionality specifically for payments use cases, saving valuable development time and reducing complexity. This method supports real-time account verification, meaning that the bank account is automatically verified and ready to send and receive funds immediately after being added. **Ready to build?** If you're looking to integrate Open Banking into your application, start by exploring our Open Banking API documentation. You'll find everything you need to get set up. ### Add a Bank via the API and verify using Microdeposits **The use case:** I want to add a U.S. bank account by supplying an account and routing number. The bank will be unverified until microdeposits are [initiated](/docs/api-reference/funding-sources/initiate-or-verify-micro-deposits) and [verified](/docs/api-reference/funding-sources/initiate-or-verify-micro-deposits). This is commonly implemented for receive-only "payout" use cases where users are receiving funds, but not sending. **Tell me more** Using the Dwolla API you can add an unverified funding source to a [Customer](/docs/api-reference/funding-sources/create-customer-funding-source) as well as your [Main Account](/docs/api-reference/accounts/create-a-funding-source-for-an-account). Additionally, you can manually add an unverified bank funding source to your Master Dwolla Account or your end users directly from the [Dwolla Dashboard](https://www.dwolla.com/platform/dashboard/). An example form UI component to present to your users. **Ready to build?** Adding a bank with the Dwolla API is easy. Take a look at our documentation to learn more about this process. After adding a bank via the API, you can leave it unverified if you will only be sending funds to it. If you or your user will be sending funds from the bank account, you can verify it using microdeposits. ### Drop-in Components Dwolla's [drop-in components](/docs/drop-in-components) library offers a convenient way for you to integrate specific functionalities or streamline workflows within your web application, providing a swift path to integrating with the Dwolla Platform. Each component is self-contained, comprising HTML, CSS, and JavaScript, allowing for easy customization to match your application's look and feel. By using the [`dwolla-funding-source-create`](/docs/drop-in-components#create-a-funding-source) component, you can securely transmit sensitive data (bank account number and routing number) from your application's front-end to Dwolla without it passing through your server. **The use case:** I want my end users to attach a bank using their account and routing number with the added security of a tokenized implementation. I do not need the bank to be in a `verified` status, but may choose to utilize the [microdeposits verification](/docs/micro-deposit-verification) method, if needed. This is commonly implemented for businesses that are paying out to this bank account. **Tell me more** Use the [`dwolla-funding-source-create`](/docs/drop-in-components#create-a-funding-source) component in your application to collect bank account number, routing number, bank account type and a name. The form generated via tha drop-in component has built-in validation that will trigger an error if any of the required fields are invalid. Optionally, you can have the component initiate microdeposits as well. Once microdeposits have completed, you can use the [`dwolla-micro-deposits-verify`](/docs/drop-in-components#verify-micro-deposits) component to collected verification from your users, or build your own form to collect microdeposit amounts from Customers and use the [API endpoint](/docs/api-reference/funding-sources/initiate-or-verify-micro-deposits) directly to submit and verify the amounts. **Ready to build?** Follow the [Building with Drop-in components](/docs/drop-in-components/building-with-drop-ins) guide to get set up with using the library in your application. ## Other Considerations While these methods of bank addition and bank verification are fine to use separately, know that you are not limited to a single option. For instance, when you are using Plaid to verify Customers' bank accounts, there may be financial institutions not supported by Plaid. Implementing a fallback method is always an option (e.g. microdeposits). You are also not limited to these four methods. If you want to utilize a different third-party provider to verify a bank account before adding it to your Dwolla Customer, we can support providers who meet the requirements and are approved by the Dwolla team. Have a different preferred bank authentication provider you want to use? Reach out to our team to learn more on getting approved. ## Wrap-up Much like choosing the correct Customer type, considering and choosing the bank addition methods in your application will have a large impact in the functionality of your integration with Dwolla. From the functionality of verifying a bank account, to the preference in user experience, each method provides its own offerings to ensure your application can provide the desired level of service to your end users. # Business Verified Customer Source: https://developers.dwolla.com/docs/business-verified-customer Verifying a business' identity enables increased functionality on the platform. Create a business Customer that can receive funds and send up to $10,000 per transfer (default). ## Overview This guide will walk through the complete process of business verification within the Dwolla API, including creating a business verified Customer, handling verification statuses, adding beneficial owners, and certifying beneficial ownership. A business verified Customer represents a business that intends to send or receive funds on your platform. In any transaction, at least one party—either the sender or the receiver—must complete the identity verification process as outlined in this guide. The business verification process consists of the following key steps: Learn how to create a business verified Customer in Dwolla, including required information for different business types and how to submit the initial verification request. Understand the possible verification statuses, how to handle retry and document requests, and what to do if additional information is needed for verification. Add and verify beneficial owners for your business Customer, including required information and how to check their verification status. Certify that all beneficial owner information is correct to fulfill compliance requirements and enable your business Customer to send funds. ### Key Terminology * **Account Admin** - The representative creating the business verified Customer on behalf of the business and Controller. * **Controller** - Any natural individual who holds significant responsibilities to control, manage, or direct a company or other corporate entity (i.e. CEO, CFO, General Partner, President, etc). A company may have more than one controller, but only one controller's information must be collected. * **Beneficial owner** - Any natural person who, directly or indirectly, owns 25% or more of the equity interests of the company. * **Beneficial ownership certification** - An action taken by the Account Admin to confirm that the information provided is correct. * **EIN (Employer Identification Number)** - A unique identification number that is assigned to a business entity so that they can easily be identified by the Internal Revenue Service. # Step 1 - Creating a Business Verified Customer Creating a business verified Customer will require you to provide information about the business entity as well as a Controller, if required. #### Business Verified Customer Quick Guide | Business Structure | Dwolla `businessType` Value | Controller Required? | Add Beneficial Owners? | Certify Beneficial Ownership? | | ---------------------------- | --------------------------- | -------------------- | ---------------------- | ----------------------------- | | Sole proprietorships | `soleProprietorship` | No | No (exempt) | No (exempt) | | Unincorporated association | `soleProprietorship` | No | No (exempt) | No (exempt) | | Trust | `soleProprietorship` | No | No (exempt) | No (exempt) | | Corporation | `corporation` | Yes | Yes (if owns 25%+) | Yes | | Publicly traded corporations | `corporation` | Yes | No (exempt) | Yes | | Non-profits | `corporation` or `llc` | Yes | No (exempt) | Yes | | LLCs | `llc` | Yes | Yes (if owns 25%+) | Yes | | Partnerships, LP's, LLP's | `partnership` | Yes | Yes (if owns 25%+) | Yes | There are two types of business verified Customers that you can create, based on if they are required to add information on the Controller or not. ## Create a business verified Customer with no Controller Follow these steps to create a business verified Customer where `"businessType": "soleProprietorship"` ##### Events As a developer, you can expect these events to be triggered when a business verified Customer is successfully created and systematically verified: 1. `customer_created` 2. `customer_verified` #### What parties are identity verified by Dwolla? | Business Type | Business Entity | Controller | Business Owner | | ----------------------- | ----------------- | ---------- | ----------------- | | **Sole Proprietorship** | Identity verified | N/A | Identity verified | In order to create a business verified Customer with `businessType` of `soleProprietorship`, Dwolla only requires information to verify the identity of the business and the Account Admin. ### Sole Propreietorship - Request parameters | Parameter | Required | Type | Description | | ---------------------- | -------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | firstName | yes | string | The legal first name of the Business Owner. Must be **≤ 50 characters** and cannot include special characters ``[<>="`!?%~${}\]``. | | lastName | yes | string | The legal last name of the Business Owner. Must be **≤ 50 characters** and cannot include special characters ``[<>="`!?%~${}\]``. | | email | yes | string | Email address of the Business Owner. Must be a **valid email format** (e.g., `example@domain.com`). | | ipAddress | no | string | IP address of the registering user is **recommended**. | | type | yes | string | Must be **`business`**. | | dateOfBirth | yes | string | The date of birth of the Business Owner.
**Format:** `YYYY-MM-DD`
**Age Range:** Must be between **18 to 125 years**. | | ssn | yes | string | Last four or full 9 digits of the Business Owner's Social Security Number. Must contain only numbers (e.g., `1234` or `123456789`). | | address1 | yes | string | Street number and street name of the business' physical address. Must be **≤ 50 characters**, contain no special characters ``[<>="`!?%~${}\]``, and **cannot be a PO Box**. | | address2 | no | string | Apartment, floor, suite, bldg. # of business' physical address. Must be **≤ 50 characters** and cannot include special characters ``[<>="`!?%~${}\]``. | | city | yes | string | City of the business' physical address. Must be **≤ 50 characters** and cannot contain numbers or special characters ``[<>="`!?%~${}\]``. | | state | yes | string | **US Persons** - Must be a **valid two-letter US state/territory abbreviation** (e.g., `CA`).
Reference: [US Postal Service guide](https://pe.usps.com/text/pub28/28apb.htm). | | postalCode | yes | string | Business' **US ZIP Code**. Must be either **5 digits** (e.g., `50314`) or **ZIP+4** (e.g., `50314-1234`). | | businessName | yes | string | Registered business name. Must be **≤ 255 characters** and cannot include special characters ``[<>="`!?%~${}\]``. | | doingBusinessAs | no | string | Preferred business name – also known as a **fictitious name** or **assumed name**. Must be **≤ 255 characters** and cannot include special characters ``[<>="`!?%~${}\]``. | | businessType | yes | string | Business structure. Must be **`soleProprietorship`**. | | businessClassification | yes | string | The **industry classification ID** corresponding to the Customer's business.
Reference: [Business Classifications](/docs/api-reference/customers/list-business-classifications). | | ein | no | string | Employer Identification Number (**EIN**). **Optional** for `soleProprietorship` business Customers. Must be **9 numeric characters** (e.g., `123456789`). | | website | no | string | Business' website. Must be a **valid URL** (e.g., `https://www.domain.com`). | | phone | no | string | Business's **10-digit phone number**. Must contain **only numbers** (e.g., `3334447777`). **No hyphens, spaces, or separators**. | #### Sole Propreietorship - Request and response ```bash HTTP theme={"dark"} POST https://api-sandbox.dwolla.com/customers Content-Type: application/vnd.dwolla.v1.hal+json Accept: application/vnd.dwolla.v1.hal+json Authorization: Bearer 0Sn0W6kzNic+oWhDbQcVSKLRUpGjIdl/YyrHqrDDoRnQwE7Q { "firstName": "Business", "lastName": "Owner", "email": "solePropBusiness@email.com", "ipAddress": "143.156.7.8", "type": "business", "dateOfBirth": "1980-01-31", "ssn": "6789", "address1": "99-99 33rd St", "city": "Some City", "state": "NY", "postalCode": "11101", "businessClassification": "9ed3f670-7d6f-11e3-b1ce-5404a6144203", "businessType": "soleProprietorship", "businessName":"Jane Corp", "ein":"00-0000000" } HTTP/1.1 201 Created Location: https://api-sandbox.dwolla.com/customers/62c3aa1b-3a1b-46d0-ae90-17304d60c3d5 ``` ```php create_business_customer.php theme={"dark"} create([ 'firstName' => 'Business', 'lastName' => 'Owner', 'email' => 'solePropBusiness@email.com', 'ipAddress' => '143.156.7.8', 'type' => 'business', 'dateOfBirth' => '1980-01-31', 'ssn' => '6789', 'address1' => '99-99 33rd St', 'city' => 'Some City', 'state' => 'NY', 'postalCode' => '11101', 'businessClassification' => '9ed3f670-7d6f-11e3-b1ce-5404a6144203', 'businessType' => 'soleProprietorship', 'businessName' => 'Jane Corp', 'ein' => '00-0000000']); ?> ``` ```ruby create_business_customer.rb theme={"dark"} # Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby (Recommended) request_body = { :firstName => 'Business', :lastName => 'Owner', :email => 'solePropBusiness@email.com', :ipAddress => '143.156.7.8', :type => 'business', :dateOfBirth => '1980-01-31', :ssn => '6789', :address1 => '99-99 33rd St', :city => 'Some City', :state => 'NY', :postalCode => '11101', :businessClassification => '9ed3f670-7d6f-11e3-b1ce-5404a6144203', :businessType => 'soleProprietorship', :businessName => 'Jane Corp', :ein => '00-0000000' } customer = app_token.post "customers", request_body customer.response_headers[:location] # => "https://api-sandbox.dwolla.com/customers/62c3aa1b-3a1b-46d0-ae90-17304d60c3d5" ``` ```python create_business_customer.py theme={"dark"} # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python (Recommended) request_body = { 'firstName': 'Business', 'lastName': 'Owner', 'email': 'solePropBusiness@email.com', 'ipAddress': '143.156.7.8', 'type': 'business', 'dateOfBirth': '1980-01-31', 'ssn': '6789', 'address1': '99-99 33rd St', 'city': 'Some City', 'state': 'NY', 'postalCode': '11101', 'businessClassification': '9ed3f670-7d6f-11e3-b1ce-5404a6144203', 'businessType': 'soleProprietorship', 'businessName': 'Jane Corp', 'ein': '00-0000000' } customer = app_token.post('customers', request_body) customer.headers['location'] # => 'https://api-sandbox.dwolla.com/customers/62c3aa1b-3a1b-46d0-ae90-17304d60c3d5' ``` ```javascript create_business_customer.js theme={"dark"} var requestBody = { firstName: "Business", lastName: "Owner", email: "solePropBusiness@email.com", ipAddress: "143.156.7.8", type: "business", dateOfBirth: "1980-01-31", ssn: "6789", address1: "99-99 33rd St", city: "Some City", state: "NY", postalCode: "11101", businessClassification: "9ed3f670-7d6f-11e3-b1ce-5404a6144203", businessType: "soleProprietorship", businessName: "Jane Corp", ein: "00-0000000", }; dwolla .post("customers", requestBody) .then((res) => res.headers.get("location")); // => 'https://api-sandbox.dwolla.com/customers/62c3aa1b-3a1b-46d0-ae90-17304d60c3d5' ``` ## Create a business verified Customer with controller #### Events As a developer, you can expect these events to be triggered when a business verified Customer is successfully created and systematically verified: 1. `customer_created` 2. `customer_verified` #### What parties are identity verified by Dwolla? | Business Type | Business Entity | Controller | Account Admin | | --------------- | ----------------- | ----------------- | --------------------- | | **Corporation** | Identity verified | Identity verified | Not identity verified | | **Partnership** | Identity verified | Identity verified | Not identity verified | | **LLC** | Identity verified | Identity verified | Not identity verified | For all other `businessType`'s other than `soleProprietorship`, your Customer will need to provide more information for verification. In order to create a business verified Customer with a controller, Dwolla requires information on an account admin, the business, and the controller. Your business verified Customer account admin will act as the agent signing up on behalf of the business. When going through the Customer creation flow, your business verified Customer account admin will only need information on one controller to successfully complete the signup flow. ### Corporation, partnership, llc - Request parameters | Parameter | Required | Type | Description | | ---------------------- | ----------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | firstName | yes | string | The legal first name of the **Account Admin** or individual signing up the business verified Customer. Must be **≤ 50 characters** and cannot include special characters ``[<>="`!?%~${}\]``. | | lastName | yes | string | The legal last name of the **Account Admin** or individual signing up the business verified Customer. Must be **≤ 50 characters** and cannot include special characters ``[<>="`!?%~${}\]``. | | email | yes | string | Email address of the **Account Admin** creating and managing the Customer account. Must be a **valid email format** (e.g., `example@domain.com`). | | ipAddress | no | string | IP address of the registering user. **Recommended** but not required. | | type | yes | string | Must be **`business`**. | | address1 | yes | string | Street number and street name of the business' physical address. Must be **≤ 50 characters**, contain no special characters ``[<>="`!?%~${}\]``, and **cannot be a PO Box**. | | address2 | no | string | Apartment, floor, suite, bldg. # of business' physical address. Must be **≤ 50 characters** and cannot include special characters ``[<>="`!?%~${}\]``. | | city | yes | string | City of the business' physical address. Must be **≤ 50 characters** and cannot contain numbers or special characters ``[<>="`!?%~${}\]``. | | state | yes | string | **US Persons** - Must be a **valid two-letter US state/territory abbreviation** (e.g., `CA`).
Reference: [US Postal Service guide](https://pe.usps.com/text/pub28/28apb.htm). | | postalCode | yes | string | Business' **US ZIP Code**. Must be either **5 digits** (e.g., `50314`) or **ZIP+4** (e.g., `50314-1234`). | | businessName | yes | string | Registered business name. Must be **≤ 255 characters** and cannot include special characters ``[<>="`!?%~${}\]``. | | doingBusinessAs | no | string | Preferred business name – also known as a **fictitious name** or **assumed name**. Must be **≤ 255 characters** and cannot include special characters ``[<>="`!?%~${}\]``. | | businessType | yes | string | Business structure. **Accepted values:** `corporation`, `llc`, `partnership`. | | businessClassification | yes | string | The **industry classification ID** corresponding to the Customer's business.
Reference: [Business Classifications](/docs/api-reference/customers/list-business-classifications). | | ein | yes | string | **Employer Identification Number (EIN)**. Must be **9 numeric characters** (e.g., `123456789`).
**Note:** If `businessType` is `soleProprietorship`, then `ein` and `controller` can be omitted. | | website | no | string | Business' website. Must be a **valid URL** (e.g., `https://www.domain.com`). | | phone | no | string | Business's **10-digit phone number**. Must contain **only numbers** (e.g., `3334447777`). **No hyphens, spaces, or separators**. | | controller | conditional | object | A **Controller JSON object**.
**Required** unless `businessType` is `soleProprietorship`. | ##### Controller JSON object | Parameter | Required | Type | Description | | ----------- | ----------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | firstName | yes | string | The **legal first name** of the Controller. Must be **≤ 50 characters** and cannot include special characters ``[<>="`!?%~${}\]``. | | lastName | yes | string | The **legal last name** of the Controller. Must be **≤ 50 characters** and cannot include special characters ``[<>="`!?%~${}\]``. | | title | yes | string | **Job title** of the Controller. Examples: `Chief Financial Officer`, `Managing Director`. Must be **≤ 100 characters** and cannot contain numbers or special characters ``[<>="`!?%~${}\]``. | | dateOfBirth | yes | string | **Controller's date of birth** in `YYYY-MM-DD` format. Must be between **18 to 125 years old**. | | ssn | conditional | string | **Last four digits** or **full 9-digit** Social Security Number (SSN).
**Required for US residents**.
If omitted, a **passport object** is required. | | address | yes | object | A **Controller Address JSON Object** containing the Controller's full physical address.
**Reference:** [Controller Address JSON Object](#controller-address-json-object). | | passport | conditional | object | A **Controller Passport JSON Object**.
**Required for non-US individuals**. Includes **Passport Identification Number** and **Country**.
**Reference:** [Controller Passport JSON Object](#controller-passport-json-object). | ##### Controller address JSON object | Parameter | Required | Type | Description | | ------------------- | ----------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | address1 | yes | string | **Street number and name** of Controller's physical address.
**Must be ≤ 50 characters**.
**Cannot contain special characters** ``[<>="`!?%~${}\]``.
**PO Boxes are not allowed**. | | address2 | no | string | **Apartment, floor, suite, or building number** of Controller's physical address.
**Must be ≤ 50 characters**.
**Cannot contain special characters** ``[<>="`!?%~${}\]``.
**PO Boxes are not allowed**. | | address3 | no | string | **Third line of the address**, if applicable.
**Must be ≤ 50 characters**.
**Cannot contain special characters** ``[<>="`!?%~${}\]``.
**PO Boxes are not allowed**. | | city | yes | string | **City name** of Controller's physical address.
**Must be ≤ 50 characters**.
**Cannot contain numbers or special characters** ``[<>="`!?%~${}\]``. | | stateProvinceRegion | yes | string | **US Persons** - **Two-letter US state abbreviation**. See the [US Postal Service guide](https://pe.usps.com/text/pub28/28apb.htm).
**Non-US Persons** - **Two-letter ISO abbreviation for state, province, or region**. See the [ISO guide](https://en.wikipedia.org/wiki/ISO_3166-1).
**If a country does not have a two-letter abbreviation for a state/province, use the country's two-letter ISO code instead**.
**Must be uppercase** (e.g., `CA`). | | postalCode | conditional | string | **US Persons** - **Must provide a 5-digit ZIP code** (e.g., `12345`) or **ZIP+4 code** (e.g., `12345-6789`).
**Non-US Persons** - Optional. Can include alphanumeric postal codes where applicable. | | country | yes | string | **Two-letter ISO country code** (e.g., `US` for United States, `CA` for Canada).
Reference the [ISO country codes list](https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes). | ##### Controller passport JSON object | Parameter | Required | Type | Description | | --------- | ----------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------ | | number | conditional | string | **Required** if the controller is a **non-US person** and does **not** have a Social Security Number (SSN).
**Must be ≤ 255 characters**. | | country | conditional | string | **Country where the passport was issued**.
**Must be a two-letter ISO country code** (e.g., `GB` for United Kingdom, `IN` for India). | Once you submit this request, Dwolla will perform some initial validation to check for formatting issues such as an invalid date of birth, invalid email format, etc. If successful, the response will be a HTTP 201/Created with the URL of the new Customer resource contained in the Location header. #### Business with Controller - Request and response ```bash HTTP theme={"dark"} POST https://api-sandbox.dwolla.com/customers Content-Type: application/vnd.dwolla.v1.hal+json Accept: application/vnd.dwolla.v1.hal+json Authorization: Bearer 0Sn0W6kzNic+oWhDbQcVSKLRUpGjIdl/YyrHqrDDoRnQwE7Q { "firstName": "Account", "lastName": "Admin", "email": "accountAdmin@email.com", "ipAddress": "143.156.7.8", "type": "business", "address1": "99-99 33rd St", "city": "Some City", "state": "NY", "postalCode": "11101", "controller": { "firstName": "John", "lastName": "Controller", "title": "CEO", "ssn": "6789", "dateOfBirth": "1980-01-31", "address": { "address1": "1749 18th st", "address2": "apt 12", "city": "Des Moines", "stateProvinceRegion": "IA", "postalCode": "50266", "country": "US" } }, "businessClassification": "9ed3f670-7d6f-11e3-b1ce-5404a6144203", "businessType": "llc", "businessName":"Jane Corp", "ein":"00-0000000" } HTTP/1.1 201 Created Location: https://api-sandbox.dwolla.com/customers/62c3aa1b-3a1b-46d0-ae90-17304d60c3d5 ``` ```php create_business_customer.php theme={"dark"} create([ 'firstName' => 'Account', 'lastName' => 'Admin', 'email' => 'accountAdmin@email.com', 'type' => 'business', 'address1' => '99-99 33rd St', 'city' => 'Some City', 'state' => 'NY', 'postalCode' => '11101', 'controller' => [ 'firstName' => 'John', 'lastName'=> 'Controller', 'title' => 'CEO', 'dateOfBirth' => '1990-01-31', 'ssn' => '1234', 'address' => [ 'address1' => '18749 18th st', 'address2' => 'apt 12', 'city' => 'Des Moines', 'stateProvinceRegion' => 'IA', 'postalCode' => '50265', 'country' => 'US' ], ], 'phone' => '5554321234', 'businessClassification' => '9ed3f670-7d6f-11e3-b1ce-5404a6144203', 'businessType' => 'llc', 'businessName' => 'Jane Corp', 'ein' => '00-0000000']); ?> ``` ```ruby create_business_customer.rb theme={"dark"} # Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby (Recommended) request_body = { :firstName => 'Account', :lastName => 'Admin', :email => 'accountAdmin@email.com', :type => 'business', :address1 => '99-99 33rd St', :city => 'Some City', :state => 'NY', :postalCode => '11101', :controller => { :firstName => 'John', :lastName => 'Controller', :title => 'CEO', :dateOfBirth => '1980-01-31', :ssn => '1234', :address => { :address1 => '1749 18th st', :address2 => 'apt 12', :city => 'Des Moines', :stateProvinceRegion => 'IA', :postalCode => '50266', :country => 'US', } }, :businessClassification => '9ed38155-7d6f-11e3-83c3-5404a6144203', :businessType => 'llc', :businessName => 'Jane Corp', :ein => '12-3456789' } customer = app_token.post "customers", request_body customer.response_headers[:location] # => "https://api-sandbox.dwolla.com/customers/62c3aa1b-3a1b-46d0-ae90-17304d60c3d5" ``` ```python create_business_customer.py theme={"dark"} # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python (Recommended) request_body = { 'firstName': 'Account', 'lastName': 'Admin', 'email': 'accountAdmin@email.com', 'type': 'business', 'address1': '99-99 33rd St', 'city': 'Some City', 'state': 'NY', 'postalCode': '11101', 'controller': { 'firstName': 'John', 'lastName': 'Controller', 'title': 'CEO', 'dateOfBirth': '1980-01-31', 'ssn': '1234', 'address': { 'address1': '1749 18th st', 'address2': 'apt12', 'city': 'Des Moines', 'stateProvinceRegion': 'IA', 'postalCode': '50266', 'country': 'US' } }, 'businessClassification': '9ed38155-7d6f-11e3-83c3-5404a6144203', 'businessType': 'llc', 'businessName': 'Jane Corp', 'ein': '12-3456789' } customer = app_token.post('customers', request_body) customer.headers['location'] # => 'https://api-sandbox.dwolla.com/customers/62c3aa1b-3a1b-46d0-ae90-17304d60c3d5' ``` ```javascript createBusinessCustomer.js theme={"dark"} var requestBody = { firstName: "Account", lastName: "Admin", email: "accountAdmin@email.com", type: "business", address1: "99-99 33rd St", city: "Some City", state: "NY", postalCode: "11101", controller: { firstName: "John", lastName: "Controller", title: "CEO", dateOfBirth: "1980-01-31", ssn: "1234", address: { address1: "1749 18th st", address2: "apt 12", city: "Des Moines", stateProvinceRegion: "IA", postalCode: "50266", country: "US", }, }, businessClassification: "9ed38155-7d6f-11e3-83c3-5404a6144203", businessType: "llc", businessName: "Jane Corp", ein: "12-3456789", }; dwolla .post("customers", requestBody) .then((res) => res.headers.get("location")); // => 'https://api-sandbox.dwolla.com/customers/62c3aa1b-3a1b-46d0-ae90-17304d60c3d5' ``` ## Check the status of the business Customer You have created a business verified Customer; however, the successful creation of a business verified Customer doesn't necessarily mean the Customer account is verified. Businesses may need to provide additional information to help verify their identity. It is important to check the status of the business Customer to determine if additional documentation is needed. #### Request and response ```bash HTTP [expandable] theme={"dark"} GET https://api-sandbox.dwolla.com/customers/62c3aa1b-3a1b-46d0-ae90-17304d60c3d5 Accept: application/vnd.dwolla.v1.hal+json Authorization: Bearer pBA9fVDBEyYZCEsLf/wKehyh1RTpzjUj5KzIRfDi0wKTii7DqY ... { "_links": { "verify-beneficial-owners": { "href": "https://api-sandbox.dwolla.com/customers/d56c07fa-3832-427d-bb88-a9eb2d375c14/beneficial-owners", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "beneficial-owner" }, "beneficial-owners": { "href": "https://api-sandbox.dwolla.com/customers/d56c07fa-3832-427d-bb88-a9eb2d375c14/beneficial-owners", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "beneficial-owner" }, "deactivate": { "href": "https://api-sandbox.dwolla.com/customers/d56c07fa-3832-427d-bb88-a9eb2d375c14", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "customer" }, "self": { "href": "https://api-sandbox.dwolla.com/customers/d56c07fa-3832-427d-bb88-a9eb2d375c14", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "customer" }, "receive": { "href": "https://api-sandbox.dwolla.com/transfers", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "transfer" }, "edit-form": { "href": "https://api-sandbox.dwolla.com/customers/d56c07fa-3832-427d-bb88-a9eb2d375c14", "type": "application/vnd.dwolla.v1.hal+json; profile=\"https://github.com/dwolla/hal-forms\"", "resource-type": "customer" }, "edit": { "href": "https://api-sandbox.dwolla.com/customers/d56c07fa-3832-427d-bb88-a9eb2d375c14", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "customer" }, "certify-beneficial-ownership": { "href": "https://api-sandbox.dwolla.com/customers/d56c07fa-3832-427d-bb88-a9eb2d375c14/beneficial-ownership", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "beneficial-ownership" }, "funding-sources": { "href": "https://api-sandbox.dwolla.com/customers/d56c07fa-3832-427d-bb88-a9eb2d375c14/funding-sources", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "funding-source" }, "transfers": { "href": "https://api-sandbox.dwolla.com/customers/d56c07fa-3832-427d-bb88-a9eb2d375c14/transfers", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "transfer" } }, "id": "d56c07fa-3832-427d-bb88-a9eb2d375c14", "firstName": "Account", "lastName": "Admin", "email": "accountAdmin@email.com", "type": "business", "status": "verified", "created": "2018-04-26T19:11:41.290Z", "address1": "99-99 33rd St", "city": "Some City", "state": "NY", "postalCode": "11101", "businessName": "Jane Corp", "controller": { "firstName": "John", "lastName": "Controller", "title": "CEO", "address": { "address1": "1749 18th st", "address2": "apt 12", "city": "Des Moines", "stateProvinceRegion": "IA", "country": "US", "postalCode": "50266" } }, "businessType": "llc", "businessClassification": "9ed3f670-7d6f-11e3-b1ce-5404a6144203" } ``` ```php retrieve_customer.php theme={"dark"} getCustomer($customerUrl); $customer->status; # => "verified" ?> ``` ```ruby retrieve_customer.rb theme={"dark"} # Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby (Recommended) customer_url = 'https://api-sandbox.dwolla.com/customers/62c3aa1b-3a1b-46d0-ae90-17304d60c3d5' customer = app_token.get customer_url customer.status # => "verified" ``` ```python retrieve_customer.py theme={"dark"} # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python (Recommended) customer_url = 'https://api-sandbox.dwolla.com/customers/62c3aa1b-3a1b-46d0-ae90-17304d60c3d5' customer = app_token.get(customer_url) customer.body['status'] ``` ```javascript retrieveCustomer.js theme={"dark"} var customerUrl = "https://api-sandbox.dwolla.com/customers/62c3aa1b-3a1b-46d0-ae90-17304d60c3d5"; dwolla.get(customerUrl).then((res) => res.body.status); // => 'verified' ``` You will want to ensure that both your Controller and your Business have been verified, as the Customer will be unable to send or receive funds until then. If the Customer is in `retry` or `document` status, head to the next step to learn how to handle these statuses. # Step 2 - Handling Business Verified Customer Statuses You have successfully created a business verified Customer; however, there are cases where Dwolla will need more information to fully verify the identity of the Controller and/or Business. Read on to learn more. If your Business and Controller are already identity verified, you can skip to the next step adding beneficial owners to continue with your business verified Customer onboarding. #### Verification statuses and corresponding events As a developer, you will want to handle the various Customer statuses that can be returned. | Customer status | Event Topic Name(s) | Transaction restricted? | Description | | --------------- | ------------------------------------------------------------------------------------- | ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | verified | customer\_verified | No | The identifying information submitted was sufficient in verifying the Customer account. | | retry | customer\_reverification\_needed
customer\_address\_verification\_failed | Yes - Cannot send funds | The initial identity verification attempt failed because the information provided did not satisfy Dwolla's verification check. You can make one additional attempt by changing some or all the attributes of the existing Customer with a POST request. All fields are required on the retry attempt. If the additional attempt fails, the resulting status will be either `document` or `suspended`. | | document | customer\_verification\_document\_needed
customer\_address\_verification\_failed | Yes - Cannot send funds | Dwolla requires additional documentation to identify the Customer in the document status. Once a document is uploaded it will be reviewed for verification. | | suspended | customer\_suspended
customer\_verification\_pending\_review | Yes - Cannot send or receive funds | The Customer is suspended and may neither send nor receive funds. This may also indicate that the Customer's verification requires manual review by Dwolla (often corresponding to the AdditionalReviewRequired directive), or that the account has been suspended for other reasons. Contact Account Management for more information or await further review. | ## Understanding Verification Directives When further action is required for a Business Verified Customer to become verified, the Dwolla API includes Verification Directives in the `_embedded.errors` array of the [Retrieve a Customer](/docs/api-reference/customers/retrieve-a-customer) response. These directives explain the outstanding requirements for verification, provide actionable instructions, and include hypermedia links (`_links`) to enable resolution. By parsing and utilizing these directives, you can build a dynamic and user-friendly interface that guides your end-users through the necessary steps, minimizing onboarding friction and reducing support overhead. See how to simulate these verification directives in the Sandbox testing guide. #### Structure of a Verification Directive Each object within the `_embedded.errors` array represents a single Verification Directive and follows this structure: ```json theme={"dark"} { "code": "ErrorCode", "message": "Human-readable explanation and instructions for resolution.", "_links": { "action-link": { // e.g., "update-customer", "upload-ein-document" "href": "URL to perform the required action", "type": "application/vnd.dwolla.v1.hal+json" } } } ``` * **code**: A specific, machine-readable string identifying the type of verification issue. Use this code in your application logic to handle different error scenarios. * **message**: A human-readable string explaining the issue and providing guidance on how to resolve it. This message is intended to help you inform your end-users about the required actions. * **\_links**: A HATEOAS object containing relevant links. This typically includes a link to the API resource needed to address the directive, such as updating the Customer resource or uploading a specific document type. The key of the link (e.g., "update-customer", "upload-ein-document") often indicates the required action. An empty `_links` object may be returned if no direct API action corresponds to the directive (e.g., for AdditionalReviewRequired). #### Verification Directives The following table lists common verification directives you may encounter for Business Verified Customers, along with their meaning and typical required actions: | Code | Message | Primary Action(s) | | -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------- | | Required | Full SSN required. | Retry Customer verification with full SSN | | POBoxNotAllowed | Business addresses must be physical street addresses. PO Boxes are not allowed. Please update to a physical address. | Update Customer Address | | AddressNotAssociatedWithBusiness | The business address provided does not match information in public records. Please update to a valid business address or upload proof of address documentation. | Update Customer Address OR Upload Proof of Address | | ResidentialAddressRequired | Invalid address type for controller. Residential address required. Please enter a valid residential address for the controller. Please note, PO Boxes cannot be used for verification purposes. | Update Controller Address | | AdditionalReviewRequired | Account verification is now under review. This review can take up to 2-3 business days. An account update event will detail the verification status and any required actions. | Wait for Update Event (No immediate API action) | | EINMismatch | The provided Employer Identification Number (EIN) does not match the EIN on the business documentation previously uploaded for verification. Please update the EIN to match the EIN letter or official business document provided. | Update Customer EIN | | LegalDBAMismatch | Business name and Doing Business As (DBA) do not match the uploaded documentation. Please correct the Legal/Registered Name and DBA fields to align with the documentation provided. | Update Customer Legal Name/DBA Name | | BusinessDBAMismatch | The provided Doing Business As (DBA) name does not match the business documentation. Please correct the DBA Name to align with the documentation provided. | Update Customer DBA Name | | BusinessAddressDocRequired | Proof of business address is required. Please upload a document showing the business name and address as registered (e.g., Utility Bill, Financial Statement, Tax Statement, Lease Agreement). | Upload Business Address Document | | EINDocumentRequired | EIN Letter is required to verify the business. Please upload an EIN assignment letter or an official business document showing the full business name and EIN. | Upload EIN Document | | BusinessFormationDocRequired | Business formation documents are required to verify good standing. Please upload Articles of Organization/Incorporation from the state of registration. | Upload Business Formation Document | | PersonalIDRequired | Personal ID is required for account verification. Please upload a valid personal ID. | Upload Personal ID Document | | CoWorkingAddress | The provided business address is a co-working location. Please provide a proof of address document for this location or update to a residential address if operating remotely (e.g., Utility Bill, Financial Statement, Tax Statement, Lease Agreement). | Upload Business Address Document OR Update Address | | RegisteredAgentAddressNotAllowed | The provided business address leads to a Registered Agent, not the business itself. Please update to a physical business address or, if the business is remote, use the residential address of the Controller or a Beneficial Owner. | Update Customer Address | | SOSNotInGoodStanding | We could not verify that this business is in good standing with the Secretary of State for the registered state. All business entities must be active and in good standing to proceed. | Update Customer Information | | ForeignPassportNumberRequired | A foreign passport has been provided for verification of the controller. Please provide the foreign passport number to complete the verification process. | Update Controller Info | | SOSStateMismatch | We could not verify the state in which this business is registered. Please update the business address to ensure it matches the registered state. | Update Customer Address | | BusinessNameMismatch | The provided business name does not match the name on the uploaded business documentation. Please update the business name to match the uploaded document. | Update Customer Legal Name | ##### Example Retrieve Customer response Here's an example of how the `_embedded.errors` array might look in a `GET /customers/{id}` response when multiple issues require attention: ```json theme={"dark"} // Example GET /customers/41432759-6d65-42e5-a6be-400ddd103b78 { "_links": { // ... other customer links }, "_embedded": { "errors": [ { "code": "POBoxNotAllowed", "message": "Business addresses must be physical street addresses. PO Boxes are not allowed. Please update to a physical address.", "_links": { "retry-verification": { "href": "https://api-sandbox.dwolla.com/customers/41432759-6d65-42e5-a6be-400ddd103b78" // type property omitted for brevity } } }, { "code": "Required", "message": "Full SSN required", "path": "", "_links": { "retry-with-full-ssn": { "href": "https://api-sandbox.dwolla.com/customers/41432759-6d65-42e5-a6be-400ddd103b78", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "customer" } } } ] }, "status": "retry" // ... other customer properties } ``` #### Handling Multiple Directives As demonstrated in the example response above, the `_embedded.errors` array can contain multiple Verification Directive objects simultaneously if several issues require attention. Your application should iterate through the entire errors array, presenting the guidance from each message to the end-user and providing access to all corresponding actions via the `_links`. Addressing all listed directives is necessary for the user to successfully complete the verification process. ## Handling `retry` status A `retry` status occurs when a Customer's identity scores are too low during a verification attempt. Typically, a `retry` status occurs after the initial creation of a business-verified customer, however, a customer can also be placed into a retry status via the Dwolla Dashboard if the customer is in a `document` status. When the customer is in the `retry` status, your application needs to re-initiate the verification process by prompting the user via a form to resubmit their identifying information. You need to gather new information if the Customer is placed into the retry status; simply passing the same information will result in the same insufficient scores. #### Determining information needed to retry verification When a business verified Customer is placed in the `retry` verification status, Dwolla will return a link in the API response after [retrieving a Customer](/docs/api-reference/customers/retrieve-a-customer). The retry link contained within the `_links` object of the response helps your application determine if a retry is needed and what type of retry is required. What data you need to request from the customer depends on the retry scenario: * **Business-only retry**: A `retry-verification` link is returned. Include all fields required during initial customer but **omit** Controller information. For business verified Customers with Controllers, different links can be returned depending on whether retry is needed for just the business, or both the Controller and business. * **Controller and business retry**: A `retry-with-full-ssn` link is returned. If the Controller information needs to be retried, all fields that were required in the initial Customer creation attempt will be required in the retry attempt, along with **the full 9-digit SSN** of the Controller in order to give our identity vendor more information in an attempt to receive a sufficient score to approve the Customer account ([see example request below](#business-with-controller-retry-with-full-ssn---request-and-response)). ##### Understanding \_embedded errors Additionally, `_embedded` errors are included in the Customer resource which include information about the next steps required to get the Customer verified (see example response below). Refer to the table below for the list of possible links and their descriptions. | Link name | Description | | ------------------- | ------------------------------------------------------------------------------- | | retry-verification | Identifies if retry information is needed for the business. | | retry-with-full-ssn | Identifies if retry information is needed for both the Controller and business. | ##### Example response ```json theme={"dark"} { "_links": { "self": { "href": "https://api-sandbox.dwolla.com/customers/20c2d8e2-8ccf-42fd-bd9e-757c396f342d", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "customer" }, "retry-verification": { "href": "https://api-sandbox.dwolla.com/customers/20c2d8e2-8ccf-42fd-bd9e-757c396f342d", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "customer" }, "retry-with-full-ssn": { "href": "https://api-sandbox.dwolla.com/customers/20c2d8e2-8ccf-42fd-bd9e-757c396f342d", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "customer" } }, "_embedded": { "errors": [ { "code": "Required", "message": "Full SSN required", "path": "", "_links": { "retry-with-full-ssn": { "href": "https://api-sandbox.dwolla.com/customers/20c2d8e2-8ccf-42fd-bd9e-757c396f342d", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "customer" } } } ] }, "id": "20c2d8e2-8ccf-42fd-bd9e-757c396f342d", "firstName": "Account", "lastName": "Admin", "email": "accountAdmin@email.com", "type": "business", "status": "retry", "created": "2024-02-20T22:53:00.727Z", "address1": "9876 Million Dollar St", "address2": "Unit 123", "city": "Des Moines", "state": "IA", "postalCode": "50265", "phone": "5555555555", "businessName": "Jane Corp", "doingBusinessAs": "This is the DBA name", "website": "https://www.dwolla.com", "correlationId": "CID-bc3b6cd8-fca0-471d-b6f2-4b10abb20956", "controller": { "firstName": "Jane", "lastName": "Doe", "title": "CEO", "address": { "address1": "1749 18th st", "address2": "apt 12", "address3": "Ste 123", "city": "Des Moines", "stateProvinceRegion": "IA", "country": "US", "postalCode": "50266" } }, "businessType": "llc", "businessClassification": "9ed38155-7d6f-11e3-83c3-5404a6144203" } ``` ### Sole Proprietorship (`retry-verification`) - Request and response ```bash theme={"dark"} POST https://api-sandbox.dwolla.com/customers/62c3aa1b-3a1b-46d0-ae90-17304d60c3d5 Content-Type: application/vnd.dwolla.v1.hal+json Accept: application/vnd.dwolla.v1.hal+json Authorization: Bearer 0Sn0W6kzNic+oWhDbQcVSKLRUpGjIdl/YyrHqrDDoRnQwE7Q { "firstName": "Business", "lastName": "Owner", "email": "solePropBusiness@email.com", "ipAddress": "143.156.7.8", "type": "business", "dateOfBirth": "1980-01-31", "ssn": "123-45-6789", "address1": "99-99 33rd St", "city": "Some City", "state": "NY", "postalCode": "11101", "businessClassification": "9ed3f670-7d6f-11e3-b1ce-5404a6144203", "businessType": "soleProprietorship", "businessName":"Jane Corp", "ein":"00-0000000" } ``` ```php theme={"dark"} updateCustomer([ 'firstName' => 'Business', 'lastName' => 'Owner', 'email' => 'solePropBusiness@email.com', 'ipAddress' => '143.156.7.8', 'type' => 'business', 'dateOfBirth' => '1980-01-31', 'ssn' => '123-45-6789', 'address1' => '99-99 33rd St', 'city' => 'Some City', 'state' => 'NY', 'postalCode' => '11101', 'businessClassification' => '9ed3f670-7d6f-11e3-b1ce-5404a6144203', 'businessType' => 'soleProprietorship', 'businessName' => 'Jane Corp', 'ein' => '00-0000000'], $customerUrl); ?> ``` ```ruby theme={"dark"} # Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby (Recommended) request_body = { :firstName => 'Business', :lastName => 'Owner', :email => 'solePropBusiness@email.com', :ipAddress => '143.156.7.8', :type => 'business', :dateOfBirth => '1980-01-31', :ssn => '123-45-6789', :address1 => '99-99 33rd St', :city => 'Some City', :state => 'NY', :postalCode => '11101', :businessClassification => '9ed3f670-7d6f-11e3-b1ce-5404a6144203', :businessType => 'soleProprietorship', :businessName => 'Jane Corp', :ein => '00-0000000' } customer = app_token.post customer_url, request_body customer.id # => "62c3aa1b-3a1b-46d0-ae90-17304d60c3d5" ``` ```python theme={"dark"} # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python (Recommended) request_body = { 'firstName': 'Business', 'lastName': 'Owner', 'email': 'solePropBusiness@email.com', 'ipAddress': '143.156.7.8', 'type': 'business', 'dateOfBirth': '1980-01-31', 'ssn': '123-45-6789', 'address1': '99-99 33rd St', 'city': 'Some City', 'state': 'NY', 'postalCode': '11101', 'businessClassification': '9ed3f670-7d6f-11e3-b1ce-5404a6144203', 'businessType': 'soleProprietorship', 'businessName': 'Jane Corp', 'ein': '00-0000000' } customer = app_token.post(customer_url, request_body) customer.body.id # => '62c3aa1b-3a1b-46d0-ae90-17304d60c3d5' ``` ```javascript theme={"dark"} var customerUrl = "https://api.dwolla.com/customers/62c3aa1b-3a1b-46d0-ae90-17304d60c3d5"; var requestBody = { firstName: "Business", lastName: "Owner", email: "solePropBusiness@email.com", ipAddress: "143.156.7.8", type: "business", dateOfBirth: "1980-01-31", ssn: "123-45-6789", address1: "99-99 33rd St", city: "Some City", state: "NY", postalCode: "11101", businessClassification: "9ed3f670-7d6f-11e3-b1ce-5404a6144203", businessType: "soleProprietorship", businessName: "Jane Corp", ein: "00-0000000", }; dwolla .post("customers", requestBody) .then((res) => res.headers.get("location")); // => 'https://api-sandbox.dwolla.com/customers/62c3aa1b-3a1b-46d0-ae90-17304d60c3d5' ``` ### Business with Controller (`retry-verification`) - Request and response ```bash theme={"dark"} POST https://api-sandbox.dwolla.com/customers/62c3aa1b-3a1b-46d0-ae90-17304d60c3d5 Content-Type: application/vnd.dwolla.v1.hal+json Accept: application/vnd.dwolla.v1.hal+json Authorization: Bearer 0Sn0W6kzNic+oWhDbQcVSKLRUpGjIdl/YyrHqrDDoRnQwE7Q { "firstName": "Jane", "lastName": "Merchant", "email": "accountAdmin@email.com", "ipAddress": "143.156.7.8", "type": "business", "address1": "123 Corrected Address St", "city": "Some City", "state": "NY", "postalCode": "11101", "businessClassification": "9ed3f670-7d6f-11e3-b1ce-5404a6144203", "businessType": "llc", "businessName":"Jane Corp", "ein":"00-0000000" } ``` ```php theme={"dark"} updateCustomer([ 'firstName' => 'Jane', 'lastName' => 'Merchant', 'email' => 'accountAdmin@email.com', 'type' => 'business', 'address1' => '123 Corrected Address St', 'city' => 'Some City', 'state' => 'NY', 'postalCode' => '11101', 'phone' => '5554321234', 'businessClassification' => '9ed3f670-7d6f-11e3-b1ce-5404a6144203', 'businessType' => 'llc', 'businessName' => 'Jane Corp', 'ein' => '00-0000000'], $customerUrl); ?> ``` ```ruby theme={"dark"} # Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby (Recommended) customer_url = 'https://api.dwolla.com/customers/62c3aa1b-3a1b-46d0-ae90-17304d60c3d5' request_body = { :firstName => 'Jane', :lastName => 'Merchant', :email => 'accountAdmin@email.com', :type => 'business', :address1 => '123 Corrected Address St', :city => 'Some City', :state => 'NY', :postalCode => '11101', :businessClassification => '9ed38155-7d6f-11e3-83c3-5404a6144203', :businessType => 'llc', :businessName => 'Jane Corp', :ein => '12-3456789' } customer = app_token.post customer_url, request_body customer.id # => "62c3aa1b-3a1b-46d0-ae90-17304d60c3d5" ``` ```python theme={"dark"} # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python (Recommended) customer_url = 'https://api.dwolla.com/customers/62c3aa1b-3a1b-46d0-ae90-17304d60c3d5' request_body = { 'firstName': 'Jane', 'lastName': 'Merchant', 'email': 'accountAdmin@email.com', 'type': 'business', 'address1': '123 Corrected Address St', 'city': 'Some City', 'state': 'NY', 'postalCode': '11101', 'businessClassification': '9ed38155-7d6f-11e3-83c3-5404a6144203', 'businessType': 'llc', 'businessName': 'Jane Corp', 'ein': '12-3456789' } customer = app_token.post(customer_url, request_body) customer.body.id # => '62c3aa1b-3a1b-46d0-ae90-17304d60c3d5' ``` ```javascript theme={"dark"} var customerUrl = "https://api.dwolla.com/customers/62c3aa1b-3a1b-46d0-ae90-17304d60c3d5"; var requestBody = { firstName: "Jane", lastName: "Merchant", email: "accountAdmin@email.com", type: "business", address1: "123 Corrected Address St", city: "Some City", state: "NY", postalCode: "11101", businessClassification: "9ed38155-7d6f-11e3-83c3-5404a6144203", businessType: "llc", businessName: "Jane Corp", ein: "12-3456789", }; dwolla.post(customerUrl, requestBody).then(function (res) { res.body.id; // => '62c3aa1b-3a1b-46d0-ae90-17304d60c3d5' }); ``` ### Business with Controller (`retry-with-full-ssn`) - Request and response ```bash theme={"dark"} POST https://api-sandbox.dwolla.com/customers/62c3aa1b-3a1b-46d0-ae90-17304d60c3d5 Content-Type: application/vnd.dwolla.v1.hal+json Accept: application/vnd.dwolla.v1.hal+json Authorization: Bearer 0Sn0W6kzNic+oWhDbQcVSKLRUpGjIdl/YyrHqrDDoRnQwE7Q { "firstName": "Jane", "lastName": "Merchant", "email": "accountAdmin@email.com", "ipAddress": "143.156.7.8", "type": "business", "address1": "123 Corrected Address St", "city": "Some City", "state": "NY", "postalCode": "11101", "controller": { "firstName": "John", "lastName": "Controller", "title": "CEO", "dateOfBirth": "1980-01-01", "ssn": "123-45-6789", "address": { "address1": "1749 18th st", "address2": "apt 12", "city": "Des Moines", "stateProvinceRegion": "IA", "postalCode": "50266", "country": "US" } }, "businessClassification": "9ed3f670-7d6f-11e3-b1ce-5404a6144203", "businessType": "llc", "businessName":"Jane Corp", "ein":"00-0000000" } ``` ```php theme={"dark"} updateCustomer([ 'firstName' => 'Jane', 'lastName' => 'Merchant', 'email' => 'accountAdmin@email.com', 'type' => 'business', 'address1' => '123 Corrected Address St', 'city' => 'Some City', 'state' => 'NY', 'postalCode' => '11101', 'controller' => [ 'firstName' => 'John', 'lastName'=> 'Controller', 'title' => 'CEO', 'dateOfBirth' => '1990-10-10', 'ssn' => '123-45-6789', 'address' => [ 'address1' => '18749 18th st', 'address2' => 'apt 12', 'city' => 'Des Moines', 'stateProvinceRegion' => 'IA', 'postalCode' => '50265', 'country' => 'US' ], ], 'phone' => '5554321234', 'businessClassification' => '9ed3f670-7d6f-11e3-b1ce-5404a6144203', 'businessType' => 'llc', 'businessName' => 'Jane Corp', 'ein' => '00-0000000'], $customerUrl); ?> ``` ```ruby theme={"dark"} # Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby (Recommended) customer_url = 'https://api.dwolla.com/customers/62c3aa1b-3a1b-46d0-ae90-17304d60c3d5' request_body = { :firstName => 'Jane', :lastName => 'Merchant', :email => 'accountAdmin@email.com', :type => 'business', :address1 => '123 Corrected Address St', :city => 'Some City', :state => 'NY', :postalCode => '11101', :controller => { :firstName => 'John', :lastName => 'Controller', :title => 'CEO', :dateOfBirth => '1980-01-01', :ssn => '123-45-6789' :address => { :address1 => '1749 18th st', :address2 => 'apt 12', :city => 'Des Moines', :stateProvinceRegion => 'IA', :postalCode => '50266', :country => 'US' } }, :businessClassification => '9ed38155-7d6f-11e3-83c3-5404a6144203', :businessType => 'llc', :businessName => 'Jane Corp', :ein => '12-3456789' } customer = app_token.post customer_url, request_body customer.id # => "62c3aa1b-3a1b-46d0-ae90-17304d60c3d5" ``` ```python theme={"dark"} # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python (Recommended) customer_url = 'https://api.dwolla.com/customers/62c3aa1b-3a1b-46d0-ae90-17304d60c3d5' request_body = { 'firstName': 'Jane', 'lastName': 'Merchant', 'email': 'accountAdmin@email.com', 'type': 'business', 'address1': '123 Corrected Address St', 'city': 'Some City', 'state': 'NY', 'postalCode': '11101', 'controller': { 'firstName': 'John', 'lastName': 'Controller', 'title': 'CEO', 'dateOfBirth': '1980-01-01', 'ssn': '123-45-6789', 'address': { 'address1': '1749 18th st', 'address2': 'apt12', 'city': 'Des Moines', 'stateProvinceRegion': 'IA', 'postalCode': '50266', 'country': 'US' } }, 'businessClassification': '9ed38155-7d6f-11e3-83c3-5404a6144203', 'businessType': 'llc', 'businessName': 'Jane Corp', 'ein': '12-3456789' } customer = app_token.post(customer_url, request_body) customer.body.id # => '62c3aa1b-3a1b-46d0-ae90-17304d60c3d5' ``` ```javascript theme={"dark"} var customerUrl = "https://api.dwolla.com/customers/62c3aa1b-3a1b-46d0-ae90-17304d60c3d5"; var requestBody = { firstName: "Jane", lastName: "Merchant", email: "accountAdmin@email.com", type: "business", address1: "123 Corrected Address St", city: "Some City", state: "NY", postalCode: "11101", controller: { firstName: "John", lastName: "Controller", title: "CEO", dateOfBirth: "1980-01-01", ssn: "123-45-6789", address: { address1: "1749 18th st", address2: "apt 12", city: "Des Moines", stateProvinceRegion: "IA", postalCode: "50266", country: "US", }, }, businessClassification: "9ed38155-7d6f-11e3-83c3-5404a6144203", businessType: "llc", businessName: "Jane Corp", ein: "12-3456789", }; dwolla.post(customerUrl, requestBody).then(function (res) { res.body.id; // => '62c3aa1b-3a1b-46d0-ae90-17304d60c3d5' }); ``` ## Handling `document` status If the Customer has a status of `document`, the Customer will need to upload additional pieces of information in order to verify the account. Use the [create a document](/docs/api-reference/documents/create-a-document-for-customer) endpoint when uploading a colored camera captured image of the identifying document. The document(s) will then be reviewed by Dwolla; this review may take up to 1-2 business days to approve or reject. You can provide the following best practices to the Customer in order to reduce the chances of a document being rejected: * Only images of the front of an ID * All 4 edges of the document should be visible * A dark/high contrast background should be used * At least 90% of the image should be the document * Should be at least 300dpi * Capture image from directly above the document * Make sure that the image is properly aligned, not rotated, tilted or skewed * No flash to reduce glare * No black and white documents * No expired IDs #### Determining verification documents needed When a business verified Customer is placed in the `document` verification status, Dwolla will return a link in the API response after [retrieving a Customer](/docs/api-reference/customers/retrieve-a-customer), which will be used by an application to determine if documentation is needed. For business verified Customers, different links can be returned depending on whether or not documents are needed for a Controller, the business, both the Controller and business, or for the DBA (Doing Business As). Additionally, embedded errors are included in the Customer resource which include information about the next steps required to get the Customer verified (see example response below). Refer to the table below for the list of possible links and their description. Refer to the [acceptable document types](#document-types) section for more information on what types of documents are accepted for businesses and Controllers. | Link name | Description | | -------------------------------------------- | ---------------------------------------------------------------------------- | | verify-with-document | Identifies if documents are needed only for a Controller. | | verify-business-with-document | Identifies if documents are needed only for a business. | | verify-controller-and-business-with-document | Identifies if documents are needed for both the Controller and business. | | upload-dba-document | Identifies if documents are needed for a business's DBA (Doing Business As). | ##### Example response ```json theme={"dark"} { "_links": { "self": { "href": "https://api-sandbox.dwolla.com/customers/41432759-6d65-42e5-a6be-400ddd103b78", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "customer" }, "document-form": { "href": "https://api-sandbox.dwolla.com/customers/41432759-6d65-42e5-a6be-400ddd103b78/documents", "type": "application/vnd.dwolla.v1.hal+json; profile=\"https://github.com/dwolla/hal-forms\"", "resource-type": "document" }, "upload-dba-document": { "href": "https://api-sandbox.dwolla.com/customers/41432759-6d65-42e5-a6be-400ddd103b78/documents", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "document" } }, "_embedded": { "errors": [ { "code": "Required", "message": "DBA (Doing Business As) document upload required", "_links": { "upload-dba-document": { "href": "https://api-sandbox.dwolla.com/customers/41432759-6d65-42e5-a6be-400ddd103b78/documents" } } } ] }, "id": "41432759-6d65-42e5-a6be-400ddd103b78", "firstName": "Account", "lastName": "Admin", "email": "accountAdmin@email.com", "type": "business", "status": "document", "created": "2018-05-10T19:59:22.643Z", "address1": "66 Walnut St", "city": "Des Moines", "state": "IA", "postalCode": "50309", "businessName": "Jane Corp", "controller": { "firstName": "document", "lastName": "Controller", "title": "CEO", "address": { "address1": "1749 18th st", "address2": "apt 12", "city": "Des Moines", "stateProvinceRegion": "IA", "country": "US", "postalCode": "50266" } }, "businessClassification": "9ed3f670-7d6f-11e3-b1ce-5404a6144203", "businessType": "llc" } ``` #### Document Types ##### Controllers **US persons:** A colored camera captured image of the Controller's identifying document can be specified as documentType: `license` (state issued driver's license), or `idCard` (U.S. government-issued photo id card). Supported Document Examples: * Non-expired State Issued Driver's License/Identification Card * Non-expired US Passport * Federal Employment Authorization Card * US Visa Unsupported Document Examples: * Military IDs * Expired government-issued IDs **Non-US persons:** A colored camera captured image of the Controller's identifying document can be specified as documentType: `passport`. Examples include: * Non-expired Foreign Passport ***Note:** Foreign Passports are only accepted when the individual does not have an ITIN or SSN and the user must alternatively enter the Passport number*. ##### Businesses Documents that are used to help identify a business are specified as documentType `other`. **Note**: A DBA document should be issued by the government and should include the DBA name along with the state registered business name. Business Identifying documents we recommend uploading can include the following: * **Partnership, General Partnership**: EIN Letter (IRS-issued SS4 confirmation letter). * **Limited Liability Corporation (LLC), Corporation**: EIN Letter (IRS-issued SS4 confirmation letter). * **Sole Proprietorship**: Sole Proprietorships can be verified by uploading Business documents as well as Personal IDs. Personal IDs need to be specified as documentType `idCard`, `license` or `passport` depending on the type of the ID. Business documents need to be specified as documentType `other`. Acceptable documents include one or more of the following, as applicable to your sole proprietorship: * Business documents (documentType `other`): * Fictitious Business Name Statement, * Certificate of Assumed Name; Business License, * Sales/Use Tax License, * Registration of Trade Name, * EIN documentation (IRS-issued SS4 confirmation letter) * Personal documents (documentType `license`, `passport` or `idCard`): * Color copy of a valid government-issued photo ID (e.g., a driver's license, passport, or state ID card). Trusts should be created as Sole Proprietor accounts and will require signed trust documents that include the trust and username that are on file. Other business documents may be acceptable on a case by case basis with Dwolla approval. These include any US government entity (federal, state, local) issued business formation or licensing exhibiting the name of the business enrolling with Dwolla, or; Any business formation documents exhibiting the name of the business entity in addition to being filed and stamped by a US government entity. Examples include: * Filed and stamped Articles of Organization or Incorporation * Sales/Use Tax License * Business License * Certificate of Good Standing ##### Proof of address If Dwolla's Compliance team is unable to find an external connection to confirm the user does in fact conduct business at their provided business address, a "proof of address" will be required. Proof of address are any of the following, current documents that show the address in question: * Utility Bill * Financial Statement * Tax Statement (Please note - Form W-9 is a tax form, and is not an acceptable Tax Statement) * Fully Executed Lease Agreement - must be valid for a minimum of the next 30 days. ### Uploading a document To upload a color photo of the document, you'll initiate a multipart form-data POST request from your backend server to `https://api.dwolla.com/customers/{id}/documents`. The file must be either a .jpg, .jpeg, or .png. Files must be no larger than 10MB in size. Additionally, Business Documents can also be uploaded in a .pdf format. You'll also get a webhook with a `customer_verification_document_uploaded` event to let you know the document was successfully uploaded. #### Request and response ```bash HTTP theme={"dark"} curl -X POST \ -H "Authorization: Bearer tJlyMNW6e3QVbzHjeJ9JvAPsRglFjwnba4NdfCzsYJm7XbckcR" \ -H "Accept: application/vnd.dwolla.v1.hal+json" \ -H "Cache-Control: no-cache" \ -H "Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW" \ -F "documentType=passport" \ -F "file=@foo.png" \ 'https://api-sandbox.dwolla.com/customers/132681fa-1b4d-4181-8ff2-619ca46235b1/documents ``` ```php upload_customer_document.php theme={"dark"} No example for this language yet. ``` ```ruby upload_customer_document.rb theme={"dark"} # Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby customer_url = 'https://api.dwolla.com/customers/132681fa-1b4d-4181-8ff2-619ca46235b1' file = Faraday::UploadIO.new('mclovin.jpg', 'image/jpeg') document = app_token.post "#{customer_url}/documents", file: file, documentType: 'license' document.response_headers[:location] # => "https://api.dwolla.com/documents/11fe0bab-39bd-42ee-bb39-275afcc050d0" ``` ```python upload_customer_document.py theme={"dark"} # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python (Recommended) customer_url = 'https://api.dwolla.com/customers/132681fa-1b4d-4181-8ff2-619ca46235b1' document = app_token.post('%s/documents' % customer_url, file = open('mclovin.jpg', 'rb'), documentType = 'license') document.headers['location'] # => 'https://api.dwolla.com/documents/11fe0bab-39bd-42ee-bb39-275afcc050d0' ``` ```javascript uploadCustomerDocument.js theme={"dark"} // Using dwolla-v2 - https://github.com/Dwolla/dwolla-v2-node var customerUrl = "https://api.dwolla.com/customers/132681fa-1b4d-4181-8ff2-619ca46235b1"; var requestBody = new FormData(); body.append("file", fs.createReadStream("mclovin.jpg"), { filename: "mclovin.jpg", contentType: "image/jpeg", knownLength: fs.statSync("mclovin.jpg").size, }); body.append("documentType", "license"); dwolla.post(`${customerUrl}/documents`, requestBody).then(function (res) { res.headers.get("location"); // => "https://api.dwolla.com/documents/11fe0bab-39bd-42ee-bb39-275afcc050d0" }); ``` If the document was successfully uploaded, the response will be a HTTP 201 Created with the URL of the new document resource contained in the Location header. ```bash theme={"dark"} HTTP/1.1 201 Created Location: https://api-sandbox.dwolla.com/documents/11fe0bab-39bd-42ee-bb39-275afcc050d0 ``` #### Document review process Once created, the document will be reviewed by Dwolla. When our team has made a decision to approve or reject, which may take up to 1-2 business days, we'll create either a `customer_verification_document_approved` or `customer_verification_document_failed` event. If the document was sufficient, the Customer may be verified in this process. If not, we may need additional documentation. Note: Reference the [determining verification documents needed](#document-types) section for more information on determining if additional documents are needed after an approved or failed event is triggered. If the document was found to be fraudulent or doesn't match the identity of the Customer, the Customer will be suspended. #### Document failure A document can fail if, for example, the Customer uploaded the wrong type of document or the `.jpg` or `.png` file supplied was not readable (i.e. blurry, not well lit, not in color, or cuts off a portion of the identifying image). If you receive a `customer_verification_document_failed` webhook, you'll need to upload another document. To retrieve the failure reason for the document upload, you'll retrieve the document by its ID. Contained in the response will be a `failureReason` field which corresponds to one or more of the following values. In case of a failure due to multiple reasons, an additional `allFailureReasons` of `reason`s and `description`s is also returned: | Failure reason | Description | Detailed description | | ------------------------- | ---------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `BusinessDocNotSupported` | Business document not supported | The business document provided is not supported for verification. Please request approved [business documentation](#document-types) from the end user for verification. | | `BusinessNameMismatch` | Business name on account does not match document | The legal business name listed in the documentation uploaded does not match the Registered Business Name on the account. The account has been moved to [retry](#handling-retry-status) so that the business name listed at the account level can be adjusted to match the business documentation which was uploaded. | | `BusinessTypeMismatch` | Business type chosen does not match document | Based on the documentation provided, the entity type for this business should be created as a/an (LLC, Corporation, Sole Prop). The account has been moved to [retry](#handling-retry-status) so that the correct business type (LLC, Corporation, Sole Prop etc.) can be submitted. | | `ScanDobMismatch` | Scan DOB does not match DOB on account | The DOB listed on the ID uploaded does not match the DOB on the user's account. The account has been placed in [retry](#handling-retry-status) so that the user can adjust the DOB listed on the account to match the ID which was provided. | | `ScanFailedOther` | ID may be fraudulent or a generic example ID image | The ID uploaded may be fraudulent or a generic example of an ID. The user needs to upload a valid ID to proceed with account verification. | | `ScanIdExpired` | ID is expired or missing expiration date | The ID uploaded by the user is expired. The user will need to upload a non-expired ID. | | `ScanIdTypeNotSupported` | ID may be a military ID, firearm license, or other unsupported ID type | The uploaded ID is not an acceptable form of ID. [Here](#document-types) is a list of ID types that Dwolla accepts for account verification. | | `ScanIdUnrecognized` | ID is not recognized | The ID which has been uploaded is unreadable. The user will need to upload a new image of their ID to proceed with verification. | | `ScanNameMismatch` | Scan name does not match name on account | The name listed on the ID which has been uploaded by the user does not match the name which is listed at the account level. The account has been placed in [retry](#handling-retry-status) so that the user can adjust the name on the account to match the ID which was provided. | | `ScanNotReadable` | Image blurry, too dark, or obscured by glare | The uploaded ID is blurry, cutoff, or unreadable. The user will need to upload a clear, color, camera-captured image of their ID to proceed with verification. Here are some [best practices](#handling-document-status) related to document uploads. | | `ScanNotUploaded` | Scan not uploaded | The uploaded image is not an ID. The user will need to upload an image of a valid ID to proceed. [Here](#document-types) is a list of valid ID's that Dwolla accepts for account verification. | ##### Request and response ```bash HTTP theme={"dark"} GET https://api-sandbox.dwolla.com/documents/11fe0bab-39bd-42ee-bb39-275afcc050d0 Accept: application/vnd.dwolla.v1.hal+json Authorization: Bearer tJlyMNW6e3QVbzHjeJ9JvAPsRglFjwnba4NdfCzsYJm7XbckcR ... { "_links": { "self": { "href": "https://api-sandbox.dwolla.com/documents/11fe0bab-39bd-42ee-bb39-275afcc050d0" } }, "id": "11fe0bab-39bd-42ee-bb39-275afcc050d0", "status": "reviewed", "type": "license", "created": "2016-01-29T21:22:22.000Z", "failureReason": "ScanNotReadable", "allFailureReasons": [ { "reason": "ScanDobMismatch", "description": "Date of Birth mismatch" }, { "reason": "ScanIdExpired", "description": "ID is expired" } ] } ``` ```php retrieve_failure_reason.php theme={"dark"} getCustomer($aDocument); print($retrieved->failureReason); # => "ScanNotReadable" ?> ``` ```ruby retrieve_failure_reason.rb theme={"dark"} # Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby (Recommended) document_url = 'https://api.dwolla.com/documents/11fe0bab-39bd-42ee-bb39-275afcc050d0' document = app_token.get document_url document.failureReason # => "ScanNotReadable" ``` ```python retrieve_failure_reason.py theme={"dark"} # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python (Recommended) document_url = 'https://api.dwolla.com/documents/11fe0bab-39bd-42ee-bb39-275afcc050d0' documents = app_token.get(document_url) documents.body['failureReason'] # => 'ScanNotReadable' ``` ```javascript theme={"dark"} var documentUrl = "https://api.dwolla.com/documents/11fe0bab-39bd-42ee-bb39-275afcc050d0"; dwolla.get(document_url).then(function (res) { res.body.failureReason; // => "ScanNotReadable" }); ``` ## Handling status: suspended If the Customer is `suspended`, there's no further action you can take to correct this using the API. You'll need to contact [support@dwolla.com](mailto:support@dwolla.com) or your account manager for assistance. The successful creation of a business verified Customer and Controller doesn't necessarily mean the Customer is fully verified and eligible to transfer. After successfully creating your business verified Customer, you will need to check to see if the beneficial ownership requirements apply to you. To learn how to add beneficial owner(s) to your Customer, read on in the next step. # Step 3 - Adding Beneficial Owner(s) To help the government fight financial crime, the existing United States Federal customer due diligence rules were amended to clarify and strengthen [customer due diligence requirements.](https://www.federalregister.gov/documents/2016/05/11/2016-10567/customer-due-diligence-requirements-for-financial-institutions#footnote-44-p29407) The customer due diligence rule imposes a requirement for verifying the identity of beneficial owner(s) of Dwolla's partners and users that are not natural persons. These legal entities can be abused to disguise involvement in terrorist financing, money laundering, tax evasion, corruption, fraud, and other financial crimes. Requiring the disclosure of key individuals who ultimately own or control a legal entity (i.e., the beneficial owners) helps law enforcement investigate and prosecute these crimes. If your business is exempt or if there is no individual with at least 25% ownership, your Customer can go straight to certifying that there are no beneficial owners #### How do I know what business structure is required to add Beneficial Owners? | If my Customer's business structure is... | ...are they required to add beneficial owners? | | ----------------------------------------- | ---------------------------------------------- | | Sole proprietorships | No (exempt) | | Unincorporated association | No (exempt) | | Trust | No (exempt) | | Corporation | Yes (if owns 25% or more) | | Publicly traded corporations | No (exempt) | | Non-profits | No (exempt) | | LLCs | Yes (if owns 25% or more) | | Partnerships, LP's, LLP's | Yes (if owns 25% or more) | ### Create a beneficial owner for a Business Verified Customer To create a beneficial owner, use the [create a beneficial owner](/docs/api-reference/beneficial-owners/create-beneficial-owner) endpoint. ##### Events As a developer, you can expect these events to be triggered when a beneficial owner is successfully created and systematically verified: 1. `customer_beneficial_owner_created` 2. `customer_beneficial_owner_verified` ##### Request Parameters | Parameter | Required | Type | Description | | ----------- | ----------- | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | | firstName | yes | string | **Legal first name** of the Beneficial Owner.
**Must be ≤ 50 characters**.
**Cannot contain numbers or special characters** ``[<>="`!?%~${}\]``. | | lastName | yes | string | **Legal last name** of the Beneficial Owner.
**Must be ≤ 50 characters**.
**Cannot contain numbers or special characters** ``[<>="`!?%~${}\]``. | | ssn | conditional | string | **Full 9-digit SSN** required **only for US persons**.
**Must be exactly 9 digits (e.g., `123456789`)**.
**No dashes or separators**. | | dateOfBirth | yes | string | **Date of birth** of the Beneficial Owner.
**Formatted as `YYYY-MM-DD`**.
**Must be between 18 to 125 years of age**. | | address | yes | object | **Physical address of the Beneficial Owner**.
[See Address JSON Object](#address-json-object). | | passport | conditional | object | **Required for non-US persons**.
Includes **passport number and issuing country**.
[See Passport JSON Object](#passport-json-object). | ##### Address JSON object | Parameter | Required | Type | Description | | ------------------- | ----------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | address1 | yes | string | **First line** of the street address of the Beneficial Owner's permanent residence.
**PO Boxes are not allowed**.
**Must be ≤ 50 characters** and contain **no special characters** ``[<>="`!?%~${}\]``. | | address2 | no | string | **Second line** of the street address.
**PO Boxes are not allowed**.
**Must be ≤ 50 characters** and contain **no special characters** ``[<>="`!?%~${}\]``. | | address3 | no | string | **Third line** of the street address.
**PO Boxes are not allowed**.
**Must be ≤ 50 characters** and contain **no special characters** ``[<>="`!?%~${}\]``. | | city | yes | string | **City** of the Beneficial Owner's permanent residence.
**Must be ≤ 50 characters**.
**Cannot contain numbers or special characters** ``[<>="`!?%~${}\]``. | | stateProvinceRegion | yes | string | **US persons** - Two-letter **US state abbreviation** of the Beneficial Owner's physical address. See the [US Postal Service guide](https://pe.usps.com/text/pub28/28apb.htm).
**Non-US persons** - Two-letter **state, province, or region ISO abbreviation**.
**If no two-letter abbreviation exists, use the country's ISO 2-letter abbreviation**.
**Must be uppercase** (e.g., `CA`). | | country | yes | string | **Country** of the Beneficial Owner's permanent residence.
**Two-digit ISO country code** (e.g., `US` for United States, `CA` for Canada). See the [ISO country codes list](https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes). | | postalCode | conditional | string | **Postal code** of the Beneficial Owner's permanent residence.
**US persons** must provide a **5-digit ZIP code** (e.g., `50314`).
**Non-US persons** - Optional, but may include alphanumeric postal codes where applicable. | ##### Passport JSON object | Parameter | Required | Type | Description | | --------- | ----------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | number | conditional | string | Required if Beneficial Owner resides **outside of the United States** and has no **Social Security Number**.
**Must be ≤ 255 characters** and contain **no special characters** ``[<>="`!?%~${}\]``. | | country | conditional | string | **Country** of issued passport.
**Two-digit ISO country code** (e.g., `US` for United States, `CA` for Canada).
**Must be 2 characters** (ISO standard). | ##### Request and Response ```bash theme={"dark"} POST https://api-sandbox.dwolla.com/customers/07d59716-ef22-4fe6-98e8-f3190233dfb8/beneficial-owners Content-Type: application/vnd.dwolla.v1.hal+json Accept: application/vnd.dwolla.v1.hal+json Authorization: Bearer pBA9fVDBEyYZCEsLf/wKehyh1RTpzjUj5KzIRfDi0wKTii7DqY { "firstName": "Jane", "lastName": "Doe", "ssn": "123-56-7890", "dateOfBirth": "1960-11-30", "address": { "address1": "123 Main St.", "address2": "Apt 12", "city": "New York", "stateProvinceRegion": "NY", "country": "US", "postalCode": "10005" } } HTTP/1.1 201 Created Location: https://api.dwolla.com/beneficial-owners/FC451A7A-AE30-4404-AB95-E3553FCD733F ``` ```php theme={"dark"} addBeneficialOwner([ 'firstName' => 'Jane', 'lastName'=> 'Doe', 'dateOfBirth' => '1960-11-30', 'ssn' => '123-56-7890', 'address' => [ 'address1' => '123 Main St', 'address2' => 'Apt 12', 'city' => 'New York', 'stateProvinceRegion' => 'NY', 'postalCode' => '10005', 'country' => 'US' ], ], $verified_customer); ?> ``` ```ruby theme={"dark"} # Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby customer_url = 'https://api-sandbox.dwolla.com/customers/AB443D36-3757-44C1-A1B4-29727FB3111C' request_body = { :firstName => 'Jane', :lastName => 'Doe', :ssn => '123-56-7890', :dateOfBirth => '1960-11-30', :address => { :address1 => '123 Main St', :address2 => 'Apt 12' :city => 'New York', :stateProvinceRegion => 'NY', :country => 'US', :postalCode => '10005' } } beneficial_owner = app_token.post "#{customer_url}/beneficial-owners", request_body beneficial_owner.response_headers[:location] # => "https://api-sandbox.dwolla.com/beneficial-owners/AB443D36-3757-44C1-A1B4-29727FB3111C" ``` ```python theme={"dark"} # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python customer_url = 'https://api-sandbox.dwolla.com/customers/AB443D36-3757-44C1-A1B4-29727FB3111C' request_body = { 'firstName': 'Jane', 'lastName': 'Doe', 'dateOfBirth': '1960-11-30', 'ssn': '123-56-7890', 'address': { 'address1': '99-99 33rd St', 'address2': 'Apt 12', 'city': 'New York', 'stateProvinceRegion': 'NY', 'country': 'US', 'postalCode': '10005' } } beneficial_owner = app_token.post('%s/beneficial-owners' % customer_url, request_body) beneficial_owner.headers['location'] # => 'https://api-sandbox.dwolla.com/beneficial-owners/AB443D36-3757-44C1-A1B4-29727FB3111C' ``` ```javascript theme={"dark"} var customerUrl = 'https://api-sandbox.dwolla.com/customers/07d59716-ef22-4fe6-98e8-f3190233dfb8'; var requestBody = { firstName: 'Jane', lastName: 'Doe', dateOfBirth: '1960-11-30', ssn: '123-56-7890', address: { address1: '99-99 33rd St', address2: 'Apt 12', city: 'Some City', stateProvinceRegion: 'NY', country: 'US' postalCode: '10005' } }; dwolla .post(`${customerUrl}/beneficial-owners`, requestBody) .then(res => res.headers.get('location')); // => 'https://api-sandbox.dwolla.com/beneficial-owners/FC451A7A-AE30-4404-AB95-E3553FCD733F' ``` ### Check the status of an individual Beneficial Owner After a beneficial owner has been created, the beneficial owner's identity needs to go through a verification process. A beneficial owner that has a status of `incomplete` or `document` will impact the business verified Customer's eligibility to send or receive funds. When a beneficial owner has been successfully verified by Dwolla, the beneficial owner's status will be set to verified. Reference the table below for more information on the events that correspond to each of the beneficial owner statuses: ##### Individual Beneficial Owner statuses and events | Individual Beneficial Owner Status | Event Topic Name | Transaction Restricted? | Description | | ---------------------------------- | --------------------------------------------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Verified | customer\_beneficial\_owner\_verified | No | Beneficial owner has been identity verified. | | Document | customer\_beneficial\_owner\_document\_needed | Yes - Cannot send funds | Beneficial owner must upload a document in order to be verified. | | Incomplete | customer\_beneficial\_owner\_reverification\_needed | Yes - Cannot send funds | The initial verification attempt failed because the information provided did not satisfy our verification check. You can make one additional attempt by changing some or all the attributes of the existing Customer with an [update request](/docs/api-reference/beneficial-owners/update-beneficial-owner). | Let's check to see if the Owner was successfully verified or not. We are going to use the location of the Beneficial Owner resource that was just created. ##### Request and response - retrieve a beneficial owner status ```bash theme={"dark"} GET https://api-sandbox.dwolla.com/beneficial-owners/07D59716-EF22-4FE6-98E8-F3190233DFB8 Accept: application/vnd.dwolla.v1.hal+json Authorization: Bearer pBA9fVDBEyYZCEsLf/wKehyh1RTpzjUj5KzIRfDi0wKTii7DqY ... { "_links": { "self": { "href": "https://api-sandbox.dwolla.com/beneficial-owners/00cb67f2-768c-4ee3-ac81-73bc4faf9c2b", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "beneficial-owner" }, "retry-verification": { "href": "https://api-sandbox.dwolla.com/beneficial-owners/00cb67f2-768c-4ee3-ac81-73bc4faf9c2b", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "beneficial-owner" } }, "id": "00cb67f2-768c-4ee3-ac81-73bc4faf9c2b", "firstName": "Jane", "lastName": "Owner", "address": { "address1": "123 Main St.", "city": "New York", "stateProvinceRegion": "NY", "country": "US", "postalCode": "10005" }, "verificationStatus": "verified" } ``` ```php theme={"dark"} $beneficialOwnersApi = new DwollaSwagger\BeneficialownersApi($apiClient); $owner = 'https://api-sandbox.dwolla.com/beneficial-owners/00cb67f2-768c-4ee3-ac81-73bc4faf9c2b'; $ownerStatus = $beneficialOwnersApi->getById($owner); ``` ```ruby theme={"dark"} # Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby beneficial_owner_url = 'https://api-sandbox.dwolla.com/beneficial-owners/07d59716-ef22-4fe6-98e8-f3190233dfb8' beneficial_owner = app_token.get beneficial_owner_url beneficial_owner.verificationStatus # => "verified" ``` ```python theme={"dark"} # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python beneficial_owner_url = 'https://api-sandbox.dwolla.com/beneficial-owners/07d59716-ef22-4fe6-98e8-f3190233dfB8' beneficial_owner = app_token.get(beneficial_owner_url) beneficial_owner.body['status'] ``` ```javascript theme={"dark"} var beneficialOwnerUrl = "https://api-sandbox.dwolla.com/beneficial-owners/07d59716-ef22-4fe6-98e8-f3190233dfb8"; dwolla.get(beneficialOwnerUrl).then((res) => res.body.verificationStatus); // => 'verified' ``` ## Handling an individual beneficial owner Status Congrats! You have created a beneficial owner for a business verified Customer, however, the successful creation of a beneficial Owner doesn't necessarily mean they are identity verified. You will want to ensure that the beneficial Owner is `verified`, as the business verified Customer will be unable to send or receive funds until the owner has a verified status. ### Handling `incomplete` status An `incomplete` status occurs when a beneficial owner's identity scores are too low during the initial verification attempt. Dwolla will trigger a `customer_beneficial_owner_reverification_needed` event which notifies your application to prompt the Customer to [submit another identity verification attempt](/docs/api-reference/beneficial-owners/update-beneficial-owner) for the beneficial owner. The second attempt will give our identity vendor more accurate information in an attempt to receive a sufficient score to approve the beneficial owner. The Customer will only have one opportunity to correct any mistakes. You need to gather new information if the beneficial owner is placed into the incomplete status; simply passing the same information will result in the same insufficient scores. All fields that were required in the initial beneficial owner creation attempt will be required in the incomplete attempt. #### Request and Response - update beneficial owner ```bash theme={"dark"} POST https://api-sandbox.dwolla.com/beneficial-owners/07d59716-ef22-4fe6-98e8-f3190233dfb8 Content-Type: application/vnd.dwolla.v1.hal+json Accept: application/vnd.dwolla.v1.hal+json Authorization: Bearer pBA9fVDBEyYZCEsLf/wKehyh1RTpzjUj5KzIRfDi0wKTii7DqY { "firstName": "beneficial", "lastName": "owner", "ssn": "123-54-6789", "dateOfBirth": "1963-11-11", "address": { "address1": "123 Corrected St.", "address2": "Apt 123", "city": "Des Moines", "stateProvinceRegion": "IA", "country": "US", "postalCode": "50309" } } ... { "_links": { "self": { "href": "https://api-sandbox.dwolla.com/beneficial-owners/07d59716-ef22-4fe6-98e8-f3190233dfb8", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "beneficial-owner" } }, "id": "00cb67f2-768c-4ee3-ac81-73bc4faf9c2b", "firstName": "beneficial", "lastName": "owner", "address": { "address1": "123 Corrected St.", "address2": "Apt 123", "city": "Des Moines", "stateProvinceRegion": "IA", "country": "US", "postalCode": "50309" }, "verificationStatus": "verified" } ``` ```php theme={"dark"} update([ 'firstName' => 'beneficial', 'lastName'=> 'owner', 'dateOfBirth' => '1963-11-11', 'ssn' => '123-54-6789', 'address' => [ 'address1' => '123 Corrected St.', 'address2' => 'Apt 123', 'city' => 'Des Moines', 'stateProvinceRegion' => 'IA', 'postalCode' => '50309', 'country' => 'US' ], ], $beneficialOwnerUrl); $updateBeneficialOwner->id; # => "07d59716-ef22-4fe6-98e8-f3190233dfb" ?> ``` ```ruby theme={"dark"} # Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby beneficial_owner_url = 'https://api-sandbox.dwolla.com/beneficial-owners/07d59716-ef22-4fe6-98e8-f3190233dfb8' request_body = { :firstName => 'beneficial', :lastName => 'owner', :ssn => '123-54-6789', :dateOfBirth => '1963-11-11', :address => { :address1 => '123 Corrected St', :city => 'Des Moines', :stateProvinceRegion => 'IA', :country => 'US', :postalCode => '50309' } } update_beneficial_owner = app_token.post beneficial_owner_url, request_body update_beneficial_owner.id # => "07d59716-ef22-4fe6-98e8-f3190233dfb8" ``` ```python theme={"dark"} # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python beneficial_owner_url = 'https://api-sandbox.dwolla.com/beneficial-owners/07d59716-ef22-4fe6-98e8-f3190233dfb8' request_body = { 'firstName': 'beneficial', 'lastName': 'owner', 'dateOfBirth': '1963-11-11', 'ssn': '123-54-6789', 'address': { 'address1': '123 Corrected St', 'city': 'Des Moines', 'stateProvinceRegion': 'IA', 'country': 'US', 'postalCode': '50309' } } update_beneficial_owner = app_token.post(beneficial_owner_url, request_body) update_beneficial_owner.body.id # => '07d59716-ef22-4fe6-98e8-f3190233dfb8' ``` ```javascript theme={"dark"} var beneficialOwnerUrl = 'https://api-sandbox.dwolla.com/beneficial-owners/07d59716-ef22-4fe6-98e8-f3190233dfb8'; var requestBody = { firstName: 'beneficial', lastName: 'owner', dateOfBirth: '1963-11-11', ssn: '123-54-6789', address: { address1: '123 Corrected St', city: 'Des Moines', stateProvinceRegion: 'IA', country: 'US' postalCode: '50309' } }; dwolla .post(beneficialOwnerUrl, requestBody) .then(res => res.body.id); // => '07d59716-ef22-4fe6-98e8-f3190233dfb8' ``` Check the beneficial owner's status again. The beneficial owner will either be in the `verified` or `document` state of verification. ### Handling `document` status If a beneficial owner is not verified after being placed in `incomplete` status and submitting a second verification attempt, the only other state the beneficial owner can be in is `document`. If the beneficial owner has a status of `document`, they will need to upload additional pieces of information in order to verify their identity. Use the [create a document](/docs/api-reference/documents/create-a-document-for-beneficial-owner) endpoint when uploading a colored camera captured image of the identifying document. The document(s) will then be reviewed by Dwolla; this review may take anywhere from a few seconds up to 1-2 business days if manual verification is required to approve or reject. You can provide the following best practices to the Customer in order to reduce the chances of a document being rejected: * All 4 Edges of the document should be visible * A dark/high contrast background should be used * At least 90% of the image should be the document * Should be at least 300dpi * Capture image from directly above the document * Make sure that the image is properly aligned, not rotated, tilted or skewed * No flash to reduce glare * No black and white documents * No expired IDs #### Determining verification `documents` needed ##### US persons A colored camera captured image of the Beneficial Owner's identifying document can be specified as documentType: `license` (state issued driver's license), or `idCard` (U.S. government-issued photo id card). Examples include: * Non-expired State Issued Driver's License/Identification Card * Non-expired US Passport * Federal Employment Authorization Card * US Visa ##### Non-US persons A colored camera captured image of the Beneficial Owner's identifying document can be specified as documentType: `passport`. Examples include: * Non-expired Foreign Passport ***Note:** Foreign Passports are only accepted when the individual does not have an ITIN or SSN and the user must alternatively enter the Passport number*. ### Uploading a document To upload a color photo of the document, you'll initiate a multipart form-data POST request from your backend server to the beneficial owners documents endpoint. The file must be either a .jpg, .jpeg, or .png. Files must be no larger than 10MB in size. You'll also get a `beneficial_owner_verification_document_uploaded` event to let you know the document was successfully uploaded. ##### Request and Response ```bash theme={"dark"} curl -X POST \ -H "Authorization: Bearer tJlyMNW6e3QVbzHjeJ9JvAPsRglFjwnba4NdfCzsYJm7XbckcR" \ -H "Accept: application/vnd.dwolla.v1.hal+json" \ -H "Cache-Control: no-cache" \ -H "Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW" \ -F "documentType=passport" \ -F "file=@foo.png" \ 'https://api-sandbox.dwolla.com/beneficial-owners/1de32ec7-ff0b-4c0c-9f09-19629e6788ce/documents' ... HTTP/1.1 201 Created Location: https://api-sandbox.dwolla.com/documents/11fe0bab-39bd-42ee-bb39-275afcc050d0 ``` ```php theme={"dark"} No example for this language yet ``` ```ruby theme={"dark"} beneficial_owner_url = 'https://api-sandbox.dwolla.com/beneficial-owners/1DE32EC7-FF0B-4C0C-9F09-19629E6788CE' file = Faraday::UploadIO.new('mclovin.jpg', 'image/jpeg') document = app_token.post "#{beneficial_owner_url}/documents", file: file, documentType: 'license' document.response_headers[:location] # => "https://api.dwolla.com/documents/fb919e0b-ffbe-4268-b1e2-947b44328a16" ``` ```python theme={"dark"} # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python beneficial_owner_url = 'https://api-sandbox.dwolla.com/beneficial-owners/1DE32EC7-FF0B-4C0C-9F09-19629E6788CE' document = app_token.post('%s/documents' % beneficial_owner_url, file = open('janedoe.jpg', 'rb'), documentType = 'license') document.headers['location'] # => 'https://api-sandbox.dwolla.com/documents/fb919e0b-ffbe-4268-b1e2-947b44328a16' ``` ```javascript theme={"dark"} var beneficialOwnerUrl = "https://api-sandbox.dwolla.com/beneficial-owners/1DE32EC7-FF0B-4C0C-9F09-19629E6788CE"; var requestBody = new FormData(); body.append("file", fs.createReadStream("mclovin.jpg"), { filename: "mclovin.jpg", contentType: "image/jpeg", knownLength: fs.statSync("mclovin.jpg").size, }); body.append("documentType", "license"); dwolla .post(`${beneficialOwnerUrl}/documents`, requestBody) .then((res) => res.headers.get("location")); // => "https://api-sandbox.dwolla.com/documents/fb919e0b-ffbe-4268-b1e2-947b44328a16" ``` ### Update Beneficial Owner Information Information can only be edited or updated when the Beneficial Owner has a status of `incomplete`. If an individual beneficial owner with a status of `verified` needs to update their information, that beneficial owner will first need to be removed and re-added. #### Request and Response ```bash theme={"dark"} DELETE https://api-sandbox.dwolla.com/beneficial-owners/692486f8-29f6-4516-a6a5-c69fd2ce854c Accept: application/vnd.dwolla.v1.hal+json Authorization: Bearer pBA9fVDBEyYZCEsLf/wKehyh1RTpzjUj5KzIRfDi0wKTii7DqY ... { "_links": { "self": { "href": "https://api-sandbox.dwolla.com/beneficial-owners/0f394602-d714-4d77-9d58-3a3e8394bcdd", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "beneficial-owner" } }, "id": "0f394602-d714-4d77-9d58-3a3e8394bcdd", "firstName": "B", "lastName": "Owner", "address": { "address1": "123 Main St.", "city": "New York", "stateProvinceRegion": "NY", "country": "US", "postalCode": "10005" }, "verificationStatus": "verified" } ... HTTP 200 OK ``` ```ruby theme={"dark"} # Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby beneficial_owner_url = 'https://api-sandbox.dwolla.com/beneficial-owners/692486f8-29f6-4516-a6a5-c69fd2ce854c' app_token.delete beneficial_owner_url ``` ```javascript theme={"dark"} var beneficialOwnerUrl = "https://api-sandbox.dwolla.com/beneficial-owners/692486f8-29f6-4516-a6a5-c69fd2ce854c"; dwolla.delete(beneficialOwnerUrl); ``` ```python theme={"dark"} # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python beneficial_owner_url = 'https://api-sandbox.dwolla.com/beneficial-owners/692486f8-29f6-4516-a6a5-c69fd2ce854c' app_token.delete(beneficial_owner_url) ``` ```php theme={"dark"} deleteById($beneficialOwner); ?> ``` After removal of a Beneficial Owner, they can be re-added and go through the verification process again. You can also remove a beneficial owner if they no longer own 25% or more of the business. The successful creation and verification of a beneficial owner doesn't necessarily mean the business verified Customer is verified and ready to send or receive funds. The final step in creating a business verified Customer is to certify that all information provided is correct. Read on to view the procedures on how to certify your owners. To learn how to certify beneficial owners to your Customer, read on to the next step. # Step 4 - Certify Beneficial Ownership In order for your business verified Customer to be eligible to send funds, the individual creating the business verified Customer account must certify beneficial owner(s). By certifying that all beneficial owner information is correct, the requirements imposed by the United States Federal customer due diligence rule and Dwolla will be successfully fulfilled. Certification of beneficial owners should be included as part of the Customer account registration and immediately following the creation of the business Verified Customer and the addition of all owners (unless exempt). #### How do I know what business structure is required to certify Beneficial Ownership? | If my Customer's business structure is... | ...are they required to certify beneficial ownership? | | ----------------------------------------- | ----------------------------------------------------- | | Sole proprietorships | No (exempt) | | Unincorporated association | No (exempt) | | Trust | No (exempt) | | Corporation | Yes | | Publicly traded corporations | Yes | | Non-profits | Yes | | LLCs | Yes | | Partnerships, LP's, LLP's | Yes | ### Determining Certification needed When a business verified Customer needs to be `certified`, Dwolla will return a link in the API response after [retrieving a Customer](/docs/api-reference/customers/retrieve-a-customer). If no certification link is returned, the Customer is either already `certified`, or is exempt from certification. | Link name | Description | | ---------------------------- | ------------------------------------------------------------ | | certify-beneficial-ownership | Indicates that `certification` is required for this Customer | ##### Example response ```json theme={"dark"} { "_links": { "self": { "href": "https://api-sandbox.dwolla.com/customers/41432759-6d65-42e5-a6be-400ddd103b78", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "customer" }, "certify-beneficial-ownership": { "href": "https://api-sandbox.dwolla.com/customers/41432759-6d65-42e5-a6be-400ddd103b78/beneficial-ownership", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "beneficial-ownership" } }, "id": "41432759-6d65-42e5-a6be-400ddd103b78", "firstName": "Account", "lastName": "Admin", "email": "accountAdmin@email.com", "type": "business", "status": "document", "created": "2018-05-10T19:59:22.643Z", "address1": "66 Walnut St", "city": "Des Moines", "state": "IA", "postalCode": "50309", "businessName": "Jane Corp", "controller": { "firstName": "Jim", "lastName": "Controller", "title": "CEO", "address": { "address1": "1749 18th st", "address2": "apt 12", "city": "Des Moines", "stateProvinceRegion": "IA", "country": "US", "postalCode": "50266" } }, "businessClassification": "9ed3f670-7d6f-11e3-b1ce-5404a6144203", "businessType": "llc" } ``` #### Certification Statuses | certification\_status | Transaction Restricted? | Description | | --------------------- | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | uncertified | Yes - Cannot send funds | New business verified Customers that are not exempt are initially placed in an uncertified status. | | recertify | No, for up to 30 days | Business verified Customers that are certified and change owner information, OR Business verified Customers that Dwolla needs to obtain more information from relating to beneficial owners are placed in this status. | | certified | No | Confirms the certification of beneficial owners. | ## Certify ownership To change the certification status of your business verified Customer account, you will want to POST to the beneficial ownership endpoint. By updating the certification status to `certified`, the Account Admin creating the business verified Customer is indicating that all information is correct. After the Account Admin certifies that the information provided is accurate and the information the Account Admin submitted has been verified through the identity verified process, your business verified Customer is now ready to transact within the Dwolla network. ```bash theme={"dark"} POST https://api-sandbox.dwolla.com/customers/56502f7a-fa59-4a2f-8579-0f8bc9d7b9cc/beneficial-ownership Accept: application/vnd.dwolla.v1.hal+json Content-Type: application/vnd.dwolla.v1.hal+json Authorization: Bearer pBA9fVDBEyYZCEsLf/wKehyh1RTpzjUj5KzIRfDi0wKTii7DqY { "status": "certified" } ... { "_links": { "self": { "href": "https://api-sandbox.dwolla.com/customers/56502f7a-fa59-4a2f-8579-0f8bc9d7b9cc/beneficial-ownership", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "beneficial-ownership" } }, "status": "certified" } ``` ```ruby theme={"dark"} # Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby customer_url = 'https://api-sandbox.dwolla.com/customers/e52006c3-7560-4ff1-99d5-b0f3a6f4f909' request_body = { :status => "certified" } app_token.post "#{customer_url}/beneficial-ownership", request_body ``` ```javascript theme={"dark"} var customerUrl = "https://api-sandbox.dwolla.com/customers/e52006c3-7560-4ff1-99d5-b0f3a6f4f909"; var requestBody = { status: "certified", }; dwolla.post(`${customerUrl}/beneficial-ownership`, requestBody); ``` ```python theme={"dark"} # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python customer_url = 'https://api-sandbox.dwolla.com/customers/e52006c3-7560-4ff1-99d5-b0f3a6f4f909' request_body = { "status": "certified" } app_token.post('%s/beneficial-ownership' % customer_url, request_body) ``` ```php theme={"dark"} changeOwnershipStatus(['status' => 'certified' ], $customerId); ?> ``` #### Certification Text Example Example for certification is as follows: ```plaintext theme={"dark"} "I,____ (name of Account Admin), hereby certify, to the best of my knowledge, that the information provided above is complete and correct." ``` ## Handling `recertify` status If you are adding, removing, or updating information of beneficial owners tied to a business verified Customer account, the certification status will change to `recertify`. Instances that you will see your `certified` business verified Customer change to `recertify` are as follows: * Adding a beneficial owner * Removing a beneficial owner * Updating a beneficial owner in `incomplete` status When a Customer has a `recertify` beneficial ownership status, they will have up to thirty days to update and verify their beneficial owners' information and update their status to `certified`. If the certification status isn't updated within this timeframe, the business verified Customer will have its `certification_status` changed to `uncertified`, leaving the Customer unable to transact. # Frequently Asked Questions #### Customer Eligibility

You can determine the eligibility of the customer to start transacting in the Dwolla platform by the presence of the send and receive links in the customer resource.

send - Denotes that the customer is eligible to start sending funds if they have a verified funding-source attached.

receive - Denotes that the customer is eligible to receive funds into their Dwolla balance or an attached bank funding-source. Note: Until the following actions have been completed, any funds received by the Customer will remain in their Dwolla Balance unable to be withdrawn or sent to another Customer or Account:


Tip: You can use the MCP server to programmatically check these links via the API.

  • Send funds - No
  • Receive funds - Yes - Note that funds will only process to their balance and the transfer will stay pending until the Customer has been verified.
  • Add and verify a bank funding source - Yes
  • Add and verify a Beneficial Owner - Yes
  • Send funds - No
  • Receive funds - Yes - Note that funds will only process to their balance and the transfer will stay pending until the Customer has been verified.
  • Add and verify a bank funding source - Yes
  • Add and verify a Beneficial Owner - Yes
  • Send funds - No
  • Receive funds - No
  • Add and verify a bank funding source - No
  • Add and verify a Beneficial Owner - No

Your Customer has likely not completed the bank verification process. You can check to see the status of the funding source via the API or by going into the Dwolla dashboard.

No. Downgrade functionality is not supported for Dwolla Verified Customers.

Yes, although this is not necessary, nor recommended. Dwolla manually reviews all documents, so sending more documents than necessary may slow down the verification process for your Customers.

No. At this time, Dwolla will create Business Verified Customers when they have a proper business EIN or SSN (for Sole Proprietorships only).

#### Beneficial Owner Eligibility

Your Customer will need to `certify` beneficial ownership information before your Customer will be eligible to send funds.

  • Send funds - No
  • Receive funds - Yes - Note that funds will only process to their balance and the transfer will stay `pending` until all of the Beneficial Owners have been verified and certified.
  • Add and verify a bank funding source - Yes
  • If your Beneficial Owner is an individual from the United States with a US-issued SSN, your Beneficial Owner will sign up using `ssn`.
  • If your Beneficial Owner is a non-US individual, they will use the `passport` object.
# Changelog Source: https://developers.dwolla.com/docs/changelog Keep up to date on new product features and enhancements, API docs, tools changes, and development updates. **ADDED** * **Facilitator Fees on Instant Payments:** * You can now include a [facilitator fee](/docs/facilitator-fee) on [Instant Payments](/docs/instant-payments) (RTP/FedNow) transfers, closing the functionality gap between our instant payment rails and existing ACH capabilities. * As with ACH, the fee can be charged to either the sending or receiving party and is created as a separate transfer resource with a unique transfer ID. **REMOVED** * **Visa Open Banking Integration:** * Dwolla has sunset support for Visa as an Open Banking provider for Instant Account Verification (IAV). This change affects the Visa-powered Open Banking integration that was previously available through Dwolla's Exchange Sessions API. * Existing integrations using Visa Open Banking will need to migrate to alternative Open Banking providers such as Plaid or MX. * For migration assistance and alternative Open Banking solutions, please refer to our [Open Banking documentation](/docs/open-banking) or contact [Dwolla Support](https://support.dwolla.com/s/). ##### Migration Information: * **Alternative Providers:** Dwolla continues to support Plaid and MX as Open Banking providers for Instant Account Verification. * **API Compatibility:** The Exchange Sessions API remains unchanged; only the Visa exchange partner option has been removed. * **Documentation Updates:** All references to Visa Open Banking have been updated to reflect current supported providers. **ADDED** * **FedNow® Service Integration:** * Dwolla now supports the FedNow Service alongside the existing RTP Network, providing businesses with comprehensive Instant Payments capabilities across two major U.S. instant payment rails. * FedNow Service processes payments 24/7/365 with funds available within seconds, expanding instant payment reach to over 1400 participating banks and financial institutions. ##### API Enhancements: * **Unified Processing Channel:** Use `instant` or `real-time-payments` as the `processingChannel.destination` value. Dwolla intelligently selects between RTP and FedNow based on availability and configuration. * **Enhanced Remittance Data Support:** Introduced `instantDetails` as the preferred method for passing remittance data, while maintaining backward compatibility with `rtpDetails`. * **Network-Specific Response Objects:** Transfer responses now include either `rtpDetails` (for RTP transfers) or `fedNowDetails` (for FedNow transfers) with identical structure including `networkId`, `endToEndReferenceId`, and `remittanceData`. * **Consistent Webhook Events:** Existing webhook events (`customer_funding_source_rtp_enabled`/`customer_funding_source_rtp_disabled`) now cover both RTP and FedNow eligibility changes. **ADDED** * **Verification Directives for Business Verified Customers:** * The API returns detailed Verification Directives in the `_embedded.errors` array of the [Retrieve a Customer](/docs/api-reference/customers/retrieve-a-customer) response for Business Verified Customers. These directives provide actionable guidance and relevant links to help resolve verification issues. See the new [Understanding Verification Directives section](/docs/business-verified-customer#understanding-verification-directives) in the Business Verified Customer guide. * **New and Updated Webhooks:** * Introduced and documented new webhook topics to better reflect the Business Verified Customer verification lifecycle, including: * `customer_verification_pending_review` * `customer_address_verification_failed` * (and updates to existing webhooks such as `customer_verified`, `customer_verification_document_needed`, `customer_reverification_needed` and `customer_suspended`) * **Expanded Testing Capabilities:** * Added documentation and examples for simulating verification directives in the Sandbox using the `/sandbox-simulations` endpoint. See the [Sandbox testing guide](/docs/testing#simulate-verification-directives) for details on how to test these scenarios and webhook events. **ADDED** * New Open Banking Provider: Dwolla Balance now supports Plaid as an Open Banking provider for bank account verification and balance checks. ##### Updated API Endpoints and new webhooks: To facilitate Open Banking integration with Plaid, we've updated three API endpoints and added new webhook events: * **/customers//exchange-sessions (POST)**: Updated API endpoint to support Plaid as a provider. * **/exchange-sessions/ (GET)**: Added new atrribute `externalProviderSessionToken` to the response to accommodate Plaid-specific information, such as Plaid link token. * **/customers//exchanges**: Modified to include a `plaid` JSON object containing a Plaid `publicToken`. * Introduced new webhook `customer_exchange_reauth_required`, an exchange related event specific to Plaid. This webhook will be triggered when a user's bank connection has been interrupted or is requiring re-authentication in order to ensure continued access to the user's financial data. **CHANGED** ##### Description: * **Multiple Redirect URLs:** You can now configure multiple redirect URLs for your exchange sessions, providing more flexibility in your integration. * **Required Redirect URL:** A redirect URL is now required when [creating an exchange session](/docs/open-banking#creating-an-exchange-session) to ensure proper validation and security. ##### Changes: * The endpoint for creating an exchange session now requires a redirect url parameter, which is a single URL that is already configured with Dwolla [API docs](/docs/open-banking#creating-an-exchange-session). * The validation process for redirect URLs has been enhanced to ensure they meet security standards and are accessible. The exchange sessions API endpoint will return an HTTP 400 `ValidationError` with an `Invalid` code and message of `The provided redirect URL must exactly match one of the configured URLs for the account`. * Refer to the [developer documentation](/docs/open-banking) for detailed usage instructions and API reference [Docs](/docs/open-banking#creating-an-exchange-session). **ADDED** * Added support for retrieving real-time bank balance information using Dwolla's Open Banking solution. Businesses can now verify the current balance of a user's bank account before initiating ACH payments. This feature helps mitigate the risk of insufficient funds and improves payment processing efficiency. ##### Updated API Endpoint: To facilitate retrieving the bank balance, we've updated an API endpoint to support a new schema for bank balances: * **/funding-sources/id/balance (GET):** This API endpoint will return a JSON response containing the balance amount, currency, available balance, closing balance, and last updated timestamp. Refer to the developer documentation for detailed usage instructions and [API reference](/docs/open-banking/bank-balance-check). **ADDED** * Dwolla Balance and Connect now supports Open Banking, a secure and standardized approach for accessing financial data from various financial institutions. This empowers your application to connect directly with a user's bank through trusted partners like Plaid and MX, enabling a seamless Instant Account Verification experience. ##### New API Endpoints: To facilitate Open Banking integration, we've introduced two new API endpoints: * **/customers/id/exchange-sessions (POST) & /external-parties/id/exchange-sessions (POST)**: These API endpoints allow you to initiate an exchange session for a specific customer or external party. The exchange session establishes a connection with a chosen Open Banking partner (e.g., Plaid or MX) to initiate the Instant Account Verification process. * /exchange-sessions/id (GET): Use this endpoint to retrieve the URL associated with an initiated exchange session which is used to handle the IAV process. **ADDED** * Added a new `retry-with-full-ssn` link to the Customer resource. This link appears whenever retry information is required for the Controller in order to verify the business verified Customer. * Check out our [Developer documentation](/docs/business-verified-customer#handling-retry-status) for more information. **SUNSET** * Dwolla has retired the `dwolla.js` library, which supported adding an unverified bank funding source. Check out the official [announcement](https://discuss.dwolla.com/t/may-2023-updates-retiring-dwolla-s-client-side-javascript-library-dwolla-js/8969). * The legacy dwolla.js has been replaced with functionality that has been added to our [Drop-in Components library](/docs/drop-in-components#latest-v220). This library will serve as our primary web UI components library and offer an enhanced developer experience. Please refer to our [migration guide](https://discuss.dwolla.com/t/migration-guide-dwolla-js-to-dwolla-web-js/8968) for more details, including alternative solutions. * The following API endpoints are removed as part of the sunset: `/customers/{id}/iav-token` and `/customers/{id}/funding-sources-token`. **DEPRECATED** * Dwolla has discontinued support for the Push-to-Debit product feature, which was powered by dwolla-cards.js. Includes removal of card related webhooks as well as the funding source type for cards to support Push-to-Debit payments. **ADDED** * Added a new third-party data provider, Flinks, to Dwolla's [Secure Exchange solution](/docs/secure-exchange). **DEPRECATED** * Dwolla has discontinued support for the Instant Account Verification (IAV) product, which was powered by Dwolla.js. * As alternative bank account verification options, we recommend utilizing one of our integrated third-party data providers — [Finicity](https://github.com/Dwolla/integration-examples/tree/main/packages/finicity-token-exchange) or [MX](https://github.com/Dwolla/integration-examples/tree/main/packages/mx-token-exchange) via our [Secure Exchange](/docs/secure-exchange), or [Plaid](/docs/secure-exchange/plaid). **ADDED** * Added support for including [facilitator fees](/docs/facilitator-fee) when creating transfers from Verified Customer's Bank into their Dwolla balance. Previously, fees could only be applied to transactions between two parties. **ADDED** * Added the `correlationId` attribute to the [webhook payload](/docs/webhook-events#webhook-payload) for transfer related webhooks if a value was specified on transfer creation. **ADDED** * Added new API endpoints for [Exchanges](/docs/api-reference/exchanges) and Exchange Partners with the release of the [Secure Exchange solution](/docs/secure-exchange). The Secure Exchange solution connects clients with integrated ecosystem partners to seamlessly share data and initiate account-to-account payments. **ADDED** * Added a new document failure reason, `ForeignPassportNotAllowed`, for when a foreign passport is uploaded for Personal Verified Customers. Foreign passports are still accepted when uploaded for Business Controllers or Beneficial Owners. **ADDED** * Added volume-based rate limits for **all Dwolla API endpoints**. To learn more, check out our [Rate Limits](/docs/api-reference/api-fundamentals/rate-limits) section under [API Reference](/docs/api-reference). **ADDED** * Added a new document failure reason, `ScanIdUnrecognized`, for both business and personal customers. **ADDED** * Added a new `documentVerificationStatus` field to the document resource. This field indicates the status of the document after it has been reviewed by Dwolla. Possible values include `pending`, `accepted` and `rejected`. * Check out our [Developer documentation](/docs/api-reference/documents) for more information and an example API response. **ADDED** * Added a new `upload-dba-document` link to the Customer resource. This link appears whenever a DBA (Doing Business As) document is required from the Customer to verify their business. * Added an `_embedded` object to the Customer resource which contains a list of errors related to getting the Customer verified. The `_embedded` object appears whenever the Customer is in `retry` or `document`. * Check out our [Developer documentation](/docs/business-verified-customer#determining-verification-documents-needed) for more information. **ADDED** * Added client-side form validation to the `Add a Debit Card` form in `dwolla-cards.js`. This enables the form to display helpful error messages to the user whenever they enter invalid data. **ADDED** * Added new Business document failure reasons to the API - `BusinessDocNotSupported`, `BusinessNameMismatch`, `BusinessTypeMismatch`. * Updated failure descriptions for `ScanFailedOther` and `ScanNameMismatch` to provide more details about the failure reason. * Check out our [Developer documentation](/docs/business-verified-customer#document-failure). **ADDED** * Added Drop-in component for [adding Beneficial Owners](/docs/drop-in-components#create-beneficial-owners). This pre-built UI component provides a low-code solution for assisting with onboarding business Verified Customers within your application. Learn more about [Drop-in components](/docs/drop-in-components#ui-components-library) in our documentation and check out our guide on [building with Drop-in components](/docs/drop-in-components/building-with-drop-ins#building-with-drop-in-components). **ADDED** * Added support for creating [access-tokens](/docs/api-reference/tokens/create-an-application-access-token) and [client-tokens](/docs/api-reference/client-tokens/create-a-client-token) in the dwolla-swagger-php SDK. Check it out on [Github](https://github.com/Dwolla/dwolla-swagger-php/releases/tag/1.6.0). **ADDED** * Added Drop-in components. These are pre-built UI components that provide a low-code solution for integrating parts of the Dwolla API into your application. Learn more about [Drop-in components](/docs/drop-in-components#ui-components-library) in our documentation and check out our guide on [building with Drop-in components](/docs/drop-in-components/building-with-drop-ins#building-with-drop-in-components). **ADDED** * Added support for simulating `document` status for Business Verified Customers in Sandbox. Check out our [Testing in the Sandbox](/docs/testing#simulate-identity-verification-statuses) guide to learn more. **ADDED** * Added a new funding source type for cards to support Push-to-Debit payments. * **Sunset Notice:** Push-to-Debit was sunset on 2023-05-31. **UPDATED** * Updated the [List of possible return codes, descriptions, and actions](/docs/transfer-failures#list-of-possible-return-codes-descriptions-and-actions) table with a new column for determining if certain ACH return codes cause a Funding Source with a `verified` status to become `unverified`. **CHANGED/ADDED** * Removed support for [uploading personal identification documents](/docs/api-reference/documents/create-a-document-for-customer) in the file format of `.pdf`. A validation error will be returned with a `code` of `"Invalid"` and a `message` of `"Invalid file type"`. **CHANGED/ADDED** * Removed support for uploading duplicate documents for a Customer in `document` status. If a request to upload a duplicate document is sent, it will fail with a validation error response that includes a link to the existing uploaded document for the Customer. **ADDED** * Added a new Knowledge-based Authentication (`KBA`) resource to the API. * KBA is a component of verifying the identity of Personal Verified Customers. * Head over to our [API Docs](/docs/api-reference/kba) or check out our [blog post](https://www.dwolla.com/updates/kba-streamline-user-onboarding-individuals/) to learn more. **ADDED** * Added support for including the `clearing` JSON object when creating a mass payment. * The `clearing` object allows for the clearing time of individual mass payment items to be upgraded or downgraded from the default ACH processing time. * Head over to our [API Docs](/docs/api-reference/mass-payments) to learn more. **CHANGED** * Added a new JSON object called `allFailureReasons` to the Document resource, which helps with further identifying the reason for the rejection of an identity verification document uploaded for a Verified Customer. * Check out our [Developer documentation](/docs/api-reference/documents#document-resource). **CHANGED** * Updated the me-to-me funds flow to support creating transfers between two banks of a Customer with a single transfer request. * Check out our [Resource Article](/docs/transfer-money-me-to-me) to learn more. **ADDED** * Added a new Kotlin SDK. * Check it out on our [Github](https://github.com/Dwolla/dwolla-v2-kotlin) for more information and to provide feedback. **ADDED** * Added a new `total` attribute to the Balance object in the API. * Introducing total and available balance amounts for a Dwolla Balance. * Head over to our [API Docs](/docs/api-reference/funding-sources/retrieve-funding-source-balance) or check out our [resource article](/docs/balance-funding-source#retrieve-the-balance-amount) to learn more. **ADDED** * Added a new `Labels` resource to the API. * A Label represents a designated portion of funds within a Verified Customer's balance. * Head over to our [API Docs](/docs/api-reference/labels) or check out our blog post [to learn more](https://www.dwolla.com/updates/real-time-usd-ledger-labels/). **ADDED** * Added a new attribute called `traceId` to the `achDetails` object within the Transfer resource, which helps with further identifying a transfer to/from a user's bank account. Jump to our [API Docs](/docs/api-reference/transfers). **UPDATED** * Dwolla allows an application to request an access token using its Client Id and Client Secret by leveraging the Client Credentials OAuth grant type. Access tokens are used to make requests to the Dwolla API on behalf of an application and its users (customers). * Previously, applications made a call to `https://www.dwolla.com/oauth/v2/token` and specified the `application/x-www-form-urlencoded` `Content-Type` header, passing their client credentials (App Key and App Secret) through the body of the HTTP message sent to Dwolla. * With this update, the token URL as well as the manner in which an application's client credentials are sent to Dwolla, will change to be inline with OAuth spec. * The new Dwolla token exchange endpoint is `https://api.dwolla.com/token` * Reference our [API Reference Docs](/docs/api-reference/tokens/create-an-application-access-token) to learn more. **DEPRECATED** * Dwolla has discontinued support for the v1 API and Transfer API * Learn more on our [blog post](https://www.dwolla.com/updates/sunsetting-a-legacy/) for more information. **ADDED** * Addenda support–The addenda record is used to provide additional information to the payment recipient about the payment. This value will be passed in a transfer request and can be exposed on your user's bank statement. Addenda records provide a unique opportunity to supply your users with more information about their transactions. * Learn more on our [blog post](https://www.dwolla.com/updates/customizing-payments-businesses/). **DEPRECATED** * Dwolla has discontinued support for TLS 1.0 and TLS 1.1. * Learn more on our [blog post](https://www.dwolla.com/updates/improving-transport-layer-security/). **UPDATED** * Dwolla has discontinued support for `.tif` file upload. \*\*UPDATED/**ADDED** * New webhook `funding_source_negative` and `customer_funding_source_negative` * Jump to our [dev docs](/docs/api-reference/events). **UPDATED** - SDK - C# * New version of C# SDK. * Breaking changes: * DwollaClient no longer throws on API errors, they should be properly deserialized into RestResponse.Error instead. * DwollaException, RestException, and RestResponse.Exception are removed. * Use `EmptyResponse` instead of `object` in DwollaClient interface. * Check it out on our [Github](https://github.com/Dwolla/dwolla-v2-csharp) **CHANGED** * Change in verified business Customer creation flow across Platform. Check out our [developer guide](/docs/business-verified-customer) to learn how to create this Customer type within the new flow. * In order to comply with United States Federal law, Dwolla also requires beneficial owners to be added to a Customer. Read our [blog post](https://www.dwolla.com/updates/understanding-impacts-benefits-customer-due-diligence-rule/) to learn more about why we need to comply with US customer due diligence rules. **CHANGED** * Verified business Customers creation flow has changed in sandbox. Check out our [developer guide](/docs/business-verified-customer) to learn how to create this Customer type within the new flow. * In order to comply with United States Federal law, Dwolla also requires beneficial owners to be added to a Customer. Read our [blog post](https://www.dwolla.com/updates/understanding-impacts-benefits-customer-due-diligence-rule/) to learn more about why we need to comply with US customer due diligence rules. * Please note that this change will go live in across platform on May 11th, 2018. **ADDED** - Developer Guide * Added a new Developer Guide that goes over the new business verified Customer creation flow with Dwolla. * [Click here](/docs/business-verified-customer) to view the article **UPDATED/ADDED** - Dev Docs update * Updated Customer endpoints to reflect the [new business verified Customer flow change](/docs/api-reference/customers). * Added the new [beneficial owners endpoints](/docs/api-reference/beneficial-owners) and [webhooks](/docs/api-reference/events#customer-account-event-topics). **UPDATED** - Dev Docs update * Added method and URL to Documents section **UPDATED** - Developer Resources Article * Changed screenshot to reflect the proper On-Demand Auth Language. **UPDATED** - Developer Resources Article * Updated transfer failures doc * Updated verification handling doc **UPDATED** - Dev Docs update * Updated docs to reflect business name updates. **CHANGED** * Bank [balance check](/docs/api-reference/funding-sources/retrieve-funding-source-balance) functionality changing to be asynchronous and immediately return an HTTP 202. The response body for the 202 will contain a status relating to the processing of this request. Subsequent requests to this endpoint will return a 202 up until processing completes and then either return an HTTP 200 with the current balance or an HTTP 400 if there was an error (i.e. `UnsupportedBank`). **ADDED** * Added a new `customer_balance_inquiry_completed` event. Upon checking a Customer's bank balance, Dwolla will immediately return an HTTP 202 with response body that includes a status of processing. This event will be triggered when the bank balance check has completed processing. To read more on how to trigger this event, check out our [forum post](https://discuss.dwolla.com/t/check-it-out-new-events/4554). **ADDED** * Added a new `customer_bank_transfer_creation_failed` event. This event will be triggered when an attempt to initiate a transfer to a verified Customer's bank was made, but failed. Transfers initiated to a verified Customer's bank must pass through the verified Customer's balance before being sent to a receiving bank. Dwolla will fail to create a transaction intended for a verified Customer's bank if the funds available in the balance are less than the transfer amount. To read more on how to trigger this event, check out our [forum post](https://discuss.dwolla.com/t/check-it-out-new-events/4554). **CHANGED/DEPRECATED** * Changing "uat" in the subdomain of public facing and API URLs with "sandbox". Reference [this blog post](https://www.dwolla.com/updates/important-sandbox-updates-subdomain-change-and-sunsetting-of-the-sandbox-console/) for more information. **ADDED** * Release support for a new (optional) `backButton` and `subscriber` options for IAV within dwolla.js. Note: Dwolla.js is a premium feature only available for Dwolla [API](https://www.dwolla.com/platform) customers. **DEPRECATED** * Removal of the Sandbox Console tool which exists at: `https://sandbox-uat.dwolla.com/` **ADDED** * Release support for a new `clearing` request parameter when [initiating a transfer](/docs/api-reference/transfers/initiate-a-transfer). Clearing is a JSON object that supports specifying same-day and standard ACH clearing per API request. Note: The clearing request parameter is a premium feature available for Dwolla API customers. **DEPRECATED** * Remove the `scope` attribute from the [application access token](/docs/api-reference/tokens/create-an-application-access-token) response. **CHANGED** * Automatic pause of webhook subscriptions after 400 consecutive failed delivery attempts. Reference the [API docs](/docs/api-reference/webhook-subscriptions) for more information. **CHANGED** * Change the `phone` request parameter from required to optional when [creating a Customer](/docs/api-reference/customers/create-a-customer). **ADDED** * Added a new `bankName` attribute to the funding source object that is returned when retrieving a funding source of type bank. **DEPRECATED** Remove the `profileId` request parameter from the Off-Site Gateway in API v1. **ADDED** * Release support for an optional `Idempotency-Key` header on requests to API v2. **CHANGED/DEPRECATED** * Change in functionality for [removing a funding source](/docs/api-reference/funding-sources/update-or-remove-a-funding-source) in API v2. The method for removing a funding source changes from a `DELETE` to a `POST` with the need to supply `{"removed": true}` in the body of the request. * A `removed` attribute is added to the [funding source object](/docs/api-reference/funding-sources#funding-source-resource). * A `removed` querystring request parameter is supplied when listing an [Account](/docs/api-reference/accounts/list-funding-sources-for-an-account) or [Customer's](/docs/api-reference/funding-sources/list-customer-funding-sources) funding sources. By default, all funding sources are returned from the listing unless the `removed` request parameter is set to `false`. **DEPRECATED** * Removal of the `description` field in API v2 error responses. Replacing description with the `message` field which is a duplication of description. * Removing the `X-Request-Signature` header from webhook requests. Replacing with a `X-Request-Signature-SHA-256` header which is a SHA-256 HMAC hash of the request body with the key being your webhook secret. **ADDED** * Release endpoint in API v2: retrieve a list of business classifications for a Customer. `POST /business-classifications` * Release endpoint in API v2: retrieve a business classification by it's id for a Customer. `POST /business-classifications/{id}` * Release business verified Customer creation in API v2. **ADDED** * Release endpoint in API v2: generate a funding sources token for a Customer. `POST /customers/{id}/funding-sources-token` * Release endpoint in API v2: retrieve ACH transfer failure reason. `GET /transfers/{id}failure` **ADDED** * Release endpoint in API v2: generate an IAV token for a Customer. `POST /customers/{id}/iav-token` * Release `dwolla.js` to the CDN. `https://cdn.dwolla.com/1/dwolla.js` **CHANGED** * Error changes - Introduce new `message` field in error response. Errors now include a `profile` link in the `Content-Type` header. Error responses with the top-level error code `ValidationError` will return an `_embedded` object containing a list of errors. **CHANGED** * OAuth `redirect_uri` must match the OAuth Redirect URL field set in Dwolla application's settings. **ADDED** * Release endpoint in API v2: initiate or verify micro-deposits for bank verification. `POST /funding-sources/{id}/micro-deposits` # Customer Types Source: https://developers.dwolla.com/docs/customer-types Understanding and selecting the appropriate Customer type is the first step to a successful payment integration. ## Overview A Dwolla API Customer is created programmatically by your CIP verified Dwolla Master Account via the [Create a Customer](/docs/api-reference/customers/create-a-customer) endpoint. All of the Customer's required information will be handled through the API and the Customer will interact directly with your application to manage their account. As a developer, you will want to create the Customer type that best suits the business case of your application. Below is a very high level detailing of the Customer types available with Dwolla. Regardless of which Customer type(s) you choose to create for your application, keep in mind that all users onboarded as Customers must be US persons of age 18 or older. #### Customer types reference | **Customer Type** | CIP Verification | Dwolla `balance` | Default Transaction Send Limit | Transact with | | ------------------------------ | ---------------- | ---------------- | ------------------------------ | ----------------------------------------- | | **Personal verified Customer** | Yes | Yes | \$10,000 per **transfer** | All Customer types | | **Business verified Customer** | Yes | Yes | \$10,000 per **transfer** | All Customer type | | **Unverified Customer** | No | No | \$5,000 per **week** | Verified Customers, Dwolla Master Account | | **Receive-only User** | No | No | N/A | Verified Customers, Dwolla Master Account | * The transaction send limits can be customized. Contact Sales for more information.
* By default, there is no limit for receiving funds.
As you decide what type of customer to create for your application, a good thing to keep in mind is [Customer Identification Program (CIP) Verification](https://www.dwolla.com/updates/guide-customer-identification-program-payments-api). Remember that regardless of your application type, a transfer between two parties requires that at least one party must be CIP verified. It is your decision about which party completes this process based on your business model. Your own Dwolla Master Account can count as a verified party. You may also consider having both parties complete CIP verification, as we also require CIP verification in order for a customer to hold funds in the Dwolla network in the form of a balance. ## Verified Customer Verified Customers are defined by their ability to both send and receive money. They can also interact with any customer type and hold a `balance` funding source within the Dwolla network. Think of the Dwolla `balance` as a wallet which a Customer can hold, send or receive funds to within the Dwolla network. There are two types of verified Customer types your Customer can sign up as: `Personal` or `Business`. ### Personal Verified Customer Personal Verified Customers can be used in any funds flows, as they can both send and receive funds. This Customer type can also hold a balance. The individual being onboarded as a personal Verified Customer will need to complete the identity verification process prior to being able to send or receive funds from/to their bank account. CIP verification involves passing Customer data to verify them, including their name, date of birth, and last four digits of their social security number. For more information about verifying this Customer type in our [Customer verification guide](/docs/personal-verified-customer). With a per-transaction default send limit of \$10,000, this Customer type is able to interact with Dwolla and your application seamlessly. ### Business Verified Customer Business Verified Customers are unique in their sign up flow, as they need multiple parties to be verified. These will include: * The Business (required) * The Controller (Conditionally Required) * The Beneficial Owner (Conditionally Required) Business Verified Customers will need an Account Admin to sign up the company during the onboarding process. This Account Admin is not identity verified. To become a fully verified Customer, a controller and/or a beneficial owner may need to be identity verified. A controller is any natural individual who holds significant responsibilities to control, manage, or direct a company or other corporate entity (i.e. CEO, CFO, General Partner, President, etc). A company may have more than one controller, but only one controller's information must be collected. A beneficial owner is any natural person who, directly or indirectly, owns 25% or more of the equity interests of the company. The Controller will need to provide information to be fully identity verified. This includes their last four SSN and date of birth for identity verification purposes. For certain business types, a business' EIN will also need to be provided as part of the CIP process. For more information about adding a controller, check out our [business verified Customer creation guide](/docs/business-verified-customer). Certain business types may also need to add and certify beneficial ownership. You can find more information about adding beneficial owners in our [developer resource article](/docs/business-verified-customer#step-3-adding-beneficial-owners). To learn more about certifying beneficial ownership, reference the [certify beneficial ownership](/docs/business-verified-customer#step-4-certify-beneficial-ownership) section in the business verified customer guide. For a full series of steps that goes in depth on business verified Customers, take a look at our [Customer verification guide](/docs/business-verified-customer). This guide also goes into detail on the identity verification process for controllers and beneficial owners. ## Unverified Customer An unverified Customer type requires a minimal amount of information : `firstName`, `lastName` `email`, and optionally `businessName` for businesses. While Customer creation and onboarding is light weight compared to a verified Customer, there are a few things to consider when choosing this customer type. Unverified Customers have a default transaction send limit of `$5000` per week. A week is defined as Monday to Sunday UTC time. If you have an unverified Customer looking to send more than `$5,000` in a week, you may want to explore [upgrading them to a verifed Customer type](/docs/api-reference/customers/update-a-customer) or [contacting sales](https://www.dwolla.com/contact?b=apidocs) for more information about customizing the send limit. As this Customer is not CIP verified, they will only be able to transact with verified Customers or your Dwolla Master Account. ## Receive-only User Receive-only Users are restricted to payouts only funds flow. This user type maintains limited functionality in the API and is only eligible to receive transfers to an attached bank account. This user type can only interact with verified Customers and a Dwolla Master Account. Receive-only Users cannot send funds back. If you need this user to send funds back for any reason, you may need to resolve this outside of the Dwolla network. # Drop-in Components Source: https://developers.dwolla.com/docs/drop-in-components Dwolla's Drop-in components are low-code solutions to alleviate the technical overhead of a payments integration to rapidly implement key functionality into an application. ## Overview Dwolla's drop-in components library allows developers to leverage isolated functions or build connected flows in their web applications, which expedites the integration process with the Dwolla Platform. Each component within Dwolla's drop-in components library includes HTML, CSS and JavaScript that developers can drop-in and customize to fit the look and feel of their application. The library comes with a collection of low-code components that solve for a variety of functions and flows including: create a customer, document upload, balance display, as well as a connected flow for accepting incoming payments from a user. Each drop-in component contains built-in features such as responsive design, custom styling, error handling and more. These components allow developers to ship more with fewer lines of code— while improving readability and maintainability of their application's code. This is a language-agnostic library, meaning that any webpage that supports client-side JavaScript will support drop-in components! For ease of use, however, we also offer [JSX/TSX bindings](https://github.com/Dwolla/react-drop-ins) if you are developing using React or a React-based environment (such as Next). As the library continues to grow, Dwolla will evaluate adding support for other frameworks based on community feedback. ## Workflow Use of Dwolla's drop-in components requires client-side and server-side interaction between your application and Dwolla. A unique "client token" is generated with limited permissions to be used in the components library to authenticate requests to Dwolla. On your application's front-end, the Dwolla components library is instantiated and configured. One or many components are dropped into the web page where they will be rendered. A request is sent from your front-end to your back-end server to generate a client-token. Using a server-side SDK, you'll specify the "action" needed for the component and the unique Customer ID that represents the end user performing the action. Your server sends the generated client token back to your front-end which is used by the components library to authenticate the client-side request to Dwolla. Your end user interacts with the Dwolla Component, either directly via submission of information in a form (e.g. upgrade customer), or indirectly by viewing data (e.g. balance display). ## Drop-in Component Example Dwolla's drop-in components are customizable to match the look and feel of your application down to the individual HTML element by applying styles via custom CSS classes. Preview the Unverified Customer component below, or refer to the [drop-ins examples repo](https://github.com/Dwolla/drop-ins-examples) to view a list of all drop-in component examples. Upgrade a Customer Drop-in Component ## Setup Every component shares the same one-time setup: load `dwolla-web.js`, then call `dwolla.configure({ ... })`. The `tokenUrl` you provide points at your own back-end proxy, which mints the scoped client token each component needs. Because the proxy handles token generation, you don't need to specify individual client-token actions per component. ```html theme={"dark"} ``` For a full walkthrough, reference the [Drop-in Components Guide](/docs/drop-in-components/building-with-drop-ins#step-1-setup-and-configuration). ## Supported Components Dwolla's UI components library contains a variety of supported components that represent isolated functions or connected flows. This section outlines the complete list of supported components. For each component you'll find a description of when to use it, its HTML tag, configurable attributes, CSS classes for customization, and a preview. For more information on integrating drop-in components, reference our Guide which walks through how to use drop-in components in full detail. ### Create a Receive Only User dwolla-customer-create Renders a form that collects the information needed to create a **Receive Only user** (`type="receive-only"`). Receive Only users are restricted to a payouts-only funds flow. Use this when you need to pay out to a recipient who won't send funds on your platform. To learn more about this customer type, [visit our docs](/docs/customer-types) on concepts. #### Usage This assumes you've already called `dwolla.configure({ ... })` once during [setup](#setup). ```html theme={"dark"} ``` ```html theme={"dark"}
```
#### Attributes | Attribute | Description | Required | | --------------- | --------------------------------------------------------------- | -------- | | `type` | Set to `receive-only` to create a Receive Only user. | Yes | | `terms` | URL to your Terms of Service, shown in the acceptance checkbox. | Yes | | `privacy` | URL to your Privacy Policy, shown in the acceptance checkbox. | Yes | | `firstName` | Pre-fills the customer's first name. | No | | `lastName` | Pre-fills the customer's last name. | No | | `email` | Pre-fills the customer's email. | No | | `businessName` | Pre-fills the business name. | No | | `ipAddress` | Pre-fills the end user's IP address. | No | | `correlationId` | Your identifier to correlate the customer to your system. | No | View all required vs. optional parameters in our [API Reference](/docs/api-reference/customers/create-a-customer). ```css theme={"dark"} dwolla-customer-create, dwolla-input-container, dwolla-customer-input, dwolla-customer-firstName, dwolla-customer-lastName, dwolla-customer-email, dwolla-customer-tos, dwolla-customer-checkbox, dwolla-customer-text, dwolla-submit, dwolla-customer-submit, dwolla-success, dwolla-success-message, dwolla-error, dwolla-error-message ``` Create a Receive Only User Drop-in Component ### Create an Unverified Customer dwolla-customer-create Renders a form that collects the minimal information needed to create an **Unverified Customer**: first name, last name, email, and optionally a business name. Add the `isBusiness` attribute to prompt for a business name. Use this for the lightest-weight customer record; you can [upgrade](#upgrade-an-unverified-customer) them to a Verified Customer later. To find out more about the abilities and limitations of this customer type, [visit our docs](/docs/customer-types#unverified-customer) on concepts. #### Usage This assumes you've already called `dwolla.configure({ ... })` once during [setup](#setup). ```html theme={"dark"} ``` ```html theme={"dark"}
```
#### Attributes | Attribute | Description | Required | | --------------- | --------------------------------------------------------------- | -------- | | `terms` | URL to your Terms of Service, shown in the acceptance checkbox. | Yes | | `privacy` | URL to your Privacy Policy, shown in the acceptance checkbox. | Yes | | `isBusiness` | Prompts the user to enter a business name. | No | | `firstName` | Pre-fills the customer's first name. | No | | `lastName` | Pre-fills the customer's last name. | No | | `email` | Pre-fills the customer's email. | No | | `businessName` | Pre-fills the business name. | No | | `ipAddress` | Pre-fills the end user's IP address. | No | | `correlationId` | Your identifier to correlate the customer to your system. | No | View all required vs. optional parameters in our [API Reference](/docs/api-reference/customers/create-a-customer). ```css theme={"dark"} dwolla-customer-create, dwolla-input-container, dwolla-customer-input, dwolla-customer-firstName, dwolla-customer-lastName, dwolla-customer-email, dwolla-customer-tos, dwolla-customer-checkbox, dwolla-customer-text, dwolla-submit, dwolla-customer-submit, dwolla-success, dwolla-success-message, dwolla-error, dwolla-error-message ``` Create a Customer Drop-in Component Unverified Customer with a business name: Create an Unverified Business Customer Drop-in Component ### Upgrade an Unverified Customer dwolla-customer-update Renders a form that upgrades an existing **Unverified Customer** into a **Personal Verified Customer**, giving them higher transaction limits and the ability to hold a balance. Use this when a customer who started out unverified is ready to be fully verified. For more information on the difference between an Unverified and Verified Customer, [visit our docs](/docs/customer-types) on concepts. #### Usage This assumes you've already called `dwolla.configure({ ... })` once during [setup](#setup). ```html theme={"dark"} ``` ```html theme={"dark"}
```
#### Attributes | Attribute | Description | Required | | --------------- | --------------------------------------------------------------- | -------- | | `customerId` | ID of the Unverified Customer to upgrade. | Yes | | `terms` | URL to your Terms of Service, shown in the acceptance checkbox. | Yes | | `privacy` | URL to your Privacy Policy, shown in the acceptance checkbox. | Yes | | `firstName` | Pre-fills the customer's first name. | No | | `lastName` | Pre-fills the customer's last name. | No | | `email` | Pre-fills the customer's email. | No | | `ipAddress` | Pre-fills the end user's IP address. | No | | `correlationId` | Your identifier to correlate the customer to your system. | No | View all required vs. optional parameters in our [API Reference](/docs/api-reference/customers/update-a-customer#update-unverified-and-receive-only). ```css theme={"dark"} dwolla-customer-update, dwolla-input-container, dwolla-customer-input, dwolla-customer-firstName, dwolla-customer-lastName, dwolla-customer-email, dwolla-customer-address1, dwolla-customer-address2, dwolla-customer-city, dwolla-customer-state, dwolla-customer-postal, dwolla-customer-dob, dwolla-customer-ssn, dwolla-customer-tos, dwolla-customer-checkbox, dwolla-customer-text, dwolla-submit, dwolla-customer-submit, dwolla-success, dwolla-success-message, dwolla-error, dwolla-error-message ``` Upgrade an Unverified Customer Drop-in Component ### Create a Personal Verified Customer dwolla-personal-vcr Renders a form that collects the information needed to create a **Personal Verified Customer** — an individual who can send, receive, and hold a Dwolla balance. Use this to onboard a fully verified individual directly, without first creating an Unverified Customer and upgrading later. To learn more about the different customer types, [visit our docs](/docs/customer-types) on concepts. The same component also handles **updates, verification retries, and document uploads** for an existing customer — just pass a `customerId`. See [Update, retry, or upload documents](#update-retry-or-upload-documents) below. #### Usage This assumes you've already called `dwolla.configure({ ... })` once during [setup](#setup). ```html theme={"dark"} ``` ```html theme={"dark"}
```
#### Attributes | Attribute | Description | Required | | --------------- | --------------------------------------------------------------- | -------- | | `terms` | URL to your Terms of Service, shown in the acceptance checkbox. | Yes | | `privacy` | URL to your Privacy Policy, shown in the acceptance checkbox. | Yes | | `customerId` | ID of an existing customer to update, retry, or document. | No | | `firstName` | Pre-fills the customer's first name. | No | | `lastName` | Pre-fills the customer's last name. | No | | `email` | Pre-fills the customer's email. | No | | `ipAddress` | Pre-fills the end user's IP address. | No | | `correlationId` | Your identifier to correlate the customer to your system. | No | View all required vs. optional parameters in our [API Reference](/docs/api-reference/customers/create-a-customer). #### Update, retry, or upload documents The `dwolla-personal-vcr` component isn't limited to creating a new customer — pass an existing customer's `customerId` and the component checks their current [verification status](/docs/personal-verified-customer#handling-verification-statuses) and renders the right flow: * **`retry`** — displays a form so the customer can correct and resubmit their identifying information. * **`document`** — presents the document upload flow so the customer can upload an identifying document to complete verification. ```html theme={"dark"} ``` ```css theme={"dark"} dwolla-input-container, dwolla-customer-input, dwolla-customer-firstName, dwolla-customer-lastName, dwolla-customer-email, dwolla-customer-address1, dwolla-customer-address2, dwolla-customer-city, dwolla-customer-state, dwolla-customer-postal, dwolla-customer-dob, dwolla-customer-ssn, dwolla-customer-tos, dwolla-customer-checkbox, dwolla-customer-text, dwolla-submit, dwolla-vcr-submit, dwolla-success, dwolla-success-message, dwolla-error, dwolla-error-message, dwolla-info, dwolla-info-message ``` Create a Personal Verified Customer Drop-in Component ### Create a Business Verified Customer dwolla-business-vcr Renders a form that collects the information needed to create a **Business Verified Customer**. Business Verified Customers can send and receive funds, hold a Dwolla balance, and have a transfer limit of `$10,000` per transfer. Use this to onboard a business entity. To learn more about the different customer types, [visit our docs](/docs/customer-types) on concepts. The same component also handles **updates, verification retries, and document uploads** for an existing customer — just pass a `customerId`. See [Update, retry, or upload documents](#update-retry-or-upload-documents-for-a-business-customer) below. #### Usage This assumes you've already called `dwolla.configure({ ... })` once during [setup](#setup). ```html theme={"dark"} ``` ```html theme={"dark"}
```
#### Attributes | Attribute | Description | Required | | --------------- | --------------------------------------------------------------- | -------- | | `terms` | URL to your Terms of Service, shown in the acceptance checkbox. | Yes | | `privacy` | URL to your Privacy Policy, shown in the acceptance checkbox. | Yes | | `customerId` | ID of an existing customer to update, retry, or document. | No | | `hideDBAField` | Hides the "Doing Business As" field from the form. | No | | `firstName` | Pre-fills the controller's first name. | No | | `lastName` | Pre-fills the controller's last name. | No | | `email` | Pre-fills the customer's email. | No | | `ipAddress` | Pre-fills the end user's IP address. | No | | `correlationId` | Your identifier to correlate the customer to your system. | No | View all required vs. optional parameters in our [API Reference](/docs/api-reference/customers/create-a-customer). #### Update, retry, or upload documents for a business customer Like the Personal VCR component, the `dwolla-business-vcr` component can be reused for an existing customer — pass their `customerId` and the component checks the current [verification status](/docs/business-verified-customer#handling-retry-status) and renders the right flow: * **`retry`** — displays a form so the customer can correct and resubmit the business and controller information. * **`document`** — presents the document upload flow so the customer can upload the required identifying document(s) to complete verification. ```html theme={"dark"} ``` ```css theme={"dark"} dwolla-input-container, dwolla-customer-input, dwolla-half-button, dwolla-half-button-secondary, dwolla-customer-firstName, dwolla-customer-lastName, dwolla-customer-email, dwolla-customer-address1, dwolla-customer-address2, dwolla-customer-city, dwolla-customer-state, dwolla-customer-country, dwolla-customer-postal, dwolla-customer-dob, dwolla-customer-ssn, dwolla-customer-tos, dwolla-customer-checkbox, dwolla-customer-text, dwolla-submit, dwolla-vcr-submit, dwolla-success, dwolla-success-message, dwolla-error, dwolla-error-message, dwolla-info, dwolla-info-message, dwolla-document-type, dwolla-document-type-select, dwolla-document-type-select-label, dwolla-document-choose, dwolla-document-chooser, dwolla-document-chooser-label, dwolla-file-name, dwolla-document-name-display, dwolla-document-name-span, dwolla-document-description, dwolla-document-label, dwolla-document-submit, dwolla-customer-businessIndustry, dwolla-customer-businessClassification, dwolla-customer-businessType, tooltip, tooltip.tooltiptext, tooltip-shift ``` Create a Business Verified Customer Drop-in Component ### Create Beneficial Owners dwolla-beneficial-owners Renders a form that collects the information needed to [add Beneficial Owners](/docs/business-verified-customer#step-3-adding-beneficial-owners) after a Business Verified Customer has been created. Use this to certify beneficial ownership; it can be paired with the [Business Verified Customer](#create-a-business-verified-customer) component or used on its own. To learn more about the different customer types, [visit our docs](/docs/customer-types) on concepts. #### Usage This assumes you've already called `dwolla.configure({ ... })` once during [setup](#setup). ```html theme={"dark"} ``` ```html theme={"dark"}
```
#### Attributes | Attribute | Description | Required | | ------------ | ----------------------------------------------------------------- | -------- | | `customerId` | ID of the Business Verified Customer to add beneficial owners to. | Yes | View all required vs. optional parameters in our [API Reference](/docs/api-reference/beneficial-owners/create-beneficial-owner). ```css theme={"dark"} dwolla-bo-submit, dwolla-document-submit, dwolla-customer-state, dwolla-customer-input, dwolla-customer-country, dwolla-width-1, dwolla-width-2, dwolla-width-3, dwolla-document-label, dwolla-document-type, dwolla-document-name, dwolla-document-name-display, dwolla-document-submit, dwolla-document-choose, dwolla-document-chooser, dwolla-document-description, dwolla-span-container, dwolla-file-name, dwolla-half-button, dwolla-half-button-secondary, dwolla-owner-name, dwolla-owner-delete, dwolla-owner-header, dwolla-owners-summary-container, dwolla-owners-empty, dwolla-add-owners-button, dwolla-owner, dwolla-owner-status, dwolla-owner-status-verified, dwolla-owner-status-incomplete, dwolla-owner-status-document, dwolla-button-label-container-nb, dwolla-button-label-container, dwolla-link, dwolla-customer-tos, dwolla-customer-checkbox, dwolla-customer-text, dwolla-text-container, dwolla-input-container, dwolla-loading ``` Add Beneficial Owners Drop-in Component ### Document Upload dwolla-document-upload Renders a document upload form for a Verified Customer or Beneficial Owner who has a `document` status and needs to upload an identifying document to complete verification. Use this when a government-issued document is required to verify an individual or business's identity. #### Usage This assumes you've already called `dwolla.configure({ ... })` once during [setup](#setup). ```html theme={"dark"} ``` ```html theme={"dark"}
```
#### Attributes | Attribute | Description | Required | | ------------ | -------------------------------------------------- | -------- | | `customerId` | ID of the customer who needs to upload a document. | Yes | View all required vs. optional parameters in our [API Reference](/docs/api-reference/documents/create-a-document-for-customer). ```css theme={"dark"} dwolla-document-type, dwolla-document-type-select, dwolla-document-type-select-label, dwolla-document-chooser, dwolla-document-chooser-label, dwolla-document-name, dwolla-document-name-span, dwolla-document-submit ``` Document Upload Drop-in Component ### Create a Funding Source dwolla-funding-source-create Renders a form that collects the information needed to create a [bank funding source](/docs/api-reference/funding-sources) attached to a [customer](/docs/api-reference/customers) record. Optionally, add the `initiateMicroDeposits` attribute to automatically [initiate micro-deposits](/docs/api-reference/funding-sources/initiate-or-verify-micro-deposits) when the funding source is created. This component currently does not support on-demand authorization when creating a funding source. Instead, if you wish to make use of this feature, the interaction between the API and the customer must happen outside of the drop-ins component library at this time. #### Usage This assumes you've already called `dwolla.configure({ ... })` once during [setup](#setup). ```html theme={"dark"} ``` ```html theme={"dark"}
```
#### Attributes | Attribute | Description | Required | | ----------------------- | ------------------------------------------------------------------------------------------------------- | -------- | | `customerId` | ID of the customer this funding source will attach to. | Yes | | `initiateMicroDeposits` | If present, micro-deposits are automatically initiated *if the funding source is created successfully*. | No | View all required vs. optional parameters in our [API Reference](/docs/api-reference/funding-sources/create-customer-funding-source). ```css theme={"dark"} /* Input Form */ .dwolla-input-container, .dwolla-funding-source-input, .dwolla-funding-source-name, .dwolla-funding-source-type, .dwolla-funding-source-routingNumber, .dwolla-funding-source-accountNumber, .dwolla-funding-source-submit /* Error Messaging */ #dwolla-error, #dwolla-error-message /* Success Messaging */ #dwolla-success, #dwolla-success-message ``` Create Funding Source Drop-In Component ### Verify Micro Deposits dwolla-micro-deposits-verify Renders a form that collects the micro-deposit amounts needed to verify a customer's [bank funding source](/docs/api-reference/funding-sources). Present this component once micro-deposits have successfully posted into the customer's bank funding source. Check out our guide on [verifying a bank with micro-deposits](/docs/micro-deposit-verification) for more information. #### Usage This assumes you've already called `dwolla.configure({ ... })` once during [setup](#setup). ```html theme={"dark"} ``` ```html theme={"dark"}
```
#### Attributes | Attribute | Description | Required | | ----------------- | ---------------------------------------------------------------- | -------- | | `customerId` | ID of the customer the funding source belongs to. | Yes | | `fundingSourceId` | ID of the funding source to which micro-deposits were initiated. | Yes | ```css theme={"dark"} /* Input Form */ dwolla-input-container, dwolla-micro-deposits-input, dwolla-micro-deposits-amount-one, dwolla-micro-deposits-amount-two, dwolla-micro-deposits-submit, dwolla-loading, /* Success Messaging */ dwolla-success, dwolla-success-message, /* Error Messaging */ dwolla-error, dwolla-error-message, /* Info Messaging */ dwolla-info, dwolla-info-message ``` Verify Micro Deposits Drop-In Component ### Display a Verified Customer's Balance dwolla-balance-display Displays the Dwolla balance for a customer who already has one — a Personal Verified Customer or a Business Verified Customer. Add the `hideZeroBalance` attribute to suppress the display when the balance is `$0.00`. #### Usage This assumes you've already called `dwolla.configure({ ... })` once during [setup](#setup). ```html theme={"dark"} ``` ```html theme={"dark"}
```
#### Attributes | Attribute | Description | Required | | ----------------- | ----------------------------------------------------------------- | -------- | | `customerId` | ID of the customer whose balance will be shown. | Yes | | `hideZeroBalance` | If present, the balance is not shown when it is equal to `$0.00`. | No | ```css theme={"dark"} dwolla-balance, dwolla-balance-display, dwolla-error ``` Balance Display Drop-in Component ### Pay-In dwolla-payin Renders a connected flow for transferring funds from a customer's verified funding source into your own Dwolla Client funding source. The customer can be an Unverified, Personal Verified, or Business Verified Customer, and must already have a verified funding source. To add and verify a funding source, you can: * [Add a bank](#create-a-funding-source) with an account and routing number, then verify it with [micro-deposits](#verify-micro-deposits) * Add and verify a bank with [Open Banking](/docs/open-banking) #### Usage This assumes you've already called `dwolla.configure({ ... })` once during [setup](#setup). ```html theme={"dark"} ``` ```html theme={"dark"}
```
#### Attributes | Attribute | Description | Required | | ------------ | ---------------------------------------------- | -------- | | `customerId` | ID of the customer sending funds. | Yes | | `blob` | Encrypted transfer payload provided by Dwolla. | Yes | | `token` | Token used to authenticate the transfer. | Yes | | `amount` | Pre-fills the transfer amount. | No | ```css theme={"dark"} dwolla-payin, dwolla-payin-submit, dwolla-amount, dwolla-amount-label, dwolla-amount-display, dwolla-funding-sources-label, dwolla-funding-sources, dwolla-payin-title ``` Pay In Drop-in Component ## Next steps Leveraging Dwolla's UI Components library is a great way to expedite your integration with the Dwolla Platform by limiting the amount of custom code that you would be required to write. Get started building with drop-in components by checking out the [getting started guide](/docs/drop-in-components) and the API Reference documentation. ## Changelog ### `v3.1.0` (Latest) The latest version of **dwolla-web.js** is `3.1.0`. If you are currently using an earlier version, we recommend [upgrading](/docs/drop-in-components/building-with-drop-ins#step-1-setup-and-configuration) to `v3.1.0`. * Enhanced multi-document verification for Business Verified Customers in the `` drop-in component. * Expanded support for "Doing Business As" (DBA) and combined controller and business document flows, so the upload UI presents all required document type options. * Improved verification tracking to evaluate each document by type against the customer's live status after every upload, so accepted documents no longer re-prompt and pending ones are not re-requested. * Added an uploaded-documents summary showing each document's type, verification status, and any failure reasons. * Added DBA-specific guidance when a "Doing Business As" document is requested. ### `v3.0.0` * **BREAKING**: Added mandatory Terms of Service and Privacy Policy acceptance checkbox for Receive-Only customers in `` drop-in component. * The `type="receive-only"` attribute now requires `terms` and `privacy` attributes to be provided. * Users must accept the Terms of Service and Privacy Policy before creating a Receive-Only customer (required for regulatory compliance). * Receive-Only customers display client's Terms of Service and Privacy Policy (not Dwolla's). * Fixed duplicate `id` attributes in checkbox inputs across multiple drop-in components. ### `v2.2.2` * Updated user hints and prompts for documentation upload screens to be correct and consistent throughout the following drop-in components: * `` * `` * `` * `` ### `v2.2.1` * Renamed `Name` field to `Account Nickname` in `` drop-in for clarity. ### `v2.2.0` * Added `` drop-in component. * Added `` drop-in component. * Updated `dwolla.configure` `token` callback function to receive `{ _links, action, links }`, rather than just `{ action, links }`. This will allow immediate pass-through without modification to the Dwolla API while maintaining backwards compatibility for existing implementations. * Made `postalCode` optional in `` drop-in for Business Verified Customers with non-US controllers. ### `v2.1.9` * Fixed `Submit` button double-click issue on customer creation drop-ins. * Improve SSN field validation. * Added optional `hideZeroBalance` attribute to `` drop-in. * Added new Account Opening drop-in ``. ### `v2.1.8` * Added optional `correlationId` field to ``, `` and `` drop-ins. * Added optional `businessName` field to `` drop-in. * Added optional `hideDBAField` attribute to `` drop-in. * Made `EIN` optional in the `` drop-in for Business Verified Customers of type Sole Proprietorship. * All components are now flow-type components requiring multiple calls to the `client-tokens` endpoint for granularly scoped actions. * Implemented hard-versioning of dwolla-web.js; to use the latest version, you will need to import the exact version rather than just the major version using the CDN script. ### `v2.1.6` * Changed `success` callback response structure. In previous versions, upon successful creation of a resource, the `location` of the newly created resource is returned. In v2.1.6 and onwards, the success JSON response will contain a top-level `resource` and a `response` object with the location to the newly created resource. ```raw theme={"dark"} # Used to be: { "location":"https://api-sandbox.dwolla.com/customers/c81cf726-77ff-4a2a-bfde-1fb1fb90cefd" } # Changed to: { "resource":"customers", "response":{ "location":"https://api-sandbox.dwolla.com/customers/c81cf726-77ff-4a2a-bfde-1fb1fb90cefd" } } ``` # Building with Drop-in Components Source: https://developers.dwolla.com/docs/drop-in-components/building-with-drop-ins Dwolla's Drop-in Components are low-code solutions to abstract away the complexity of integrating with a payment API and act as a shortcut to completing a payment integration. ## Overview Dwolla offers a robust white labeled payments API that enables businesses to fully customize the user experience end-to-end. The flexibility provided with the Dwolla platform offers a high degree of customization enabling you, the integrating business, to design and build the entire payments experience. Your business is responsible for all interactions with your end users, including collecting required information via your application and sending it to Dwolla via [the API](https://developers.dwolla.com/docs/api-reference). Dwolla's UI Components are low-code tools designed to abstract away as much of the front-end work involved for interacting with your end user for Dwolla related functions. The components library has modularized many aspects of the Dwolla Platform and offers pre-built "drop-in" components that can be customized to match the look and feel of your application's UI. In this guide, we'll cover the basics of leveraging Dwolla's UI Components library to drop-in a wide range of payment functionalities into your application. ## Before you begin If you haven't already, we encourage you to create a Sandbox account. This will allow you to follow along with the steps outlined in this guide. Check out our [Sandbox guide](/docs/testing) to learn more. In addition to using the Dwolla Components Library, you'll need a [server-side SDK](/docs/sdks-tools) in order to facilitate the API request to Dwolla to retrieve a [client-token](/docs/api-reference/client-tokens/create-a-client-token). A client-token is a unique token scoped to the end user/Customer performing the action on the front-end of your web application. In order to utilize these drop-in components, you will want to have an understanding of the different Customer account types available to be created for your end users. For more information on Customer types, c​heck out our [Concept article](/docs/customer-types/). In this guide, we'll learn how to use the "Create a Business Verified Customer" component. Let's get started! # Step 1 - Setup and configuration We'll begin by setting up and configuring both the Dwolla Components library and installing a server-side SDK. This guide assumes that you have the basic structure of a web application set up and running, which includes a backend language (e.g. Node.js, Python, etc.) and a web front end (HTML, CSS, Javascript). To get up and running quickly, take a look at our [drop-ins examples repo](https://github.com/Dwolla/drop-ins-examples) which is a basic Node.js app that uses [Express](https://expressjs.com/). ### Include dwolla-web.js Begin the client-side implementation by including dwolla-web.js in the 'head' of your HTML page. The Dwolla Components library containing all of the drop-in UI components are available directly from Dwolla's content delivery network (CDN). As we continue to evolve the components library, it will soon be available to be installed via NPM. ```html theme={"dark"} ``` ### Install and configure server-side SDK Dwolla has a collection of SDKs available in a variety of server-side programming languages. You'll want to be sure to install an SDK or utilize a third party HTTP client library before completing Step 2. #### Install ```ruby theme={"dark"} gem install dwolla_v2 ``` ```python theme={"dark"} pip install dwollav2 ``` ```javascript theme={"dark"} npm install dwolla-v2 ``` ```php theme={"dark"} composer require dwolla/dwollaswagger composer install ``` #### Configure ```ruby configure_client.rb theme={"dark"} require 'dwolla_v2' # Navigate to https://dashboard.dwolla.com/applications (production) or https://dashboard-sandbox.dwolla.com/applications (Sandbox) for your application key and secret. app_key = "..." app_secret = "..." $dwolla = DwollaV2::Client.new(key: app_key, secret: app_secret) do |config| config.environment = :sandbox # optional - defaults to production end ``` ```python configure_client.py theme={"dark"} import dwollav2 # Navigate to https://dashboard.dwolla.com/applications (production) or https://dashboard-sandbox.dwolla.com/applications (Sandbox) for your application key and secret. app_key = '...' app_secret = '...' client = dwollav2.Client(key = app_key, secret = app_secret, environment = 'sandbox') # optional - defaults to production ``` ```javascript configureClient.js theme={"dark"} const dwolla = require("dwolla-v2"); // Navigate to https://dashboard.dwolla.com/applications (production) or https://dashboard-sandbox.dwolla.com/applications (Sandbox) for your application key and secret. const appKey = "..."; const appSecret = "..."; const client = new dwolla.Client({ key: appKey, secret: appSecret, environment: "sandbox", // optional - defaults to production }); ``` ```php configure_client.php theme={"dark"} ``` Now that we've completed our initial setup, we'll move on to the next step of generating a "client-token" which will be used when configuring the drop-in components library. # Step 2 - Generate a client token Regardless of which drop-in component is being used by your application, the dwolla-web.js JavaScript library will require a unique "client-token" or a server side route that is used to generate a client token to be passed in on [configuration](#configure-dwolla-webjs). A client token contains granular permissions and is scoped to the end user/Customer that is performing the action within your web application. Client-tokens are single-use tokens that are valid for up to 1 hour after being generated. More than one client token can be generated and be valid at one time. ## Token URL vs Token Configuration A majority of the drop-in components have multiple isolated functions connected together into a single user flow. Throughout the flow of a component, the dwolla-web library will make multiple HTTP calls to an endpoint you have set up on your server side for fetching client-tokens as needed. In the following sections we'll outline the difference between the two options available in dwolla-web.js for retrieving client-tokens; using either the `tokenUrl` or `token` configuration. ### Using Token URL Using the `tokenUrl` option when configuring dwolla-web.js requires creating a server-side endpoint that can be called by the library when an action needs to be performed within a flow component. Establishing an endpoint enables the ability for the component to fetch client-tokens on-demand in order to render the appropriate UI. The server-side endpoint should act as a pass-through by taking in the request body required in order to make the appropriate call to the '/client-tokens' endpoint. Upon success, the '/client-tokens' endpoint will return a response body including a "token" string value. The HTTP response that is returned to the client-side is expecting an object in the format of `{token: 'Token string value'}`. More information on configuration of dwolla-web.js can be found in [step 3](#configure-dwolla-webjs) of this guide. #### Example of setting up a token endpoint server side using Express In this example we're using Express.js to set up a token URL which will be called by `dwolla-web.js` when the component that's being used needs to generate a client token that's being performed in the flow. ```javascript theme={"dark"} /** * Using Dwolla Node.js SDK - https://github.com/Dwolla/dwolla-v2-node * Refer to Step 1 on setup and configuration of Dwolla SDK */ app.post("/tokenUrl", function (req, res) { generateClientTokenWithBody(req.body).then((clientTokenRes) => { console.log(clientTokenRes); res.send({ token: clientTokenRes.token }); }); }); function generateClientTokenWithBody(body) { const url = `/client-tokens`; return dwolla .post(url, body) .then((response) => { return response.body; }) .catch((error) => { return error; }); } ``` ##### Example configuration using `tokenUrl` ```javascript theme={"dark"} ``` ### Using Token Using the `token` option when configuring the dwolla-web.js library allows you to create a function that calls your server-side `tokenUrl` endpoint (as described above). The function you create takes in two arguments, 1) the request body that is required to make the '/client-tokens' API request; and 2) a JSON object that includes key:value pairs for specifying custom headers. When an action needs to be performed within a flow component, the dwolla-web.js library will call your custom function and dynamically pass in a request body as the first argument. The server-side endpoint should act as a pass-through by taking in the request body required in order to make the appropriate call to the '/client-tokens' endpoint. Upon success, the '/client-tokens' endpoint will return a response body including a "token" string value. The HTTP response that is returned to the client-side is expecting an object in the format of `{token: 'Token string value'}`. #### Example of setting up a function to call `tokenUrl` using Express ```javascript theme={"dark"} function dwollaAPIToken(req, additional) { const tokenUrl = "/tokenUrl"; const data = { action: req.action, }; if (req.links) { data._links = req.links; } const headers = { Accept: "application/json", "Content-Type": "application/json", "X-Requested-With": "Dwolla-Drop-Ins-Library", }; return fetch(`${tokenUrl}`, { credentials: "include", method: "POST", body: JSON.stringify(data), headers, }) .then((response) => { return response.json(); }) .then((result) => { return result; }) .catch((error) => { console.log(error); return error; }); } ``` ##### Example configuration using `token` ```javascript theme={"dark"} ``` # Step 3 - Using a drop-in Next, you'll create a custom HTML container that the drop-in component will render in on page load. A drop-in component will be rendered when the dwolla-web.js library is initialized using dwolla.configure() (referenced below). ### Configure dwolla-web.js Configuration of the dwolla object includes: token or tokenUrl, environment, optional styles, along with success and error overrides. #### Configure options object | Parameter | Type | Description | | ----------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | environment | string | Acceptable values of: `sandbox` or `production` | | styles | string | Optional. A relative or absolute URL linking to a hosted stylesheet containing component styles. | | token | function | A function that gets called by the component for fetching client-tokens as needed throughout the flow.
Example usage: `token: (req) => Promise.resolve(dwollaAPIToken(req, {blah: "abcd"}))` | | tokenUrl | function | A URL pointing to a server-side endpoint that can be used to generate client-token.
Example usage: `tokenUrl: "tokenUrl"` | | success | function | A function that gets called upon a successful request from the Component. | | error | function | A function that gets called when an error occurs in the Component. | Using `tokenUrl` ```javascript theme={"dark"} ``` Using `token` ```javascript theme={"dark"} ``` ### Component Styles Dwolla provides a list of CSS classes available for styling certain elements of the component. These elements can be customized to match the look and feel of your application and are styled by passing in a custom stylesheet when configuring the dwolla-web client library. By default, the elements within your specified container are responsive to any change in screen size. For a full list of supported CSS classes available for each component, view our [Concept article](/docs/drop-in-components#supported-components). ### Using Drop-in Components It's important to note that with the exception of a "Create a Customer" component like the one found below, all components require a Customer ID to be passed into a `customerId` element in order to initialize the component. This Customer ID should come from your back-end server when generating a client-token for an end user/Customer and can optionally be stored in a session or cookie. ```html theme={"dark"}
``` When loading the page containing the component, you should see the following: Business Verified Customer Drop-in Component ## Handle component success and errors Upon submission of form components, a user facing message will be displayed to the end user on success or error. These user facing messages can be styled via custom div and span CSS classes corresponding to: * `dwolla-success` and `dwolla-success-message` * `dwolla-error` and `dwolla-error-message` ### Error callback The error callback function catches any exceptions encountered with the component and returns a string. Example: `We have encountered an issue fetching a token.` ### Success callback The success callback function is called when the component makes a request to the API. However, it doesn't necessarily mean that the API request itself was successful. It means that the component was implemented correctly and no exceptions were encountered. The success function returns a JSON object that contains a `resource` which denotes the resource in the API related to the action being performed, and a `response` which contains any relevant information from the API response. If the component creates a new resource in the API, the `response` will contain a `location` key:value pair including a link to the created resource. Otherwise, it will contain the JSON response body from the API that corresponds to the action that occurred. Here are some example responses you can expect in the success callback: ##### Example - HTTP 201 Created - New Customer creation ```bash theme={"dark"} { "resource": "customers", "response": { "location": "https://api-sandbox.dwolla.com/customers/e169aa24-92ad-4dd2-acdf-6803bb09c63e" } } ``` ##### Example - HTTP 200 Ok - Beneficial Ownership certification ```bash theme={"dark"} { "resource": "customers/ffbbc743-5e5c-4c54-b675-06ddf4bc4029/beneficial-ownership" "response": { "_links": { "self": { "href": "https://api-sandbox.dwolla.com/customers/ffbbc743-5e5c-4c54-b675-06ddf4bc4029/beneficial-ownership", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "beneficial-ownership" } }, "status": "certified" } } ``` ##### Example - HTTP 400 Error - Duplicate Document upload ```bash theme={"dark"} { "resource": "customers/f4a8b9f1-ad93-40d6-8115-7ed228780282/documents", "response": { "code": "DuplicateResource", "message": "Document already exists.", "_links": { "about": { "href": "https://api-sandbox.dwolla.com/documents/a8c89bb1-df17-4d23-8a8c-e0c6b65a0909", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "document" } } } } ``` # Facilitator Fee Source: https://developers.dwolla.com/docs/facilitator-fee Charge users a flat rate amount to be removed from a payment as a fee. The fee is sent to the creator of the application. ## Overview The facilitator fee is a feature allowing for a flat rate amount to be removed from a payment as a fee, and sent to the creator of the Dwolla application. The fee does not affect the original payment amount, and exists as a separate Transfer resource with a unique transfer ID. #### Items to note before charging fees: * Fees cannot be applied on [me-to-me](/docs/transfer-money-me-to-me) transfers where the source is the Customer's Dwolla Balance * Facilitator fees are supported on both ACH and [Instant Payments](/docs/instant-payments) (RTP/FedNow) transfers * Facilitator fees can be debited from either the sending or receiving user of the payment * The sum of fees cannot exceed 50% of the original transfer amount This limit can be removed upon further approval from the Dwolla team. If approved, only the sender of the transfer can be charged fees that exceed 50% of the transfer amount. Furthermore, the sum of fees cannot exceed the transaction limit of the sender. Please contact [support@dwolla.com](mailto:support@dwolla.com) or your account manager for approval. * A facilitator fee must be at least \$0.01 * Multiple facilitator fees can be associated with a single payment * You must clearly communicate the fee and its payment terms to your end user and obtain the user's express consent to charge the fee ### Charging fees on transfers Fees are programmatically set on an individual transfer API request, regardless of the processing channel—the same `fees` array works for both ACH and [Instant Payments](/docs/instant-payments) transfers. Within a transfer request you can specify an optional `fees` request parameter, which is an array of fee objects that can represent many unique fee transfers. If your platform wishes to charge a percentage of the total transfer amount then your application will need to compute the percentage prior to initiating the transfer request. **Note:** Fees must be deducted from one of the accounts that is involved in either sending or receiving the funds for a transfer (not an alternative account). A fee object is made up of a `_links` and an `amount` JSON object. The `_links` object contains `charge-to`, which represents the associated source or destination [Customer](/docs/api-reference/customers) or [Account](/docs/api-reference/accounts) resource that will assume the fee. The `amount` object contains `value` and `currency` keys corresponding to the fee amount and `USD` respectively. When a facilitator fee is added to a transfer request, it is treated as a separate transfer which does not affect a Customer's weekly, or per transaction limit. #### A fee object: ```json theme={"dark"} { "_links": { "charge-to": { "href": "https://api-sandbox.dwolla.com/customers/d795f696-2cac-4662-8f16-95f1db9bddd8" } }, "amount": { "value": "4.00", "currency": "USD" } } ``` ##### Example transfer request: ```raw HTTP theme={"dark"} POST https://api-sandbox.dwolla.com/transfers Accept: application/vnd.dwolla.v1.hal+json Content-Type: application/vnd.dwolla.v1.hal+json Authorization: Bearer pBA9fVDBEyYZCEsLf/wKehyh1RTpzjUj5KzIRfDi0wKTii7DqY { "_links":{ "source":{ "href":"https://api-sandbox.dwolla.com/funding-sources/707177c3-bf15-4e7e-b37c-55c3898d9bf4" }, "destination":{ "href":"https://api-sandbox.dwolla.com/funding-sources/a2152a8a-b1a6-4b5e-9354-79e2bb8753ee" } }, "amount":{ "value":"10.00", "currency":"USD" }, "fees":[ { "_links":{ "charge-to":{ "href":"https://api-sandbox.dwolla.com/customers/479ce4c8-385f-4cfa-9693-262c0c3b6408" } }, "amount":{ "value":"2.00", "currency":"USD" } } ] } ``` ```php create_transfer.php theme={"dark"} create([ '_links' => [ 'source' => [ 'href' => 'https://api-sandbox.dwolla.com/funding-sources/707177c3-bf15-4e7e-b37c-55c3898d9bf4', ], 'destination' => [ 'href' => 'https://api-sandbox.dwolla.com/funding-sources/a2152a8a-b1a6-4b5e-9354-79e2bb8753ee' ] ], 'amount' => [ 'currency' => 'USD', 'value' => '98.00' ], 'fees' => [ [ '_links' => [ 'charge-to' => [ 'href' => 'https://api-sandbox.dwolla.com/customers/479ce4c8-385f-4cfa-9693-262c0c3b6408' ] ], 'amount' => [ 'value' => '2.00', 'currency' => 'USD' ] ] ] ]); print_r($transfer); # => "https://api-sandbox.dwolla.com/transfers/74c9129b-d14a-e511-80da-0aa34a9b2388" ?> ``` ```ruby create_transfer.rb theme={"dark"} # Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby request_body = { :_links => { :source => { :href => "https://api-sandbox.dwolla.com/funding-sources/707177c3-bf15-4e7e-b37c-55c3898d9bf4" }, :destination => { :href => "https://api-sandbox.dwolla.com/funding-sources/a2152a8a-b1a6-4b5e-9354-79e2bb8753ee" } }, :amount => { :currency => "USD", :value => "98.00" }, :fees => [ { :_links => { :charge-to => { :href => "https://api-sandbox.dwolla.com/customers/479ce4c8-385f-4cfa-9693-262c0c3b6408" } }, :amount => { :value => '2.00', :currency => 'USD' } } ] } transfer = app_token.post "transfers", request_body transfer.response_headers[:location] # => "https://api.dwolla.com/transfers/74c9129b-d14a-e511-80da-0aa34a9b2388" ``` ```python create_transfer.py theme={"dark"} # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python request_body = { '_links': { 'source': { 'href': 'https://api-sandbox.dwolla.com/funding-sources/707177c3-bf15-4e7e-b37c-55c3898d9bf4' }, 'destination': { 'href': 'https://api-sandbox.dwolla.com/funding-sources/a2152a8a-b1a6-4b5e-9354-79e2bb8753ee' } }, 'amount': { 'currency': 'USD', 'value': '98.00' }, 'fees':[ { '_links': { 'charge-to': { 'href': 'https://api-sandbox.dwolla.com/customers/479ce4c8-385f-4cfa-9693-262c0c3b6408' } }, 'amount':{ "value":'2.00', 'currency':'USD' } } ] } transfer = app_token.post('transfers', request_body) transfer.headers['location'] # => 'https://api.dwolla.com/transfers/74c9129b-d14a-e511-80da-0aa34a9b2388' ``` ```javascript createTransfer.js theme={"dark"} var requestBody = { _links: { source: { href: 'https://api-sandbox.dwolla.com/funding-sources/707177c3-bf15-4e7e-b37c-55c3898d9bf4' }, destination: { href: 'https://api-sandbox.dwolla.com/funding-sources/a2152a8a-b1a6-4b5e-9354-79e2bb8753ee' } }, amount: { currency: 'USD', value: '98.00' }, fees: [ { _links: { 'charge-to': { href: 'https://api-sandbox.dwolla.com/customers/479ce4c8-385f-4cfa-9693-262c0c3b6408' } }, amount: { value: '2.00', currency: 'USD' } } ] }; dwolla .post('transfers', requestBody) .then(res => res.headers.get('location')); // => 'https://api-sandbox.dwolla.com/transfers/74c9129b-d14a-e511-80da-0aa34a9b2388' ``` ### Retrieve fees charged on a transfer Once a transfer is successfully created, subsequent transfers will be created that represent the associated fees on that transfer. These fees will not be charged until the transfer processes successfully to the destination user. In the event of a `failed` or `cancelled` payment no fees will be charged. ##### Example Get a transfer's fees request: ```bash HTTP theme={"dark"} GET https://api-sandbox.dwolla.com/transfers/83eb4b5e-a5d9-e511-80de-0aa34a9b2388/fees Accept: application/vnd.dwolla.v1.hal+json Authorization: Bearer pBA9fVDBEyYZCEsLf/wKehyh1RTpzjUj5KzIRfDi0wKTii7DqY ... { "transactions": [ { "_links": { "self": { "href": "https://api-sandbox.dwolla.com/transfers/416a2857-c887-4cca-bd02-8c3f75c4bb0e" }, "source": { "href": "https://api-sandbox.dwolla.com/customers/b442c936-1f87-465d-a4e2-a982164b26bd" }, "destination": { "href": "https://api-sandbox.dwolla.com/accounts/ca32853c-48fa-40be-ae75-77b37504581b" }, "created-from-transfer": { "href": "https://api-sandbox.dwolla.com/transfers/83eb4b5e-a5d9-e511-80de-0aa34a9b2388" } }, "id": "416a2857-c887-4cca-bd02-8c3f75c4bb0e", "status": "pending", "amount": { "value": "2.00", "currency": "usd" }, "created": "2016-02-22T20:46:38.777Z" }, { "_links": { "self": { "href": "https://api-sandbox.dwolla.com/transfers/e58ae1f1-7007-47d3-a308-7e9aa6266d53" }, "source": { "href": "https://api-sandbox.dwolla.com/customers/b442c936-1f87-465d-a4e2-a982164b26bd" }, "destination": { "href": "https://api-sandbox.dwolla.com/accounts/ca32853c-48fa-40be-ae75-77b37504581b" }, "created-from-transfer": { "href": "https://api-sandbox.dwolla.com/transfers/83eb4b5e-a5d9-e511-80de-0aa34a9b2388" } }, "id": "e58ae1f1-7007-47d3-a308-7e9aa6266d53", "status": "pending", "amount": { "value": "1.00", "currency": "usd" }, "created": "2016-02-22T20:46:38.860Z" } ], "total": 2 } ``` ```php retrieve_transfer_fees.php theme={"dark"} getFeesBySource($transferUrl); $transferFees->total; # => "2" ?> ``` ```ruby retrieve_transfer_fees.rb theme={"dark"} # Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby transfer_url = "https://api-sandbox.dwolla.com/transfers/83eb4b5e-a5d9-e511-80de-0aa34a9b2388" fees = app_token.get "#{transfer_url}/fees" fees.total # => 2 ``` ```python retrieve_transfer_fees.py theme={"dark"} # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python transfer_url = 'https://api-sandbox.dwolla.com/transfers/83eb4b5e-a5d9-e511-80de-0aa34a9b2388' fees = app_token.get('%s/fees' % transfer_url) fees.body['total'] # => 2 ``` ```javascript retrieveTransferFees.js theme={"dark"} var transferUrl = "https://api-sandbox.dwolla.com/transfers/83eb4b5e-a5d9-e511-80de-0aa34a9b2388"; dwolla.get(`${transferUrl}/fees`).then((res) => res.body.total); // => 2 ``` ### Reconciling fees Since a fee is a separate transfer in itself, it will show up in the transfer listing of either the [Account](/docs/api-reference/accounts/list-and-search-transfers-for-an-account) or [Customer](/docs/api-reference/customers/list-and-search-customers) resource, depending on which party is sending or receiving the fee. To correlate a fee to the transfer that the fee was charged on, a key of `created-from-transfer` will be returned in the list of links on a unique transfer resource. The `created-from-transfer` key can also be used to differentiate a fee from other transfer types. **Refunding fees:** Within the Dwolla API, an endpoint does not exist to `refund` a processed transfer from the receiving user or account back to the sending party—this includes fees if any were charged. Refunds occur by the destination user initiating a separate transfer in reverse from the funding source in which they received the funds. As a facilitator who received funds from the fee, you must determine if the charged user will incur the cost of the fee or be refunded for the original fee amount charged. ```bash HTTP theme={"dark"} GET https://api-sandbox.dwolla.com/transfers/416a2857-c887-4cca-bd02-8c3f75c4bb0e Accept: application/vnd.dwolla.v1.hal+json Authorization: Bearer pBA9fVDBEyYZCEsLf/wKehyh1RTpzjUj5KzIRfDi0wKTii7DqY ... { "_links": { "self": { "href": "https://api-sandbox.dwolla.com/transfers/416a2857-c887-4cca-bd02-8c3f75c4bb0e" }, "source": { "href": "https://api-sandbox.dwolla.com/customers/b442c936-1f87-465d-a4e2-a982164b26bd" }, "destination": { "href": "https://api-sandbox.dwolla.com/accounts/ca32853c-48fa-40be-ae75-77b37504581b" }, "created-from-transfer": { "href": "https://api-sandbox.dwolla.com/transfers/83eb4b5e-a5d9-e511-80de-0aa34a9b2388" } }, "id": "416a2857-c887-4cca-bd02-8c3f75c4bb0e", "status": "processed", "amount": { "value": "2.00", "currency": "usd" }, "created": "2016-02-22T20:46:38.777Z" } ``` ```php retrieve_transfer.php theme={"dark"} byId($transferUrl); $transfer->_links->created-from-transfer; # => "https://api-sandbox.dwolla.com/transfers/83eb4b5e-a5d9-e511-80de-0aa34a9b2388" ?> ``` ```ruby retrieve_transfer.rb theme={"dark"} # Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby transfer_url = "https://api.dwolla.com/transfers/416a2857-c887-4cca-bd02-8c3f75c4bb0e" transfer = app_token.get transfer_url transfer._links.created-from-transfer # => "https://api-sandbox.dwolla.com/transfers/83eb4b5e-a5d9-e511-80de-0aa34a9b2388" ``` ```python retrieve_transfer.py theme={"dark"} # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python transfer_url = 'https://api-sandbox.dwolla.com/transfers/416a2857-c887-4cca-bd02-8c3f75c4bb0e' transfer = app_token.get(transfer_url) transfer.body['_links']['created-from-transfer'] # => 'https://api-sandbox.dwolla.com/transfers/83eb4b5e-a5d9-e511-80de-0aa34a9b2388' ``` ```javascript retrieveTransfer.js theme={"dark"} var transferUrl = "https://api-sandbox.dwolla.com/transfers/416a2857-c887-4cca-bd02-8c3f75c4bb0e"; dwolla .get(transferUrl) .then((res) => res.body._links.created - from - transfer); // => 'https://api-sandbox.dwolla.com/transfers/83eb4b5e-a5d9-e511-80de-0aa34a9b2388' ``` # Instant Payments Source: https://developers.dwolla.com/docs/instant-payments Learn about Instant Payments (defined as both FedNow Service and RTP), and how to identify and send transfers to instant payment-enabled funding sources. ## Overview Instant Payments combine two powerful US-based payment networks to provide 24/7/365 processing of payments within minutes for banks and other financial institutions: * **RTP® Network** - Launched by The Clearing House in 2017 as the first new payment rail in the U.S. in over 40 years * **FedNow® Service** - Launched by the Federal Reserve in July 2023, providing instant payment capabilities to over 1400 banks and financial institutions Both networks are exclusively for US domestic transfers, with RTP requiring participants to be US residents or persons domiciled in the United States. Payment speed and availability are the primary benefits of utilizing Instant Payments, as businesses can send and receive payments within seconds to eligible bank accounts 24/7/365. In addition, Instant Payments offer other benefits, including: * **Greater data transparency:** Get real-time tracking of payment statuses and immediate confirmation of successful payments, failed payments and errors. * **Finality:** Transactions settle within seconds, and payments are irrevocable. * **Data-Driven Insights:** Instant payments operate via modern protocols and allow for more data to be attached to each payment, providing valuable insights for strategic decision-making. The core of the Dwolla Platform was built around a simplified connection to the U.S. banking infrastructure for businesses to easily initiate digital payments via the ACH Network. As the Dwolla Platform evolves, Instant Payments add a new processing channel for businesses, providing flexible payment capabilities to build within their own applications. While Instant Payments and ACH are similar in many ways, there are some key differences: | ACH | Instant Payments (RTP/FedNow) | | ------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | | Sent via the ACH Network operators (either the Federal Reserve Bank or The Clearing House). | RTP: Sent via the RTP® Network operator, The Clearing House.
FedNow: Sent via the FedNow Service, Federal Reserve. | | Rules and regulations issued by National Automated Clearing House Association (Nacha). | RTP: Rules and regulations issued by The Clearing House (TCH).
FedNow: Rules and regulations issued by the Federal Reserve. | | Supports both credit (push) and debit (pull) transactions. | Credit (push) transfers only - no debit (pull) capabilities. | | Funds can take 1-3 business days to be available. | Funds made available within seconds. | | Agreed-upon processes for correcting erroneous transactions (i.e. reversal requests). | Funds are irrevocable once sent. | Instant Payments is a premium feature available for Dwolla customers. Enabling Instant Payments does require additional Dwolla approvals before getting started. Please contact Sales or your Relationship Manager for more information on enabling this account feature. ### Use cases and characteristics Instant Payments support credit "push" transfers, whereas ACH supports credit push as well as debit pull transfers. Instant payments are particularly suited to support [disbursement use cases](/docs/send-money). Whether your business is built around B2B, B2C or a combination, you can power your application with Instant Payments. * **Balance Funded Transfers** - Instant Payment networks are good funds models, and to initiate Instant Payments transactions through the Dwolla Platform, funds must be available in the sender's Dwolla [balance](/docs/balance-funding-source). * **Credit Sends Only** - Instant Payments transactions are all credits. While the RTP network and the FedNow Service do include Request for Payment capabilities, only a very small number of financial institutions support this, and it still relies on a credit to be initiated by the payer upon receiving the request. As adoption of Request for Payment increases among businesses, we expect more FIs will support this feature. * **Network Coverage** - [RTP Enabled Financial Institutions](https://www.theclearinghouse.org/payment-systems/rtp/rtp-participating-financial-institutions) now reach 71% of US Demand Deposit Accounts with over 950 participating financial institutions (with technical reach extending to institutions holding close to 90% of DDAs), while [FedNow participating organizations](https://www.frbservices.org/financial-services/fednow/organizations) include over 1,400 banks and financial institutions, with adoption growing at an aggressive pace. ### Transaction limits Instant Payments (defined as including both RTP and FedNow® Service) have specific transaction limits that are configured on a per-client basis. Understanding these limits is crucial for successful payment processing. #### Daily transaction limits Each client has a single configurable daily transaction limit that applies to all instant payment transactions made under their account, including transactions initiated by the client and their customers. * **Reset time**: Daily limits reset at 12:00 midnight Central Time (CT). * **Configuration**: Daily limits are configured by Dwolla upon approval. They are set during client onboarding and can be adjusted based on your business needs. Daily transaction limits are configured on a per-client basis and are not set by default. Contact your Relationship Manager to configure appropriate limits for your use case. #### Per-transaction limits By default, individual instant payment transactions are capped at a maximum of \$500,000 per transaction. Your account's individual-transaction limit also applies to instant payments. When your configured individual-transaction limit is lower than the $500,000 instant payment cap, the lower limit is what takes effect. For example, if your account has a $100,000 individual send limit, a transfer above $100,000 is rejected by that limit before the overarching $500,000 instant payment cap is ever reached. The effective per-transaction ceiling is the lower of the \$500,000 instant payment cap and your account's configured individual-transaction limit. These per-transaction limits apply independently of your daily transaction limit. Contact your Relationship Manager if you need your individual-transaction limit adjusted. #### Error handling for exceeding limit When a transaction exceeds the daily limit the Dwolla API will return a HTTP 400 `ValidationError` with specific details about the limit violation. ```bash Initiate Transfer Request theme={"dark"} POST https://api.dwolla.com/transfers Authorization: Bearer YOUR_ACCESS_TOKEN Content-Type: application/vnd.dwolla.v1.hal+json { "_links": { "source": { "href": "https://api.dwolla.com/funding-sources/0ce0900f-fe9e-49c9-ac3a-6e7daafdaaef" }, "destination": { "href": "https://api.dwolla.com/funding-sources/469ccaed-76ec-4fd4-a3b4-4c1805fb225b" } }, "amount": { "currency": "USD", "value": "150000.00" }, "processingChannel": { "destination": "instant" }, "instantDetails": { "destination": { "remittanceData": "ABC_123 Remittance" } } } ``` ```json Error Response theme={"dark"} { "code": "ValidationError", "message": "Validation error(s) present. See embedded errors list for more details.", "_embedded": { "errors": [ { "code": "Restricted", "message": "Real Time Payment daily limit reached", "path": "/amount/value", "_links": {} } ] } } ``` Once you reach your daily transaction limit, you cannot process additional instant payment transactions until the limit resets at midnight CT. Plan your payment volumes accordingly to avoid service interruptions. ### Identifying an Instant Payment-enabled Funding Source On the Dwolla Platform, Funding Sources allow bank accounts to be added or retrieved. Available for both a Dwolla [Master Account](/docs/api-reference/accounts) and [Customers](/docs/api-reference/customers) (end users) resources, Funding Sources have represented payment accounts used for ACH and/or wire activities. Dwolla will be able to identify a bank account as Instant Payment-enabled upon creation of a Funding Source, so no additional information is needed from your end users or application. #### Retrieving an Instant Payment-enabled Funding Source When [retrieving an existing Funding Source](/docs/api-reference/funding-sources/retrieve-a-funding-source) resource from the API, the response will contain a "channels" attribute which represents the different capabilities available for transfers. For a bank account, historically the only values available have been "ach" and "wire". The example below represents how "real-time-payments" is returned within the "channels" array to identify an Instant Payment-eligible account (supporting either RTP, FedNow, or both). ```json theme={"dark"} "channels": [ "ach", "real-time-payments" ] ``` The `real-time-payments` channel indicates eligibility for Instant Payments. The specific Instant Payments method used, RTP or FedNow, is determined internally based on availability and company preference when a transfer is initiated. This channel value remains the same regardless of whether the Funding Source supports RTP, FedNow, or both networks. ### Initiating an Instant Payment transfer In order to initiate a transfer with Instant Payment processing, an optional `processingChannel` JSON object must be included in the transfer request. The `processingChannel` object contains the `destination` key with a value of `instant`. You can also use `real-time-payments` as an acceptable alternative value. The `processingChannel` object will be returned when [retrieving the transfer](/docs/api-reference/transfers/retrieve-a-transfer) from the API, though the returned value may differ from the request value depending on the payment network used (see retrieval examples below). An optional `instantDetails` object can be included in the transfer request body. This allows additional information to be passed to the payment recipient's bank account about their Instant Payment credit transfer. For backward compatibility, you may also use `rtpDetails`, but `instantDetails` is recommended for new integrations. You can also include an optional [`fees`](/docs/facilitator-fee) array on an Instant Payments transfer to collect a facilitator fee. The fee can be charged to either the sending or receiving party, and—just like on ACH—each fee is created as a separate transfer resource with a unique transfer ID, and does not affect the original payment amount. See [Adding a facilitator fee](#adding-a-facilitator-fee) below. The following example assumes the sending party has funds pre-loaded to their `balance` Funding Source and that the destination party has a bank account connected that is Instant Payments-enabled. ```bash HTTP theme={"dark"} POST https://api-sandbox.dwolla.com/transfers Accept: application/vnd.dwolla.v1.hal+json Content-Type: application/vnd.dwolla.v1.hal+json Authorization: Bearer pBA9fVDBEyYZCEsLf/wKehyh1RTpzjUj5KzIRfDi0wKTii7DqY Idempotency-Key: 19051a62-3403-11e6-ac61-9e71128cae77 { "_links": { "source": { "href": "https://api-sandbox.dwolla.com/funding-sources/b268f6b9-db3b-4ecc-83a2-8823a53ec8b7" }, "destination": { "href": "https://api-sandbox.dwolla.com/funding-sources/ecf993e2-fa22-4cea-8022-c7861200288f" } }, "amount": { "currency": "USD", "value": "10000.00" }, "processingChannel": { "destination": "instant" }, "instantDetails": { "destination": { "remittanceData": "ABC_123 Remittance Data" } } } ... HTTP/1.1 201 Created Location: https://api-sandbox.dwolla.com/transfers/636de847-7d02-e711-80ee-0aa34a9b2388 ``` ```ruby create_transfer.rb theme={"dark"} request_body = { :_links => { :source => { :href => "https://api-sandbox.dwolla.com/funding-sources/b268f6b9-db3b-4ecc-83a2-8823a53ec8b7" }, :destination => { :href => "https://api-sandbox.dwolla.com/funding-sources/ecf993e2-fa22-4cea-8022-c7861200288f" } }, :amount => { :currency => "USD", :value => "10000.00" }, :metadata => { :paymentId => "12345678", :note => "payment for completed work Dec. 1" }, :processingChannel => { :destination => "instant" }, :instantDetails => { :destination => { :remittanceData => "ABC_123 Remittance Data" } } } # Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby (Recommended) transfer = app_token.post "transfers", request_body transfer.response_headers[:location] # => "https://api-sandbox.dwolla.com/transfers/636de847-7d02-e711-80ee-0aa34a9b2388" ``` ```php create_transfer.php theme={"dark"} create([ '_links' => [ 'source' => [ 'href' => 'https://api-sandbox.dwolla.com/funding-sources/b268f6b9-db3b-4ecc-83a2-8823a53ec8b7', ], 'destination' => [ 'href' => 'https://api-sandbox.dwolla.com/funding-sources/ecf993e2-fa22-4cea-8022-c7861200288f' ] ], 'amount' => [ 'currency' => 'USD', 'value' => '10000.00' ], 'processingChannel' => [ 'destination' => 'instant' ], 'instantDetails' => [ 'destination' => [ 'remittanceData' => 'ABC_123 Remittance Data' ] ] ]); $transfer; # => "https://api-sandbox.dwolla.com/transfers/636de847-7d02-e711-80ee-0aa34a9b2388" ?> ``` ```python create_transfer.py theme={"dark"} request_body = { '_links': { 'source': { 'href': 'https://api-sandbox.dwolla.com/funding-sources/b268f6b9-db3b-4ecc-83a2-8823a53ec8b7' }, 'destination': { 'href': 'https://api-sandbox.dwolla.com/funding-sources/ecf993e2-fa22-4cea-8022-c7861200288f' } }, 'amount': { 'currency': 'USD', 'value': '10000.00' }, 'processingChannel': { 'destination': 'instant' }, 'instantDetails': { 'destination': { 'remittanceData': 'ABC_123 Remittance Data' } } } # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python (Recommended) transfer = app_token.post('transfers', request_body) transfer.headers['location'] # => 'https://api-sandbox.dwolla.com/transfers/636de847-7d02-e711-80ee-0aa34a9b2388' ``` ```javascript createTransfer.js theme={"dark"} var requestBody = { _links: { source: { href: "https://api-sandbox.dwolla.com/funding-sources/b268f6b9-db3b-4ecc-83a2-8823a53ec8b7", }, destination: { href: "https://api-sandbox.dwolla.com/funding-sources/ecf993e2-fa22-4cea-8022-c7861200288f", }, }, amount: { currency: "USD", value: "10000.00", }, processingChannel: { destination: "instant", }, instantDetails: { destination: { remittanceData: "ABC_123 Remittance Data", }, }, }; dwolla .post("transfers", requestBody) .then((res) => res.headers.get("location")); // => 'https://api.dwolla.com/transfers/636de847-7d02-e711-80ee-0aa34a9b2388' ``` **Recommended approach for new integrations:** Use `instant` as the `processingChannel.destination` value and `instantDetails` for remittance data. This combination provides the most modern and future-proof approach for Instant Payments. **Backward compatibility options:** You can also use `real-time-payments` as the processing channel and `rtpDetails` for remittance data, or mix and match as needed. Both `instantDetails` and `rtpDetails` are functionally equivalent and will work with both RTP and FedNow transfers. ### Adding a facilitator fee Instant Payments transfers support the optional `fees` array, bringing facilitator fees to parity with ACH. To collect a fee, include a `fees` array alongside `processingChannel` in your transfer request. Each fee object contains a `_links.charge-to` link pointing to the [Customer](/docs/api-reference/customers) or [Account](/docs/api-reference/accounts) that will assume the fee—this may be either the sending or receiving party—and an `amount` object. Refer to the [Facilitator Fee](/docs/facilitator-fee) resource article for the full behavior and reconciliation guidance. The following example builds on the request above, adding a `$2.00` fee charged to one of the parties involved in the transfer: ```bash HTTP theme={"dark"} POST https://api-sandbox.dwolla.com/transfers Accept: application/vnd.dwolla.v1.hal+json Content-Type: application/vnd.dwolla.v1.hal+json Authorization: Bearer pBA9fVDBEyYZCEsLf/wKehyh1RTpzjUj5KzIRfDi0wKTii7DqY Idempotency-Key: 19051a62-3403-11e6-ac61-9e71128cae77 { "_links": { "source": { "href": "https://api-sandbox.dwolla.com/funding-sources/b268f6b9-db3b-4ecc-83a2-8823a53ec8b7" }, "destination": { "href": "https://api-sandbox.dwolla.com/funding-sources/ecf993e2-fa22-4cea-8022-c7861200288f" } }, "amount": { "currency": "USD", "value": "10000.00" }, "processingChannel": { "destination": "instant" }, "fees": [ { "_links": { "charge-to": { "href": "https://api-sandbox.dwolla.com/customers/479ce4c8-385f-4cfa-9693-262c0c3b6408" } }, "amount": { "value": "2.00", "currency": "USD" } } ] } ``` Standard facilitator fee constraints still apply on Instant Payments: a fee must be at least `$0.01`, the sum of fees cannot exceed 50% of the original transfer amount, and each fee must be charged to a party involved in the transfer. See the [Facilitator Fee](/docs/facilitator-fee) article for full details. ### Retrieving an Instant Payment transfer When retrieving the [transfer from the API](/docs/api-reference/transfers/retrieve-a-transfer), the response will contain either an `rtpDetails` object (for RTP transfers) or a `fedNowDetails` object (for FedNow transfers), depending on which payment network was used. Both objects have the same structure and contain a `destination` JSON object that includes: * `remittanceData` - The remittance data if included in the original transfer request * `networkId` - A unique identifier for the transfer within the payment network * `endToEndReferenceId` - An end-to-end reference identifier for the transfer These network-specific identifiers appear on the transfer API resource once the credit entry clears into the destination bank account. The `fedNowDetails` object only appears in API responses and has the same structure as `rtpDetails`. You cannot include `fedNowDetails` in transfer creation requests - use `rtpDetails` or `instantDetails` for request payloads. ##### Request and response examples **RTP Transfer Response:** ```bash HTTP theme={"dark"} GET https://api-sandbox.dwolla.com/transfers/243fd252-3fcf-eb11-8134-d050ab358a03 Accept: application/vnd.dwolla.v1.hal+json Authorization: Bearer 0Sn0W6kzNicvoWhDbQcVSKLRUpGjIdlPSEYyrHqrDDoRnQwE7Q { "_links": { "source": { "href": "https://api-sandbox.dwolla.com/accounts/0ee84069-47c5-455c-b425-633523291dc3", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "account" }, "destination-funding-source": { "href": "https://api-sandbox.dwolla.com/funding-sources/a67d47f0-73de-4a6c-8de4-105d30aad395", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "funding-source" }, "self": { "href": "https://api-sandbox.dwolla.com/transfers/243fd252-3fcf-eb11-8134-d050ab358a03", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "transfer" }, "source-funding-source": { "href": "https://api-sandbox.dwolla.com/funding-sources/7dc2e1df-9a88-4d9a-868f-90b46f1defcc", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "funding-source" }, "destination": { "href": "https://api-sandbox.dwolla.com/customers/3f65869e-61de-4efb-9b60-f6d0b9f804ed", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "customer" } }, "id": "243fd252-3fcf-eb11-8134-d050ab358a03", "status": "processed", "amount": { "value": "1000.00", "currency": "USD" }, "created": "2021-06-17T07:40:45.400Z", "processingChannel": { "destination": "real-time-payments" }, "rtpDetails": { "destination": { "networkId": "20210617021214273T1BG27487110796028", "endToEndReferenceId": "E2E-RTP-20210617-001", "remittanceData": "ABC_123 Remittance Data" } } } ``` ```ruby retrieve_transfer.rb theme={"dark"} transfer_url = 'https://api.dwolla.com/transfers/243fd252-3fcf-eb11-8134-d050ab358a03' # Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby (Recommended) transfer = app_token.get transfer_url transfer.status # => "processed" ``` ```javascript retrieveTransfer.js theme={"dark"} var transferUrl = "https://api.dwolla.com/transfers/243fd252-3fcf-eb11-8134-d050ab358a03"; dwolla.get(transferUrl).then(function (res) { res.body.status; // => 'processed' }); ``` ```python retrieve_transfer.py theme={"dark"} transfer_url = 'https://api.dwolla.com/transfers/243fd252-3fcf-eb11-8134-d050ab358a03' # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python (Recommended) fees = app_token.get(transfer_url) fees.body['status'] # => 'processed' ``` ```php retrieve_transfer.php theme={"dark"} byId($transferUrl); print($transfer->status); # => "processed" ?> ``` **FedNow Transfer Response:** ```json theme={"dark"} { "_links": { "source": { "href": "https://api-sandbox.dwolla.com/accounts/0ee84069-47c5-455c-b425-633523291dc3", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "account" }, "destination-funding-source": { "href": "https://api-sandbox.dwolla.com/funding-sources/a67d47f0-73de-4a6c-8de4-105d30aad395", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "funding-source" }, "self": { "href": "https://api-sandbox.dwolla.com/transfers/243fd252-3fcf-eb11-8134-d050ab358a03", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "transfer" }, "source-funding-source": { "href": "https://api-sandbox.dwolla.com/funding-sources/7dc2e1df-9a88-4d9a-868f-90b46f1defcc", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "funding-source" }, "destination": { "href": "https://api-sandbox.dwolla.com/customers/3f65869e-61de-4efb-9b60-f6d0b9f804ed", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "customer" } }, "id": "243fd252-3fcf-eb11-8134-d050ab358a03", "status": "processed", "amount": { "value": "1000.00", "currency": "USD" }, "created": "2021-06-17T07:40:45.400Z", "processingChannel": { "destination": "fed-now" }, "fedNowDetails": { "destination": { "networkId": "20240115123456789FEDNOW123456", "endToEndReferenceId": "E2E-FEDNOW-20240115-001", "remittanceData": "ABC_123 Remittance Data" } } } ``` The response will contain either `rtpDetails` (for RTP transfers) or `fedNowDetails` (for FedNow transfers) depending on which payment method was used. Both objects have identical structure but contain network-specific identifiers. This allows you to identify the specific payment network used for troubleshooting purposes. **Processing Channel Behavior:** The `processingChannel.destination` value in the response reflects the actual payment network used, regardless of the original request value: * If the transfer went via FedNow, the response will show `fed-now` * If the transfer went via RTP, the response will show `real-time-payments` This means that even if you specify `real-time-payments` in your request, if the destination bank only supports FedNow, the response will show `fed-now` to indicate the actual network used. ### Webhook notifications Webhook notification events follow the same created → completed sequence as an ACH transfer. The primary difference is that once the transfer is created, the completion or failure events are triggered moments later rather than days. **Instant Payment webhooks differ from ACH.** For Instant Payments, the recipient always receives `customer_bank_transfer_*` events regardless of whether the recipient is a Receive-only User, an Unverified Customer, or a Verified Customer. This differs from ACH, where `customer_bank_transfer_*` events fire only when a Verified Customer is involved and `customer_transfer_*` events represent activity for Unverified Customers and Receive-only Users. If your integration relies on ACH webhook behavior, make sure it handles recipients of Instant Payments through the `customer_bank_transfer_*` events. The specific events include: | Event Topic | Description | | ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | customer\_bank\_transfer\_created | Sent when the Instant Payment transfer is created for the recipient, regardless of whether the recipient is a Verified Customer, Unverified Customer, or Receive-only User. | | customer\_bank\_transfer\_failed | Sent if the Instant Payment transfer fails for the recipient, regardless of recipient type. | | customer\_bank\_transfer\_completed | Sent when the Instant Payment transfer completes successfully for the recipient, regardless of recipient type. | | customer\_funding\_source\_rtp\_enabled | Sent when a funding source is identified as Instant Payment eligible. | | customer\_funding\_source\_rtp\_disabled | Sent when an Instant Payment eligible funding source is later identified as ineligible. | For complete descriptions of each event, see the [webhook event reference](/docs/webhook-events). The webhook event names remain the same for both RTP and FedNow transfers. The `customer_funding_source_rtp_enabled` and `customer_funding_source_rtp_disabled` events are used for all Instant Payment eligibility changes, regardless of whether the funding source supports RTP, FedNow, or both networks. This means you'll receive the same webhook events whether a funding source becomes eligible for RTP, FedNow, or both payment methods. ### Error Codes Error codes provide the reason a message did not complete and can be found on the payment `Result.Code`. The `Result.Code` will be OK if the payment was successfully sent/received. | Code | Description | | ---- | ---------------------------------------------------------------------------------------------------------- | | 650 | Cannot parse the message | | 690 | Signature mismatch or verification error | | AB05 | Transaction stopped due to timeout at the Creditor Agent | | AB06 | Transaction stopped due to timeout at the Instructed Agent | | AB08 | Creditor Agent is not online | | AB09 | Transaction stopped due to error at the Creditor Agent | | AC01 | Account number is invalid or missing | | AC02 | Debtor account is invalid | | AC03 | Creditor account is invalid | | AC04 | Account closed | | AC06 | Account is blocked | | AC07 | Creditor account closed | | AC10 | Debtor account currency is invalid or missing | | AC11 | Creditor account currency is invalid or missing | | AC13 | Debtor account type missing or invalid | | AC14 | Creditor account type missing or invalid | | ACWP | Accepted without posting - receiving FI accepted the payment but has not yet posted to the account | | AG01 | Transaction is forbidden on this type of account | | AG03 | Transaction type is not supported/authorized on this account | | AGNT | Incorrect Agent | | AM02 | Specific transaction/message amount is greater than allowed maximum | | AM04 | Amount of funds available to cover specified message amount is insufficient | | AM09 | Amount received is not the amount agreed or expected | | AM11 | Transaction currency is invalid or missing | | AM12 | Amount is invalid or missing | | AM13 | Transaction amount exceeds limits set by clearing system | | AM14 | Transaction amount exceeds limits agreed between bank and client | | BE04 | Specification of creditor's address, which is required for payment, is missing/not correct | | BE06 | End customer specified is not known at associated Sort/National Bank Code or no longer exists in the books | | BE07 | Specification of debtor's address, which is required for payment, is missing/not correct | | BE10 | Debtor country code is missing or invalid | | BE11 | Creditor country code is missing or invalid | | BE13 | Country code of debtor's residence is missing or invalid | | BE14 | Country code of creditor's residence is missing or invalid | | BE16 | Debtor identification code missing or invalid | | BE17 | Creditor identification code missing or invalid | | BLKD | Payment has been blocked | | COMM | Error communicating with real time payments provider | | DS04 | Order was rejected by the bank side for reasons concerning content | | DS0H | Signer is not allowed to sign for this account | | DS24 | Waiting time expired due to incomplete order | | DT04 | Future date is not supported | | DUPL | Payment is a duplicate of another payment | | FF02 | Syntax error reason is provided as narrative information in the additional reason information | | FF03 | Invalid Payment Type Information | | FF08 | End to End ID is missing or invalid | | FF10 | File or transaction cannot be processed due to technical issues at the bank side | | MD07 | End customer is deceased | | NARR | Reason is provided as narrative information in the additional reason information | | NARR | Cannot validate retail account number | | NOAT | Receiving Customer Account does not support/accept this message type | | OK | Completed | | RC01 | Bank identifier code specified in the message has an incorrect format | | RC02 | Bank identified is invalid or missing | | RC03 | Debtor FI identifier is invalid or missing | | RC04 | Creditor FI identifier is invalid or missing | | SL03 | Token service not responding | | TK01 | Invalid Token | | TK02 | Sender Token Not Found | | TK03 | Receiver Token Not Found | | TK04 | Token Expired | | TK05 | Token Found with Counterparty Mismatch | | TK06 | Token Found with Value Limit Rule Violation | | TK07 | Single Use Token Already Used | | TK08 | Token Suspended | | TM01 | Invalid Cut Off Time | | UE01 | Technical error that may clear if the message is retried | | 1100 | Any Other Reasons Reason is provided as narrative in the additional information | | 9909 | Central Switch (RTP) system malfunction | | 9910 | Instructed Agent signed-off | | 9912 | Recipient connection is not available | | 9934 | Instructing Agent signed-off | | 9946 | Instructing Agent suspended | | 9947 | Instructed Agent suspended | | 9948 | Central Switch (RTP) service is suspended | # Model Context Protocol Source: https://developers.dwolla.com/docs/mcp-server Learn how to use the Dwolla MCP Server to enable AI agents to retrieve and analyze data from Dwolla's payment platform using natural language. The Dwolla Model Context Protocol (MCP) Server enables AI agents to retrieve and analyze data from the Dwolla payment platform using natural language. It provides read-only access to inspect accounts, analyze transfer history, monitor customer data, and generate insights from your payment operations. Unlike a traditional REST API, the MCP Server is designed for AI inference, allowing you to ask questions and get insights from your Dwolla data conversationally. View the full README, report issues, and explore the source code on GitHub. Learn more about the Model Context Protocol that powers this server. ## Use Cases Leverage the MCP Server to build AI-powered workflows for various business needs: Quickly investigate customer issues and transfer failures. * *"Find all failed transfers for customer `jane.doe@example.com` and explain why they failed."* * *"Show me customer details for customer ID `62c3aa1b-3a1b-46d0-ae90-17304d60c3d5`."* Automate reconciliation and reporting tasks. * *"Calculate total transfer volume for last quarter."* * *"Show me all pending transfers over \$5,000 this week."* Monitor for suspicious activity and ensure regulatory compliance. * *"List all customers missing required beneficial ownership information."* * *"Identify customers with multiple failed transfers this month."* Gain insights into payment patterns and customer behavior. * *"What's the average transfer amount by customer segment?"* * *"Which funding source types have the highest failure rates?"* ## Getting Started Follow these steps to set up and run the Dwolla MCP Server. ### Prerequisites Before you begin, ensure you have the following: * **Node.js v18+ and npm**: The server is a Node.js application. * **Dwolla Account**: You'll need a Dwolla account to generate API credentials. A [Sandbox Account](https://accounts-sandbox.dwolla.com/sign-up) is recommended for development. * **Access Token**: An access token from your Dwolla application is required for authentication. ### Setup and Configuration All access tokens are short-lived and expire after one hour. The primary way to generate a token is to programmatically exchange your application's key and secret. This method works for both Sandbox and Production. For full details, see our API reference on [creating an application access token](/docs/api-reference/tokens/create-an-application-access-token). For convenience during development, the **Sandbox** environment also allows you to generate a token directly from the Applications tab in the [Sandbox Dashboard](https://dashboard-sandbox.dwolla.com/applications-legacy). Treat your application key, secret, and tokens like passwords. Do not commit them to version control. Use environment variables or a secret manager. You must specify which Dwolla environment the MCP server should interact with. Use the `--server-url` argument when starting the server. * **Sandbox (Recommended for testing):** `--server-url https://api-sandbox.dwolla.com` * **Production (Live data):** `--server-url https://api.dwolla.com` You can run the server directly using `npx`, or install it in your preferred AI-powered development tool. **Recommended**: Install the MCP server as a Desktop Extension using the pre-built `mcp-server.dxt` file. 1. Download the `mcp-server.dxt` file from the [GitHub repo](https://github.com/Dwolla/dwolla-mcp/blob/main/mcp-server.dxt). 2. Simply drag and drop the `mcp-server.dxt` file onto Claude Desktop to install the extension. 3. The DXT package includes the MCP server and all necessary configuration. Once installed, the server will be available without additional setup. DXT (Desktop Extensions) provide a streamlined way to package and distribute MCP servers. Learn more about [Desktop Extensions](https://www.anthropic.com/engineering/desktop-extensions). If you prefer to run from source or the DXT method doesn't work, you can run the MCP server locally by cloning this repository. 1. Clone the repository and set it up locally: ```bash theme={"dark"} git clone https://github.com/dwolla/dwolla-mcp.git cd dwolla-mcp npm install npm run build ``` 2. Open Claude Desktop and go to `Settings > Developer > Edit Config`. 3. Add the following server configuration to `claude_desktop_config.json`, replacing the path with your local clone location: ```json theme={"dark"} { "mcpServers": { "DwollaMcp": { "type": "stdio", "command": "node", "args": [ "/path/to/your/dwolla-mcp/bin/mcp-server.js", "start", "--bearer-auth", "your_token_here", "--server-url", "https://api-sandbox.dwolla.com" ] } } } ``` 4. Save and restart Claude Desktop. 1. Open `Settings > Tools & Integrations`. 2. Select `New MCP Server`. 3. Paste the following JSON configuration, replacing `your_token_here`. ```json theme={"dark"} { "mcpServers": { "DwollaMcp": { "command": "npx", "args": [ "@dwolla/mcp-server", "start", "--bearer-auth", "your_token_here", "--server-url", "https://api-sandbox.dwolla.com" ] } } } ``` 1. Open `Settings` (`Cmd + ,`) and search for "mcp". 2. In your `settings.json`, add the following server configuration. 3. Restart VS Code after saving. ```json theme={"dark"} { "mcp": { "servers": { "dwolla": { "type": "stdio", "command": "npx", "args": [ "@dwolla/mcp-server", "start", "--bearer-auth", "your_token_here", "--server-url", "https://api-sandbox.dwolla.com" ] } } } } ``` For contributing or running from source: ```bash theme={"dark"} # Clone the repository git clone https://github.com/dwolla/dwolla-mcp.git cd dwolla-mcp # Install dependencies npm install # Build the project npm run build # Run with your access token node bin/mcp-server.js start --bearer-auth "your_token_here" --server-url https://api-sandbox.dwolla.com ``` Run the following command, replacing `your_token_here` with your bearer token. ```bash theme={"dark"} npx @dwolla/mcp-server start \ --bearer-auth your_token_here \ --server-url https://api-sandbox.dwolla.com ``` For a full list of server arguments, run `npx @dwolla/mcp-server start --help`. ## Available Operations (Tools) The MCP Server exposes functionalities as "tools" that an AI agent can discover and invoke. **Read-Only Operations**: All tools provided by this server are for data retrieval and analysis only. Creating, updating, or deleting data (e.g., initiating transfers or creating customers) is not currently supported. For a complete and up-to-date list of available tools, consult the [project README](https://github.com/Dwolla/dwolla-mcp/blob/main/README.md#%EF%B8%8F-available-operations). Here is a summary of available tool categories: * **Account Operations**: Retrieve account details, funding sources, transfers, etc. * **Customer Management**: List, search, and get details for customers and their associated resources. * **Transfer Operations**: Get details for specific transfers and their fees or failure reasons. * **Mass Payment Operations**: Retrieve details about mass payments and their individual items. * **Funding Source Operations**: Get details for specific funding sources, including balance or micro-deposit status. * **Compliance & Documents**: Access documents and information related to beneficial ownership and KBA. * **Exchange Operations**: Retrieve details about exchanges, partners, and sessions. * **Labels & Ledger**: Manage and query labels and ledger entries. * **Webhooks & Events**: Get details about webhooks, subscriptions, and events. * **Reference Data**: List business classifications. When using an AI agent framework, it will automatically discover the available tools from the server. You can then ask the agent to list the tools it has available. ## Using with AI Agents You can interact with the Dwolla MCP server through integrated AI-powered clients like Cursor, Claude, Windsurf, VS Code etc., which provide a conversational interface to your data. In our testing, Claude Pro has been particularly effective at using natural language prompts to access information from the Dwolla API via the MCP tools. For developers who want to build their own custom AI applications or agents that communicate with the MCP server, using a framework like LangChain or Semantic Kernel is recommended. These frameworks simplify development by handling tool discovery, context management, and the underlying communication protocol. ### Example Workflow: Investigating Failed Payments Here's how an AI-powered workflow with the MCP server can drastically reduce investigation time. **Scenario**: A support agent needs to understand a recent spike in failed payments. **Traditional Process (Manual)** 1. Log into multiple dashboards. 2. Manually look up customer and transfer data. 3. Cross-reference failure codes with documentation. 4. Potentially escalate to engineering for database queries. **Time: \~45 minutes** 🐢 **AI-Powered Process (with MCP)** 1. Ask the AI agent: *"We're seeing more transfer failures lately. Can you investigate?"* 2. The agent uses the MCP server to analyze recent transfers, group them by failure reason, and identify patterns. 3. The agent provides a concise summary with actionable insights. **Time: \~5 minutes** 🚀 ## Troubleshooting If you encounter issues, refer to the detailed [troubleshooting section in the README](https://github.com/Dwolla/dwolla-mcp/blob/main/README.md#-troubleshooting) on GitHub. Here are some common solutions: * **Invalid Access Token**: Ensure your token is correct, has not expired, and matches the selected environment (Sandbox vs. Production). * **Node.js Version**: Verify you are using Node.js v18 or higher (`node --version`). * **NPM Cache**: Try clearing the npm cache with `npm cache clean --force`. * **Restart the Client**: After making configuration changes in Cursor, or another client, a restart is often required. * **Check Configuration**: Double-check your JSON configuration for syntax errors. * **Validate Token with cURL**: Test your token directly against the API. ```bash theme={"dark"} curl -H "Authorization: Bearer your_token_here" \ https://api-sandbox.dwolla.com/ ``` * **Use MCP Inspector**: Test your server setup with the official MCP Inspector tool. See the [`README`](https://github.com/Dwolla/dwolla-mcp/blob/main/README.md) for instructions. # Verify Bank with Micro-deposits Source: https://developers.dwolla.com/docs/micro-deposit-verification Learn how to verify a user's bank account ownership by initiating micro-deposits - small deposits that are sent to the account and must be verified by the user to confirm account ownership. ## Overview If you choose the micro-deposit method of bank verification, Dwolla will transfer two deposits of less than \$0.10 to your customer's linked bank or credit union account. After [initiating micro-deposits](/docs/api-reference/funding-sources/initiate-or-verify-micro-deposits), two random amounts will post to your customer's bank account in 1-2 business days. Once your customer sees these deposits in their account, they need to verify the two amounts in your application. If subscribed to [webhooks](/docs/working-with-webhooks), your application will be notified throughout this process via micro-deposit related [events](/docs/api-reference/events). ### Retrieve the funding source After your customer has added a bank account you'll want to retrieve the funding source to check if a `initiate-micro-deposits` link relation exists. A link to `initiate-micro-deposits` will return when an unverified `bank` funding source is eligible to receive micro-deposits. ```bash HTTP theme={"dark"} GET https://api.dwolla.com/funding-sources/e52006c3-7560-4ff1-99d5-b0f3a6f4f909 Accept: application/vnd.dwolla.v1.hal+json Authorization: Bearer pBA9fVDBEyYZCEsLf/wKehyh1RTpzjUj5KzIRfDi0wKTii7DqY ... { '_links': { 'self': { 'href': 'https://api.dwolla.com/funding-sources/e52006c3-7560-4ff1-99d5-b0f3a6f4f909' }, 'customer': { 'href': 'https://api.dwolla.com/customers/36e9dcb2-889b-4873-8e52-0c9404ea002a' }, 'initiate-micro-deposits': { 'href': 'https://api.dwolla.com/funding-sources/e52006c3-7560-4ff1-99d5-b0f3a6f4f909/micro-deposits' } }, 'id': 'e52006c3-7560-4ff1-99d5-b0f3a6f4f909', 'status': 'unverified', 'type': 'bank', 'name': 'Test checking account', 'created': '2015-10-23T20:37:57.137Z' } ``` ```ruby retrieve_funding_source.rb theme={"dark"} funding_source_url = 'https://api.dwolla.com/funding-sources/e52006c3-7560-4ff1-99d5-b0f3a6f4f909' # Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby (Recommended) retrieved = app_token.get funding_source_url retrieved.name # => 'Test checking account' ``` ```php retrieve_funding_source.php theme={"dark"} id($fundingSourceUrl); print($retrieved->name); # => 'Test checking account' ?> ``` ```python retrieve_funding_source.py theme={"dark"} funding_source_url = 'https://api.dwolla.com/funding-sources/e52006c3-7560-4ff1-99d5-b0f3a6f4f909' # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python (Recommended) retrieved = app_token.get(funding_source_url) retrieved.body['name'] # => 'Test checking account' ``` ```javascript theme={"dark"} var fundingSourceUrl = "https://api.dwolla.com/funding-sources/e52006c3-7560-4ff1-99d5-b0f3a6f4f909"; dwolla.get(fundingSourceUrl).then(function(res) { res.body.name; // => 'Test checking account' }); ``` # Step 1: Initiate micro-deposits Once you POST to the [initiate-micro-deposits](/docs/api-reference/funding-sources/initiate-or-verify-micro-deposits) link, Dwolla will send two small amounts to your customer's bank or credit union account. If the request is successful, Dwolla returns a `HTTP 201` and a link to the created micro-deposits resource `funding-sources/{id}/micro-deposits` in the location header. The micro-deposits resource can be later used to retrieve the status of micro-deposits or verify micro-deposit amounts. If your application is subscribed to webhooks, a webhook will be sent with the `microdeposits_added` event, notifying your application that micro-deposits are en route to your customer's bank account. ```bash HTTP theme={"dark"} POST https://api-sandbox.dwolla.com/funding-sources/e52006c3-7560-4ff1-99d5-b0f3a6f4f909/micro-deposits Authorization: Bearer 8tJjM7iTjujLthkbVPMUcHLqMNw4uv5kG712g9j1RRBHplGpwo Content-Type: application/vnd.dwolla.v1.hal+json Accept: application/vnd.dwolla.v1.hal+json Cache-Control: no-cache HTTP/1.1 201 Created Location: https://api.dwolla.com/funding-sources/e52006c3-7560-4ff1-99d5-b0f3a6f4f909/micro-deposits ``` ```ruby initiate_micro_deposits.rb theme={"dark"} funding_source_url = 'https://api-sandbox.dwolla.com/funding-sources/e52006c3-7560-4ff1-99d5-b0f3a6f4f909' # Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby (Recommended) app_token.post '#{funding_source_url}/micro-deposits' ``` ```javascript initiateMicroDeposits.js theme={"dark"} var fundingSourceUrl = "https://api-sandbox.dwolla.com/funding-sources/e52006c3-7560-4ff1-99d5-b0f3a6f4f909"; dwolla.post(`#{fundingSourceUrl}/micro-deposits`); ``` ```python initiate_micro_deposits.py theme={"dark"} funding_source_url = 'https://api-sandbox.dwolla.com/funding-sources/e52006c3-7560-4ff1-99d5-b0f3a6f4f909' # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python (Recommended) app_token.post('%s/micro-deposits' % funding_source_url) ``` ```php initiate_micro_deposits.php theme={"dark"} microDeposits([ 'amount1' => [ 'value' => '0.03', 'currency' => 'USD' ], 'amount2' => [ 'value' => '0.09', 'currency' => 'USD' ]], $fundingSourceUrl ); ?> ``` # Step 2 - Verify micro-deposits In the Dwolla production environment, you must wait until the micro-deposits actually post to the customer's bank account before the account can be verified, which can take 1-2 business days. A `microdeposits_completed` event will be triggered once micro-deposits have successfully posted to the bank. Once micro-deposits have completed, a `verify-micro-deposits` link relation will return on the funding source letting your application know the funding source can be verified. When the amounts are entered for verification, the order in which they are entered doesn't matter. In the Sandbox environment, any amount below \$0.10 will allow you to verify the account immediately. ```bash HTTP theme={"dark"} POST /funding-sources/e52006c3-7560-4ff1-99d5-b0f3a6f4f909/micro-deposits Authorization: Bearer 8tJjM7iTjujLthkbVPMUcHLqMNw4uv5kG712g9j1RRBHplGpwo Content-Type: application/vnd.dwolla.v1.hal+json Accept: application/vnd.dwolla.v1.hal+json { 'amount1': { 'value': '0.03', 'currency': 'USD' }, 'amount2': { 'value': '0.09', 'currency': 'USD' } } HTTP 200 OK ``` ```ruby verify_micro_deposits.rb theme={"dark"} funding_source_url = 'https://api-sandbox.dwolla.com/funding-sources/e52006c3-7560-4ff1-99d5-b0f3a6f4f909' request_body = { :amount1 => { :value => '0.03', :currency => 'USD' }, :amount2 => { :value => '0.09', :currency => 'USD' } } # Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby (Recommended) app_token.post '#{funding_source_url}/micro-deposits', request_body ``` ```javascript verifyMicroDeposits.js theme={"dark"} var fundingSourceUrl = "https://api-sandbox.dwolla.com/funding-sources/e52006c3-7560-4ff1-99d5-b0f3a6f4f909"; var requestBody = { amount1: { value: "0.03", currency: "USD", }, amount2: { value: "0.09", currency: "USD", }, }; dwolla.post(`${fundingSourceUrl}/micro-deposits`, requestBody); ``` ```python verify_micro_deposits.py theme={"dark"} funding_source_url = 'https://api-sandbox.dwolla.com/funding-sources/e52006c3-7560-4ff1-99d5-b0f3a6f4f909' request_body = { 'amount1': { 'value': '0.03', 'currency': 'USD' }, 'amount2': { 'value': '0.09', 'currency': 'USD' } } # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python (Recommended) app_token.post('%s/micro-deposits' % funding_source_url, request_body) ``` ```php verify_micro_deposits.php theme={"dark"} microDeposits([ 'amount1' => [ 'value' => '0.03', 'currency' => 'USD' ], 'amount2' => [ 'value' => '0.09', 'currency' => 'USD' ]], $fundingSourceUrl ); ?> ``` ### Handle failed verification attempts Your [customer](/docs/api-reference/customers) will have only three attempts to correctly input the two posted micro-deposit amounts. If your customer reaches the max attempts allowed, a `microdeposits_maxattempts` [event](/docs/api-reference/events) will be triggered and a `failed-verification-micro-deposits` link will be returned in the [response for the funding source](/docs/api-reference/funding-sources#funding-source-links). As a result, they will no longer be allowed to verify the funding source using the same two posted micro-deposit amounts. In order to retry bank account verification via micro-deposits, the following steps will need to be taken by your customer and application: 1. [Removal of the funding source](/docs/api-reference/funding-sources/update-or-remove-a-funding-source) with failed micro-deposit verification attempts. 2. Wait 48 hours after the initial funding source was added to re-add the funding source. 3. [Initiate micro-deposits](/docs/api-reference/funding-sources/initiate-or-verify-micro-deposits) to the funding source created in the previous step. 4. [Verify the funding source](/docs/api-reference/funding-sources/initiate-or-verify-micro-deposits) using the new posted micro-deposit amounts. Links returned on the funding source resource, e.g. `failed-verification-micro-deposits`, `initiate-micro-deposits` or `verify-micro-deposits`, will give your application insight into whether the funding source is eligible to receive or verify micro-deposits, or if it has already failed micro-deposit verification. # Overview Source: https://developers.dwolla.com/docs/open-banking Simplify Open Banking Integration with Dwolla's API. Leverage a single API to connect to various Open Banking providers and streamline account linking, verification, and data retrieval for your application. ## Open Banking Open Banking is a technology that allows secure data sharing between financial institutions and third-party providers (e.g. Plaid, MX) with user consent. It's transforming financial services by granting users greater control over their financial data. This data, accessible through secure APIs, opens doors for innovative financial applications. Dwolla's API integrates Open Banking seamlessly, making it easier than ever to leverage Account-to-Account (A2A) payments, also known as pay by bank, within your application. Dwolla has partnered with leading Open Banking providers, like [Plaid](https://plaid.com/docs/auth/partnerships/dwolla/) and [MX](https://docs.mx.com/), to offer pre-built connections. This documentation explores the technical aspects of Open Banking and its implementation within the Dwolla API. #### Benefits of Using Dwolla for Open Banking Dwolla acts as a bridge between your application and participating Open Banking service providers. We offer a single API that simplifies the integration process and ongoing maintenance for A2A payment solutions. * **Faster Verification:** Open Banking allows for real-time data access, enabling faster and more efficient account verification compared to traditional methods like micro-deposit verification. This creates an improved user experience and leads to higher user conversion rates. * **Enhanced Security:** Leverage secure APIs and reduce the risks associated with manual data entry, such as data entry errors and ACH returns. * **Reduced Costs:** Open Banking features and functionality can lead to cost savings in the long run through reduced return risk, decreased risk of fraud and lower customer service costs. * **Streamlined Development:** Dwolla's pre-built connections with Open Banking providers like Plaid and MX save you valuable development time. They tailor Open Banking features and functionality to payments use cases and eliminate the complexities of managing multiple API integrations. ## Instant Account Verification Flow Dwolla’s Exchange Sessions streamlines the Instant Account Verification (IAV) process for your application. When initiating an Exchange Session, Dwolla securely establishes a connection with your chosen Open Banking Provider (OBP). The user is then redirected and presented with a screen to select their financial institution and authenticate using their online banking credentials. The specific authentication flow may vary slightly depending on the chosen OBP, but Exchange Sessions handles the communication and data exchange throughout the process, ensuring a smooth and secure user experience. We explore the specific flows for each provider below. ### Plaid #### 1. Initiate Exchange Session: * **Create an Exchange Session**: Use the `create an exchange session` endpoint to initiate an Exchange Session for your customer. Specify Plaid as the Open Banking provider in the request body. #### 2. Retrieve Exchange Session and Complete IAV Flow: * **Retrieve Exchange Session**: Make an API call to retrieve the exchange session. Upon success, Dwolla returns an `externalProviderSessionToken`. * **Complete Plaid Link Flow**: Use the `externalProviderSessionToken` on the frontend to initialize Plaid’s Link flow. The user authenticates their bank and grants permission for verification. #### 3. Create Exchange with Plaid Public Token * **Create Exchange**: Once the user completes the Plaid Link flow, retrieve the `publicToken` returned by Plaid. Use this token to [create an exchange](/docs/api-reference/exchanges/create-an-exchange-for-a-customer) resource in Dwolla. This creates an "exchange" representing the link between the Dwolla customer and their external bank account. #### 3. Create Funding Source * **Create Funding Source:** Use the [create a funding source endpoint](/docs/api-reference/funding-sources/create-customer-funding-source) with the exchange resource from the previous step. * **Verification Complete**: Dwolla responds with a 201 status, triggering the `customer_funding_source_added` and `customer_funding_source_verified` webhooks to indicate successful verification. ### MX #### 1. Initiate Exchange Session: * **Create an Exchange Session:** Use the [create an exchange session endpoint](/docs/api-reference/exchange-sessions/create-customer-exchange-session) to initiate an Exchange Session, specifying MX as the Open Banking provider. #### 2. Retrieve Exchange Session and Complete IAV Flow: * **Retrieve Exchange Session:** Make an API call to [retrieve an exchange session](/docs/api-reference/exchange-sessions/retrieve-exchange-session) to get the `external-provider-session` URL. Redirect your user to this secure login page for verification. * **User Grants Permissions:** The user logs in to their bank account and grants your application permission to access specific financial data. * **Error Handling:** Implement proper mechanisms to handle potential errors during the verification process. #### 3. Handle MX Callback and List Available Connections: * **MX Callback:** You'll [receive an event](https://docs.mx.com/connect/guides/handling-events/) from the MX Connect Widget indicating the completion of the IAV flow and providing details about the authorized account(s). * **List Available Connections:** 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. #### 4. Exchange and Funding Source Creation: * **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. * **Create Funding Source:** Use the [create a funding source endpoint](/docs/api-reference/funding-sources/create-customer-funding-source) with the exchange resource from the previous step. * **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. ## Getting Started with Open Banking Sign up for a free Sandbox account and read our Open Banking documentation, as well as explore our account verification guides for step-by-step instructions and code examples that cover initiating an exchange session, completing the instant account verification flow and creating a funding source. The following guides utilize an [integration-examples](https://github.com/Dwolla/integration-examples) sample app which provides a hands-on experience for integrating Open Banking into your application. By working through a real-world example, you can understand how to structure your own application, interact with the Dwolla API and implement key functionalities like account verification. Step-by-step guide to integrating Dwolla's Open Banking solution with Plaid for instant account verification. Step-by-step guide to integrating Dwolla's Open Banking solution with MX for instant account verification. # Bank Balance Check Source: https://developers.dwolla.com/docs/open-banking/bank-balance-check Learn how to use Dwolla's Open Banking Bank Balance Check to verify account balances in real time, reduce payment failures, and improve the success rate of your ACH transactions. ## Overview Account-to-account (A2A) payments offer businesses an efficient and cost-effective means for handling account-to-account transfers. However, they are not without risks, the most notable being ACH returns due to insufficient funds. Such returns can cause significant delays, frustrations and even fees for both businesses and customers. As part of Dwolla's [Open Banking Services](/docs/open-banking), the real-time balance check add-on to our Instant Account Verification (IAV) feature ("Bank Balance Check") enables businesses to see the current and available balance of a sender's bank account before processing a transaction. By giving insights into bank account balances, real-time Bank Balance Checks help reduce the likelihood of ACH returns, helping businesses maintain smoother and more reliable payment flows. ### **Overview of Benefits** * **Reduced Risk of Insufficient Funds Returns:** Receiving information about the available and current balance in a bank account prior to initiating a transaction helps businesses avoid initiating payments that are likely to fail. * **Improved Payment Success Rate:** With fewer returned transactions, businesses experience smoother operations and faster transaction processing times. * **Enhanced User Experience:** Notifying users of insufficient funds before initiating the transaction gives them the opportunity to take corrective action (e.g., cancel or modify the payment, move money between accounts to cover the transaction, etc.). * **Cost Savings:** Minimizing ACH returns reduces potential fees associated with failed transactions, saving businesses both time and money. ### **How It Works** 1. **Initiating a Bank Balance Check:** * Before calling the API to create a transfer, you can initiate a Bank Balance Check to retrieve the sender's current and available account balance in real time. 2. **Evaluating the Response:** * Even if the Bank Balance Check shows there are sufficient funds in a sender's account, you should still use your own discretion when deciding whether to initiate a transaction. You may have your own criteria, such as requiring a buffer beyond the transaction amount. As an example as to why, the sender could make a withdrawal from his/her account from an ATM right after the Bank Balance Check shows sufficient funds in the sender's account. While the Bank Balance Check offers valuable insight, it's important to consider your specific risk policies and other factors before proceeding with an ACH debit. 3. **ACH Window Processing:** * If the payment is queued, the Bank Balance Check can be triggered just before the ACH processing window closes so you receive the most up-to-date balance information available. ### **Retrieving current and available balance** Bank Balance Checks are most effective when performed as close as possible to the close of an ACH processing window, as this is the final moment to show the available and current balance in a user's bank account before funds are debited or credited . By checking the balance at this time, businesses can provide users with actionable insights, such as the need to top up their account or adjust the transaction, avoiding potential delays or returns. When checking a user's bank account balance, it's important to differentiate between the **closing** balance and the **available** balance. `available` - The amount of funds the customer is able to withdraw from the account, not including any credit facility that may be available. The balance includes pending inflows or outflows on the account. `closing` - This represents the current balance of the account, not accounting for pending debits and credits. `lastUpdated` - When calling the balance endpoint on a Customer's funding source initially or on a refresh request, the response will include a lastUpdated parameter with a UTC timestamp value. This timestamp value refers to the last time the Customer's bank account balance was retrieved from the open banking provider. ##### Request and response ```bash HTTP theme={"dark"} GET https://api-sandbox.dwolla.com/funding-sources/c2eb3f03-1b0e-4d18-a4a2-e552cc111418/balance Accept: application/vnd.dwolla.v1.hal+json Authorization: Bearer pBA9fVDBEyYZCEsLf/wKehyh1RTpzjUj5KzIRfDi0wKTii7DqY { "_links": { "self": { "href": "https://api.dwolla.com/funding-sources/42f48a64-2a9b-40df-9777-603ed2fe2764/balance", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "balance" }, "funding-source": { "href": "https://api.dwolla.com/funding-sources/42f48a64-2a9b-40df-9777-603ed2fe2764", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "funding-source" } }, "available": { "value": "542.00", "currency": "USD" }, "closing": { "value": "542.00", "currency": "USD" }, "lastUpdated": "2024-09-09T16:39:14.219Z" } ``` ```ruby retrieve_balance.rb theme={"dark"} # Using dwolla_v2 - https://github.com/Dwolla/dwolla-v2-ruby funding_source_url = 'https://api-sandbox.dwolla.com/funding-sources/c2eb3f03-1b0e-4d18-a4a2-e552cc111418' funding_source = app_token.get "#{funding_source_url}/balance" ``` ```php retrieve_balance.php theme={"dark"} getBalance($fundingSourceUrl); ?> ``` ```python retrieve_balance.py theme={"dark"} # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python funding_source_url = 'https://api-sandbox.dwolla.com/funding-sources/c2eb3f03-1b0e-4d18-a4a2-e552cc111418' funding_source = app_token.get('%s/balance' % funding_source_url) ``` ```javascript retrieveBalance.js theme={"dark"} // Using dwolla-v2 - https://github.com/Dwolla/dwolla-v2-node var fundingSourceUrl = "https://api-sandbox.dwolla.com/funding-sources/c2eb3f03-1b0e-4d18-a4a2-e552cc111418"; dwolla .get(`${fundingSourceUrl}/balance`) .then((res) => res.body.available.value); ``` # MX: Instant Account Verification Source: https://developers.dwolla.com/docs/open-banking/mx 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. 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. ## 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. 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. ## 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. 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. #### Example: Initiating an Exchange Session via Dwolla API ```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 { 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 { 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 ``` ### 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 ```javascript dwolla-v2-node theme={"dark"} // Retrieve exchange session by ID export async function getExchangeSessionUrl(exchangeSessionId: string): Promise { 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" } } } ``` #### 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(null); const widgetInstance = useRef(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 (
); } ``` 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. ### 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 ```javascript dwolla-v2-node theme={"dark"} // List available exchange connections for a customer export async function getAvailableExchangeConnections(customerId: string): Promise { 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" } ] } } ``` #### 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 (
setSelectedBank(e.target.value)} > {availableAccounts.map((account) => ( } label={account.name} /> ))}
); } ``` ### 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. ```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 { 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 ``` ### 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. ```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 { 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 ``` #### 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) # Plaid: Instant Account Verification Source: https://developers.dwolla.com/docs/open-banking/plaid Step-by-step guide to integrating Dwolla's Open Banking solution with Plaid. 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 **Plaid** 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 the steps to set up and integrate **Plaid Instant Account Verification** (Plaid IAV) using **Dwolla's Exchange Sessions API**. Dwolla's powerful Exchange Sessions API acts as the bridge between your application and Plaid's Open Banking API. 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/plaid) 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** or **sandbox** account. 2. **Create a Customer**: Create a customer in your Dwolla account if you haven't already done so. Check out the Dwolla [Create a Customer](/docs/api-reference/customers/create-a-customer) API documentation for guidance. Note: You do not need a Plaid account or contract with Plaid to leverage Plaid via Dwolla's Open Banking Services. Dwolla handles the integration with Plaid for you, simplifying the process. ## Sandbox Testing Testing in the **sandbox environment** is essential before deploying Plaid Open Banking in production. The sandbox allows you to validate functionality with test data, ensuring a smooth experience for live users. ### Test Credentials for Plaid Link Flow Use the following credentials to simulate successful authentication with test banks: * **Username**: `user_good` * **Password**: `pass_good` ### Returning User Testing in Sandbox Plaid's Sandbox includes pre-seeded test users for validating different returning user scenarios. Use the phone numbers and OTP (always `123456`) below to simulate these cases: | Scenario | Phone Number | | -------------------------------------------- | ------------ | | New User | 415-555-0010 | | Verified Returning User | 415-555-0011 | | Verified Returning User: Linked New Account | 415-555-0012 | | Verified Returning User: Linked OAuth Bank | 415-555-0013 | | Verified Returning User + New Device | 415-555-0014 | | Verified Returning User: Auto Account Select | 415-555-0015 | ### Key Notes About Plaid Test Banks Plaid offers two test banks: one for **checking** accounts and one for **savings** accounts. Both have predefined account and routing numbers. This means: * You can add up to two banks per Customer (one checking, one savings). * To test additional banks, remove an existing funding source and repeat the Plaid Link flow. ### Plaid SDKs for Account Authentication Plaid Link serves as the front-end widget for Plaid IAV, enabling users to securely authenticate and link their bank accounts. This widget can be seamlessly embedded into your application using one of [Plaid's Link client SDKs](https://plaid.com/docs/api/libraries/#link-client-sdks). In this guide, we'll be using the `react-plaid-link` package, which is specifically designed for React web applications. It simplifies the integration of Plaid Link by providing a React hook to handle initialization and user interactions with the widget. If you're using a different framework or platform, Plaid offers [SDKs](https://plaid.com/docs/api/libraries/#link-client-sdks) tailored to various environments from which you can select the appropriate package for your application infrastructure. ## Demo Coast Demo: Plaid Open Banking Flow ## Integration Steps ### Step 1 - Initiate Exchange Session with Plaid 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 Plaid as the **exchange partner**. The Exchange Partner ID for Plaid can be found by calling the [List Exchange Partners](/docs/api-reference/exchanges/list-exchange-partners) API endpoint. **About the `redirect-url` Field:** If have an **Android** and/or **iOS** app, you must include a platform-specific `redirect-url` in the request body when creating an exchange session. This URL determines where the user is redirected after completing the Plaid Link flow. The `redirect-url` is a **required** field only for Android and iOS apps. It is not needed for web-based implementations. * For **Android**, use the Android package name as the redirect-url value (e.g., `com.example.app123`). * For **iOS**, use a valid HTTP or HTTPS URL (e.g., `https://example.com/app123`) that can handle redirects in the app. Dwolla will validate the provided `redirect-url` based on these conventions and send the appropriate value to Plaid. If the redirect-url does not start with a valid protocol (https\:// or http\://), it will be assumed to be an Android package name. 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. ##### Example: Initiating an Exchange Session via Dwolla API ```javascript theme={"dark"} import { Client } from "dwolla-v2"; const dwolla = new Client({ key: "YOUR_KEY", secret: "YOUR_SECRET", environment: "sandbox", // or 'production' }); // Retrieve Plaid's exchange partner href export async function getExchangePartnerHref(): Promise { const response = await dwolla.get("/exchange-partners"); const partnersList = response.body._embedded["exchange-partners"]; return partnersList.find( (partner: { name: string }) => partner.name.toLowerCase() === "plaid" )._links.self.href; } // Create an exchange session for a Customer export async function createExchangeSession( customerId: string ): Promise { const exchangePartnerHref = await getExchangePartnerHref(); const requestBody = { _links: { "exchange-partner": { href: exchangePartnerHref }, }, }; const response = await dwolla.post( `customers/${customerId}/exchange-sessions`, requestBody ); return response.headers.get("location"); // URL of the exchange session } // Example usage const exchangeSessionUrl = await createExchangeSession("your-customer-id"); console.log("Exchange session URL:", exchangeSessionUrl); // => Exchange session URL: https://api.dwolla.com/exchange-sessions/fcd15e5f-8d13-4570-a9b7-7fb49e55941d ``` ```bash 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/f53ffb32-c84f-496a-9d9d-acd100d396ef" } } } HTTP/1.1 201 Created Location: https://api.dwolla.com/exchange-sessions/fcd15e5f-8d13-4570-a9b7-7fb49e55941d ``` ##### Example Request: Android ```bash 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/f53ffb32-c84f-496a-9d9d-acd100d396ef" }, "redirect-url": { "href": "com.example.app123" } } } ``` ##### Example Request: iOS ```bash 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/f53ffb32-c84f-496a-9d9d-acd100d396ef" }, "redirect-url": { "href": "https://example.com/app123" } } } ``` ### Step 2 - Retrieve Exchange Session and Complete Plaid Link Flow After creating the exchange session, retrieve the session details from Dwolla to obtain the **Plaid Link Token**. This token initializes the Plaid Link flow on the front end, where the Customer authenticates their bank account through Plaid's secure interface. Once the flow is successfully completed, the `onSuccess` handler captures the `publicToken` returned by Plaid, which can be used for subsequent operations like creating an exchange. #### Code Example ##### Backend - Backend: Retrieve Plaid Link Token ```javascript theme={"dark"} // Retrieve exchange session by ID export async function getPlaidExchangeSession( exchangeSessionId: string ): Promise { const response = await dwolla.get(`/exchange-sessions/${exchangeSessionId}`); return response.body.externalProviderSessionToken; // Plaid Link Token } // Example usage: const plaidLinkToken = await getPlaidExchangeSession( "your-exchange-session-id" ); console.log("Plaid Link Token:", plaidLinkToken); // => Plaid Link Token: link-production-b41e8ed3-0874-4c64-b07d-a77088979d5f ``` ```bash 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/f53ffb32-c84f-496a-9d9d-acd100d396ef", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "exchange-partner" } }, "externalProviderSessionToken": "link-production-b41e8ed3-0874-4c64-b07d-a77088979d5f" } ``` ##### Frontend - Initialize and Handle Plaid Link Flow (using React) ```javascript theme={"dark"} import { usePlaidLink } from "react-plaid-link"; const plaidLinkToken = "your-session-link-token"; // Replace with the actual link token from the exchange session const { open, ready } = usePlaidLink({ token: plaidLinkToken, onSuccess: (publicToken) => { // Public token is retrieved here. This will be used when creating an exchange console.log("Plaid Public Token:", publicToken); }, onExit: () => { console.log("User exited the Plaid Link flow."); }, }); ``` ### Step 3 - Create Exchange with Plaid Public Token After successfully completing the Plaid Link flow and retrieving the `public token`, create an Exchange resource in the Dwolla API by passing the `public token` returned by Plaid. This creates an "exchange" representing the link between the Dwolla [Customer](/docs/api-reference/customers/create-a-customer) and their external bank account. ##### Example: Create Exchange Resource in Dwolla ```javascript theme={"dark"} // Create an exchange for a Dwolla customer using Plaid's public token export async function createExchange( customerId: string, plaidPublicToken: string ): Promise { const exchangePartnerHref = await getExchangePartnerHref(); // Retrieve Plaid's exchange partner href const requestBody = { _links: { "exchange-partner": { href: exchangePartnerHref }, }, plaid: { publicToken: plaidPublicToken, }, }; 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", "your-plaid-public-token" ); console.log("Exchange URL:", exchangeUrl); // => Exchange URL: https://api.dwolla.com/exchanges/73463f82-3f9f-499f-a0ae-630c0808e09f ``` ```bash 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/f53ffb32-c84f-496a-9d9d-acd100d396ef" } }, "plaid": { "publicToken": "public-production-d5456acb-01d5-4932-9783-e4c883cf1c0c" } } HTTP/1.1 201 Created Location: https://api.dwolla.com/exchanges/fcd15e5f-8d13-4570-a9b7-7fb49e55941d ``` ### Step 4 - Create Funding Source After successfully creating the exchange, create a **funding source** for the Customer. 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 Customers's funding source. ##### Example: Create Funding Source in Dwolla ```javascript theme={"dark"} // Creates a funding source for a customer. export async function createFundingSource( customerId: string, exchangeId: string, name: string, type: string ): Promise { 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", "Your Funding Source Name", "checking" ); console.log("Funding Source URL:", fundingSourceUrl); // => Funding Source URL: https://api.dwolla.com/funding-sources/f41ab99c-7748-4f84-a3ed-3f669c002f4f ``` ```bash 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 ``` ## Handling Re-authentication When Customers initially connect their bank account via Instant Account Verification, they authenticate their bank account with Plaid and grant permission to access their account information. This allows Dwolla to perform actions like [checking bank balances](https://developers.dwolla.com/docs/balance/api-reference/open-banking/retrieve-bank-balance). However, this access can be interrupted by changes made by the Customer, such as if the Customer updates their bank password, multi-factor authentication method or revokes consent to access their account information. To maintain a smooth user experience, your application needs to handle these scenarios gracefully. Dwolla's API provides an `UpdateCredentials` error response to signal when a Customer's bank connection needs to be refreshed. Additionally, Dwolla will also send a `customer_exchange_reauth_required` webhook denoting an exchange has been deactivated (or is pending deactivation) and requires re-authentication. Re-authentication is only required for actions that need real-time access to the Customer's bank account through Plaid, like Balance Check. Once a bank account is successfully added to Dwolla as a funding source, transfers can continue to function normally without re-authentication. ##### `UpdateCredentials` error response ``` { "code": "UpdateCredentials", "message": "Re-authentication is required in order to access account data. Please initiate the exchange session flow to regain access.", "_links": { "about": { "href": "https://api.dwolla.com/exchanges/036c8a60-fa45-45d4-8f5d-181d348c6ec8/exchange-sessions", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "exchange-session" } } } ``` #### Steps to manage re-authentication 1. **Detect the `UpdateCredentials` error:** When making calls to Dwolla's API (e.g., checking a bank balance), implement error handling to catch the UpdateCredentials response, which is an HTTP 400 error. 2. **Communicate with your Customer:** It's crucial to inform the Customer why they need to re-authenticate and how to do so. Use clear and concise language in in-app messages, emails or text messages to guide them back to your application to complete the process. 3. **Initiate the re-authentication flow:** Upon receiving this error, redirect the Customer to re-authenticate their bank account. This is done by creating a new [re-authentication exchange session](/docs/api-reference/exchange-sessions/create-re-authentication-exchange-session). This will guide the Customer through the necessary steps to re-establish their bank connection. By following these steps, you can ensure that your application can handle interruptions to bank connections effectively, providing a smooth and user-friendly experience. ### Initiate re-authentication exchange-session Use Dwolla's API endpoint to [create a re-authentication exchange session](/docs/api-reference/exchange-sessions/create-re-authentication-exchange-session), which initiates an Exchange Session for a Customer. While an optional redirect URL can be specified in the request body, it's not required. #### Example using Dwolla Node SDK ##### Example: Re-Authenticate an Exchange in Dwolla ```node theme={"dark"} import { Client } from "dwolla-v2"; const dwolla = new Client({ key: "YOUR_KEY", secret: "YOUR_SECRET", environment: "production", // or 'sandbox' }); /** * Creates a re-auth exchange session for a customer * @param exchangeId - The ID of the deactivated exchange to complete the re-authentication process. */ export async function initiateReauthentication(exchangeId: string) { const requestBody = {}; try { const response = await dwolla.post( `exchanges/${exchangeId}/exchange-sessions`, requestBody ); const location = response.headers.get("location"); return location; } catch (error) { // Return an error message or handle the error appropriately } } // Example usage: const reauthExchangeSessionUrl = initiateReauthentication(exchangeId); // => https://api.dwolla.com/exchange-sessions/fcd15e5f-8d13-4570-a9b7-7fb49e55941d ``` ```bash theme={"dark"} POST https://api.dwolla.com/exchanges/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 ... HTTP/1.1 201 Created Location: https://api.dwolla.com/exchange-sessions/fcd15e5f-8d13-4570-a9b7-7fb49e55941d ``` ##### Example Request: Android ```bash theme={"dark"} POST https://api.dwolla.com/exchanges/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": { "redirect-url": { "href": "com.example.app123" } } } ``` ##### Example Request: iOS ```bash theme={"dark"} POST https://api.dwolla.com/exchanges/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": { "redirect-url": { "href": "https://example.com/app123" } } } ``` ## Resources * [Integration Example App](https://github.com/Dwolla/integration-examples/tree/main/packages/open-banking/plaid) * [Plaid Link SDKs](https://plaid.com/docs/api/libraries/#link-client-sdks) # Operational Notifications Source: https://developers.dwolla.com/docs/operational-notifications Understand what Dwolla events are tied to notifications that are sent to your end users. Preview the customizable email templates here. ## Overview When using Dwolla's white-label API, your application is responsible for all interactions to your end users. You decide how to customize the payments experience end-to-end, which includes taking on the required delivery of notifications to end users for their account and payment activity. By default, Dwolla provides functionality via Operational Notifications and sends email notifications to your end users on your behalf. When actions occur within the Dwolla Network on a resource (Customers, Transfers, etc.), events are created to record those changes. Dwolla systematically delivers an email using the email address tied to the [creation of a Customer](/docs/api-reference/customers) when an applicable event occurs. ## Operational notification events Email notifications are sent to your end users when they complete the activities focused on the following key functions in your application (powered by the Dwolla Platform) or via the Dwolla Dashboard: * Customer creation * Bank account management and validation * Money added to a balance * Money withdrawn from a balance * Money sent to another end user (or your company) * Money received from another end user (or your company) * Customer verification and suspension A full list of events are listed below. Please review to ensure you know when your users will receive notifications via Operational Notifications. In addition to the email notifications listed in this article, further communications, per Dwolla's requirements and your own application's functionality, will need to be sent by you to your end users. Please contact your Integration Manager with questions specific to your integration and recommended (or required) emails. ## Customization When configuring Operational Notifications, there are several items that can be customized to your business. Below is a list of the items that you can currently customize: | Item | Required? | Description | | -------------------------- | ----------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Account Name** | Yes | The name that will display in the **From** section, defaults to the name on your Dwolla account or specified DBA name.
Example: `Your Business Name` | | **From Email Address** | Yes | The email address that will display in the \[From] address, only the local-part can be customized.
Example: `yourbusinessname@dwolla.com` | | **Reply-to Email Address** | Yes | The email address your company will specify to receive replies.
Example: `support@yourbusinessname.com` | | **Logo** | No | The branded image for your company, to be displayed at the top of the email content. The upload file format must be: .jpg, .png, or .gif. The max file size is 10 MB. The recommended width is 135 pixels. | | **Logo Link** | No | The link that when the logo is clicked on, will redirect the receiver.
Example: `www.yourbusinessname.com` | | **Support Address** | Yes | The physical address that will display in the email footer, defaulted to the address on the account.
Example: `123 Main Street, Des Moines, IA 50309` | | **Support phone** | Conditional | An optional item. Required if a `Support email` isn't provided. The phone number that will show up in the support section, defaulted to the account phone number.
Example: `"If you have any questions or concerns please contact support at 555-555-5555."` | | **Support Email Address** | Conditional | An optional item. Required if a `Support phone` isn't provided. The email address that will show up in the support section, optional and defaults to the account email address.
Example: `"If you have any questions or concerns please contact support at support@yourbusinessname.com."` | #### Example email template An example email template is shown below for a payment initiated email. At the present time, only the information listed in the customization section can be edited. Emails are viewable within the Dwolla Dashboard upon delivery attempt to your customers. Operational Notifications email template ## List of emails by type Dwolla will systematically deliver emails for a subset of events existing in the API. As API enhancements are made, Dwolla may add new emails at any point in the future. ## Customer account emails ### Customers ##### Email topic Name customer\_created ##### Email subject and body **Subject:** Account Created ```plaintext theme={"dark"} Hello {CustomerName}, Congrats! Your {AccountName} account was successfully created. By creating a {AccountName} account you have also agreed to the Dwolla Terms of Service and Privacy Policy, and opened a Dwolla account. ``` ##### Email topic name customer\_verified ##### Email subject and body **Subject:** Account Verified ```plaintext theme={"dark"} Hello {CustomerName}, Your account has been successfully verified! ``` ##### Email topic name customer\_suspended ##### Email subject and body **Subject:** Account Suspended ```plaintext theme={"dark"} Hello {CustomerName}, Your account has been suspended. ``` ##### Email topic name customer\_verification\_document\_needed ##### Email subject and body **Subject:** Verification Document Required ```plaintext theme={"dark"} Hello {CustomerName}, Additional documentation is required to verify your account. Please login to upload a document. ``` ##### Email topic name customer\_verification\_document\_uploaded ##### Email subject and body **Subject:** Verification Document Uploaded ```plaintext theme={"dark"} Hello {CustomerName}, Your document was successfully uploaded. You will receive another email when the document has been reviewed. It will either be approved or rejected. ``` ##### Email topic name customer\_verification\_document\_approved ##### Email subject and body **Subject:** Verification Document Approved ```plaintext theme={"dark"} Hello {CustomerName}, The document you uploaded for account verification was approved. ``` ##### Email topic name customer\_verification\_document\_failed ##### Email subject and body **Subject:** Verification Document Failed ```plaintext theme={"dark"} Hello {CustomerName}, A document you uploaded for account verification was rejected. Please login to your account and upload another document. ``` ##### Email topic name customer\_reverification\_needed ##### Email subject and body **Subject:** Verification Required ```plaintext theme={"dark"} Hello {CustomerName}, Thank you for choosing {AccountName}. While you have started to create an account, more information is needed in order to verify your account. Please visit the {AccountName} application to complete your account registration. ``` ### Funding sources ##### Email topic name customer\_funding\_source\_added ##### Email subject and body **Subject:** Funding Source Added ```plaintext theme={"dark"} Hello {CustomerName}, "{BankAccountNickName}" was added to your {AccountName} account. Here are the details: Bank Name: {FinancialInstitutionName} Account: {BankAccountNickName} Date: {Timestamp} {#OnDemandAuthPresent} You've agreed that future payments initiated through {AccountName}'s application will be processed by the Dwolla payment system using the bank account identified above and, if you want to cancel this, please contact support at {SupportEmail} or {#SupportPhone}. {/OnDemandAuthPresent} ``` ##### Email topic name customer\_funding\_source\_verified ##### Email subject and body **Subject:** Funding Source Verified ```plaintext theme={"dark"} Hello {CustomerName}, "{BankAccountNickName}" was verified on {Timestamp}. ``` ##### Email topic name customer\_funding\_source\_removed ##### Email subject and body **Subject:** Funding Source Removed ```plaintext theme={"dark"} Hello {CustomerName}, "{BankAccountNickName}" was removed from your {AccountName} account on {Timestamp}. ``` ##### Email topic name customer\_microdeposits\_added ##### Email subject and body **Subject:** Micro-deposits Initiated ```plaintext theme={"dark"} Hello {CustomerName}, Two micro-deposits were initiated to your bank account. Here are the details: Bank Name: {FinancialInstitutionName} Account: {BankAccountNickName} Date: {Timestamp} ``` ##### Email topic name customer\_microdeposits\_completed ##### Email subject and body **Subject:** Micro-deposits Completed ```plaintext theme={"dark"} Hello {CustomerName}, Two micro-deposits were successfully processed. Here are the details: Bank Name: {FinancialInstitutionName} Account: {BankAccountNickName} Date: {Timestamp} Please check your bank account for two, less than $.20, micro-deposit amounts. You can complete the verification process within the {AccountName} application. ``` ##### Email topic name customer\_microdeposits\_failed ##### Email subject and body **Subject:** Micro-deposits Failed ```plaintext theme={"dark"} Hello {CustomerName}, The transfer of two micro-deposits sent to your bank account failed. Here are the details: Bank Name: {FinancialInstitutionName} Account: {BankAccountNickName} Date: {Timestamp} ``` ### Transfers ##### Email topic name customer\_money\_sent ##### Email subject and body **Subject:** Payment Initiated ```plaintext theme={"dark"} Hello {CustomerName}, A payment was initiated to {OtherPartyName}. Here are the details of this payment: Source: {FundingSource} Recipient: {OtherPartyName} Amount: {Amount} Date Initiated: {Timestamp} ``` ##### Email topic name customer\_money\_sent\_failed ##### Email subject and body **Subject:** Payment Unsuccessful ```plaintext theme={"dark"} Hello {CustomerName}, Your payment to {OtherPartyName} was unsuccessful. Here are the details of this payment: Source: {FundingSource} Recipient: {OtherPartyName} Amount: {Amount} Date Failed: {Timestamp} ``` ##### Email topic name customer\_money\_sent\_cancelled ##### Email subject and body **Subject:** Payment Cancelled ```plaintext theme={"dark"} Hello {CustomerName}, Your payment to {OtherPartyName} was cancelled. Here are the details of this payment: Source: {FundingSource} Recipient: {OtherPartyName} Amount: {Amount} Date Cancelled: {Timestamp} ``` ##### Email topic name customer\_money\_received ##### Email subject and body **Subject:** Payment Pending ```plaintext theme={"dark"} Hello {CustomerName}, A payment was initiated from {OtherPartyName}. Here are the details of this payment: Source: {OtherPartyName} Destination: {FundingSource} Amount: {Amount} Date Initiated: {Timestamp} ``` ##### Email topic name customer\_money\_received\_completed ##### Email subject and body **Subject:** Payment Successful ```plaintext theme={"dark"} Hello {CustomerName}, A payment from {OtherPartyName} has completed. Here are the details of this payment: Source: {OtherPartyName} Destination: {FundingSource} Amount: {Amount} Date Completed: {Timestamp} ``` ##### Email topic name customer\_money\_received\_cancelled ##### Email subject and body **Subject:** Payment Cancelled ```plaintext theme={"dark"} Hello {CustomerName}, A payment from {OtherPartyName} was cancelled. Here are the details of this payment: Source: {OtherPartyName} Destination: {FundingSource} Amount: {Amount} Date Cancelled: {Timestamp} ``` ##### Email topic name customer\_money\_received\_failed ##### Email subject and body **Subject:** Payment Unsuccessful ```plaintext theme={"dark"} Hello {CustomerName}, A payment from {OtherPartyName} was unsuccessful. Here are the details of this payment: Source: {OtherPartyName} Destination: {FundingSource} Amount: {Amount} Date Failed: {Timestamp} ``` ##### Email topic name customer\_money\_added ##### Email Subject and Body **Subject:** Money Added ``` Hello {CustomerName}, An ACH transfer was initiated into your {AccountName} balance. Here are the details of this payment: Source: {FundingSource} Destination: {Destination} Amount: {Amount} Date Initiated: {Timestamp} ``` ##### Email topic name customer\_money\_add\_completed ##### Email subject and body **Subject:** Add to Balance Completed ```plaintext theme={"dark"} Hello {CustomerName}, An ACH transfer into your {AccountName} balance has cleared. Here are the details of this payment: Source: {FundingSource} Destination: {Destination} Amount: {Amount} Date Cleared: {Timestamp} ``` ##### Email topic name customer\_money\_add\_cancelled ##### Email subject and body **Subject:** Add to Balance Cancelled ```plaintext theme={"dark"} Hello {CustomerName}, An ACH transfer into your {AccountName} balance was cancelled. Here are the details of this payment: Source: {FundingSource} Destination: {Destination} Amount: {Amount} Date Cancelled: {Timestamp} ``` ##### Email topic name customer\_money\_add\_failed ##### Email subject and body **Subject:** Add to Balance Failed ```plaintext theme={"dark"} Hello {CustomerName}, An ACH transfer failed to clear into your {AccountName} balance. Here are the details of this payment: Source: {FundingSource} Destination: {Destination} Amount: {Amount} Date Failed: {Timestamp} ``` ##### Email topic name customer\_money\_withdrawn ##### Email subject and body **Subject:** Money Withdrawn ```plaintext theme={"dark"} Hello {CustomerName}, An ACH transfer was initiated out of your {AccountName} balance. Here are the details of this payment: Source: {FundingSource} Destination: {Destination} Amount: {Amount} Date Initiated: {Timestamp} ``` ##### Email topic name customer\_money\_withdrawal\_cancelled ##### Email subject and body **Subject:** Withdrawal Cancelled ```plaintext theme={"dark"} Hello {CustomerName}, An ACH transfer out of your {AccountName} balance was cancelled. Here are the details of this payment: Source: {FundingSource} Destination: {Destination} Amount: {Amount} Date Cancelled: {Timestamp} ``` ##### Email topic name customer\_money\_withdrawal\_failed ##### Email subject and body **Subject:** Withdrawal Failure ```plaintext theme={"dark"} Hello {CustomerName}, An ACH transfer out of your {AccountName} balance was unsuccessful. Here are the details of this payment: Source: {FundingSource} Destination: {Destination} Amount: {Amount} Date Failed: {Timestamp} ``` # Personal Verified Customer Source: https://developers.dwolla.com/docs/personal-verified-customer Learn how to create a Verified personal Customer that can send and receive funds. ## Overview This guide will walk through the identity verification process for [personal verified Customers](/docs/customer-types) within the Dwolla API. A `personal` verified Customer represents an individual that intends to send or receive funds on your platform. In any transaction, at least one party—either the sender or the receiver—must complete the [identity verification](https://www.dwolla.com/updates/guide-to-cip-customer-identification-program-dwolla-payments-api/) process as outlined in this guide. ## Create a personal verified Customer To create a personal verified Customer, use the [create a Customer](/docs/api-reference/customers/create-a-customer) endpoint. A personal verified Customer is determined by setting the value of the `type` request parameter to `personal` and including additional fields required for identifying the individual. ##### Events As a developer, you can expect these events to be triggered when a personal verified Customer is successfully created and systematically verified: 1. `customer_created` 2. `customer_verified` ### Request parameters - personal verified Customer An individual Customer's first name. Must be ≤ 50 characters and contain no special characters ``<[<>="`!?%~${}\]>``. An individual Customer's last name. Must be ≤ 50 characters and contain no special characters ``<[<>="`!?%~${}\]>``. Customer's email address. Must be a valid email format (e.g., [example@domain.com](mailto:example@domain.com)). Customer's IP address. The Verified Customer type. Set to personal if creating a verified personal Customer. First line of the street address of the Customer's permanent residence. Must be ≤ 50 characters, contain no special characters ``<[<>="`!?%~${}\]>``, and cannot be a PO Box. Second line of the street address of the Customer's permanent residence. Must be ≤ 50 characters, contain no special characters ``<[<>="`!?%~${}\]>``, and cannot be a PO Box. City of Customer's permanent residence. Must be ≤ 50 characters and cannot contain numbers or special characters ``<[<>="`!?%~${}\]>``. Two-letter abbreviation of the state in which the Customer resides, e.g., CA. Must be a valid U.S. state. Postal code of Customer's permanent residence. Must be a US 5-digit ZIP code (e.g., 50314) or ZIP+4 (e.g., 50314-1234). Customer's date of birth in YYYY-MM-DD format. Must be between 18 to 125 years old at the time of submission. Last four or full 9 digits of the Customer's Social Security Number. Must contain only numbers (e.g., 1234 or 123456789). Customer's 10-digit phone number. Must contain only numbers, no hyphens, spaces, or separators (e.g., 3334447777). A unique string value attached to a customer which can be used for traceability between Dwolla and your application. Must be ≤ 255 characters and contain no spaces. Acceptable characters: `a-z, 0-9, -, ., and _`. Note: Do not use sensitive Personal Identifying Information (PII). Uniqueness is enforced across Customers. Once you submit this request, Dwolla will perform some initial validation to check for formatting issues such as an invalid date of birth, invalid email format, etc. If successful, the response will be an HTTP 201/Created with the URL of the new Customer resource contained in the `Location` header. ##### Request and response ```bash HTTP theme={"dark"} POST https://api-sandbox.dwolla.com/customers Content-Type: application/vnd.dwolla.v1.hal+json Accept: application/vnd.dwolla.v1.hal+json Authorization: Bearer pBA9fVDBEyYZCEsLf/wKehyh1RTpzjUj5KzIRfDi0wKTii7DqY { "firstName": "John", "lastName": "Doe", "email": "johndoe@email.net", "ipAddress": "10.10.10.10", "type": "personal", "address1": "99-99 33rd St", "city": "Some City", "state": "NY", "postalCode": "11101", "dateOfBirth": "1970-01-01", "ssn": "1234" } HTTP/1.1 201 Created Location: https://api.dwolla.com/customers/FC451A7A-AE30-4404-AB95-E3553FCD733F ``` ```php create_customer.php theme={"dark"} create([ 'firstName' => 'John', 'lastName' => 'Doe', 'email' => 'jdoe@nomail.net', 'type' => 'personal', 'address1' => '99-99 33rd St', 'city' => 'Some City', 'state' => 'NY', 'postalCode' => '11101', 'dateOfBirth' => '1970-01-01', # For the first attempt, only the # last 4 digits of SSN required # If the entire SSN is provided, # it will still be accepted 'ssn' => '1234' ]); $newCustomer; # => "https://api-sandbox.dwolla.com/customers/AB443D36-3757-44C1-A1B4-29727FB3111C" ?> ``` ```ruby create_customer.rb theme={"dark"} # Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby request_body = { :firstName => 'John', :lastName => 'Doe', :email => 'jdoe@nomail.net', :type => 'personal', :address1 => '99-99 33rd St', :city => 'Some City', :state => 'NY', :postalCode => '11101', :dateOfBirth => '1970-01-01', # For the first attempt, only the # last 4 digits of SSN required # If the entire SSN is provided, # it will still be accepted :ssn => '1234' } new_customer = app_token.post "customers", request_body new_customer.response_headers[:location] # => "https://api-sandbox.dwolla.com/customers/AB443D36-3757-44C1-A1B4-29727FB3111C" ``` ```python create_customer.py theme={"dark"} # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python request_body = { 'firstName': 'John', 'lastName': 'Doe', 'email': 'jdoe@nomail.net', 'type': 'personal', 'address1': '99-99 33rd St', 'city': 'Some City', 'state': 'NY', 'postalCode': '11101', 'dateOfBirth': '1970-01-01', # For the first attempt, only the # last 4 digits of SSN required # If the entire SSN is provided, # it will still be accepted 'ssn': '1234' } new_customer = app_token.post('customers', request_body) new_customer.headers['location'] # => 'https://api-sandbox.dwolla.com/customers/AB443D36-3757-44C1-A1B4-29727FB3111C' ``` ```javascript createCustomer.js theme={"dark"} var requestBody = { firstName: "John", lastName: "Doe", email: "jdoe@nomail.net", type: "personal", address1: "99-99 33rd St", city: "Some City", state: "NY", postalCode: "11101", dateOfBirth: "1970-01-01", // For the first attempt, only the // last 4 digits of SSN required // If the entire SSN is provided, // it will still be accepted ssn: "1234", }; dwolla .post("customers", requestBody) .then((res) => res.headers.get("location")); // => 'https://api-sandbox.dwolla.com/customers/FC451A7A-AE30-4404-AB95-E3553FCD733F' ``` ### Check the status of the personal verified Customer The successful creation of a Customer doesn't necessarily mean the Customer is verified and eligible to send or receive funds. When a Customer has been successfully verified by Dwolla, their status will be set to `verified`. Let's check to see if the Customer was successfully verified or not. We are going to use the location of the Customer resource that was just created, which is in `new_customer`. ```bash check_customer.sh theme={"dark"} GET https://api-sandbox.dwolla.com/customers/FC451A7A-AE30-4404-AB95-E3553FCD733F Accept: application/vnd.dwolla.v1.hal+json Authorization: Bearer pBA9fVDBEyYZCEsLf/wKehyh1RTpzjUj5KzIRfDi0wKTii7DqY { "_links": { "self": { "href": "https://api-sandbox.dwolla.com/customers/FC451A7A-AE30-4404-AB95-E3553FCD733F", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "customer" }, "receive": { "href": "https://api-sandbox.dwolla.com/transfers", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "transfer" }, "edit-form": { "href": "https://api-sandbox.dwolla.com/customers/FC451A7A-AE30-4404-AB95-E3553FCD733F", "type": "application/vnd.dwolla.v1.hal+json; profile=\"https://github.com/dwolla/hal-forms\"", "resource-type": "customer" }, "edit": { "href": "https://api-sandbox.dwolla.com/customers/FC451A7A-AE30-4404-AB95-E3553FCD733F", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "customer" }, "funding-sources": { "href": "https://api-sandbox.dwolla.com/customers/FC451A7A-AE30-4404-AB95-E3553FCD733F/funding-sources", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "funding-source" }, "transfers": { "href": "https://api-sandbox.dwolla.com/customers/FC451A7A-AE30-4404-AB95-E3553FCD733F/transfers", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "transfer" }, "send": { "href": "https://api-sandbox.dwolla.com/transfers", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "transfer" } }, "id": "FC451A7A-AE30-4404-AB95-E3553FCD733F", "firstName": "John", "lastName": "Doe", "email": "jdoe@nomail.net", "type": "personal", "status": "verified", "created": "2016-11-28T19:51:48.050Z", "address1": "99-99 33rd St", "address2": "Apt 8", "city": "Some City", "state": "NY", "postalCode": "11101", "phone": "5554321234" } ``` Congrats! Our Customer was successfully verified! However, if the Customer was unable to be verified on the initial flow, they will be given a verification status of either retry, kba, document, or suspended. Continue reading for instructions on [handling various Customer verification statuses](/docs/personal-verified-customer#handle-verification-statuses) and guidelines for providing additional information to verify these Customers. # Handling verification statuses After successfully creating a personal identity-verified `Customer`, they will immediately be given a status. There are various reasons a Customer status can be something other than `verified`; you will want to account for this after the Customer is created. A Customer's verification status is determined by an identity verification score based on the data submitted; this score is returned from Dwolla's identity vendor. Therefore, it is important that the user enters accurate and complete identifying data, and that you exercise [best practices](https://www.owasp.org/index.php/Input_Validation_Cheat_Sheet) in input field validation to ensure the best possible success rate. As an example, the `retry` status can occur when an individual mis-keys or uses incorrect identifying information upon Customer creation (i.e. submitting a date of birth that differs from the user's actual date of birth). It is recommended to have an active [webhook subscription](/docs/api-reference/webhook-subscriptions) to listen for Customer verification related events. Reference the table below for Customer verification statuses and the related events. ### Verification statuses | Customer status | Event | Description | | --------------- | ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | verified | customer\_verified | The identifying information submitted was sufficient in verifying the Customer account. | | retry | customer\_reverification\_needed | The initial identity verification attempt failed because the information provided did not satisfy Dwolla's verification check. You can make one additional attempt by changing some or all the attributes of the existing Customer with a POST request. All fields are required on the retry attempt. If the additional attempt fails, the resulting status will be either `document` or `suspended`. | | kba | customer\_kba\_verification\_needed | The `retry` identity verification attempt failed due to insufficient scores on the submitted data. The end user will have a single kba attempt to answer a set of "out of wallet" questions about themselves for identity verification. **Note:** KBA is a premium feature. Please contact Sales or your account manager for more information on enabling KBA functionality. | | document | customer\_verification\_document\_needed | Dwolla requires additional documentation to identify the Customer in the document status. Once a document is uploaded it will be reviewed for verification. | | suspended | customer\_suspended | The Customer is suspended and may neither send nor receive funds. Contact Account Management for more information. | ### Testing verification statuses in Sandbox Dwolla's Sandbox environment allows you to submit `verified`, `retry`, `kba`, `document`, or `suspended` as the value of the firstName parameter to create a new verified Customer with their respective status. To simulate transitioning a verified Customer with a `retry` status to `verified`, you'll need to call the [Update a Customer](https://developers.dwolla.com/api-reference/customers/update) endpoint and submit full identifying information with an updated firstName value and full SSN. To simulate transitioning a verified Customer with a `document` status to `verified` in the Sandbox, you'll need to upload a test document as outlined in the [Testing in the Sandbox](/docs/testing#simulate-document-upload-approved-and-failed-events) resource article. ## Handling status - `retry` A `retry` status occurs when a Customer's identity scores are too low during the initial verification attempt. Dwolla will require the **full 9-digits** of the individual's SSN on the retry attempt in order to give our identity vendor more information in an attempt to receive a sufficient score to approve the Customer account. The Customer will have one more opportunity to correct any mistakes. You need to gather new information if the Customer is placed into the retry status; simply passing the same information will result in the same insufficient scores. All fields that were required in the initial Customer creation attempt will be required in the retry attempt, along with the full 9-digit SSN. ```bash HTTP theme={"dark"} POST https://api.dwolla.com/customers/132681fa-1b4d-4181-8ff2-619ca46235b1 Content-Type: application/vnd.dwolla.v1.hal+json Accept: application/vnd.dwolla.v1.hal+json Authorization: Bearer pBA9fVDBEyYZCEsLf/wKehyh1RTpzjUj5KzIRfDi0wKTii7DqY { "firstName": "John", "lastName": "Doe", "email": "johndoe@nomail.net", "ipAddress": "10.10.10.10", "type": "personal", "address1": "221 Corrected Address St.", "address2": "Fl 8", "city": "Ridgewood", "state": "NY", "postalCode": "11385", "dateOfBirth": "1990-07-11", "ssn ": "202-99-1516" } ``` ```ruby retry_customer.rb theme={"dark"} # Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby (Recommended) customer_url = 'https://api.dwolla.com/customers/132681fa-1b4d-4181-8ff2-619ca46235b1' request_body = { "firstName" => "John", "lastName" => "Doe", "email" => "jdoe@nomail.com", "ipAddress" => "10.10.10.10", "type" => "personal", "address1" => "221 Corrected Address St..", "address2" => "Apt 201", "city" => "San Francisco", "state" => "CA", "postalCode" => "94104", "dateOfBirth" => "1970-07-11", "ssn" => "123-45-6789" } customer = app_token.post customer_url, request_body customer.id # => "132681fa-1b4d-4181-8ff2-619ca46235b1" ``` ```javascript retryCustomer.js theme={"dark"} // Using dwolla-v2 - https://github.com/Dwolla/dwolla-v2-node var customerUrl = "https://api.dwolla.com/customers/132681fa-1b4d-4181-8ff2-619ca46235b1"; var requestBody = { firstName: "John", lastName: "Doe", email: "johndoe@dwolla.com", ipAddress: "10.10.10.10", type: "personal", address1: "221 Corrected Address St..", address2: "Fl 8", city: "Ridgewood", state: "NY", postalCode: "11385", dateOfBirth: "1990-07-11", ssn: "202-99-1516", }; dwolla.post(customerUrl, requestBody).then(function (res) { res.body.id; // => '132681fa-1b4d-4181-8ff2-619ca46235b1' }); ``` ```python retry_customer.py theme={"dark"} # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python (Recommended) customer_url = 'https://api.dwolla.com/customers/132681fa-1b4d-4181-8ff2-619ca46235b1' request_body = { 'firstName': 'John', 'lastName': 'Doe', 'email': 'jdoe@nomail.com', 'ipAddress': '10.10.10.10', 'type': 'personal', 'address1': '221 Corrected Address St..', 'address2': 'Apt 201', 'city': 'San Francisco', 'state': 'CA', 'postalCode': '94104', 'dateOfBirth': '1970-07-11', 'ssn': '123-45-6789' } customer = app_token.post(customer_url, request_body) customer.body.id # => '132681fa-1b4d-4181-8ff2-619ca46235b1' ``` ```php retry_customer.php theme={"dark"} updateCustomer(array ( 'firstName' => 'John', 'lastName' => 'Doe', 'email' => 'johndoe@nomail.net', 'ipAddress' => '10.10.10.10', 'type' => 'personal', 'address1' => '221 Corrected Address St.', 'address2' => 'Fl 8', 'city' => 'Ridgewood', 'state' => 'NY', 'postalCode' => '11385', 'dateOfBirth' => '1990-07-11', 'ssn' => '202-99-1516', ), $customerUrl); print($retryCustomer->id); # => 132681fa-1b4d-4181-8ff2-619ca46235b1 ?> ``` Check the Customer's status again. The Customer will either be in the `verified`, `kba`, `document`, or `suspended` state of verification. ## Handling status - `kba` This section outlines a premium feature for the Dwolla API. Please contact Sales or your account manager for more information on enabling KBA functionality. ### Initiating the KBA session The first step in the KBA flow is to make a request to the Dwolla API to [generate a unique KBA ID](https://developers.dwolla.com/api-reference/kba/initiate-kba-session) which is used to represent the KBA session. #### Example request and response ```bash HTTP theme={"dark"} POST https://api.dwolla.com/customers/33aa88b1-97df-424a-9043-d5f85809858b/kba Authorization: Bearer cRahPzURfaIrTKL18tmslWPqKdzkLeYJm0oB1hGJ1vMPArft1v Content-Type: application/json Accept: application/vnd.dwolla.v1.hal+json ... HTTP/1.1 201 Created\ Location: https://api.dwolla.com/kba/33aa88b1-97df-424a-9043-d5f85809858b ``` ```ruby initiate_kba.rb theme={"dark"} customer_url = 'https://api-sandbox.dwolla.com/customers/ca22d192-48f1-4b72-b29d-681e9e20795d' kba = app_token.post "#{customer_url}/kba" kba.response_headers[:location] # => "https://api-sandbox.dwolla.com/kba/70b0e9cc-020d-4de2-9a82-a2281afa4c31" ``` ```php initiate_kba.php theme={"dark"} initiateKba($customer_url); $kba; # => "https://api-sandbox.dwolla.com/kba/70b0e9cc-020d-4de2-9a82-a2281afa4c31" ?> ``` ```python initiate_kba.py theme={"dark"} customer_url = 'https://api-sandbox.dwolla.com/customers/61a74e62-e27d-46f1-9fa6-a8e57226bb3e' kba = app_token.post('%s/kba' % customer_url) kba.headers['location'] # => "https://api-sandbox.dwolla.com/kba/70b0e9cc-020d-4de2-9a82-a2281afa4c31" ``` ```javascript initiate_kba.js theme={"dark"} var customerUrl = "https://api-sandbox.dwolla.com/customers/61a74e62-e27d-46f1-9fa6-a8e57226bb3e"; dwolla.post(`${customerUrl}/kba`).then((res) => res.headers.get("location")); // => 'https://api-sandbox.dwolla.com/kba/70b0e9cc-020d-4de2-9a82-a2281afa4c31' ``` ### Retrieve KBA Question set Once the KBA ID is created, your application will have a **single attempt** to [retrieve](https://developers.dwolla.com/api-reference/kba/retrieve-kba-questions) and [answer](https://developers.dwolla.com/api-reference/kba/verify-kba-questions) the question set returned from the Dwolla API. Upon a successful request to retrieve the question set, your end user will have two minutes to complete the submission of their selected answers. #### Example request and response ```bash [expandable] theme={"dark"} GET https://api.dwolla.com/kba/33aa88b1-97df-424a-9043-d5f85809858b Accept: application/vnd.dwolla.v1.hal+json Authorization: Bearer cRahPzURfaIrTewKL18tmslWPqKdzkLeYJm0oB1hGJ1vMPArft1v ... { "_links": { "answer": { "href": "https://api-sandbox.dwolla.com/kba/33aa88b1-97df-424a-9043-d5f85809858b", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "kba" } }, "id": "33aa88b1-97df-424a-9043-d5f85809858b", "questions": [ { "id": "2355953375", "text": "In what county do you currently live?", "answers": [ { "id": "2687969295", "text": "Pulaski" }, { "id": "2687969305", "text": "St. Joseph" }, { "id": "2687969315", "text": "Daviess" }, { "id": "2687969325", "text": "Jackson" }, { "id": "2687969335", "text": "None of the above" } ] }, { "id": "2355953385", "text": "Which team nickname is associated with a college you attended?", "answers": [ { "id": "2687969345", "text": "Colts" }, { "id": "2687969355", "text": "Eagles" }, { "id": "2687969365", "text": "Gator" }, { "id": "2687969375", "text": "Sentinels" }, { "id": "2687969385", "text": "None of the above" } ] }, { "id": "2355953395", "text": "What kind of IA license plate has been on your 1996 Acura TL?", "answers": [ { "id": "2687969395", "text": "Antique" }, { "id": "2687969405", "text": "Disabled Veteran" }, { "id": "2687969415", "text": "Educational Affiliation" }, { "id": "2687969425", "text": "Military Honor" }, { "id": "2687969435", "text": "I have never been associated with this vehicle" } ] } ] } ``` ```ruby retrieve_kba.rb theme={"dark"} kba_url = 'https://api-sandbox.dwolla.com/kba/70b0e9cc-020d-4de2-9a82-a2281afa4c31' kba_questions = app_token.get kba_url kba_questions.id # => "70b0e9cc-020d-4de2-9a82-a2281afa4c31" ``` ```php retrieve_kba.php theme={"dark"} getKbaQuestions($kbaUrl); print $kbaQuestions->id; # => "70b0e9cc-020d-4de2-9a82-a2281afa4c31" ?> ``` ```python retrieve_kba.py theme={"dark"} kba_url = 'https://api-sandbox.dwolla.com/kba/70b0e9cc-020d-4de2-9a82-a2281afa4c31' kba_questions = app_token.get(kba_url) kba_questions.id # => '70b0e9cc-020d-4de2-9a82-a2281afa4c31' ``` ```javascript retrieveKba.js theme={"dark"} var kbaUrl = "https://api-sandbox.dwolla.com/kba/70b0e9cc-020d-4de2-9a82-a2281afa4c31"; dwolla.get(kbaUrl).then((res) => res.body.id); // => '70b0e9cc-020d-4de2-9a82-a2281afa4c31' ``` ### Answer KBA Questions Questions and answers will have their own unique identifiers. Questions and answers are submitted via an `answers` array that contains a list of four JSON objects that include key-value pairs for specifying a questionId and answerId. #### Example request and response ```bash HTTP theme={"dark"} POST https://api.dwolla.com/kba/33aa88b1-97df-424a-9043-d5f85809858b Accept: application/vnd.dwolla.v1.hal+json Authorization: Bearer cRahPzURfaIrTewKL18tmslWPqKdzkLeYJm0oB1hGJ1vMPArft1v ... { "answers": [ { "questionId": "2355953375", "answerId": "2687969335" }, { "questionId": "2355953385", "answerId": "2687969385" }, { "questionId": "2355953395", "answerId": "2687969435" }, { "questionId": "2355953405", "answerId": "2687969485" } ] } ``` ```ruby answer_kba.rb theme={"dark"} kba_url = 'https://api-sandbox.dwolla.com/kba/70b0e9cc-020d-4de2-9a82-a2281afa4c31' request_body = { :answers => [ { :questionId => "2355953375", :answerId => "2687969335" }, { :questionId => "2355953385", :answerId => "2687969385" }, { :questionId => "2355953395", :answerId => "2687969435" }, { :questionId => "2355953405", :answerId => "2687969485" } ] } kba_answers = app_token.post kba_url, request_body ``` ```php answer_kba.php theme={"dark"} answerKbaQuestions([ "answers" => [ [ "questionId" => "2355953375", "answerId" => "2687969335" ], [ "questionId" => "2355953385", "answerId" => "2687969385" ], [ "questionId" => "2355953395", "answerId" => "2687969435" ], [ "questionId" => "2355953405", "answerId" => "2687969485" ] ] ], $kbaUrl); ?> ``` ```python answer_kba.py theme={"dark"} kba_url = 'https://api-sandbox.dwolla.com/kba/70b0e9cc-020d-4de2-9a82-a2281afa4c31' request_body = { 'answers' : [ { 'questionId': "2355953375", 'answerId': "2687969335" }, { 'questionId': "2355953385", 'answerId': "2687969385" }, { 'questionId': "2355953395", 'answerId':"2687969435" }, { 'questionId': "2355953405", 'answerId': "2687969485" } ] } kba_answers = app_token.post (kba_url, request_body) ``` ```javascript answer_kba.js theme={"dark"} var kbaUrl = "https://api.dwolla.com/kba/70b0e9cc-020d-4de2-9a82-a2281afa4c31"; var requestBody = { answers: [ { questionId: "2355953375", answerId: "2687969335", }, { questionId: "2355953385", answerId: "2687969385", }, { questionId: "2355953395", answerId: "2687969435", }, { questionId: "2355953405", answerId: "2687969485", }, ], }; dwolla.post(kbaUrl, requestBody); ``` #### KBA Success If your Customer is able to correctly answer at least three of the four (total) KBA questions, your Customer will be moved into `verified` status. You will receive the `customer_kba_verification_passed` and webhooks to indicate that your Customer has passed the KBA attempt and has been successfully verified. #### KBA Failure A Customer that is unable to answer at least three questions correctly will be moved into `document` status. You will receive the `customer_kba_verification_failed` and `customer_verification_document_needed` webhooks to indicate that your Customer has failed the KBA attempt and must upload a photo Id in order to become `verified`. ## Handling status - `document` If the Customer has a status of `document`, the Customer will need to upload additional pieces of information in order to verify the account. Use the [create a document](https://developers.dwolla.com/api-reference/documents/create-document-for-customer) endpoint when uploading a colored camera captured image of the identifying document. The document(s) will then be reviewed by Dwolla; this review may take up to 1-2 business days to approve or reject. You can provide the following best practices to the Customer in order to reduce the chances of a document being rejected: * Only images of the front of an ID * All 4 Edges of the document should be visible * A dark/high contrast background should be used * At least 90% of the image should be the document * Should be at least 300dpi * Capture image from directly above the document * Make sure that the image is properly aligned, not rotated, tilted or skewed * No flash to reduce glare * No black and white documents * No expired IDs ### Document types A colored camera captured image of the Customer's identifying document can be specified as documentType: `passport`, `license` (state issued driver's license), or `idCard` (other U.S. government-issued photo id card). Note: Military IDs are not accepted When a Customer is placed in the `document` verification status, Dwolla will return a link in the API response after [retrieving a Customer](https://developers.dwolla.com/api-reference/customers/retrieve-a-customer) which will be used by an application to determine if documentation is needed. | Link name | Description | | -------------------- | --------------------------------------------------------- | | verify-with-document | Identifies if documents are needed only for an individual | ##### Example response ```json theme={"dark"} { "_links": { "document-form": { "href": "https://api-sandbox.dwolla.com/customers/64dd2beb-fe56-4cdc-80dd-82a3d7f5b921/documents", "type": "application/vnd.dwolla.v1.hal+json; profile=\"https://github.com/dwolla/hal-forms\"", "resource-type": "document" }, "self": { "href": "https://api-sandbox.dwolla.com/customers/64dd2beb-fe56-4cdc-80dd-82a3d7f5b921", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "customer" }, "funding-sources": { "href": "https://api-sandbox.dwolla.com/customers/64dd2beb-fe56-4cdc-80dd-82a3d7f5b921/funding-sources", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "funding-source" }, "transfers": { "href": "https://api-sandbox.dwolla.com/customers/64dd2beb-fe56-4cdc-80dd-82a3d7f5b921/transfers", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "transfer" }, "verify-with-document": { "href": "https://api-sandbox.dwolla.com/customers/64dd2beb-fe56-4cdc-80dd-82a3d7f5b921/documents", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "document" } }, "id": "64dd2beb-fe56-4cdc-80dd-82a3d7f5b921", "firstName": "document", "lastName": "doctest", "email": "docTest@email.com", "type": "personal", "status": "document", "created": "2017-08-31T14:28:11.047Z", "address1": "99-99 33rd St", "address2": "Apt 8", "city": "Some City", "state": "NY", "postalCode": "11101" } ``` ### Uploading a document To upload a color photo of the document, you'll initiate a multipart form-data POST request from your backend server to `https://api.dwolla.com/customers/{id}/documents`. The file must be either a .jpg, .jpeg, or .png. Files must be no larger than 10MB in size. ```bash theme={"dark"} curl -X POST \ -H "Authorization: Bearer tJlyMNW6e3QVbzHjeJ9JvAPsRglFjwnba4NdfCzsYJm7XbckcR" \ -H "Accept: application/vnd.dwolla.v1.hal+json" \ -H "Cache-Control: no-cache" \ -H "Content-Type: multipart/form-data" \ -F "documentType=passport" \ -F "file=@foo.png" \ 'https://api-sandbox.dwolla.com/customers/132681fa-1b4d-4181-8ff2-619ca46235b1/documents' HTTP/1.1 201 Created Location: https://api-sandbox.dwolla.com/documents/11fe0bab-39bd-42ee-bb39-275afcc050d0 ``` ```ruby theme={"dark"} # Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby customer_url = 'https://api.dwolla.com/customers/132681fa-1b4d-4181-8ff2-619ca46235b1' file = Faraday::UploadIO.new('mclovin.jpg', 'image/jpeg') document = app_token.post "#{customer_url}/documents", file: file, documentType: 'license' document.response_headers[:location] # => "https://api.dwolla.com/documents/11fe0bab-39bd-42ee-bb39-275afcc050d0" ``` ```javascript theme={"dark"} // Using dwolla-v2 - https://github.com/Dwolla/dwolla-v2-node var customerUrl = "https://api.dwolla.com/customers/132681fa-1b4d-4181-8ff2-619ca46235b1"; var requestBody = new FormData(); body.append("file", fs.createReadStream("mclovin.jpg"), { filename: "mclovin.jpg", contentType: "image/jpeg", knownLength: fs.statSync("mclovin.jpg").size, }); body.append("documentType", "license"); dwolla.post(`${customerUrl}/documents`, requestBody).then(function (res) { res.headers.get("location"); // => "https://api.dwolla.com/documents/11fe0bab-39bd-42ee-bb39-275afcc050d0" }); ``` ```python theme={"dark"} # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python (Recommended) customer_url = 'https://api.dwolla.com/customers/132681fa-1b4d-4181-8ff2-619ca46235b1' document = app_token.post('%s/documents' % customer_url, file = open('mclovin.jpg', 'rb'), documentType = 'license') document.headers['location'] # => 'https://api.dwolla.com/documents/11fe0bab-39bd-42ee-bb39-275afcc050d0' ``` ```php theme={"dark"} // No SDK support. Coming soon ``` If the document was successfully uploaded, the response will be a HTTP 201 Created with the URL of the new document resource contained in the Location header. ```bash theme={"dark"} HTTP/1.1 201 Created Location: https://api-sandbox.dwolla.com/documents/11fe0bab-39bd-42ee-bb39-275afcc050d0 ``` You'll also get a webhook with a `customer_verification_document_uploaded` event to let you know the document was successfully uploaded. ### Document review process Once created, the document will be reviewed by Dwolla. When the document has been reviewed, which may take anywhere from a few seconds up to 1-2 business days if manual verification is required to approve or reject, we'll create either a `customer_verification_document_approved` or `customer_verification_document_failed` event. If the document was sufficient, the Customer may be verified in this process. If not, we may need additional documentation. Note: Reference the [determining verification documents needed](#handling-status-document) section for more information on determining if additional documents are needed after an approved or failed event is triggered. If the document was found to be fraudulent or doesn't match the identity of the Customer, the Customer will be suspended. ### Document failure A document can fail if, for example, the Customer uploaded the wrong type of document or the `.jpg` or `.png` file supplied was not readable (i.e. blurry, not well lit, not in color, or cuts off a portion of the identifying image). If you receive a `customer_verification_document_failed` webhook, you'll need to upload another document. To retrieve the failure reason for the document upload, you'll retrieve the document by its ID. Contained in the response will be a `failureReason` field which corresponds to one or more of the following values. In case of a failure due to multiple reasons, an additional `allFailureReasons` array of `reason`s and `description`s is also returned : | Failure reason | Description | | -------------------------------------- | ---------------------------------------------------------------------- | | ForeignPassportNotAllowed | The passport's country of origin was not the United States of America | | ScanNotReadable | Image blurry, too dark, or obscured by glare | | ScanNotUploaded | Scan not uploaded | | ScanIdExpired | ID is expired | | ScanIdTypeNotSupported | ID may be a military ID, firearm license, or other unsupported ID type | | ScanIdUnrecognized | ID is not recognized | | ScanNameMismatch | Name mismatch | | ScanDobMismatch | Date of birth mismatch | | ScanFailedOther | ID may be fraudulent or a generic example ID image | ##### Request and response ```bash theme={"dark"} GET https://api-sandbox.dwolla.com/documents/11fe0bab-39bd-42ee-bb39-275afcc050d0 Accept: application/vnd.dwolla.v1.hal+json Authorization: Bearer tJlyMNW6e3QVbzHjeJ9JvAPsRglFjwnba4NdfCzsYJm7XbckcR ... { "_links": { "self": { "href": "https://api-sandbox.dwolla.com/documents/11fe0bab-39bd-42ee-bb39-275afcc050d0" } }, "id": "11fe0bab-39bd-42ee-bb39-275afcc050d0", "status": "reviewed", "type": "license", "created": "2016-01-29T21:22:22.000Z", "failureReason": "ScanNotReadable", "allFailureReasons": [ { "reason": "ScanDobMismatch", "description": "Date of Birth mismatch" }, { "reason": "ScanIdExpired", "description": "ID is expired" } ] } ``` ```ruby theme={"dark"} # Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby (Recommended) document_url = 'https://api.dwolla.com/documents/11fe0bab-39bd-42ee-bb39-275afcc050d0' document = app_token.get document_url document.failureReason # => "ScanNotReadable" ``` ```javascript theme={"dark"} var documentUrl = "https://api.dwolla.com/documents/11fe0bab-39bd-42ee-bb39-275afcc050d0"; dwolla.get(document_url).then(function (res) { res.body.failureReason; // => "ScanNotReadable" }); ``` ```python theme={"dark"} # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python (Recommended) document_url = 'https://api.dwolla.com/documents/11fe0bab-39bd-42ee-bb39-275afcc050d0' documents = app_token.get(document_url) documents.body['failureReason'] # => 'ScanNotReadable' ``` ```php theme={"dark"} getCustomer($aDocument); print($retrieved->failureReason); # => "ScanNotReadable" ?> ``` ## Handling status: suspended If the Customer is `suspended`, there's no further action you can take to correct this using the API. You'll need to contact [support@dwolla.com](mailto:support@dwolla.com) or your account manager for assistance. # Frequently Asked Questions
  • Send funds - No
  • Receive funds - Yes - Note that funds will only process to their balance and the transfer will stay pending until the Customer has been verified.
  • Add and verify a bank funding source - Yes
  • Send funds - No
  • Receive funds - Yes - Note that funds will only process to their balance and the transfer will stay pending until the Customer has been verified.
  • Add and verify a bank funding source - Yes
  • Send funds - No
  • Receive funds - No
  • Add and verify a bank funding source - No

Your Customer has likely not completed the bank verification process. You can check to see the status of the funding source via the API or by going into the Dwolla dashboard.

No. Downgrade functionality is not supported for Dwolla Verified Customers.

Yes, although this is not necessary, nor recommended. Dwolla manually reviews all documents, so sending more documents than necessary may slow down the verification process for your Customers.

No. At this time, Dwolla only supports end users that are US residents when creating Personal Verified Customers.

Your Customer is able to send multiple separate transfers as long as each transfer amount is less than the transfer limit defined in your services agreement.

Example Scenario: The transaction limit for my Customer is $10,000 and they need to send $15,000. In this case, you can prompt your Customer to send two transfers. One for $10,000 and another for $5,000.

# Quickstart Source: https://developers.dwolla.com/docs/quickstart Make your first authenticated Dwolla API call in under 5 minutes. ## What you'll do By the end of this guide you'll have an access token and a successful response from the Dwolla sandbox — the foundation for every other API call. From there, pick a funds flow to build your real integration. The Dwolla sandbox is a complete, free replica of production. You only need a valid email address to sign up. You'll be prompted to verify your email, then redirected to the sandbox dashboard. Need more detail? See the [Testing in the Sandbox guide](/docs/testing). From the [sandbox dashboard](https://dashboard-sandbox.dwolla.com/), navigate to **Applications** to find your `client_id` and `client_secret`. Dwolla automatically creates an application for your sandbox account along with a `$5,000` test balance. Treat your client\_secret like a password. Never commit it to source control or expose it on the client side. Exchange your credentials for an access token by POSTing to `/token` with `grant_type=client_credentials`. The `Authorization` header uses HTTP Basic auth: `Base64(client_id:client_secret)`. ```bash curl theme={"dark"} curl -X POST 'https://api-sandbox.dwolla.com/token' \ -H "Authorization: Basic $(echo -n 'YOUR_CLIENT_ID:YOUR_CLIENT_SECRET' | base64)" \ -H 'Content-Type: application/x-www-form-urlencoded' \ -d 'grant_type=client_credentials' ``` ```typescript TypeScript theme={"dark"} // Using dwolla — https://github.com/Dwolla/dwolla-typescript import { Dwolla } from "dwolla"; const dwolla = new Dwolla({ security: { clientID: process.env.DWOLLA_CLIENT_ID ?? "", clientSecret: process.env.DWOLLA_CLIENT_SECRET ?? "", }, server: "sandbox", }); // The SDK requests and caches tokens automatically on your first API call. ``` ```python Python theme={"dark"} # Using dwollav2 — https://github.com/Dwolla/dwolla-v2-python import dwollav2 client = dwollav2.Client( key=os.environ["DWOLLA_APP_KEY"], secret=os.environ["DWOLLA_APP_SECRET"], environment="sandbox", ) app_token = client.Auth.client() ``` ```php PHP theme={"dark"} setSecurity( new Components\Security( clientID: getenv('DWOLLA_CLIENT_ID'), clientSecret: getenv('DWOLLA_CLIENT_SECRET'), ) ) ->setServer('sandbox') ->build(); // The SDK requests and caches tokens automatically on your first API call. ?> ``` ```ruby Ruby theme={"dark"} # Using dwolla-v2-ruby — https://github.com/Dwolla/dwolla-v2-ruby require 'dwolla_v2' $dwolla = DwollaV2::Client.new( key: ENV['DWOLLA_APP_KEY'], secret: ENV['DWOLLA_APP_SECRET'] ) do |config| config.environment = :sandbox end app_token = $dwolla.auths.client ``` A successful response returns a JSON payload with an `access_token`, expiration, and scope. Tokens are valid for one hour — request a new one when yours expires. ```json Response theme={"dark"} { "access_token": "0Sn0W6kzNicvoWhDbQcVSKLRUpGjIdlPSEYyrHqrDDoRnQwE7Q", "token_type": "bearer", "expires_in": 3600 } ``` Use the access token as a Bearer credential to call the [Root endpoint](/docs/api-reference/root). The response contains the `_links` you'll use to interact with the rest of the API — most importantly, your account URL. ```bash curl theme={"dark"} curl -X GET 'https://api-sandbox.dwolla.com/' \ -H 'Accept: application/vnd.dwolla.v1.hal+json' \ -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' ``` ```typescript TypeScript theme={"dark"} const root = await dwolla.root.get(); console.log(root.object?._links?.account?.href); // => "https://api-sandbox.dwolla.com/accounts/..." ``` ```python Python theme={"dark"} root = app_token.get("/") print(root.body["_links"]["account"]["href"]) # => "https://api-sandbox.dwolla.com/accounts/..." ``` ```php PHP theme={"dark"} root->get(); if ($response->root !== null) { // handle response } ?> ``` ```ruby Ruby theme={"dark"} root = app_token.get "/" puts root._links["account"].href # => "https://api-sandbox.dwolla.com/accounts/..." ``` ```json Response theme={"dark"} { "_links": { "account": { "href": "https://api-sandbox.dwolla.com/accounts/ad5f2162-404a-4c4c-994e-6ab6c3a13254", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "account" }, "customers": { "href": "https://api-sandbox.dwolla.com/customers", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "customer" } } } ``` That `account` href is your Platform Account — you'll reference it when creating funding sources, initiating transfers, and listing activity. ## You did it You've authenticated against the Dwolla API and made your first request. Next, pick the funds flow that matches your use case and start building. Disburse funds (payouts) to your end users' bank accounts. Pull funds (pay-ins) from your end users' bank accounts. Facilitate transfers between parties on your platform — B2B, B2C, or C2B. Move money between two bank accounts belonging to the same user. ## Going deeper OAuth 2.0 client credentials flow, token lifecycle, and request headers. Choose the right customer types for your end users. Subscribe to events so your app stays in sync with the API. Official client libraries for Node, Python, PHP, Ruby, Kotlin, and C#. # Receive Money from Users Source: https://developers.dwolla.com/docs/receive-money Learn the key steps involved with receiving funds from your end user's bank account. ## Overview This guide is designed to get you up and running quickly through creating a one-time transfer from an end user via the Dwolla API. In this guide, we'll cover the basics of integrating this lightweight payment flow, receiving funds (also referred to as "pay-ins"), by breaking down the steps to create a bank transfer. For simplicity, we'll represent a one-to-one transfer between two end users, where the `source` user is the individual or business that has been onboarded as a Dwolla Customer record. The `destination` user is identified as your Main Dwolla Account. Funds Flow Receive Money In this quickstart guide, you'll learn the following key concepts involved with receiving funds from your end user's bank account: Select and create the appropriate Customer type for your sending Customer. Both unverified and verified Customer types are eligible to send funds. Add and verify a bank account (funding source) to the Customer. This is required for the Customer to be eligible to send funds. Retrieve the list of available funding sources for both your Customer and your Main Dwolla Account. Create a transfer from your Customer's bank account to your Main Dwolla Account's bank account. ## Before you begin We encourage you to create a Sandbox account, if you haven't already. This will allow you to follow along with the steps outlined in this guide. Check out our [Sandbox guide](/docs/testing) to learn more on creating an account. After creating a sandbox account, you'll obtain your API Key and Secret, which are used to obtain an OAuth access token. An access token is required in order to authenticate against the Dwolla API. If you haven't already, run through the [Quickstart](/docs/quickstart) to get your first token, or see the [Authentication guide](/docs/api-reference/api-fundamentals/making-requests-and-authentication) for OAuth details. Lastly, in this sandbox walkthrough, we recommend having an active webhook subscription. This will help notify your application of various events that occur within the Dwolla API. Check out our guide to [learn more](/docs/working-with-webhooks). Let's get started! # Step 1 - Creating your Customer #### Choose the Customer Type for your Funds Flow Before your end user can send funds, they must be created as a Customer via the Dwolla API. The pay-ins funds flow is flexible in terms of choosing a Customer type to onboard, as both the `unverified` Customer and `verified` Customer types are eligible to send funds. To learn more on the different Customer types and the capabilities of each, check out our [customer types resource article](/docs/customer-types). ### Create the Customer While you can use the `verified` Customer type in this funds flow, we will be creating an `unverified` Customer in this guide. ##### Request Parameters - Unverified Customer | Parameter | Required? | Type | Description | | ------------ | ----------- | ------ | ----------------------------------------------------------------------- | | firstName | yes | string | Customer's first name | | lastName | yes | string | Customer's last name | | email | yes | string | Customer's email address | | businessName | conditional | string | Customer's registered business name (optional if not a business entity) | | ipAddress | no | string | Customer's IP address | ```bash HTTP theme={"dark"} POST https://api-sandbox.dwolla.com/customers Content-Type: application/vnd.dwolla.v1.hal+json Accept: application/vnd.dwolla.v1.hal+json Authorization: Bearer pBA9fVDBEyYZCEsLf/wKehyh1RTpzjUj5KzIRfDi0wKTii7DqY { "firstName": "Jane", "lastName": "Doe", "email": "janeDoe@nomail.net", "ipAddress": "99.99.99.99", } HTTP/1.1 201 Created Location: https://api-sandbox.dwolla.com/customers/FC451A7A-AE30-4404-AB95-E3553FCD733F ``` ```ruby create_customer.rb theme={"dark"} request_body = { :firstName => 'Jane', :lastName => 'Doe', :email => 'janeDoe@nomail.net', :ipAddress => '99.99.99.99' } # Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby (Recommended) customer = app_token.post "customers", request_body customer.response_headers[:location] # => "https://api-sandbox.dwolla.com/customers/c7f300c0-f1ef-4151-9bbe-005005aa3747" ``` ```javascript create_customer.js theme={"dark"} var requestBody = { firstName: "Jane", lastName: "Merchant", email: "jmerchant@nomail.net", ipAddress: "99.99.99.99", }; dwolla.post("customers", requestBody).then(function (res) { res.headers.get("location"); // => 'https://api-sandbox.dwolla.com/customers/c7f300c0-f1ef-4151-9bbe-005005aa3747' }); ``` ```python create_customer.py theme={"dark"} request_body = { 'firstName': 'Jane', 'lastName': 'Merchant', 'email': 'jmerchant@nomail.net', 'ipAddress': '99.99.99.99' } # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python (Recommended) customer = app_token.post('customers', request_body) customer.headers['location'] # => 'https://api-sandbox.dwolla.com/customers/c7f300c0-f1ef-4151-9bbe-005005aa3747' ``` ```php create_customer.php theme={"dark"} create([ 'firstName' => 'Jane', 'lastName' => 'Merchant', 'email' => 'jmerchant@nomail.net', 'ipAddress' => '99.99.99.99' ]); print($customer); # => "https://api-sandbox.dwolla.com/customers/c7f300c0-f1ef-4151-9bbe-005005aa3747" ?> ``` Providing the IP address of the end user accessing your application as the ipAddress parameter. This enhances fraud detection and tracking. When the Customer is successfully created by your application, you will receive a `201` HTTP response with an empty response body. You can reference the Location header to retrieve a link that represents the created Customer resource. We recommend storing the full URL for future use, as it will be necessary to complete additional actions, such as attaching a bank or correlating webhooks that are triggered for the end user in the Dwolla system. ### Handle Webhooks If you have an active webhook subscription, you will receive the `customer_created` webhook immediately after the resource has been created. # Step 2 - Adding a Funding Source Within Dwolla, the sending party must always have a verified funding source. Since your Customer is the one sending funds, they will need to both add and verify their bank funding source before being eligible to send funds. The destination party, or the party receiving funds, does not need to have a verified funding source to receive these funds. #### Bank Addition and Verification Methods There are multiple ways of adding a bank to a Customer with the Dwolla API. A simplified table below outlines the similarities and differences of each method. | Bank Addition Method | Will the bank be verified? | Required Information | | ---------------------------------------------------------- | ----------------------------- | ------------------------------- | | API - Account & Routing Number | Optional - With Microdeposits | Bank Account and Routing Number | | [Dwolla + Open Banking](/docs/open-banking) | Yes | Online banking credentials | | [Drop-in components](/docs/drop-in-components) | Optional - With Microdeposits | Bank Account and Routing Number | | [Dwolla + Secure Exchange solution](/docs/secure-exchange) | Yes | Online banking credentials | | Other Approved Third-party Provider | Yes | Variable | For more information on securely submitting a user's bank details directly to Dwolla from the client-side of your application, reference our Drop-in components . ### Add a Bank to an Unverified Customer In this step, we will create and attach a verified funding source to your Customer using Dwolla's Open Banking solution with Plaid, a leading Open Banking service provider that Dwolla partners with. This method will give your Customers the ability to add and verify their bank account in a matter of seconds by authenticating using their online banking credentials. Once your Customer reaches the page in your application to add a bank account, you will use Open Banking with Plaid to authenticate the user's bank account. This involves initiating an Exchange Session with Dwolla, guiding the user through the verification process with their bank, and then using the Exchange details to create a funding source in Dwolla. To integrate Open Banking with Plaid, we recommend checking out our [integration guide](/docs/open-banking/plaid). Additionally, if you would like to see a working example that verifies a bank using Open Banking with Plaid and attaches it as a verified funding source to a Dwolla Customer, please check out our [open-banking/plaid](https://github.com/Dwolla/integration-examples/tree/main/packages/open-banking/plaid) integration example on our GitHub profile. ### Handle Webhooks If you have an active webhook subscription, you should receive both the `customer_funding_source_added` and `customer_funding_source_verified` webhooks immediately following the request to Dwolla to add a funding source using Open Banking. # Step 3 - Retrieving Funding Sources Now that you've created a Customer and associated its funding source, you are close to being able to initiate your first transfer. The transfer requires the following information: * A funding source to pull the funds from (your Customer's linked bank funding source) * A funding source to push the funds to (your Main Dwolla Account's linked bank funding source) Dwolla uses URLs to represent relations between resources. Therefore, you'll need to provide the full URL of the funding source and recipient. ### Retrieve your Customer's list of available Funding Sources In order to find your Customer's available bank and balance funding sources, you will need to first retrieve the funding sources from your Customer, via the API. ##### Request and response ```bash HTTP [expandable] theme={"dark"} GET https://api-sandbox.dwolla.com/customers/2e09d295-e0b4-48e1-9ad0-69eafd47f212/funding-sources Accept: application/vnd.dwolla.v1.hal+json Authorization: Bearer [YOUR_OAUTH_TOKEN_HERE] { "_links": { "self": { "href": "https://api-sandbox.dwolla.com/customers/2e09d295-e0b4-48e1-9ad0-69eafd47f212/funding-sources?removed=false", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "funding-source" }, "customer": { "href": "https://api-sandbox.dwolla.com/customers/2e09d295-e0b4-48e1-9ad0-69eafd47f212", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "customer" } }, "_embedded": { "funding-sources": [ { "_links": { "transfer-from-balance": { "href": "https://api-sandbox.dwolla.com/transfers", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "transfer" }, "self": { "href": "https://api-sandbox.dwolla.com/funding-sources/c4805686-dee2-4f5a-ae8c-f269e29658b2", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "funding-source" }, "transfer-to-balance": { "href": "https://api-sandbox.dwolla.com/transfers", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "transfer" }, "transfer-send": { "href": "https://api-sandbox.dwolla.com/transfers", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "transfer" }, "remove": { "href": "https://api-sandbox.dwolla.com/funding-sources/c4805686-dee2-4f5a-ae8c-f269e29658b2", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "funding-source" }, "customer": { "href": "https://api-sandbox.dwolla.com/customers/2e09d295-e0b4-48e1-9ad0-69eafd47f212", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "customer" }, "transfer-receive": { "href": "https://api-sandbox.dwolla.com/transfers", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "transfer" } }, "id": "c4805686-dee2-4f5a-ae8c-f269e29658b2", "status": "verified", "type": "bank", "bankAccountType": "checking", "name": "Plaid Test", "created": "2022-06-01T20:50:17.276Z", "removed": false, "channels": [ "ach", "real-time-payments" ], "bankName": "SANDBOX TEST BANK", "fingerprint": "dcd236c37358c1e4a306e6fb1b37dac85e0b2cc9925d4719f1e72d7731a80923" } ] } } ``` ```php get_funding_sources.php theme={"dark"} getCustomerFundingSources($customerUrl); $fundingSources->_embedded->{'funding-sources'}[0]->name; # => "Jane Doe's Checking" ?> ``` ```ruby get_funding_sources.rb theme={"dark"} # Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby customer_url = 'https://api-sandbox.dwolla.com/customers/5b29279d-6359-4c87-a318-e09095532733' funding_sources = app_token.get "#{customer_url}/funding-sources" funding_sources._embedded['funding-sources'][0].name # => "Jane Doe's Checking" ``` ```python get_funding_sources.py theme={"dark"} # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python customer_url = 'https://api-sandbox.dwolla.com/customers/5b29279d-6359-4c87-a318-e09095532733' funding_sources = app_token.get('%s/funding-sources' % customer_url) funding_sources.body['_embedded']['funding-sources'][0]['name'] # => 'Jane Doe's Checking' ``` ```javascript get_funding_sources.js theme={"dark"} var customerUrl = "https://api-sandbox.dwolla.com/customers/5b29279d-6359-4c87-a318-e09095532733"; dwolla .get(`${customerUrl}/funding-sources`) .then((res) => res.body._embedded["funding-sources"][0].name); // => 'Jane Doe's Checking' ``` ### Retrieve your Main Dwolla Account's list of available Funding Sources In order to find your Main Account's available bank funding sources, you will need to first retrieve the funding sources from your Main Account, via the API. You'll need your account URL which can be retrieved by calling the Root of the API. ##### Request and response ```bash HTTP [expandable] theme={"dark"} GET https://api-sandbox.dwolla.com/accounts/ad5f2162-404a-4c4c-994e-6ab6c3a13254/funding-sources?removed=false Accept: application/vnd.dwolla.v1.hal+json Authorization: Bearer 0Sn0W6kzNicvoWhDbQcVSKLRUpGjIdlPSEYyrHqrDDoRnQwE7Q { "_links": { "self": { "href": "https://api-sandbox.dwolla.com/accounts/ad5f2162-404a-4c4c-994e-6ab6c3a13254/funding-sources" } }, "_embedded": { "funding-sources": [ { "_links": { "transfer-from-balance": { "href": "https://api-sandbox.dwolla.com/transfers", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "transfer" }, "self": { "href": "https://api-sandbox.dwolla.com/funding-sources/b5e68264-7d4d-42a9-88d4-5616c77c6baa", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "funding-source" }, "remove": { "href": "https://api-sandbox.dwolla.com/funding-sources/b5e68264-7d4d-42a9-88d4-5616c77c6baa", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "funding-source" }, "transfer-send": { "href": "https://api-sandbox.dwolla.com/transfers", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "transfer" }, "transfer-receive": { "href": "https://api-sandbox.dwolla.com/transfers", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "transfer" }, "account": { "href": "https://api-sandbox.dwolla.com/accounts/ad5f2162-404a-4c4c-994e-6ab6c3a13254", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "account" } }, "id": "b5e68264-7d4d-42a9-88d4-5616c77c6baa", "status": "verified", "type": "bank", "bankAccountType": "checking", "name": "ABC Bank Checking", "created": "2019-03-14T15:18:51.336Z", "removed": false, "channels": [ "ach" ], "bankName": "SANDBOX TEST BANK" } ] } } ``` ```ruby get_account_funding_sources.rb theme={"dark"} account_url = 'https://api.dwolla.com/accounts/ad5f2162-404a-4c4c-994e-6ab6c3a13254' # Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby (Recommended) funding_sources = app_token.get "#{account_url}/funding-sources?removed=false" funding_sources._embedded['funding-sources'][0].name # => "ABC Bank Checking" ``` ```javascript get_account_funding_sources.js theme={"dark"} var accountUrl = "https://api-sandbox.dwolla.com/accounts/ad5f2162-404a-4c4c-994e-6ab6c3a13254"; dwolla.get(`${accountUrl}/funding-sources?removed=false`).then(function (res) { res.body._embedded["funding-sources"][0].name; // => 'ABC Bank Checking' }); ``` ```python get_account_funding_sources.py theme={"dark"} account_url = 'https://api.dwolla.com/accounts/ad5f2162-404a-4c4c-994e-6ab6c3a13254' # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python (Recommended) funding_sources = app_token.get('%s/funding-sources?removed=false' % account_url) funding_sources.body['_embedded']['funding-sources'][0]['name'] # => 'ABC Bank Checking' ``` ```php get_account_funding_sources.php theme={"dark"} getAccountFundingSources($accountUrl, $removed = false); # Access desired information in response object fields print($fundingSources->_embedded) # => PHP associative array of _embedded contents in schema ?> ``` When the funding sources are successfully retrieved, you will receive a `200` HTTP response with the details of the funding sources. After retrieving the funding sources, we recommend storing the full URL for future use as it will be referenced when creating the transfer to this end user's bank account. # Step 4 - Initiating a Transfer #### Identify Source and Destination For Transfer Since you are utilizing a `receive` funds flow, you will need to ensure that you know who the funds are going to. * Source - Your Customer's Bank Funding Source * Destination - Your Main Dwolla Account Bank Funding Source ### Initiate a Transfer To initiate a transfer, we will need to specify the source and destination funding source URLs in the \_links parameter. ##### Request Parameters | Parameter | Required | Type | Description | | --------- | -------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | \_links | yes | object | A \_links JSON object describing the desired source and destination of a transfer. [Reference the Source and Destination object](/docs/api-reference/transfers) to learn more about possible values for source and destination. | | amount | yes | object | An amount JSON object. [Reference the amount JSON object](/docs/api-reference/transfers) to learn more. | Within a transfer request, Dwolla supports additional optional parameters. These can range from clearing to specify the processing timing for the transfer, or correlationId to help correlate transfers from end-to-end. The object facilitator-fee isn't supported for this funds flow. For more information on all available transfer request parameters, check out our API reference documentation. ##### Request and response ```bash HTTP theme={"dark"} POST https://api-sandbox.dwolla.com/transfers Accept: application/vnd.dwolla.v1.hal+json Content-Type: application/vnd.dwolla.v1.hal+json Authorization: Bearer pBA9fVDBEyYZCEsLf/wKehyh1RTpzjUj5KzIRfDi0wKTii7DqY Idempotency-Key: 19051a62-3403-11e6-ac61-9e71128cae77 { "_links": { "source": { "href": "https://api-sandbox.dwolla.com/funding-sources/707177c3-bf15-4e7e-b37c-55c3898d9bf4" }, "destination": { "href": "https://api-sandbox.dwolla.com/funding-sources/AB443D36-3757-44C1-A1B4-29727FB3111C" } }, "amount": { "currency": "USD", "value": "10.00" }, } ... HTTP/1.1 201 Created Location: https://api-sandbox.dwolla.com/transfers/74c9129b-d14a-e511-80da-0aa34a9b2388 ``` ```php create_transfer.php theme={"dark"} array ( 'source' => array ( 'href' => 'https://api-sandbox.dwolla.com/funding-sources/b5e68264-7d4d-42a9-88d4-5616c77c6baa', ), 'destination' => array ( 'href' => 'https://api-sandbox.dwolla.com/funding-sources/3152c22b-3d72-442d-a83b-e575df3a043e', ), ), 'amount' => array ( 'currency' => 'USD', 'value' => '225.00', ) ); $transferApi = new DwollaSwagger\TransfersApi($apiClient); $transfer = $transferApi->create($transfer_request); print($transfer); # => https://api-sandbox.dwolla.com/transfers/d76265cd-0951-e511-80da-0aa34a9b2388 ?> ``` ```ruby create_transfer.rb theme={"dark"} transfer_request = { :_links => { :source => { :href => "https://api-sandbox.dwolla.com/funding-sources/b5e68264-7d4d-42a9-88d4-5616c77c6baa" }, :destination => { :href => "https://api-sandbox.dwolla.com/funding-sources/3152c22b-3d72-442d-a83b-e575df3a043e" } }, :amount => { :currency => "USD", :value => "225.00" } } # Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby (Recommended) transfer = app_token.post "transfers", transfer_request transfer.response_headers[:location] # => "https://api-sandbox.dwolla.com/transfers/d76265cd-0951-e511-80da-0aa34a9b2388" ``` ```python create_transfer.py theme={"dark"} transfer_request = { '_links': { 'source': { 'href': 'https://api-sandbox.dwolla.com/funding-sources/b5e68264-7d4d-42a9-88d4-5616c77c6baa' }, 'destination': { 'href': 'https://api-sandbox.dwolla.com/funding-sources/3152c22b-3d72-442d-a83b-e575df3a043e' } }, 'amount': { 'currency': 'USD', 'value': '225.00' } } # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python (Recommended) transfer = app_token.post('transfers', transfer_request) transfer.headers['location'] # => 'https://api-sandbox.dwolla.com/transfers/d76265cd-0951-e511-80da-0aa34a9b2388' ``` ```javascript create_transfer.js theme={"dark"} var transferRequest = { _links: { source: { href: "https://api-sandbox.dwolla.com/funding-sources/b5e68264-7d4d-42a9-88d4-5616c77c6baa", }, destination: { href: "https://api-sandbox.dwolla.com/funding-sources/3152c22b-3d72-442d-a83b-e575df3a043e", }, }, amount: { currency: "USD", value: "225.00", }, }; dwolla.post("transfers", transferRequest).then(function (res) { res.headers.get("location"); // => 'https://api-sandbox.dwolla.com/transfers/d76265cd-0951-e511-80da-0aa34a9b2388' }); ``` When the transfer is created, you will receive a `201` HTTP response with an empty response body. You can refer to the Location header to retrieve a link to the created Transfer resource. All bank transactions that are sourced from a bank or that are going to a bank will have an initial status of `pending`. We recommend storing the full Transfer URL for future use, as it will be needed for correlating transfer update webhooks that are triggered for the user in the Dwolla system. ### Handle Webhooks A single API call to create a payment transfer can trigger several transfer-related webhook events. The number of webhooks and type of webhook events can vary depending on the Customer type(s) involved in the transfer, as well as the source and destination for the funds transfer. For more information on which webhooks will be fired for a given section of a transfer, refer to our [Developer Resource Article](/docs/webhook-events). ### Simulate Payment Processing To simulate payment ACH processing in the Dwolla Sandbox environment, navigate to the Sandbox Dashboard. From here, you will want to click the "Process Bank Transfers" button on the top of the screen. Your Sandbox transfer will be moved out of a `pending` status and moved to a `processed` status. process bank transfers **Production payment processing timing** While ACH funds transfer processing can be simulated at any time in the sandbox, behavior will vary in production depending on what transfer clearing options you specify. Refer to our developer resource article to [learn more on transfer timing](/docs/transfer-processing-times) in production. ### Verify Status of Transfer Since ACH transactions in production can take a few days to complete, webhooks are an efficient way to notify you of when a transfer is completed and `processed` to a destination funding source. However, if you want to verify the status of a transfer at any given point in time, you can make a call to the API to retrieve the transfer by its unique id. ```javascript theme={"dark"} { "_links": { "source": { "href": "https://api-sandbox.dwolla.com/accounts/30a6cb55-1754-4948-b431-ebe48288ef25", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "account" }, "funding-transfer": { "href": "https://api-sandbox.dwolla.com/transfers/6fdd095c-afd7-e811-8111-bec1f96924ed", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "transfer" }, "destination-funding-source": { "href": "https://api-sandbox.dwolla.com/funding-sources/AB443D36-3757-44C1-A1B4-29727FB3111C", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "funding-source" }, "self": { "href": "https://api-sandbox.dwolla.com/transfers/74c9129b-d14a-e511-80da-0aa34a9b2388", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "transfer" }, "source-funding-source": { "href": "https://api-sandbox.dwolla.com/funding-sources/707177c3-bf15-4e7e-b37c-55c3898d9bf4", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "funding-source" }, "destination": { "href": "https://api-sandbox.dwolla.com/customers/4e988dba-0a1e-4591-ad04-eab3613e2f83", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "customer" } }, "id": "74c9129b-d14a-e511-80da-0aa34a9b2388", "status": "processed", "amount": { "value": "10.00", "currency": "USD" } } ``` # Same-Day ACH Source: https://developers.dwolla.com/docs/same-day-ach An overview of Same Day ACH and leveraging next-available processing times for faster transfers. ## Overview Use the Dwolla API for faster payments to enable your application to take advantage of [Same Day ACH](https://www.dwolla.com/features/same-day-ach/) credit or debit transfers on a [per transfer request](/docs/api-reference/transfers/initiate-a-transfer) basis. A `clearing` request parameter is supplied in the request to the Dwolla API which tells Dwolla to expedite clearing for the source or the destination account involved in the transaction. Same Day ACH is a simple and powerful feature for platforms looking to differentiate themselves, streamline cash flows, and improve their end user experiences. ### Understanding Same Day ACH Same Day ACH allows you to move money electronically within the **same business day** (excluding weekends and holidays). This is a significant improvement compared to standard ACH transfers that can take several days. The ACH network typically processes batches of transactions once or twice a day, resulting in clearing times of several business days. Same Day ACH builds upon this existing infrastructure by introducing additional processing windows throughout the day. This allows for funds to be received by the destination bank on the same business day that the transfer is initiated, provided it meets the specific deadline set by the originating financial institution. #### Types of Same Day ACH transfers * Same Day Debit: Move funds from a user's bank account into the Dwolla network. * Same Day Credit: Move funds out of the Dwolla network to a user's bank account. #### Benefits of Same Day ACH * Faster payments: Funds are available in the recipient's account by the end of the **same business day** if initiated before the deadline. * Improved user experience: Offer a faster and more modern payment option for your users. * Stand out from the competition: Differentiate your platform by providing faster transactions. #### Important Considerations * Transaction limit: There's a [limit of \$1 million per transfer](https://www.dwolla.com/updates/same-day-ach-transaction-limits/), enforced by Nacha. * Cost: Same Day ACH transfers are generally more expensive than standard ACH transfers. * Risk and compliance: Using higher transaction limits and payment speeds might require additional review from Dwolla. #### Deadlines and Availability The table below shows the cut-off times (Central Time) for initiating Same Day ACH transfers and the corresponding estimated time when the funds will be available in the destination account. For example, your application can initiate a debit or credit transfer with Same Day clearing prior to 3 PM Central Time and funds will be available in the destination account by the end of the same business day. For more information on the timing of transactions, reference our resource article on [transaction timelines](https://www.dwolla.com/resources/understanding-payment-transfer-timelines/). ##### Deadlines and Availability | Cut-off Time (Central Time) | Estimated Availability | | :-------------------------: | :--------------------: | | 9:00 AM | 11:30 AM | | 1:00 PM | 5:00 PM | | 3:00 PM | 5:00 PM | ### Creating a Customer and Attaching a Bank Account Before you can initiate a debit or credit transfer using same-day clearing, you must first have a [Customer created](/docs/api-reference/customers/create-a-customer) and a [funding source](/docs/api-reference/funding-sources/create-customer-funding-source) attached for the user. If it’s a debit transfer, then the funding source must be `verified` before you can pull funds. For a credit transfer, the funding source can be `unverified` or `verified`. Once your customer has connected a bank account and/or verified it, you'll then want to store the funding source id (e.g. `https://api-sandbox.dwolla.com/funding-sources/ecf993e2-fa22-4cea-8022-c7861200288f`) which will be used when specifying the bank account as the either the source or destination href in the request to the [Transfers API](/docs/api-reference/transfers/initiate-a-transfer). ### Initiating a Same Day ACH Transfer In order to initiate a transfer with Same Day ACH processing, an optional `clearing` JSON object must be included in the transfer request. The clearing object contains `source` and `destination` keys with respective values of `standard` or `next-available` and `next-available`. Specifying the destination clearing as `next-available` will allow requests to default to the earliest available processing window based on the time submitted. In addition, transfers greater than `$1 million` will default to a processing window permitting larger amounts. #### Initiating a Same Day Debit Transfer The following example assumes the sending party has a verified funding source. We're receiving a pay-in from a Customer’s funding source to our Dwolla account balance, which represents the Dwolla Network. ```bash HTTP theme={"dark"} POST https://api-sandbox.dwolla.com/transfers Accept: application/vnd.dwolla.v1.hal+json Content-Type: application/vnd.dwolla.v1.hal+json Authorization: Bearer pBA9fVDBEyYZCEsLf/wKehyh1RTpzjUj5KzIRfDi0wKTii7DqY Idempotency-Key: 19051a62-3403-11e6-ac61-9e71128cae77 { "_links": { "source": { "href": "https://api-sandbox.dwolla.com/funding-sources/b268f6b9-db3b-4ecc-83a2-8823a53ec8b7" }, "destination": { "href": "https://api-sandbox.dwolla.com/funding-sources/ecf993e2-fa22-4cea-8022-c7861200288f" } }, "amount": { "currency": "USD", "value": "10000.00" }, "clearing": { "source": "next-available" } } ... HTTP/1.1 201 Created Location: https://api-sandbox.dwolla.com/transfers/636de847-7d02-e711-80ee-0aa34a9b2388 ``` ```ruby initiate_transfer.rb theme={"dark"} request_body = { :_links => { :source => { :href => "https://api-sandbox.dwolla.com/funding-sources/b268f6b9-db3b-4ecc-83a2-8823a53ec8b7" }, :destination => { :href => "https://api-sandbox.dwolla.com/funding-sources/ecf993e2-fa22-4cea-8022-c7861200288f" } }, :amount => { :currency => "USD", :value => "10000.00" }, :clearing => { :source => "next-available" } } # Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby (Recommended) transfer = app_token.post "transfers", request_body transfer.response_headers[:location] # => "https://api-sandbox.dwolla.com/transfers/636de847-7d02-e711-80ee-0aa34a9b2388" ``` ```php initiate_transfer.php theme={"dark"} create([ '_links' => [ 'source' => [ 'href' => 'https://api-sandbox.dwolla.com/funding-sources/b268f6b9-db3b-4ecc-83a2-8823a53ec8b7', ], 'destination' => [ 'href' => 'https://api-sandbox.dwolla.com/funding-sources/ecf993e2-fa22-4cea-8022-c7861200288f' ] ], 'amount' => [ 'currency' => 'USD', 'value' => '10000.00' ], 'clearing' => [ 'source' => 'next-available' ] ]); $transfer; # => "https://api-sandbox.dwolla.com/transfers/636de847-7d02-e711-80ee-0aa34a9b2388" ?> ``` ```python initiate_transfer.py theme={"dark"} request_body = { '_links': { 'source': { 'href': 'https://api-sandbox.dwolla.com/funding-sources/b268f6b9-db3b-4ecc-83a2-8823a53ec8b7' }, 'destination': { 'href': 'https://api-sandbox.dwolla.com/funding-sources/ecf993e2-fa22-4cea-8022-c7861200288f' } }, 'amount': { 'currency': 'USD', 'value': '10000.00' }, 'clearing': { 'source': 'next-available' } } # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python (Recommended) transfer = app_token.post('transfers', request_body) transfer.headers['location'] # => 'https://api-sandbox.dwolla.com/transfers/636de847-7d02-e711-80ee-0aa34a9b2388' ``` ```javascript initiateTransfer.js theme={"dark"} var requestBody = { _links: { source: { href: "https://api-sandbox.dwolla.com/funding-sources/b268f6b9-db3b-4ecc-83a2-8823a53ec8b7", }, destination: { href: "https://api-sandbox.dwolla.com/funding-sources/ecf993e2-fa22-4cea-8022-c7861200288f", }, }, amount: { currency: "USD", value: "10000.00", }, clearing: { source: "next-available", }, }; dwolla .post("transfers", requestBody) .then((res) => res.headers.get("location")); // => 'https://api.dwolla.com/transfers/636de847-7d02-e711-80ee-0aa34a9b2388' ``` #### Initiating a Same Day Credit Transfer The following example assumes the sending party has a verified account and a verified funding source. We're sending a payout from our Dwolla account balance to our Customer’s funding source which represents the receiving bank account. ```bash HTTP theme={"dark"} POST https://api-sandbox.dwolla.com/transfers Accept: application/vnd.dwolla.v1.hal+json Content-Type: application/vnd.dwolla.v1.hal+json Authorization: Bearer pBA9fVDBEyYZCEsLf/wKehyh1RTpzjUj5KzIRfDi0wKTii7DqY Idempotency-Key: 19051a62-3403-11e6-ac61-9e71128cae77 { "_links": { "source": { "href": "https://api-sandbox.dwolla.com/funding-sources/b268f6b9-db3b-4ecc-83a2-8823a53ec8b7" }, "destination": { "href": "https://api-sandbox.dwolla.com/funding-sources/ecf993e2-fa22-4cea-8022-c7861200288f" } }, "amount": { "currency": "USD", "value": "10000.00" }, "clearing": { "destination": "next-available" } } ... HTTP/1.1 201 Created Location: https://api-sandbox.dwolla.com/transfers/636de847-7d02-e711-80ee-0aa34a9b2388 ``` ```ruby initiate_transfer.rb theme={"dark"} request_body = { :_links => { :source => { :href => "https://api-sandbox.dwolla.com/funding-sources/b268f6b9-db3b-4ecc-83a2-8823a53ec8b7" }, :destination => { :href => "https://api-sandbox.dwolla.com/funding-sources/ecf993e2-fa22-4cea-8022-c7861200288f" } }, :amount => { :currency => "USD", :value => "10000.00" }, :metadata => { :paymentId => "12345678", :note => "payment for completed work Dec. 1" }, :clearing => { :destination => "next-available" } } # Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby (Recommended) transfer = app_token.post "transfers", request_body transfer.response_headers[:location] # => "https://api-sandbox.dwolla.com/transfers/636de847-7d02-e711-80ee-0aa34a9b2388" ``` ```php initiate_transfer.php theme={"dark"} create([ '_links' => [ 'source' => [ 'href' => 'https://api-sandbox.dwolla.com/funding-sources/b268f6b9-db3b-4ecc-83a2-8823a53ec8b7', ], 'destination' => [ 'href' => 'https://api-sandbox.dwolla.com/funding-sources/ecf993e2-fa22-4cea-8022-c7861200288f' ] ], 'amount' => [ 'currency' => 'USD', 'value' => '10000.00' ], 'clearing' => [ 'destination' => 'next-available' ] ]); $transfer; # => "https://api-sandbox.dwolla.com/transfers/636de847-7d02-e711-80ee-0aa34a9b2388" ?> ``` ```python initiate_transfer.py theme={"dark"} request_body = { '_links': { 'source': { 'href': 'https://api-sandbox.dwolla.com/funding-sources/b268f6b9-db3b-4ecc-83a2-8823a53ec8b7' }, 'destination': { 'href': 'https://api-sandbox.dwolla.com/funding-sources/ecf993e2-fa22-4cea-8022-c7861200288f' } }, 'amount': { 'currency': 'USD', 'value': '10000.00' }, 'clearing': { 'destination': 'next-available' } } # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python (Recommended) transfer = app_token.post('transfers', request_body) transfer.headers['location'] # => 'https://api-sandbox.dwolla.com/transfers/636de847-7d02-e711-80ee-0aa34a9b2388' ``` ```javascript theme={"dark"} var requestBody = { _links: { source: { href: "https://api-sandbox.dwolla.com/funding-sources/b268f6b9-db3b-4ecc-83a2-8823a53ec8b7", }, destination: { href: "https://api-sandbox.dwolla.com/funding-sources/ecf993e2-fa22-4cea-8022-c7861200288f", }, }, amount: { currency: "USD", value: "10000.00", }, clearing: { destination: "next-available", }, }; dwolla .post("transfers", requestBody) .then((res) => res.headers.get("location")); // => 'https://api.dwolla.com/transfers/636de847-7d02-e711-80ee-0aa34a9b2388' ``` ### Retrieving a Transfer With Same Day Clearing When retrieving the [transfer from the API](/docs/api-reference/transfers/retrieve-a-transfer), the response should contain the clearing object with a `source` or `destination` key and a value of either `same-day` or `next-day` depending on if the transfer was initiated prior to the last same day processing window and the transfer amount is less than `$1 million` (as mentioned above). #### Retrieving a Same Day Debit Transfer ```bash HTTP theme={"dark"} GET https://api-sandbox.dwolla.com/transfers/636de847-7d02-e711-80ee-0aa34a9b2388 Accept: application/vnd.dwolla.v1.hal+json Authorization: Bearer pBA9fVDBEyYZCEsLf/wKehyh1RTpzjUj5KzIRfDi0wKTii7DqY ... { "_links": { "source": { "href": "https://api-sandbox.dwolla.com/accounts/ad5f2162-404a-4c4c-994e-6ab6c3a13254", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "account" }, "destination-funding-source": { "href": "https://api-sandbox.dwolla.com/funding-sources/ecf993e2-fa22-4cea-8022-c7861200288f", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "funding-source" }, "self": { "href": "https://api-sandbox.dwolla.com/transfers/636de847-7d02-e711-80ee-0aa34a9b2388", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "transfer" }, "funded-transfer": { "href": "https://api-sandbox.dwolla.com/transfers/646de847-7d02-e711-80ee-0aa34a9b2388", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "transfer" }, "source-funding-source": { "href": "https://api-sandbox.dwolla.com/funding-sources/b268f6b9-db3b-4ecc-83a2-8823a53ec8b7", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "funding-source" }, "destination": { "href": "https://api-sandbox.dwolla.com/customers/99dd22de-6ec6-4ba1-a0d1-09eb169a4bb1", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "customer" } }, "id": "636de847-7d02-e711-80ee-0aa34a9b2388", "status": "processed", "amount": { "value": "10000.00", "currency": "usd" }, "created": "2017-03-06T14:57:56.803Z", "clearing": { "source": "same-day" } } ``` ```ruby retrieve_transfer.rb theme={"dark"} transfer_url = 'https://api-sandbox.dwolla.com/transfers/636de847-7d02-e711-80ee-0aa34a9b2388' # Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby (Recommended) transfer = app_token.get transfer_url transfer.status # => "processed" ``` ```php retrieve_transfer.php theme={"dark"} byId($transferUrl); $transfer->status; # => "processed" ?> ``` ```python retrieve_transfer.py theme={"dark"} transfer_url = 'https://api-sandbox.dwolla.com/transfers/636de847-7d02-e711-80ee-0aa34a9b2388' # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python (Recommended) transfer = app_token.get(transfer_url) transfer.body['status'] # => 'processed' ``` ```javascript retrieveTransfer.js theme={"dark"} var transferUrl = "https://api-sandbox.dwolla.com/transfers/636de847-7d02-e711-80ee-0aa34a9b2388"; dwolla.get(transferUrl).then((res) => res.body.status); // => 'processed' ``` #### Retrieving a Same Day Credit Transfer ```bash HTTP theme={"dark"} GET https://api-sandbox.dwolla.com/transfers/636de847-7d02-e711-80ee-0aa34a9b2388 Accept: application/vnd.dwolla.v1.hal+json Authorization: Bearer pBA9fVDBEyYZCEsLf/wKehyh1RTpzjUj5KzIRfDi0wKTii7DqY ... { "_links": { "source": { "href": "https://api-sandbox.dwolla.com/accounts/ad5f2162-404a-4c4c-994e-6ab6c3a13254", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "account" }, "destination-funding-source": { "href": "https://api-sandbox.dwolla.com/funding-sources/ecf993e2-fa22-4cea-8022-c7861200288f", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "funding-source" }, "self": { "href": "https://api-sandbox.dwolla.com/transfers/636de847-7d02-e711-80ee-0aa34a9b2388", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "transfer" }, "funded-transfer": { "href": "https://api-sandbox.dwolla.com/transfers/646de847-7d02-e711-80ee-0aa34a9b2388", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "transfer" }, "source-funding-source": { "href": "https://api-sandbox.dwolla.com/funding-sources/b268f6b9-db3b-4ecc-83a2-8823a53ec8b7", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "funding-source" }, "destination": { "href": "https://api-sandbox.dwolla.com/customers/99dd22de-6ec6-4ba1-a0d1-09eb169a4bb1", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "customer" } }, "id": "636de847-7d02-e711-80ee-0aa34a9b2388", "status": "processed", "amount": { "value": "10000.00", "currency": "usd" }, "created": "2017-03-06T14:57:56.803Z", "clearing": { "destination": "same-day" } } ``` ```ruby retrieve_transfer.rb theme={"dark"} transfer_url = 'https://api-sandbox.dwolla.com/transfers/636de847-7d02-e711-80ee-0aa34a9b2388' # Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby (Recommended) transfer = app_token.get transfer_url transfer.status # => "processed" ``` ```php retrieve_transfer.php theme={"dark"} byId($transferUrl); $transfer->status; # => "processed" ?> ``` ```python retrieve_transfer.py theme={"dark"} transfer_url = 'https://api-sandbox.dwolla.com/transfers/636de847-7d02-e711-80ee-0aa34a9b2388' # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python (Recommended) transfer = app_token.get(transfer_url) transfer.body['status'] # => 'processed' ``` ```javascript retrieveTransfer.js theme={"dark"} var transferUrl = "https://api-sandbox.dwolla.com/transfers/636de847-7d02-e711-80ee-0aa34a9b2388"; dwolla.get(transferUrl).then((res) => res.body.status); // => 'processed' ``` # SDKS & Tools Source: https://developers.dwolla.com/docs/sdks-tools Choose from a collection of client libraries and tools to get up and running quickly in a variety of languages. Run: ```bash bash theme={"dark"} npm install dwolla-v2 ``` or: ```bash bash theme={"dark"} yarn add dwolla-v2 ``` Check out the [API docs](/docs/sdks-tools/node), or see the source on [GitHub](https://github.com/Dwolla/dwolla-v2-node). Add the following to your `Gemfile`: ```ruby ruby theme={"dark"} gem "dwolla_v2", "~> 3.0" ``` Then run: ```bash bash theme={"dark"} bundle ``` Or run: ```bash bash theme={"dark"} gem install dwolla_v2 ``` Check out the [API docs](/docs/sdks-tools/ruby), or see the source on [GitHub](https://github.com/Dwolla/dwolla-v2-ruby). Add the following to your `requirements.txt`: ```plaintext theme={"dark"} dwollav2>=2.0.0 ``` Or run: ```bash bash theme={"dark"} pip install dwollav2 ``` Check out the [API docs](/docs/sdks-tools/python), or see the source on [GitHub](https://github.com/Dwolla/dwolla-v2-python). Run: ```bash bash theme={"dark"} composer require "dwolla/dwolla-php" ``` Check out the [API docs](/docs/sdks-tools/php-new), or see the source on [GitHub](https://github.com/Dwolla/dwolla-php). > Looking for the legacy SDK? See [dwolla-swagger-php](/docs/sdks-tools/php). ```bash bash theme={"dark"} Install-Package Dwolla.Client ``` Check out the [API docs](/docs/sdks-tools/c-sharp), or see the source on [GitHub](https://github.com/Dwolla/dwolla-v2-csharp). `Maven` - Add the following to your project’s POM: ```xml xml theme={"dark"} jitpack.io https://jitpack.io com.github.Dwolla dwolla-v2-kotlin 0.1 ``` `Gradle` - Add the following to your project’s build file: ```kotlin kotlin theme={"dark"} repositories { // ... maven(url = "https://jitpack.io") { name = "jitpack" } } dependencies { implementation("com.github.Dwolla:dwolla-v2-kotlin:0.1.0") } ``` Check out the [API docs](/docs/sdks-tools/kotlin), or see the source on [GitHub](https://github.com/Dwolla/dwolla-v2-kotlin). Run: ```bash bash theme={"dark"} npm install dwolla ``` or: ```bash bash theme={"dark"} yarn add dwolla ``` or: ```bash bash theme={"dark"} pnpm add dwolla ``` or: ```bash bash theme={"dark"} bun add dwolla ``` Check out the [API docs](/docs/sdks-tools/typescript), or see the source on [GitHub](https://github.com/Dwolla/dwolla-typescript). ## Tools All Dwolla API requests grouped into Postman collections for testing.
[Dwolla API Sandbox Collection](https://www.postman.com/dwolladev/dwolla/collection/s6jh7uv/dwolla-balance-api-sandbox)
OpenAPI specification document for the Dwolla API. Reference the OpenAPI specs for catalogs of tooling that implements the specification.
[Dwolla Balance Spec](https://github.com/Dwolla/dwolla-openapi)
Model Context Protocol server for AI assistants to interact with the Dwolla API. Enables natural language interactions with payments infrastructure.
[Dwolla MCP Server](https://github.com/Dwolla/dwolla-mcp)
# C# Source: https://developers.dwolla.com/docs/sdks-tools/c-sharp Use Dwolla's SDK for C# to build applications that interact with the Dwolla API to perform account-to-account payment functions. ## Getting Started ### Installation To begin using this SDK, you will first need to download it to your machine. We use [NuGet](https://www.nuget.org/packages/Dwolla.Client) to distribute this package. Check out the [Microsoft](https://docs.microsoft.com/en-us/nuget/consume-packages/install-use-packages-visual-studio) documentation for more information on how to install and manage packages from Nuget using Visual Studio. Here's an example using the [Package Manager Console](https://docs.microsoft.com/en-us/nuget/consume-packages/install-use-packages-powershell?view=vsmac-2022) ```shell Shell theme={"dark"} $ Install-Package Dwolla.Client -Version 5.2.2 ``` ### Initialization Before any API requests can be made, you must first determine which environment you will be using, as well as fetch the application key and secret. To fetch your application key and secret, please visit one of the following links: Finally, you can create an instance of `DwollaClient` by specifying which environment you will be using—Production or Sandbox—via the `isSandbox` boolean flag. ```csharp csharp theme={"dark"} var client = DwollaClient.Create(isSandbox: true); ``` #### Tokens Application access tokens are used to authenticate against the API on behalf of an application. Application tokens can be used to access resources in the API that either belong to the application itself (`webhooks`, `events`, `webhook-subscriptions`) or the Dwolla Account that owns the application (`accounts`, `customers`, `funding-sources`, etc.). Application tokens are obtained by using the [`client_credentials`](https://tools.ietf.org/html/rfc6749#section-4.4) OAuth grant type: ```csharp csharp theme={"dark"} var tokenRes = await client.PostAuthAsync( new Uri($"{client.AuthBaseAddress}/token"), new AppTokenRequest {Key = "...", Secret = "..."}); ``` *Application access tokens are short-lived: 1 hour. They do not include a `refresh_token`. When it expires, generate a new one using `AppTokenRequest`.* ## Making Requests Once you've created a `DwollaClient`, currently, you can make low-level HTTP requests. ### Low-Level Requests To make low-level HTTP requests, you can use the `GetAsync()`, `PostAsync()`, `UploadAsync()` and `DeleteAsync()` methods with the available [request models](https://github.com/Dwolla/dwolla-v2-csharp/blob/main/Dwolla.Client/Models/Requests). These methods will return responses that can be mapped to one of the available [response models](https://github.com/Dwolla/dwolla-v2-csharp/blob/main/Dwolla.Client/Models/Responses). #### Setting Headers To specify headers for a request (e.g., `Authorization`), you can pass a `Headers` object as the last argument. ```csharp csharp theme={"dark"} var headers = new Headers {{"Authorization", $"Bearer {tokenRes.Content.Token}"}}; client.GetAsync(url, headers); ``` #### `GET` ```csharp csharp theme={"dark"} // GET api.dwolla.com/customers var url = new Uri("https://api.dwolla.com/customers"); client.GetAsync(url); ``` #### `POST` ```csharp csharp theme={"dark"} // POST api.dwolla.com/customers var url = new Uri("https://api.dwolla.com/customers/"); var request = new CreateCustomerRequest { FirstName = "Jane", LastName = "Doe", Email = "jane.doe@email.com" }; var res = await PostAsync(url, request, headers); //res.Response.Headers.Location => "https://api-sandbox.dwolla.com/customers/fc451a7a-ae30-4404-aB95-e3553fcd733f // POST api.dwolla.com/customers/{id}/documents multipart/form-data foo=... var url = new Uri("https://api-sandbox.dwolla.com/customers/{id}/documents"); var request = new UploadDocumentRequest { DocumentType = "idCard", Document = new File { ContentType = "image/png", Filename = "filename.jpg", Stream = fileStream } }; client.UploadAsync(url, request, headers); ``` #### `DELETE` ```csharp csharp theme={"dark"} // DELETE api.dwolla.com/resource var url = "https://api.dwolla.com/labels/{id}" client.DeleteAsync(url, null); ``` ### Example App Take a look at the [Example Application](https://github.com/Dwolla/dwolla-v2-csharp/tree/main/ExampleApp) for examples on how to use the available C# models to call the Dwolla API. Before you can begin using the app, however, you will need to specify a `DWOLLA_APP_KEY` and `DWOLLA_APP_SECRET` environment variable. #### Docker If you prefer to use Docker to run ExampleApp locally, a Dockerfile file is included in the root directory. You can either build the Docker image with your API key and secret (by passing the values via CLI), or you can specify the values for the `app_key` and `app_secret` build arguments in Dockerfile. Finally, you will need to build and run the Docker image. More information on this topic can be found on [Docker's website](https://docs.docker.com/build/hellobuild/), or you can find some example commands below. ##### Building Docker Container ```shell Shell theme={"dark"} # Building container by specifying build arguments. # In this configuration, you will not need to modify Dockerfile. All of the # necessary arguments are passed via Docker's \`--build-arg\` option. $ docker build \ --build-arg app_key=YOUR_API_KEY \ --build-arg app_secret=YOUR_APP_SECRET \ -t dwolla/csharp-example-app:latest . # Building container without specifying build arguments. # In this configuration, you will need to specify your account API key and # secret (retrieved from Dwolla) in the Dockerfile file. $ docker build -t dwolla/csharp-example-app:latest . ``` ##### Running Container Instance ```shell Shell theme={"dark"} # Running Docker container in interactive shell $ docker run --init -it dwolla/csharp-example-app:latest ``` ## Community * If you have any feedback, please reach out to us on [our forums](https://discuss.dwolla.com/) or by [creating a GitHub issue](https://github.com/Dwolla/dwolla-v2-csharp/issues/new). * If you would like to contribute to this library, [bug reports](https://github.com/Dwolla/dwolla-v2-csharp/issues) and [pull requests](https://github.com/Dwolla/dwolla-v2-csharp/pulls) are always appreciated! # Kotlin Source: https://developers.dwolla.com/docs/sdks-tools/kotlin Use Dwolla’s SDK for Kotlin to build applications that interact with the Dwolla API to perform account-to-account payment functions. `dwolla-v2-kotlin` is an actively maintained client library for Java/Kotlin applications and is used to facilitate interactions with the Dwolla API. The [source code](https://github.com/Dwolla/dwolla-v2-kotlin) is available on our GitHub page. ## Getting Started ### Installation To begin using this SDK, you will first need to download it to your machine. You can use Maven or Gradle to do so, depending on which build tool your project is using. #### Maven Add this to your project's POM: ```xml theme={"dark"} jitpack.io https://jitpack.io ``` ```xml theme={"dark"} com.github.Dwolla dwolla-v2-kotlin 0.6.1 ``` #### Gradle Add this to your project's build file: ```groovy theme={"dark"} repositories { // ... maven(url = "https://jitpack.io") { name = "jitpack" } } ``` ```groovy theme={"dark"} dependencies { implementation("com.github.Dwolla:dwolla-v2-kotlin:0.6.1") } ``` ### Initialization Before any API requests can be made, you must first determine which environment you will be using, as well as fetch the application key and secret. To fetch your application key and secret, please visit one of the following links: * Production: [https://dashboard.dwolla.com/applications](https://dashboard.dwolla.com/applications) * Sandbox: [https://dashboard-sandbox.dwolla.com/applications](https://dashboard-sandbox.dwolla.com/applications) Finally, you can create an instance of `Dwolla` with `key` and `secret` replaced with the application key and secret that you fetched from one of the aforementioned links, respectively. #### Kotlin ```kotlin theme={"dark"} import com.dwolla.Dwolla import com.dwolla.DwollaEnvironment val dwolla = Dwolla( key = "YOUR_APP_KEY", secret = "YOUR_APP_SECRET", environment = DwollaEnvironment.SANDBOX // defaults to PRODUCTION ) ``` #### Java ```java theme={"dark"} import com.dwolla.Dwolla; import com.dwolla.DwollaEnvironment; Dwolla dwolla = new Dwolla( "YOUR_APP_KEY", "YOUR_APP_SECRET", DwollaEnvironment.SANDBOX // defaults to PRODUCTION ); ``` ## Making Requests The Dwolla client provides high-level and low-level methods for interacting with the Dwolla API. ### High-Level Requests > The best SDKs are not just simple; they’re intuitive. Developers would rather stay in the flow of their code than > troubleshoot back-and-forth trying to figure out someone else’s code. Luckily, statically typed languages let us > include information typically found in docs within type signatures. > > — [Taking Our SDKs Higher](https://www.dwolla.com/updates/improving-sdks/) While the low-level methods are all you need, high-level methods exist to make things easier. They embed information you would typically refer to the docs for in the SDK itself such as endpoints, request parameters, and response parameters. As of now, a subset of the Dwolla API has high-level methods available: * [x] [`dwolla.accounts.*`](https://github.com/Dwolla/dwolla-v2-kotlin/blob/main/docs/snippets/high_level_requests/accounts.md) * [x] [`dwolla.beneficialOwners.*`](https://github.com/Dwolla/dwolla-v2-kotlin/blob/main/docs/snippets/high_level_requests/beneficial-owners.md) * [x] [`dwolla.businessClassifications.*`](https://github.com/Dwolla/dwolla-v2-kotlin/blob/main/docs/snippets/high_level_requests/business-classifications.md) * [x] [`dwolla.customers.*`](https://github.com/Dwolla/dwolla-v2-kotlin/blob/main/docs/snippets/high_level_requests/customers.md) * [x] [`dwolla.documents.*`](https://github.com/Dwolla/dwolla-v2-kotlin/blob/main/docs/snippets/high_level_requests/documents.md) * [x] [`dwolla.fundingSources.*`](https://github.com/Dwolla/dwolla-v2-kotlin/blob/main/docs/snippets/high_level_requests/funding-sources.md) * [x] [`dwolla.fundingSourcesTokens.*`](https://github.com/Dwolla/dwolla-v2-kotlin/blob/main/docs/snippets/high_level_requests/funding-sources-tokens.md) * [x] [`dwolla.root.*`](https://github.com/Dwolla/dwolla-v2-kotlin/blob/main/docs/snippets/high_level_requests/root.md) * [ ] `dwolla.events.*` * [ ] `dwolla.labels.*` * [ ] `dwolla.massPayments.*` * [ ] `dwolla.transfers.*` * [ ] `dwolla.webhooks.*` * [ ] `dwolla.webhookSubscriptions.*` ### Low-Level Requests To make low-level HTTP requests, you can use the `get()`, `post()`, and `delete()` methods. * `dwolla.get` * `dwolla.post` * `dwolla.delete` Examples: * [Kotlin](https://github.com/Dwolla/dwolla-v2-kotlin/blob/main/docs/snippets/low_level_requests/low_level_examples_kotlin.md) * [Java](https://github.com/Dwolla/dwolla-v2-kotlin/blob/main/docs/snippets/low_level_requests/low_level_examples_java.md) ## Handling errors Dwolla V2 Kotlin has 3 types of exceptions: ``` DwollaException ├── DwollaApiException └── DwollaAuthException ``` * `DwollaApiException`: Thrown when the Dwolla API returns an error response. This could occur for a variety of reasons such as invalid request parameters. * `DwollaAuthException`: Thrown when an error occurs obtaining authenticating with the API. You should not encounter this exception unless your `Dwolla` key/secret are incorrect. * `DwollaException`: The base class other exceptions inherit from. ##### Kotlin ```kotlin theme={"dark"} try { dwolla.customers.list() } catch (e: DwollaApiException) { e.message // String e.statusCode // Int e.headers // Headers e.error // DwollaError } catch (e: DwollaAuthException) { e.message // String e.statusCode // Int e.headers // Headers e.error // OAuthError } catch (e: DwollaException) { e.message // String e.cause // Throwable? } ``` ##### Java ```java theme={"dark"} try { dwolla.customers.list(); } catch (DwollaApiException e) { String message = e.message; Integer statusCode = e.statusCode; Headers headers = e.headers; DwollaError error = e.error; } catch (DwollaAuthException e) { String message = e.message; Integer statusCode = e.statusCode; Headers headers = e.headers; OAuthError error = e.error; } catch (DwollaAuthException e) { String message = e.message; Throwable cause = e.cause; } ``` ## Community * If you have any feedback, please reach out to us on [our forums](https://discuss.dwolla.com/) or by [creating a GitHub issue](https://github.com/Dwolla/dwolla-v2-kotlin/issues/new). * If you would like to contribute to this library, [bug reports](https://github.com/Dwolla/dwolla-v2-kotlin/issues) and [pull requests](https://github.com/Dwolla/dwolla-v2-kotlin/pulls) are always appreciated! ## Docker If you prefer to use Docker to run dwolla-v2-kotlin locally, a Dockerfile file is included in the root directory. You can either build the Docker image with your API key and secret (by passing the values via CLI), or you can specify the values for the `app_key` and `app_secret` build arguments in Dockerfile. Finally, you will need to build and run the Docker image. More information on this topic can be found on [Docker's website](https://docs.docker.com/build/hellobuild/), or you can find some example commands below. ##### Building Docker Container ```shell theme={"dark"} # Building container by specifying build arguments. # In this configuration, you will not need to modify Dockerfile. All of the # necessary arguments are passed via Docker's `--build-arg` option. $ docker build \ --build-arg app_key=YOUR_API_KEY \ --build-arg app_secret=YOUR_APP_SECRET \ -t dwolla/kotlin:latest . # Building container without specifying build arguments. # In this configuration, you will need to specify your account API key and # secret (retrieved from Dwolla) in the Dockerfile file. $ docker build -t dwolla/kotlin:latest . ``` # Node Source: https://developers.dwolla.com/docs/sdks-tools/node Use Dwolla’s SDK for Node to build applications that interact with the Dwolla API to perform account-to-account payment functions. `dwolla-v2` is available on [NPM](https://www.npmjs.com/package/dwolla-v2) with [source code](https://github.com/Dwolla/dwolla-v2-node) available on our GitHub page. ## Getting Started ### Installation To begin using this SDK, you will first need to download and install it on your machine. We use [npm](https://www.npmjs.com/package/dwolla-v2) to distribute this package. ```shell theme={"dark"} # npm $ npm install --save dwolla-v2 # yarn $ yarn add dwolla-v2 # pnpm $ pnpm add dwolla-v2 ``` ### Initialization Before any API requests can be made, you must first determine which environment you will be using, as well as fetch the application key and secret. To fetch your application key and secret, please visit one of the following links: * Production: [https://dashboard.dwolla.com/applications](https://dashboard.dwolla.com/applications) * Sandbox: [https://dashboard-sandbox.dwolla.com/applications](https://dashboard-sandbox.dwolla.com/applications) Finally, you can create an instance of `Client` with `key` and `secret` replaced with the application key and secret that you fetched from one of the aforementioned links, respectively. ```javascript theme={"dark"} const Client = require("dwolla-v2").Client; const dwolla = new Client({ environment: "sandbox", // Defaults to "production" key: process.env.DWOLLA_APP_KEY, secret: process.env.DWOLLA_APP_SECRET, }); ``` ## Making Requests Once you've created a `Client`, currently, you can make low-level HTTP requests. High-level abstraction is planned for this SDK; however, at the time of writing, it has not yet been fully implemented. ### Low-Level Requests To make low-level HTTP requests, you can use the [`get()`](#get), [`post()`](#post), and [`delete()`](#delete) methods. These methods will return a [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) containing the response object. The following snippet defines Dwolla's response object, both with a successful and errored response. Although the snippet uses [`try`/`catch`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch), you can also use [`.then()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then)/[`.catch()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch) if you prefer. An errored response is returned when Dwolla's servers respond with a status code that is greater than or equal to 400, whereas a successful response is when Dwolla's servers respond with a 200-level status code. ```javascript theme={"dark"} try { const response = await dwolla.get("customers"); // response.body => Object or String depending on response type // response.headers => Headers { ... } // response.status => 200 } catch (error) { // error.body => Object or String depending on response type // error.headers => Headers { ... } // error.status => 400 } ``` #### `GET` ```javascript theme={"dark"} // GET https://api.dwolla.com/customers?offset=20&limit=10 const response = await dwolla.get("customers", { offset: 20, limit: 10, }); console.log("Response Total: ", response.body.total); ``` #### `POST` ```javascript theme={"dark"} // POST https://api.dwolla.com/customers body={ ... } // This request is not idempotent since `Idempotecy-Key` is not passed as a header const response = await dwolla.post("customers", { firstName: "Jane", lastName: "Doe", email: "jane.doe@example.com", }); console.log("Created Resource: ", response.headers.get("Location")); // POST https://api.dwolla.com/customers/{id}/documents multipart/form-data ... // Note: Requires form-data peer dependency to be downloaded and installed const formData = new FormData(); formData.append("documentType", "license"); formData.append( "file", ffs.createReadStream("mclovin.jpg", { contentType: "image/jpeg", filename: "mclovin.jpg", knownLength: fs.statSync("mclovin.jpg").size, }) ); const response = await dwolla.post(`${customerUrl}/documents`, formData); console.log("Created Resource: ", response.headers.get("Location")); ``` #### `DELETE` ```javascript theme={"dark"} // DELETE https://api.dwolla.com/[resource] await dwolla.delete("resource"); ``` #### Setting Headers When a request is sent to Dwolla, a few headers are automatically sent (e.g., `Accept`, `Content-Type`, `User-Agent`); however, if you would like to send additional headers, such as `Idempotency-Key`, this can be done by passing in a third (3rd) argument for `POST` requests. To learn more about how to make your requests idempotent, check out our [developer documentation](https://developers.dwolla.com/api-reference#idempotency-key) on this topic! ```javascript theme={"dark"} // POST https://api.dwolla.com/customers body={ ... } headers={ ..., Idempotency-Key=... } // This request is idempotent since `Idempotency-Key` is passed as a header const response = await dwolla.post( "customers", { firstName: "Jane", lastName: "Doe", email: "jane.doe@example.com", }, { "Idempotency-Key": "[RANDOMLY_GENERATED_KEY_HERE]", } ); ``` ## Community * If you have any feedback, please reach out to us on [our forums](https://discuss.dwolla.com/) or by [creating a GitHub issue](https://github.com/Dwolla/dwolla-v2-node/issues/new). * If you would like to contribute to this library, [bug reports](https://github.com/Dwolla/dwolla-v2-node/issues) and [pull requests](https://github.com/Dwolla/dwolla-v2-node/pulls) are always appreciated! ## Docker If you prefer to use Docker to run dwolla-v2-node locally, a Dockerfile is included at the root directory. Follow these instructions from [Docker's website](https://docs.docker.com/build/hellobuild/) to create a Docker image from the Dockerfile, and run it. # PHP Source: https://developers.dwolla.com/docs/sdks-tools/php Use Dwolla's SDK for PHP to build applications that interact with the Dwolla API to perform account-to-account payment functions. `dwolla-php` is available on [Packagist](https://packagist.org/packages/dwolla/dwolla-php) with [source code](https://github.com/Dwolla/dwolla-php) available on our GitHub page. **Beta Release** – This SDK is currently in beta. All API operations are fully supported, and we're gathering feedback from early adopters before making this generally available. Breaking changes may occur as we continue refining the SDK. Please use caution when integrating into production environments. We welcome beta users to integrate, report issues, and help us identify any edge cases. ## Getting Started ### Installation The SDK relies on [Composer](https://getcomposer.org/) to manage its dependencies. To install the SDK and add it as a dependency to an existing `composer.json` file: ```shell theme={"dark"} composer require "dwolla/dwolla-php" ``` ### Initialization Before any API requests can be made, you must first determine which environment you will be using, as well as fetch the application key and secret. To fetch your application key and secret, please visit one of the following links: * Production: [https://dashboard.dwolla.com/applications](https://dashboard.dwolla.com/applications) * Sandbox: [https://dashboard-sandbox.dwolla.com/applications](https://dashboard-sandbox.dwolla.com/applications) Finally, you can create an instance of `Dwolla` with your application credentials: ```php theme={"dark"} declare(strict_types=1); require 'vendor/autoload.php'; use Dwolla; use Dwolla\Models\Components; $sdk = Dwolla\Dwolla::builder() ->setSecurity( new Components\Security( clientID: 'YOUR_CLIENT_ID', clientSecret: 'YOUR_CLIENT_SECRET', ) ) ->build(); ``` ## Making Requests Once you've created a `Dwolla` client, you can make requests using the SDK methods. ### High-Level SDK Methods The PHP SDK provides strongly-typed methods for all Dwolla API operations: ```php theme={"dark"} // Get root API information $response = $sdk->root->get(); // List customers $response = $sdk->customers->list(); // Get customer details $response = $sdk->customers->get(id: 'customer-id-here'); ``` ### Authentication The SDK supports multiple authentication schemes: #### OAuth2 Client Credentials (Recommended) ```php theme={"dark"} use Dwolla; use Dwolla\Models\Components; $sdk = Dwolla\Dwolla::builder() ->setSecurity( new Components\Security( clientID: 'YOUR_CLIENT_ID', clientSecret: 'YOUR_CLIENT_SECRET', ) ) ->build(); ``` #### Application Access Token Creation When creating application access tokens, you'll need to provide Basic Authentication at the request level: ```php theme={"dark"} use Dwolla; use Dwolla\Models\Operations; $sdk = Dwolla\Dwolla::builder()->build(); $request = new Operations\CreateApplicationAccessTokenRequest( grantType: Operations\GrantType::ClientCredentials, ); $requestSecurity = new Operations\CreateApplicationAccessTokenSecurity( basicAuth: 'YOUR_BASIC_AUTH', ); $response = $sdk->tokens->create( request: $request, security: $requestSecurity ); if ($response->object !== null) { // handle response } ``` ### Working with Transfers ```php theme={"dark"} // Initiate a transfer $response = $sdk->transfers->create(/* transfer parameters */); // Get transfer details $response = $sdk->transfers->get(id: 'transfer-id-here'); // Cancel a transfer (if eligible) $response = $sdk->transfers->cancel(id: 'transfer-id-here'); ``` ### Working with Funding Sources ```php theme={"dark"} // List customer funding sources $response = $sdk->customers->fundingSources->list(id: 'customer-id-here'); // Create a funding source $customerUrl = "https://api-sandbox.dwolla.com/customers/customer-id-here"; $response = $sdk->customers->fundingSources->create( id: 'customer-id-here', requestBody: [ "routingNumber" => "222222226", "accountNumber" => "123456789", "bankAccountType" => "checking", "name" => "My Checking Account" ] ); // Get funding source balance $response = $sdk->fundingSources->balance->get(id: 'funding-source-id-here'); ``` ### Error Handling The SDK provides comprehensive error handling with typed exception classes: ```php theme={"dark"} use Dwolla; use Dwolla\Models\Errors; use Dwolla\Models\Operations; $sdk = Dwolla\Dwolla::builder()->build(); try { $request = new Operations\CreateApplicationAccessTokenRequest( grantType: Operations\GrantType::ClientCredentials, ); $requestSecurity = new Operations\CreateApplicationAccessTokenSecurity( basicAuth: 'YOUR_BASIC_AUTH', ); $response = $sdk->tokens->create( request: $request, security: $requestSecurity ); if ($response->object !== null) { // handle response } } catch (Errors\UnauthorizedExceptionThrowable $e) { // handle unauthorized error throw $e; } catch (Errors\APIException $e) { // handle default exception throw $e; } ``` By default, an API error will raise an `Errors\APIException` exception, which has the following properties: | Property | Type | Description | | -------------- | -------------------------------------- | --------------------- | | `$message` | `string` | The error message | | `$statusCode` | `int` | The HTTP status code | | `$rawResponse` | `?\Psr\Http\Message\ResponseInterface` | The raw HTTP response | | `$body` | `string` | The response content | ### Server Selection You can specify which Dwolla environment to use: ```php theme={"dark"} use Dwolla; // Use sandbox environment $sdk = Dwolla\Dwolla::builder() ->setServer('sandbox') ->build(); // Use production environment $sdk = Dwolla\Dwolla::builder() ->setServer('prod') ->build(); // Use custom server URL $sdk = Dwolla\Dwolla::builder() ->setServerURL('https://api-sandbox.dwolla.com') ->build(); ``` | Name | Server | Description | | --------- | -------------------------------- | ----------------- | | `prod` | `https://api.dwolla.com` | Production server | | `sandbox` | `https://api-sandbox.dwolla.com` | Sandbox server | ## Available Resources and Operations The SDK provides access to the following resources: * **Root** - API entry point * **Tokens** - Application access token management * **Accounts** - Account details, funding sources, transfers, mass payments, and exchanges * **Customers** - Customer management, beneficial owners, documents, funding sources, transfers, labels, KBA, and exchanges * **Beneficial Owners** - Beneficial owner management and documents * **Business Classifications** - Business classification lookup * **Documents** - Document retrieval * **Events** - Event listing and retrieval * **Exchange Partners** - Exchange partner management * **Exchanges** - Exchange resource management and sessions * **Exchange Sessions** - Exchange session management * **Funding Sources** - Funding source management, balances, micro-deposits, and VAN routing * **KBA** - Knowledge-based authentication * **Labels** - Label management, ledger entries, and reallocations * **Mass Payments** - Mass payment initiation and management * **Transfers** - Transfer initiation, retrieval, cancellation, and fee management * **Webhooks** - Webhook retrieval and retry management * **Webhook Subscriptions** - Webhook subscription management * **Sandbox Simulations** - Bank transfer processing simulation (Sandbox only) For detailed information on each resource and its operations, see the [SDK documentation on GitHub](https://github.com/Dwolla/dwolla-php/tree/main/docs/sdks). ## Community * If you have any feedback, please reach out to us on [our forums](https://discuss.dwolla.com/) or by [creating a GitHub issue](https://github.com/Dwolla/dwolla-php/issues/new). * While we value open-source contributions to this SDK, this library is generated programmatically. Any manual changes added to internal files will be overwritten on the next generation. We look forward to hearing your feedback. Feel free to open a PR or an issue with a proof of concept and we'll do our best to include it in a future release. # PHP (Legacy) Source: https://developers.dwolla.com/docs/sdks-tools/php-legacy Use Dwolla's legacy SDK for PHP to build applications that interact with the Dwolla API to perform account-to-account payment functions. **A newer PHP SDK is available!** We recommend using the new [`dwolla-php`](/docs/sdks-tools/php-new) SDK for new projects. The new SDK offers improved type safety, better error handling, and a more intuitive API. This legacy SDK (`dwolla-swagger-php`) will continue to be available, but new features and improvements will be focused on the new SDK. `dwolla-swagger-php` is available on [Packagist](https://packagist.org/packages/dwolla/dwollaswagger) with [source code](https://github.com/Dwolla/dwolla-swagger-php) available on our GitHub page. ## Getting Started ### Installation To begin using this SDK, you will first need to download it to your machine. We use [Packagist](https://packagist.org/packages/dwolla/dwollaswagger) to distribute this package, which allows it to be downloaded via [Composer](https://getcomposer.org/). ```shell theme={"dark"} $ composer require dwolla/dwollaswagger $ composer install ``` To use, just `require` your Composer `autoload.php` file. ```php theme={"dark"} require("../path/to/vendor/autoload.php"); ``` ### Initialization Before any API requests can be made, you must first determine which environment you will be using, as well as fetch the application key and secret. To fetch your application key and secret, please visit one of the following links: * Production: [https://dashboard.dwolla.com/applications](https://dashboard.dwolla.com/applications) * Sandbox: [https://dashboard-sandbox.dwolla.com/applications](https://dashboard-sandbox.dwolla.com/applications) Finally, you can create an instance of `ApiClient` after configuring the `username` and `password` values as the application key and secret that you fetched from one of the aforementioned links, respectively. ```php theme={"dark"} DwollaSwagger\Configuration::$username = "API_KEY"; DwollaSwagger\Configuration::$password = "API_SECRET"; # For Sandbox $apiClient = new DwollaSwagger\ApiClient("https://api-sandbox.dwolla.com"); # For Production $apiClient = new DwollaSwagger\ApiClient("https://api.dwolla.com"); ``` #### Tokens Application access tokens are used to authenticate against the API on behalf of an application. Application tokens can be used to access resources in the API that either belong to the application itself (`webhooks`, `events`, `webhook-subscriptions`) or the Dwolla Account that owns the application (`accounts`, `customers`, `funding-sources`, etc.). Application tokens are obtained by using the [`client_credentials`](https://tools.ietf.org/html/rfc6749#section-4.4) OAuth grant type: ```php theme={"dark"} $tokensApi = new DwollaSwagger\TokensApi($apiClient); $appToken = $tokensApi->token(); ``` *Application access tokens are short-lived: 1 hour. They do not include a `refresh_token`. When it expires, generate a new one using `$tokensApi->token()`.* ## Making Requests The Dwolla client provides high-level methods for interacting with the Dwolla API. ### High-Level Requests High-level methods make development easier by embedding information you would typically refer to [Dwolla's API reference](https://developers.dwolla.com/api-reference) for in the SDK itself, such as endpoints, request arguments, and response deserialization. `DwollaSwagger` contains the `API` module, which allows the user to make requests, as well as `models`, which are [data access objects](https://en.wikipedia.org/wiki/Data_access_object) that the library uses to deserialize responses. Each model represents the different kinds of requests and responses that can be made with the Dwolla API. View the full list in the [`models` directory](https://github.com/Dwolla/dwolla-swagger-php/tree/master/lib/models). The following API modules are available: * [Accounts](https://github.com/Dwolla/dwolla-swagger-php/blob/main/docs/snippets/Accounts.md) * [Beneficial Owners](https://github.com/Dwolla/dwolla-swagger-php/blob/main/docs/snippets/BeneficialOwnersApi.md) * [Business Classifications](https://github.com/Dwolla/dwolla-swagger-php/blob/main/docs/snippets/BusinessClassifications.md) * [Customers](https://github.com/Dwolla/dwolla-swagger-php/blob/main/docs/snippets/Customers.md) * [Documents](https://github.com/Dwolla/dwolla-swagger-php/blob/main/docs/snippets/Documents.md) * [Events](https://github.com/Dwolla/dwolla-swagger-php/blob/main/docs/snippets/Events.md) * [Funding Sources](https://github.com/Dwolla/dwolla-swagger-php/blob/main/docs/snippets/FundingSources.md) * [Knowledge-Based Authentication (KBA)](https://github.com/Dwolla/dwolla-swagger-php/blob/main/docs/snippets/KBAs.md) * [Labels](https://github.com/Dwolla/dwolla-swagger-php/blob/main/docs/snippets/Labels.md) * [Label Reallocations](https://github.com/Dwolla/dwolla-swagger-php/blob/main/docs/snippets/LabelReallocations.md) * [Ledger Entries](https://github.com/Dwolla/dwolla-swagger-php/blob/main/docs/snippets/LedgerEntries.md) * [Mass Payment Items](https://github.com/Dwolla/dwolla-swagger-php/blob/main/docs/snippets/MassPaymentItems.md) * [On-Demand Authorizations](https://github.com/Dwolla/dwolla-swagger-php/blob/main/docs/snippets/OnDemandAuthorizations.md) * [Root](https://github.com/Dwolla/dwolla-swagger-php/blob/main/docs/snippets/Root.md) * [Sandbox](https://github.com/Dwolla/dwolla-swagger-php/blob/main/docs/snippets/Sandbox.md) * [Tokens](https://github.com/Dwolla/dwolla-swagger-php/blob/main/docs/snippets/Tokens.md) * [Transfers](https://github.com/Dwolla/dwolla-swagger-php/blob/main/docs/snippets/Transfers.md) * [Webhooks](https://github.com/Dwolla/dwolla-swagger-php/blob/main/docs/snippets/Webhooks.md) * [Webhook Subscriptions](https://github.com/Dwolla/dwolla-swagger-php/blob/main/docs/snippets/WebhookSubscriptions.md) #### Setting Headers You can pass custom headers in your requests as per the schema of the API models. Here is an example of creating a Customer with an [Idempotency-Key](https://developers.dwolla.com/api-reference#idempotency-key) header. ```php theme={"dark"} $customersApi = new DwollaSwagger\CustomersApi($apiClient); $customer = $customersApi->create([ "firstName" => "Jane", "lastName" => "Merchant", "email" => "jmerchant@nomail.net", "type" => "receive-only", "businessName" => "Jane Corp llc", "ipAddress" => "99.99.99.99" ], [ "Idempotency-Key" => "51a62-3403-11e6-ac61-9e71128cae77" ]); $customer; # => "https://api-sandbox.dwolla.com/customers/fc451a7a-ae30-4404-aB95-e3553fcd733f" ``` ### Responses #### Success ```php theme={"dark"} # Retrieve an Account by ID $accountsApi = new DwollaSwagger\AccountsApi($apiClient); $account = $accountsApi->id("8a2cdc8d-629d-4a24-98ac-40b735229fe2"); # Retrieve a Customer by ID $customerUrl = 'https://api-sandbox.dwolla.com/customers/07d59716-ef22-4fe6-98e8-f3190233dfb8'; $customersApi = new DwollaSwagger\CustomersApi($apiClient); $customer = $customersApi->getCustomer($customerUrl); # Create a customer funding source $customerUrl = "https://api-sandbox.dwolla.com/customers/AB443D36-3757-44C1-A1B4-29727FB3111C"; $fsApi = new DwollaSwagger\FundingsourcesApi($apiClient); $fundingSource = $fsApi->createCustomerFundingSource([ "routingNumber" => "222222226", "accountNumber" => "123456789", "bankAccountType" => "checking", "name" => "Jane Doe’s Checking" ], $customerUrl); $fundingSource; # => "https://api-sandbox.dwolla.com/funding-sources/375c6781-2a17-476c-84f7-db7d2f6ffb31" ``` #### Errors You can wrap your requests in a try/catch block to handle errors. ```php theme={"dark"} try{ $new_customer = $customersApi->create([ //request_body ]); } catch (Exception $e) { echo 'Caught exception: ', $e->getResponseBody(), "\n"; } ``` ## Community * If you have any feedback, please reach out to us on [our forums](https://discuss.dwolla.com/) or by [creating a GitHub issue](https://github.com/Dwolla/dwolla-swagger-php/issues/new). * If you would like to contribute to this library, [bug reports](https://github.com/Dwolla/dwolla-swagger-php/issues) and [pull requests](https://github.com/Dwolla/dwolla-swagger-php/pulls) are always appreciated! # Python Source: https://developers.dwolla.com/docs/sdks-tools/python Use Dwolla’s SDK for Python to build applications that interact with the Dwolla API to perform account-to-account payment functions. `dwollav2` is available on [PyPi](https://pypi.python.org/pypi/dwollav2) with [source code](https://github.com/Dwolla/dwolla-v2-python) available on our GitHub page. ## Getting Started ### Installation To begin using this SDK, you will first need to download it to your machine. We use [PyPi](https://pypi.python.org/pypi/dwollav2) to distribute this package from where you can automagically download it via [pip](https://pip.pypa.io/en/stable/installing/). ```shell theme={"dark"} $ pip install dwollav2 ``` ### Initialization Before any API requests can be made, you must first determine which environment you will be using, as well as fetch the application key and secret. To fetch your application key and secret, please visit one of the following links: * Production: [https://dashboard.dwolla.com/applications](https://dashboard.dwolla.com/applications) * Sandbox: [https://dashboard-sandbox.dwolla.com/applications](https://dashboard-sandbox.dwolla.com/applications) Finally, you can create an instance of `Client` with `key` and `secret` replaced with the application key and secret that you fetched from one of the aforementioned links, respectively. ```python theme={"dark"} client = dwollav2.Client( key = os.environ['DWOLLA_APP_KEY'], secret = os.environ['DWOLLA_APP_SECRET'], environment = 'sandbox', # defaults to 'production' requests = {'timeout': 0.001} ) ``` ##### Configure an `on_grant` callback (optional) An `on_grant` callback is useful for storing new tokens when they are granted. The `on_grant` callback is called with the `Token` that was just granted by the server. ```python theme={"dark"} client = dwollav2.Client( key = os.environ['DWOLLA_APP_KEY'], secret = os.environ['DWOLLA_APP_SECRET'], on_grant = lambda t: save(t) ) ``` It is highly recommended that you encrypt any token data you store. #### Tokens ##### Generating New Access Tokens Application access tokens are used to authenticate against the API on behalf of an application. Application tokens can be used to access resources in the API that either belong to the application itself (`webhooks`, `events`, `webhook-subscriptions`) or the Dwolla Account that owns the application (`accounts`, `customers`, `funding-sources`, etc.). Application tokens are obtained by using the [`client_credentials`](https://tools.ietf.org/html/rfc6749#section-4.4) OAuth grant type: ```python theme={"dark"} application_token = client.Auth.client() ``` *Application access tokens are short-lived: 1 hour. They do not include a `refresh_token`. When it expires, generate a new one using `client.Auth.client()`.* ##### Initializing Pre-Existing Tokens: The [Dwolla Sandbox Dashboard](https://dashboard-sandbox.dwolla.com/applications-legacy) allows you to generate tokens for your application. A `Token` can be initialized with the following attributes: ```python theme={"dark"} client.Token(access_token = '...', expires_in = 123) ``` ## Making Requests Once you've created a `Token`, currently, you can make low-level HTTP requests. ### Low-level Requests To make low-level HTTP requests, you can use the `get()`, `post()`, and `delete()` methods. These methods will return a `Response` object. #### `GET` ```python theme={"dark"} # GET api.dwolla.com/resource?foo=bar token.get('resource', foo = 'bar') # GET requests can also use objects as parameters # GET api.dwolla.com/resource?foo=bar token.get('resource', {'foo' = 'bar', 'baz' = 'foo'}) ``` #### `POST` ```python theme={"dark"} # POST api.dwolla.com/resource {"foo":"bar"} token.post('resource', foo = 'bar') # POST api.dwolla.com/resource multipart/form-data foo=... token.post('resource', foo = ('mclovin.jpg', open('mclovin.jpg', 'rb'), 'image/jpeg')) ``` #### `DELETE` ```python theme={"dark"} # DELETE api.dwolla.com/resource token.delete('resource') ``` #### Setting headers To set additional headers on a request you can pass a `dict` of headers as the 3rd argument. For example: ```python theme={"dark"} token.post('customers', { 'firstName': 'John', 'lastName': 'Doe', 'email': 'jd@doe.com' }, { 'Idempotency-Key': 'a52fcf63-0730-41c3-96e8-7147b5d1fb01' }) ``` ### Responses The following snippets demonstrate successful and errored responses from the Dwolla API. An errored response is returned when Dwolla's servers respond with a status code that is greater than or equal to 400, whereas a successful response is when Dwolla's servers respond with a 200-level status code. #### Success ```python theme={"dark"} res = token.get('/') res.status # => 200 res.headers # => {'server'=>'cloudflare-nginx', 'date'=>'Mon, 28 Mar 2016 15:30:23 GMT', 'content-type'=>'application/vnd.dwolla.v1.hal+json; charset=UTF-8', 'content-length'=>'150', 'connection'=>'close', 'set-cookie'=>'__cfduid=d9dcd0f586c166d36cbd45b992bdaa11b1459179023; expires=Tue, 28-Mar-17 15:30:23 GMT; path=/; domain=.dwolla.com; HttpOnly', 'x-request-id'=>'69a4e612-5dae-4c52-a6a0-2f921e34a88a', 'cf-ray'=>'28ac1f81875941e3-MSP'} res.body['_links']['events']['href'] # => 'https://api-sandbox.dwolla.com/events' ``` #### Error If the server returns an error, a `dwollav2.Error` (or one of its subclasses) will be raised. `dwollav2.Error`s are similar to `Response`s. ```python theme={"dark"} try: token.get('/not-found') except dwollav2.NotFoundError as e: e.status # => 404 e.headers # => {"server"=>"cloudflare-nginx", "date"=>"Mon, 28 Mar 2016 15:35:32 GMT", "content-type"=>"application/vnd.dwolla.v1.hal+json; profile=\"http://nocarrier.co.uk/profiles/vnd.error/\"; charset=UTF-8", "content-length"=>"69", "connection"=>"close", "set-cookie"=>"__cfduid=da1478bfdf3e56275cd8a6a741866ccce1459179332; expires=Tue, 28-Mar-17 15:35:32 GMT; path=/; domain=.dwolla.com; HttpOnly", "access-control-allow-origin"=>"*", "x-request-id"=>"667fca74-b53d-43db-bddd-50426a011881", "cf-ray"=>"28ac270abca64207-MSP"} e.body.code # => "NotFound" except dwollav2.Error: # ... ``` ##### `dwollav2.Error` subclasses: *See [https://developers.dwolla.com/api-reference#errors](https://developers.dwolla.com/api-reference#errors) for more info.* * `dwollav2.AccessDeniedError` * `dwollav2.InvalidCredentialsError` * `dwollav2.NotFoundError` * `dwollav2.BadRequestError` * `dwollav2.InvalidGrantError` * `dwollav2.RequestTimeoutError` * `dwollav2.ExpiredAccessTokenError` * `dwollav2.InvalidRequestError` * `dwollav2.ServerError` * `dwollav2.ForbiddenError` * `dwollav2.InvalidResourceStateError` * `dwollav2.TemporarilyUnavailableError` * `dwollav2.InvalidAccessTokenError` * `dwollav2.InvalidScopeError` * `dwollav2.UnauthorizedClientError` * `dwollav2.InvalidAccountStatusError` * `dwollav2.InvalidScopesError` * `dwollav2.UnsupportedGrantTypeError` * `dwollav2.InvalidApplicationStatusError` * `dwollav2.InvalidVersionError` * `dwollav2.UnsupportedResponseTypeError` * `dwollav2.InvalidClientError` * `dwollav2.MethodNotAllowedError` * `dwollav2.ValidationError` * `dwollav2.TooManyRequestsError` * `dwollav2.ConflictError` ### Example App Take a look at the [Sample Application](https://github.com/Dwolla/dwolla-v2-python/tree/main/sample_app) for examples on how to use this SDK to call the Dwolla API. Before you can begin using the app, however, you will need to specify a `DWOLLA_APP_KEY` and `DWOLLA_APP_SECRET` environment variable. ## Community * If you have any feedback, please reach out to us on [our forums](https://discuss.dwolla.com/) or by [creating a GitHub issue](https://github.com/Dwolla/dwolla-v2-python/issues/new). * If you would like to contribute to this library, [bug reports](https://github.com/Dwolla/dwolla-v2-python/issues) and [pull requests](https://github.com/Dwolla/dwolla-v2-python/pulls) are always appreciated! * After checking out the repo, run `pip install -r requirements.txt` to install dependencies. Then, run `python setup.py` test to run the tests. * To install this gem onto your local machine, `run pip install -e .`. ## Docker If you prefer to use Docker to run dwolla-v2-python locally, a Dockerfile is included at the root directory. Follow these instructions from [Docker's website](https://docs.docker.com/build/hellobuild/) to create a Docker image from the Dockerfile, and run it. # Ruby Source: https://developers.dwolla.com/docs/sdks-tools/ruby Use Dwolla’s SDK for Ruby to build applications that interact with the Dwolla API to perform account-to-account payment functions. `dwolla_v2` is available on [RubyGems](https://rubygems.org/gems/dwolla_v2) with [source code](https://github.com/Dwolla/dwolla-v2-ruby) available on our GitHub page. ## Getting Started ### Installation To begin using this SDK, you will first need to download it to your machine. We use [RubyGems](https://rubygems.org/gems/dwolla_v2) to distribute this package. Add this line to your application's Gemfile: ```ruby theme={"dark"} gem 'dwolla_v2', '~> 3.1' ``` And then execute: \$ bundle Or install it yourself as: \$ gem install dwolla\_v2 ### Initialization Before any API requests can be made, you must first determine which environment you will be using, as well as fetch the application key and secret. To fetch your application key and secret, please visit one of the following links: * Production: [https://dashboard.dwolla.com/applications](https://dashboard.dwolla.com/applications) * Sandbox: [https://dashboard-sandbox.dwolla.com/applications](https://dashboard-sandbox.dwolla.com/applications) Finally, you can create an instance of `Client` with `key` and `secret` replaced with the application key and secret that you fetched from one of the aforementioned links, respectively. ```ruby theme={"dark"} # config/initializers/dwolla.rb $dwolla = DwollaV2::Client.new( key: ENV["DWOLLA_APP_KEY"], secret: ENV["DWOLLA_APP_SECRET"], environment: :sandbox # defaults to :production ) ``` #### Configure Faraday (Optional) Dwolla for Ruby uses [Faraday][faraday] to make HTTP requests. You can configure your own [Faraday middleware][faraday-middleware] and adapter when configuring your client. Remember to always include an adapter last, even if you want to use the default adapter. [faraday]: https://github.com/lostisland/faraday [faraday-middleware]: https://github.com/lostisland/faraday_middleware ```ruby theme={"dark"} # config/initializers/dwolla.rb $dwolla = DwollaV2::Client.new( key: ENV["DWOLLA_APP_KEY"], secret: ENV["DWOLLA_APP_SECRET"] ) do |config| config.faraday do |faraday| faraday.response :logger faraday.adapter Faraday.default_adapter end end ``` ## Making Requests Once you've created a `Client`, currently, you can make low-level HTTP requests. ### Low-level Requests To make low-level HTTP requests, you can use the [`get()`](#get), [`post()`](#post), and [`delete()`](#delete) methods. #### `GET` ```ruby theme={"dark"} # GET api.dwolla.com/resource?foo=bar $dwolla.get "resource", foo: "bar" ``` #### `POST` ```ruby theme={"dark"} # POST api.dwolla.com/resource {"foo":"bar"} $dwolla.post "resource", foo: "bar" # POST api.dwolla.com/resource multipart/form-data foo=... $dwolla.post "resource", foo: Faraday::UploadIO.new("/path/to/bar.png", "image/png") ``` #### `DELETE` ```ruby theme={"dark"} # DELETE api.dwolla.com/resource $dwolla.delete "resource" ``` ##### Setting Headers To set additional headers on a request, you can pass a `Hash` of headers as the 3rd argument. For example: ```ruby theme={"dark"} $dwolla.post "customers", { firstName: "John", lastName: "Doe", email: "jd@doe.com" }, { 'Idempotency-Key': 'a52fcf63-0730-41c3-96e8-7147b5d1fb01' } ``` ### Responses The following snippets demonstrate successful and errored responses from the Dwolla API. An errored response is returned when Dwolla's servers respond with a status code that is greater than or equal to 400, whereas a successful response is when Dwolla's servers respond with a 200-level status code. #### Success Successful requests return a `DwollaV2::Response`. ```ruby theme={"dark"} res = $dwolla.get "/" # => #"cloudflare-nginx", "date"=>"Mon, 28 Mar 2016 15:30:23 GMT", "content-type"=>"application/vnd.dwolla.v1.hal+json; charset=UTF-8", "content-length"=>"150", "connection"=>"close", "set-cookie"=>"__cfduid=d9dcd0f586c166d36cbd45b992bdaa11b1459179023; expires=Tue, 28-Mar-17 15:30:23 GMT; path=/; domain=.dwolla.com; HttpOnly", "x-request-id"=>"69a4e612-5dae-4c52-a6a0-2f921e34a88a", "cf-ray"=>"28ac1f81875941e3-MSP"} {"_links"=>{"events"=>{"href"=>"https://api-sandbox.dwolla.com/events"}, "webhook-subscriptions"=>{"href"=>"https://api-sandbox.dwolla.com/webhook-subscriptions"}}}> res.response_status # => 200 res.response_headers # => {"server"=>"cloudflare-nginx", "date"=>"Mon, 28 Mar 2016 15:30:23 GMT", "content-type"=>"application/vnd.dwolla.v1.hal+json; charset=UTF-8", "content-length"=>"150", "connection"=>"close", "set-cookie"=>"__cfduid=d9dcd0f586c166d36cbd45b992bdaa11b1459179023; expires=Tue, 28-Mar-17 15:30:23 GMT; path=/; domain=.dwolla.com; HttpOnly", "x-request-id"=>"69a4e612-5dae-4c52-a6a0-2f921e34a88a", "cf-ray"=>"28ac1f81875941e3-MSP"} res._links.events.href # => "https://api-sandbox.dwolla.com/events" ``` #### Error If the server returns an error, a `DwollaV2::Error` (or one of its subclasses) will be raised. `DwollaV2::Error`s are similar to `DwollaV2::Response`s. ```ruby theme={"dark"} begin $dwolla.get "/not-found" rescue DwollaV2::NotFoundError => e e # => #"cloudflare-nginx", "date"=>"Mon, 28 Mar 2016 15:35:32 GMT", "content-type"=>"application/vnd.dwolla.v1.hal+json; profile=\"http://nocarrier.co.uk/profiles/vnd.error/\"; charset=UTF-8", "content-length"=>"69", "connection"=>"close", "set-cookie"=>"__cfduid=da1478bfdf3e56275cd8a6a741866ccce1459179332; expires=Tue, 28-Mar-17 15:35:32 GMT; path=/; domain=.dwolla.com; HttpOnly", "access-control-allow-origin"=>"*", "x-request-id"=>"667fca74-b53d-43db-bddd-50426a011881", "cf-ray"=>"28ac270abca64207-MSP"} {"code"=>"NotFound", "message"=>"The requested resource was not found."}> e.response_status # => 404 e.response_headers # => {"server"=>"cloudflare-nginx", "date"=>"Mon, 28 Mar 2016 15:35:32 GMT", "content-type"=>"application/vnd.dwolla.v1.hal+json; profile=\"http://nocarrier.co.uk/profiles/vnd.error/\"; charset=UTF-8", "content-length"=>"69", "connection"=>"close", "set-cookie"=>"__cfduid=da1478bfdf3e56275cd8a6a741866ccce1459179332; expires=Tue, 28-Mar-17 15:35:32 GMT; path=/; domain=.dwolla.com; HttpOnly", "access-control-allow-origin"=>"*", "x-request-id"=>"667fca74-b53d-43db-bddd-50426a011881", "cf-ray"=>"28ac270abca64207-MSP"} e.code # => "NotFound" rescue DwollaV2::Error => e # ... end ``` ##### `DwollaV2::Error` subclasses: *See [https://developers.dwolla.com/api-reference#errors](https://developers.dwolla.com/api-reference#errors) for more info.* * `DwollaV2::AccessDeniedError` * `DwollaV2::InvalidCredentialsError` * `DwollaV2::NotFoundError` * `DwollaV2::BadRequestError` * `DwollaV2::InvalidGrantError` * `DwollaV2::RequestTimeoutError` * `DwollaV2::ExpiredAccessTokenError` * `DwollaV2::InvalidRequestError` * `DwollaV2::ServerError` * `DwollaV2::ForbiddenError` * `DwollaV2::InvalidResourceStateError` * `DwollaV2::TemporarilyUnavailableError` * `DwollaV2::InvalidAccessTokenError` * `DwollaV2::InvalidScopeError` * `DwollaV2::UnauthorizedClientError` * `DwollaV2::InvalidAccountStatusError` * `DwollaV2::InvalidScopesError` * `DwollaV2::UnsupportedGrantTypeError` * `DwollaV2::InvalidApplicationStatusError` * `DwollaV2::InvalidVersionError` * `DwollaV2::UnsupportedResponseTypeError` * `DwollaV2::InvalidClientError` * `DwollaV2::MethodNotAllowedError` * `DwollaV2::ValidationError` * `DwollaV2::TooManyRequestsError` * `DwollaV2::ConflictError` ## Community * If you have any feedback, please reach out to us on [our forums](https://discuss.dwolla.com/) or by [creating a GitHub issue](https://github.com/Dwolla/dwolla-v2-ruby/issues/new). * If you would like to contribute to this library, [bug reports](https://github.com/Dwolla/dwolla-v2-ruby/issues) and [pull requests](https://github.com/Dwolla/dwolla-v2-ruby/pulls) are always appreciated! * After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. * To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). ## Docker If you prefer to use Docker to run dwolla-v2-python locally, a Dockerfile is included at the root directory. Follow these instructions from [Docker's website](https://docs.docker.com/build/hellobuild/) to create a Docker image from the Dockerfile, and run it. # TypeScript Source: https://developers.dwolla.com/docs/sdks-tools/typescript Use Dwolla's SDK for TypeScript to build applications that interact with the Dwolla API to perform account-to-account payment functions. `dwolla` is available on [NPM](https://www.npmjs.com/package/dwolla) with [source code](https://github.com/Dwolla/dwolla-typescript) available on our GitHub page. ## Getting Started ### Installation To begin using this SDK, you will first need to download and install it on your machine. We use [npm](https://www.npmjs.com/package/dwolla) to distribute this package. ```shell theme={"dark"} # npm $ npm install dwolla # yarn $ yarn add dwolla # pnpm $ pnpm add dwolla # bun $ bun add dwolla ``` > \[!NOTE] > This package is published with CommonJS and ES Modules (ESM) support. ### Initialization Before any API requests can be made, you must first determine which environment you will be using, as well as fetch the application key and secret. To fetch your application key and secret, please visit one of the following links: * Production: [https://dashboard.dwolla.com/applications](https://dashboard.dwolla.com/applications) * Sandbox: [https://dashboard-sandbox.dwolla.com/applications](https://dashboard-sandbox.dwolla.com/applications) Finally, you can create an instance of `Dwolla` with your application credentials: ```typescript theme={"dark"} import { Dwolla } from "dwolla"; const dwolla = new Dwolla({ security: { clientID: process.env.DWOLLA_CLIENT_ID ?? "", clientSecret: process.env.DWOLLA_CLIENT_SECRET ?? "", }, server: "sandbox", // Defaults to "prod" for production }); ``` ## Making Requests Once you've created a `Dwolla` client, you can make requests using the high-level SDK methods or low-level HTTP requests. ### High-Level SDK Methods The TypeScript SDK provides strongly-typed methods for all Dwolla API operations: ```typescript theme={"dark"} // Get root API information const rootInfo = await dwolla.root.get(); // List customers const customers = await dwolla.customers.list({ limit: 10, offset: 0, }); // Create a customer const newCustomer = await dwolla.customers.create({ firstName: "Jane", lastName: "Doe", email: "jane.doe@example.com", }); // Get customer details const customer = await dwolla.customers.get({ id: "customer-id-here", }); ``` ### Authentication The SDK supports multiple authentication schemes: #### OAuth2 Client Credentials (Recommended) ```typescript theme={"dark"} import { Dwolla } from "dwolla"; const dwolla = new Dwolla({ security: { clientID: process.env.DWOLLA_CLIENT_ID ?? "", clientSecret: process.env.DWOLLA_CLIENT_SECRET ?? "", }, }); ``` #### Application Access Token Creation When creating application access tokens, you'll need to provide Basic Authentication at the request level: ```typescript theme={"dark"} const result = await dwolla.tokens.create({ basicAuth: process.env.DWOLLA_BASIC_AUTH ?? "", }, { grantType: "client_credentials", }); ``` ### Working with Transfers ```typescript theme={"dark"} // Initiate a transfer const transfer = await dwolla.transfers.create({ _links: { source: { href: "https://api-sandbox.dwolla.com/funding-sources/source-id", }, destination: { href: "https://api-sandbox.dwolla.com/funding-sources/destination-id", }, }, amount: { currency: "USD", value: "10.00", }, }); // Get transfer details const transferDetails = await dwolla.transfers.get({ id: "transfer-id-here", }); // Cancel a transfer (if eligible) await dwolla.transfers.cancel({ id: "transfer-id-here", }); ``` ### Working with Funding Sources ```typescript theme={"dark"} // List customer funding sources const fundingSources = await dwolla.customers.fundingSources.list({ id: "customer-id-here", }); // Create a funding source const newFundingSource = await dwolla.customers.fundingSources.create({ id: "customer-id-here", requestBody: { routingNumber: "222222226", accountNumber: "123456789", bankAccountType: "checking", name: "My Checking Account", }, }); // Get funding source balance const balance = await dwolla.fundingSources.balance.get({ id: "funding-source-id-here", }); ``` ### File Uploads The SDK supports file uploads for document verification: ```typescript theme={"dark"} import { openAsBlob } from "node:fs"; const result = await dwolla.customers.documents.create({ id: "customer-id-here", requestBody: { documentType: "license", file: await openAsBlob("path/to/document.jpg"), }, }); ``` ### Error Handling The SDK provides comprehensive error handling with typed error classes: ```typescript theme={"dark"} import { Dwolla } from "dwolla"; import * as errors from "dwolla/models/errors"; try { const result = await dwolla.customers.get({ id: "invalid-customer-id", }); } catch (error) { if (error instanceof errors.NotFoundError) { console.log("Customer not found:", error.message); console.log("Status code:", error.statusCode); } else if (error instanceof errors.BadRequestError) { console.log("Bad request:", error.message); } else if (error instanceof errors.DwollaError) { console.log("API error:", error.message); } } ``` ### Retries The SDK supports configurable retry strategies: ```typescript theme={"dark"} // Configure retries globally const dwolla = new Dwolla({ retryConfig: { strategy: "backoff", backoff: { initialInterval: 1, maxInterval: 50, exponent: 1.1, maxElapsedTime: 100, }, retryConnectionErrors: false, }, }); // Configure retries per-operation const result = await dwolla.customers.list({}, { retries: { strategy: "backoff", backoff: { initialInterval: 1, maxInterval: 50, exponent: 1.1, maxElapsedTime: 100, }, retryConnectionErrors: false, }, }); ``` ### Server Selection You can specify which Dwolla environment to use: ```typescript theme={"dark"} // Use sandbox environment const dwolla = new Dwolla({ server: "sandbox", }); // Use production environment const dwolla = new Dwolla({ server: "prod", }); // Use custom server URL const dwolla = new Dwolla({ serverURL: "https://api-sandbox.dwolla.com", }); ``` ## Standalone Functions All SDK methods are also available as standalone functions for tree-shaking and smaller bundle sizes. This is particularly useful in serverless environments like AWS Lambda, Google Cloud Functions, or Vercel Edge Functions where minimizing bundle size improves cold start performance: ```typescript theme={"dark"} // AWS Lambda function example import { customersGet, transfersCreate } from "dwolla"; export const handler = async (event) => { // Only import the specific functions you need const customer = await customersGet({ security: { clientID: process.env.DWOLLA_CLIENT_ID ?? "", clientSecret: process.env.DWOLLA_CLIENT_SECRET ?? "", }, }, { id: event.customerId, }); // Create a transfer for this customer const transfer = await transfersCreate({ security: { clientID: process.env.DWOLLA_CLIENT_ID ?? "", clientSecret: process.env.DWOLLA_CLIENT_SECRET ?? "", }, }, { _links: { source: { href: event.sourceUrl }, destination: { href: event.destinationUrl }, }, amount: { currency: "USD", value: event.amount, }, }); return { statusCode: 200, body: JSON.stringify({ transferId: transfer.headers.get("Location") }), }; }; ``` ## Community * If you have any feedback, please reach out to us on [our forums](https://discuss.dwolla.com/) or by [creating a GitHub issue](https://github.com/Dwolla/dwolla-typescript/issues/new). * While we value open-source contributions to this SDK, this library is generated programmatically. Any manual changes added to internal files will be overwritten on the next generation. We look forward to hearing your feedback. Feel free to open a PR or an issue with a proof of concept and we'll do our best to include it in a future release. # Overview Source: https://developers.dwolla.com/docs/secure-exchange The Secure Exchange solution connects clients with integrated ecosystem partners to seamlessly share data and initiate account-to-account payments. # Secure Exchange The Secure Exchange solution connects clients and reseller partners with integrated ecosystem partners to seamlessly share data for the purposes of initiating account-to-account transactions. With Dwolla's Secure Exchange solution, programmatically perform actions for end-users such as connecting bank accounts without needing to store or handle sensitive data like account and routing number. The creation of an exchange establishes a secure channel for data retrieval between Dwolla and a trusted ecosystem partner. The exchange contains a limited set of permissions that grants Dwolla the ability to perform requests to an integrated ecosystem partner based on what is designated by you as the client. The end user can be very intentional about what data they share and who they share it with. The client, or partner, can connect with the Secure Exchange solution to make sure the data that is shared is done so in a safe, seamless way. Quick overview of the benefits of the Secure Exchange solution for your business and end users: * Generate limited permission tokenized access between ecosystem partner and Dwolla. * Create an exchange for an end-user or your Dwolla client account. * Perform action in the Dwolla API using Secure Exchange solution. Check our Exchanges and Exchange Partners API Reference documentation. Flow of exchange between integrated ecosystem partners #### Key Benefits * Dwolla clients and reseller partners don't need to receive and store sensitive data. * Improved developer experience through tokenized integration. * Feature expansion by leveraging trusted ecosystem partners. ## Exchange Partners Dwolla's Secure Exchange solution facilitates account verification connections with leading data aggregation partners by providing a tokenized solution. Supported ecosystem partners can be found by calling the [exchange partners API](/docs/api-reference/exchanges/list-exchange-partners), which contains a catalog of activated partners. Each exchange partner contains a unique ID that can be used to reference when creating and interacting with the exchange. | Partner | Documentation | Additional Resources | | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Mastercard | Contact Mastercard | Dwolla supported [sample application](https://github.com/Dwolla/integration-examples/tree/main/packages/secure-token-exchange/mastercard). | | Flinks |
  • API Reference - [Data Sharing Endpoints](https://docs.flinks.com/reference/data-sharing)
| Dwolla supported [sample application](https://github.com/Dwolla/integration-examples/tree/main/packages/secure-token-exchange/flinks). | | MX |
  • Quickstart Guide - [Processor Token](https://docs.mx.com/products/connectivity/instant-account-verification/processor-token/client-guide)
|
  • MX supported [sample application](https://github.com/mxenabled/processor-tokens-client-quickstart)
  • Dwolla supported [sample application](https://github.com/Dwolla/integration-examples/tree/main/packages/secure-token-exchange/mx)
| | Plaid |
  • API Reference - [Processor Token](https://plaid.com/docs/auth/partnerships/dwolla/)
| Dwolla supported [sample application](https://github.com/Dwolla/integration-examples/tree/main/packages/secure-token-exchange/plaid). | ## Creating an Exchange The creation of an exchange serves as the "hand-shake" between Dwolla and a trusted ecosystem partner. Depending on the integration partner, there may be fine-grained permissions applied to the exchange which limits the scope of data that can be accessed on an end-user's behalf. Refer to the relevant ecosystem partner documentation for more information such as access duration, API call limits and product functionality. Currently, Dwolla only supports connections with certain data aggregators for the purposes of retrieving ACH account details. # Dwolla + Plaid Source: https://developers.dwolla.com/docs/secure-exchange/plaid Learn how to integrate Dwolla and Plaid using Dwolla's Secure Token Exchange solution to securely verify and link your users' bank accounts for ACH payments, without handling sensitive account data directly. ## Overview Dwolla partners with Plaid to provide customers with bank account verification through Secure Exchange solution. Bank accounts are verified and linked with purpose-built exchanges between the Dwolla and Plaid platforms. While **bank account verification is required** before initiating an ACH transaction from a [funding source](/docs/api-reference/funding-sources), Dwolla clients are able to choose between Dwolla's [micro-deposit solution](/docs/api-reference/funding-sources/initiate-or-verify-micro-deposits), [Open Banking solution](/docs/open-banking), other third-party data aggregators via Secure Exchange, such as Mastercard, MX, or, as we will discuss in further detail in this guide, Plaid. ## Prerequisites Let's first go over a few items you need to check off before you begin your integration. * Set up a [Dwolla Production](https://accounts.dwolla.com) account, or a [Dwolla Sandbox](https://accounts-sandbox.dwolla.com) account if you're still developing or testing your application. * Set up a [Plaid Production](https://dashboard.plaid.com) account, or a Plaid Sandbox account if you're still developing or testing your application. * Enable Dwolla's Plaid integration in your Plaid [account settings](https://dashboard.plaid.com/team/integrations). If you need assistance with this step, reach out to your Account Manager with Plaid. * Customize Plaid Link's account selection to only be "[enabled for one account](https://dashboard.plaid.com/link/account-select)." Adding a bank is a one-time process that verifies the account ownership of the underlying bank account being added by an end user. 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 ACH debits and/or credits. ## Integrate with Plaid Once you have your Dwolla and Plaid accounts set up, you can begin the integration process. For the following steps, we will make use of two Plaid libraries: Plaid Link (more specifically, [react-plaid-link](https://github.com/plaid/react-plaid-link)), Plaid Link's React bindings, and [plaid-node](https://github.com/plaid/plaid-node), Plaid's server-side Node SDK. In this guide we'll use Plaid Link's React bindings. However, Plaid offers additional libraries if you are developing for another environment, such as Android or iOS. Additionally, since most of our interaction with Plaid's API will occur server-side, we'll make use of Plaid's Node bindings. Similar to Plaid Link's React bindings, Plaid offers additional libraries for other server-side programming languages. ### Create a Link Token In order to instantiate a Plaid Link instance — in our case, by using `usePlaidLink` — you first need to have your server generate a Link token. To learn more about what properties are available, check out Plaid's [Create Link Token](https://plaid.com/docs/api/link/#linktokencreate) documentation. Though you are welcome to use additional Plaid products and customize the `LinkTokenCreateRequest` object to your liking, **a couple properties are required in order to be compatible with Dwolla**: * `country_codes` must include "US" (or an enumerative equivalent), as Dwolla is currently only able to transact with U.S.-based bank accounts. * `products` must include "auth" (or an enumerative equivalent), as Dwolla will fetch the associated account and routing number using Plaid Auth. ```typescript theme={"dark"} import type { LinkTokenCreateRequest, LinkTokenCreateResponse } from "plaid"; import { CountryCode, Products } from "plaid"; import { v4 as uuidv4 } from "uuid"; const createLinkToken = async (): Promise => { const request: LinkTokenCreateRequest = { client_name: "Dwolla-Plaid Integration Example", country_codes: [CountryCode.Us], language: "en", products: [Products.Auth], user: { client_user_id: uuidv4() }, redirect_uri: "http://localhost:3000", }; return (await plaidClient.linkTokenCreate(request)).data; }; ``` ### Instantiate Plaid Link Once your client environment has made the necessary requests to your server to fetch the Link token, you can now give that token to `usePlaidLink` with an additional `onSuccess` handler. The `onSuccess` handler will be responsible for proxying client requests to your server to, at a minimum, 1. Exchange the public token for an access token. 2. Exchange the access token and account ID for a processor token. 3. Create a funding source using the processor token. ```typescript theme={"dark"} import type { PlaidLinkOnSuccess } from "react-plaid-link"; import { usePlaidLink } from "react-plaid-link"; const handlePlaidLinkSuccess: PlaidLinkOnSuccess = async ( publicToken, metadata ) => { // When handling the Plaid Link token, you'll want to call the following functions // in order to create a Dwolla funding source. Function implementations can be found below. // // [1] Exchange public token for an access token // [2] Exchange access token & accountID for a processor token // [3] Create a Dwolla exchange resource using the processor token // [4] Create a funding source using the exchange }; const { open, ready } = usePlaidLink({ onSuccess: handlePlaidLinkSuccess, token: linkToken, // linkToken is the Link token that your server sent }); ``` ### Exchange Public Token for Access Token Now that you have your public token, you will need to exchange it for an access token back in your server environment. An access token is an intermediate step between a public token and a processor token, and can be generated in a one-line JavaScript function using Plaid's Node SDK. ```typescript theme={"dark"} // 1. Exchange public token for an access token export const exchangeForAccessToken = async ( publicToken: string ): Promise => (await plaidClient.itemPublicTokenExchange({ public_token: publicToken })) .data.access_token; ``` ### Exchange Access Token and Account ID for Processor Token Continuing in your server environment, once you have an access token and an account ID (account ID is returned by Plaid Link in your client environment), you can now create a processor token! Similar to how you created a Link token, when creating a processor token, you must specify Dwolla ("dwolla") as the processor. If you are using Plaid's SDK, you can use an enumerative equivalent, such as `ProcessorTokenCreateRequestProcessorEnum.Dwolla`. ```typescript theme={"dark"} // 2. Exchange access token & accountID for a processor token import { ProcessorTokenCreateRequestProcessorEnum } from "plaid"; export const exchangeForProcessorToken = async ( accessToken: string, accountId: string ): Promise => ( await plaidClient.processorTokenCreate({ access_token: accessToken, account_id: accountId, processor: ProcessorTokenCreateRequestProcessorEnum.Dwolla, }) ).data.processor_token; ``` Once your application has generated the processor token, you are now ready to integrate with Dwolla! ## Integrate with Dwolla Before creating a funding source, your application will need to create a Customer. If you have not created a customer yet, check out our [Create a Customer](/docs/api-reference/customers/create-a-customer) API reference, or our [Create a Business Verified Customer](/docs/business-verified-customer) or [Create a Personal Verified Customer](/docs/personal-verified-customer) guides. Once you have a customer set up, we will use Dwolla's [server-side Node SDK](https://github.com/Dwolla/dwolla-v2-node) to create the exchange. While we are using Dwolla's Node SDK for the sake of demonstration in this guide, Dwolla also offers additional libraries for other server-side programming languages. ### Create an Exchange The creation of an [exchange](/docs/api-reference/exchanges) serves as a "hand-shake" between Dwolla and Plaid. To create the exchange for a customer, you will supply two required properties: `_link` and `token`. In the API request, `_link` defines a JSON object containing an [exchange partner](/docs/api-reference/exchanges/list-exchange-partners) link relation, and `token` defines the Plaid processor token that was generated in 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 your exchange resource. ```typescript theme={"dark"} // 3. Create an exchange // 3a. Retrieve the exchange partner link for Plaid async function getExchangeHref(): Promise { const response = await dwollaClient.get("exchange-partners"); const partnersList = response.body._embedded["exchange-partners"]; const plaidPartner = partnersList.filter( (obj: { name: string }) => "Plaid" )[0]; return plaidPartner._links.self.href; } const exchangePartnerHref = getExchangeHref(); // 3b. Create an exchange using the exchange partner link and the processor token interface CreateExchangeOptions { customerId: string; exchangePartnerHref: string; token: string; } const createExchange = async ( options: CreateExchangeOptions ): Promise => ( await dwollaClient.post(`customers/${options.customerId}/exchanges`, { _links: { "exchange-partner": { href: options.exchangePartnerHref, }, }, token: options.token, }) ).headers.get("location"); ``` ### Create a Funding Source To create a verified funding source for a customer, you will supply three required properties: `_link`, `bankAccountType`, and `name`. In the API request, `_link` defines a JSON object containing an exchange link relation, `bankAccountType` defines the type of the bank account: checking or savings, and `name` defines an arbitrary name that you or your user will assign to the funding source. 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 your funding source. ```typescript theme={"dark"} // 4. Create a funding source using the exchange interface CreateFundingSourceOptions { customerId: string; exchangeUrl: string; fundingSourceName: string; type: "checking" | "savings"; } const createFundingSource = async ( options: CreateFundingSourceOptions ): Promise => ( await dwollaClient.post(`customers/${options.customerId}/funding-sources`, { _links: { exchange: { href: options.exchangeUrl, }, }, bankAccountType: options.type, name: options.fundingSourceName, }) ).headers.get("location"); ``` ## Frequently Asked Questions When creating a funding source, a Plaid processor\_token value is passed in via an exchange resource. With this information, Dwolla executes a call to Plaid's API to securely retrieve the account and routing number and creates a funding source on your behalf. Upon success, Dwolla returns a URL that represents the new funding source via the location response header. Yes, if the end user has 2FA enabled with their bank, Plaid will attempt to mimic their bank's login flow, meaning that the user should receive a 2FA code, and is subsequently prompted to enter it in on the next screen. If the processor\_token has already been used to create a funding source, then the token changing or expiring will not affect it; Dwolla only uses the processor\_token to fetch the account and routing number from Plaid and then immediately discards it.

However, if you have not yet used the processor\_token to create a funding source, then you will need to follow Plaid's process for creating a new token before sending it over to Dwolla.
Yes, it is possible to use Plaid Link to both automatically verify bank accounts and manually verify via micro-deposits if the bank is not supported or an error occurs during automatic verification. No. Since Dwolla only uses Plaid to securely retrieve the account and routing number, any changes to the underlying Plaid account will not affect the funding source(s) that have already been created in Dwolla. This issue can occur when the user's bank utilizes Tokenized Account Numbers (TANs) for bank verification, and Plaid provides Dwolla with the TAN instead of the actual Account Number. Dwolla processes the ACH payment using the TAN. If the user has disabled TAN transactions on their bank's website, the ACH payment made by Dwolla will fail with an ACH return (e.g., R04 - Invalid Account Number).

To resolve this issue, please advise the user to contact their bank or visit their bank's website and enable TANs for future transfers. In some cases, it might be necessary to re-add the Plaid verified bank using a new processor token.
# Send Money Source: https://developers.dwolla.com/docs/send-money Learn the key steps involved with sending funds to your end user's bank account. ## Overview This guide is designed to get you up and running quickly with digital disbursements by creating a one-time transfer to an end user via the Dwolla API. In this guide we'll cover the basics of integrating the most lightweight payment flow, sending funds (also referred to as "payouts"), by outlining and walking through the necessary steps, to create a bank transfer. For simplicity, we'll represent a one-to-one transfer between two users, where the source user is identified as the Master Dwolla account and the destination user is an individual or business that has been on-boarded via the Dwolla API. If your use case involves sending several digital disbursements in a single batch, it's recommended that you leverage our [Mass Payment API](https://developers.dwolla.com/api-reference/mass-payments), which allows you to send up to 5,000 payments with a single API request. Funds Flow Send Money ### Key Concepts In this quickstart guide, you'll learn the key concepts involved with sending money to a recipient's bank account via digital disbursements: Select the appropriate Customer type (receive-only, verified, etc.) and create a new Customer record through the Dwolla API to represent your recipient. Add a bank account as a funding source to your recipient's Customer record, which will serve as the destination for the funds transfer. Get a list of verified funding sources from both your Dwolla account and the recipient's account to use as source and destination for the transfer. Initiate a transfer from your verified funding source to the recipient's bank account using the Dwolla API. ## Before You Begin We encourage you to create a sandbox account, if you haven't already. This will allow you to follow along with the steps outlined in this guide. Check out our [Sandbox guide](/docs/testing) to learn more. After creating a sandbox account, you'll obtain your API Key and Secret, which are used to obtain an OAuth access token. An access token is required in order to authenticate against the Dwolla API. If you haven't already, run through the [Quickstart](/docs/quickstart) to get your first token, or see the [Authentication guide](/docs/api-reference/api-fundamentals/making-requests-and-authentication) for OAuth details. Lastly, in this sandbox walkthrough, we recommend having an active webhook subscription. This will help notify your application of various events that occur within Dwolla. [Check out our guide to learn more](/docs/working-with-webhooks). **Let's get started!** ## Step 1 - Create a recipient Before your end user can receive funds to their connected bank account, they must be created as a Customer via the Dwolla API. The ability to send funds to end users is very flexible in that all Customer types can be used to leverage this funds flow. To learn more about the different types of Customers and the capabilities of each, check out our [developer resource article](/docs/customer-types). ### Create the Customer While you can use any Customer type for this funds flow, we will be creating a `receive-only` user in this guide, as it offers a lightweight onboarding experience for users. Just as the name implies, receive-only users are only eligible to receive funds into their attached bank account. Providing the IP address of the end user accessing your application as the ipAddress parameter. This enhances fraud detection and tracking. ##### Request Parameters - Receive-only User | Parameter | Required? | Type | Description | | ------------ | --------- | ------ | ----------------------------------------------------------------------- | | firstName | yes | string | Customer's first name | | lastName | yes | string | Customer's last name | | email | yes | string | Customer's email address | | type | yes | string | Value of `receive-only` | | businessName | no | string | Customer's registered business name (optional if not a business entity) | | ipAddress | no | string | Customer's IP address | ```bash create customer theme={"dark"} POST https://api-sandbox.dwolla.com/customers Content-Type: application/vnd.dwolla.v1.hal+json Accept: application/vnd.dwolla.v1.hal+json Authorization: Bearer 0Sn0W6kzNicvoWhDbQcVSKLRUpGjIdlPSEYyrHqrDDoRnQwE7Q { "firstName": "Jane", "lastName": "Merchant", "email": "jmerchant@nomail.net", "type": "receive-only", "ipAddress": "99.99.99.99" } HTTP/1.1 201 Created Location: https://api-sandbox.dwolla.com/customers/c7f300c0-f1ef-4151-9bbe-005005aa3747 ``` ```ruby create_customer.rb theme={"dark"} request_body = { :firstName => 'Jane', :lastName => 'Merchant', :email => 'jmerchant@nomail.net', :type => 'receive-only', :ipAddress => '99.99.99.99' } # Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby (Recommended) customer = app_token.post "customers", request_body customer.response_headers[:location] # => "https://api-sandbox.dwolla.com/customers/c7f300c0-f1ef-4151-9bbe-005005aa3747" ``` ```javascript createCustomer.js theme={"dark"} var requestBody = { firstName: "Jane", lastName: "Merchant", email: "jmerchant@nomail.net", type: "receive-only", ipAddress: "99.99.99.99", }; dwolla.post("customers", requestBody).then(function (res) { res.headers.get("location"); // => 'https://api-sandbox.dwolla.com/customers/c7f300c0-f1ef-4151-9bbe-005005aa3747' }); ``` ```python create_customer.py theme={"dark"} request_body = { 'firstName': 'Jane', 'lastName': 'Merchant', 'email': 'jmerchant@nomail.net', 'type': 'receive-only', 'ipAddress': '99.99.99.99' } # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python (Recommended) customer = app_token.post('customers', request_body) customer.headers['location'] # => 'https://api-sandbox.dwolla.com/customers/c7f300c0-f1ef-4151-9bbe-005005aa3747' ``` ```php createCustomer.php theme={"dark"} create([ 'firstName' => 'Jane', 'lastName' => 'Merchant', 'email' => 'jmerchant@nomail.net', 'type' => 'receive-only' 'ipAddress' => '99.99.99.99' ]); print($customer); # => "https://api-sandbox.dwolla.com/customers/c7f300c0-f1ef-4151-9bbe-005005aa3747" ?> ``` When the Customer is successfully created on your application, you will receive a `201` HTTP response with an empty response body. You can reference the Location header to retrieve a link that represents the created Customer resource. We recommend storing the full URL for future use, as it will be needed for actions such as attaching a bank or correlating webhooks that are triggered for the user in the Dwolla system. ### Handle Webhooks If you have an active [webhook subscription](/docs/working-with-webhooks), you will receive the `customer_created` webhook immediately after the resource has been created. ## Step 2 - Adding a Funding Source After creating our receive-only User, the next step is to attach a bank funding source. This will be the funding source where they will receive funds. #### Bank Addition and Verification methods Within Dwolla, the sending party must always verify their bank account in order to be eligible to create a transfer. Although it's recommended, the party that is receiving the funds does not need to undergo bank verification. There are three ways of adding a bank to a Customer with the Dwolla API. A simplified table below outlines the similarities and differences of each method. | Bank Addition Method | Will the bank be `verified`? | Required Information | | --------------------------------------------------------------------------------------------------------------------- | ----------------------------- | ------------------------------- | | API - Account & Routing Number | Optional - With Microdeposits | Bank Account and Routing Number | | [Dwolla + Open Banking](/docs/open-banking) | Yes | Online banking credentials | | [Drop-in components](/docs/drop-in-components) | Optional - With Microdeposits | Bank Account and Routing Number | | Third Party - Plaid ([Example](https://github.com/Dwolla/integration-examples/tree/main/dwolla-plaid-funding-source)) | Yes | Online Bank Credentials | For more information on securely submitting a user's bank details directly to Dwolla from the client-side of your application, reference our Drop-in Components . ### Adding a Bank to the Receive-only User In this step, we will be adding a bank account to our receive-only user by collecting their bank details within a form on our application. After initial validation of the form fields, the user's bank details will be submitted to our back-end server where the API request is made to Dwolla to add a bank account. ##### Request Parameters - Create a Funding Source | Parameter | Required? | Type | Description | | --------------- | --------- | ------ | ------------------------------------------------------------------------ | | routingNumber | yes | string | The bank routing number | | accountNumber | yes | string | The bank account number | | bankAccountType | yes | string | Type of bank account: `checking` or `savings` | | name | yes | string | Arbitrary nickname for the funding source. Must be 50 characters or less | ```bash create customer funding source theme={"dark"} POST https://api.dwolla.com/customers/c7f300c0-f1ef-4151-9bbe-005005aa3747/funding-sources Content-Type: application/vnd.dwolla.v1.hal+json Accept: application/vnd.dwolla.v1.hal+json Authorization: Bearer 0Sn0W6kzNicvoWhDbQcVSKLRUpGjIdlPSEYyrHqrDDoRnQwE7Q { "routingNumber": "222222226", "accountNumber": "123456789", "bankAccountType": "checking", "name": "Jane Merchant - Checking 6789" } HTTP/1.1 201 Created Location: https://api-sandbox.dwolla.com/funding-sources/375c6781-2a17-476c-84f7-db7d2f6ffb31 ``` ```ruby create_funding_source.rb theme={"dark"} customer_url = 'https://api-sandbox.dwolla.com/customers/c7f300c0-f1ef-4151-9bbe-005005aa3747' request_body = { routingNumber: '222222226', accountNumber: '123456789', bankAccountType: 'checking', name: 'Jane Merchant - Checking 6789' } # Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby (Recommended) funding_source = app_token.post "#{customer_url}/funding-sources", request_body funding_source.response_headers[:location] # => "https://api-sandbox.dwolla.com/funding-sources/375c6781-2a17-476c-84f7-db7d2f6ffb31" ``` ```javascript create_funding_source.js theme={"dark"} var customerUrl = "https://api-sandbox.dwolla.com/customers/c7f300c0-f1ef-4151-9bbe-005005aa3747"; var requestBody = { routingNumber: "222222226", accountNumber: "123456789", bankAccountType: "checking", name: "Jane Merchant - Checking 6789", }; dwolla.post(`${customerUrl}/funding-sources`, requestBody).then(function (res) { res.headers.get("location"); // => 'https://api-sandbox.dwolla.com/funding-sources/375c6781-2a17-476c-84f7-db7d2f6ffb31' }); ``` ```python create_funding_source.py theme={"dark"} customer_url = 'https://api-sandbox.dwolla.com/customers/c7f300c0-f1ef-4151-9bbe-005005aa3747' request_body = { 'routingNumber': '222222226', 'accountNumber': '123456789', 'bankAccountType': 'checking', 'name': 'Jane Merchant - Checking 6789' } # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python (Recommended) customer = app_token.post('%s/funding-sources' % customer_url, request_body) customer.headers['location'] # => 'https://api-sandbox.dwolla.com/funding-sources/375c6781-2a17-476c-84f7-db7d2f6ffb31' ``` ```php create_funding_source.php theme={"dark"} createCustomerFundingSource([ "routingNumber" => "222222226", "accountNumber" => "123456789", "bankAccountType" => "checking", "name" => "Jane Merchant - Checking 6789" ], "https://api-sandbox.dwolla.com/customers/c7f300c0-f1ef-4151-9bbe-005005aa3747" ); print($new_fs); # => https://api-sandbox.dwolla.com/funding-sources/375c6781-2a17-476c-84f7-db7d2f6ffb31 ?> ``` When the funding source is created, you will receive a `201` HTTP response with an empty response body. You can refer to the Location header to retrieve a link to the created funding source resource. We recommend storing the full URL for future use as it will be referenced when creating the transfer to this user's bank account. ### Handle Webhooks If you have an active webhook subscription (required in production & optional in Sandbox), you will receive the `customer_funding_source_created` webhook immediately after the resource has been created. ## Step 3 - Retrieve funding sources Now that you've created a Customer and associated its funding source, you are close to being able to initiate your first transfer. The transfer requires the following information: * The funding source to pull the funds from (a bank attached to your Dwolla Master Account) * The funding source to push the funds to (a bank attached to your created Customer) Dwolla uses URLs to represent relations between resources. Therefore, you'll need to provide the full URL of the funding source when creating the transfer. ### Retrieve your Dwolla Master Account's list of available Funding Sources Use the [list an account's funding sources endpoint](/docs/api-reference/accounts/list-funding-sources-for-an-account) to fetch a list of your own funding sources. You'll need your account URL which can be retrieved by calling [the Root](/docs/api-reference/root) of the API. ##### Request and response ```bash List Funding Sources [expandable] theme={"dark"} GET https://api-sandbox.dwolla.com/accounts/ad5f2162-404a-4c4c-994e-6ab6c3a13254/funding-sources?removed=false Accept: application/vnd.dwolla.v1.hal+json Authorization: Bearer 0Sn0W6kzNicvoWhDbQcVSKLRUpGjIdlPSEYyrHqrDDoRnQwE7Q { "_links": { "self": { "href": "https://api-sandbox.dwolla.com/accounts/ad5f2162-404a-4c4c-994e-6ab6c3a13254/funding-sources" } }, "_embedded": { "funding-sources": [ { "_links": { "transfer-from-balance": { "href": "https://api-sandbox.dwolla.com/transfers", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "transfer" }, "self": { "href": "https://api-sandbox.dwolla.com/funding-sources/b5e68264-7d4d-42a9-88d4-5616c77c6baa", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "funding-source" }, "remove": { "href": "https://api-sandbox.dwolla.com/funding-sources/b5e68264-7d4d-42a9-88d4-5616c77c6baa", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "funding-source" }, "transfer-send": { "href": "https://api-sandbox.dwolla.com/transfers", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "transfer" }, "transfer-receive": { "href": "https://api-sandbox.dwolla.com/transfers", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "transfer" }, "account": { "href": "https://api-sandbox.dwolla.com/accounts/ad5f2162-404a-4c4c-994e-6ab6c3a13254", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "account" } }, "id": "b5e68264-7d4d-42a9-88d4-5616c77c6baa", "status": "verified", "type": "bank", "bankAccountType": "checking", "name": "ABC Bank Checking", "created": "2019-03-14T15:18:51.336Z", "removed": false, "channels": [ "ach" ], "bankName": "SANDBOX TEST BANK" }, { "_links": { "self": { "href": "https://api-sandbox.dwolla.com/funding-sources/b268f6b9-db3b-4ecc-83a2-8823a53ec8b7", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "funding-source" }, "balance": { "href": "https://api-sandbox.dwolla.com/funding-sources/b268f6b9-db3b-4ecc-83a2-8823a53ec8b7/balance", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "balance" }, "transfer-send": { "href": "https://api-sandbox.dwolla.com/transfers", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "transfer" }, "with-available-balance": { "href": "https://api-sandbox.dwolla.com/funding-sources/b268f6b9-db3b-4ecc-83a2-8823a53ec8b7", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "funding-source" }, "transfer-receive": { "href": "https://api-sandbox.dwolla.com/transfers", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "transfer" }, "account": { "href": "https://api-sandbox.dwolla.com/accounts/ad5f2162-404a-4c4c-994e-6ab6c3a13254", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "account" } }, "id": "b268f6b9-db3b-4ecc-83a2-8823a53ec8b7", "status": "verified", "type": "balance", "name": "Balance", "created": "2014-07-09T20:39:33.000Z", "removed": false, "channels": [] } ] } } ``` ```ruby get_funding_sources.rb theme={"dark"} account_url = 'https://api.dwolla.com/accounts/ad5f2162-404a-4c4c-994e-6ab6c3a13254' # Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby (Recommended) funding_sources = app_token.get "#{account_url}/funding-sources?removed=false" funding_sources._embedded['funding-sources'][0].name # => "ABC Bank Checking" ``` ```javascript get_funding_sources.js theme={"dark"} var accountUrl = "https://api-sandbox.dwolla.com/accounts/ad5f2162-404a-4c4c-994e-6ab6c3a13254"; dwolla.get(`${accountUrl}/funding-sources?removed=false`).then(function (res) { res.body._embedded["funding-sources"][0].name; // => 'ABC Bank Checking' }); ``` ```python get_funding_sources.py theme={"dark"} account_url = 'https://api.dwolla.com/accounts/ad5f2162-404a-4c4c-994e-6ab6c3a13254' # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python (Recommended) funding_sources = app_token.get('%s/funding-sources?removed=false' % account_url) funding_sources.body['_embedded']['funding-sources'][0]['name'] # => 'ABC Bank Checking' ``` ```php get_funding_sources.php theme={"dark"} getAccountFundingSources($accountUrl, $removed = false); # Access desired information in response object fields print($fundingSources->_embedded) # => PHP associative array of _embedded contents in schema ?> ``` When the funding sources list is successfully retrieved, you will receive a `200` HTTP response with the details of each funding source. After retrieving your list of funding sources, we recommend storing the full URL for future use as it will be referenced when creating the transfer to your user's bank account. ### Retrieve your Customer's list of available funding sources Use the [list an Customer's funding sources](/docs/api-reference/funding-sources/list-customer-funding-sources) endpoint to fetch a list of your own funding sources. You'll need the Customer URL which can be [retrieved from the API.](/docs/api-reference/customers/list-and-search-customers) ##### Request and response ```bash List Customer Funding Sources [expandable] theme={"dark"} GET https://api-sandbox.dwolla.com/customers/ad5f2162-404a-4c4c-994e-6ab6c3a13254/funding-sources Accept: application/vnd.dwolla.v1.hal+json Authorization: Bearer 0Sn0W6kzNicvoWhDbQcVSKLRUpGjIdlPSEYyrHqrDDoRnQwE7Q { "_links": { "self": { "href": "https://api-sandbox.dwolla.com/customers/ad5f2162-404a-4c4c-994e-6ab6c3a13254/funding-sources" } }, "_embedded": { "funding-sources": [ { "_links": { "self": { "href": "https://api-sandbox.dwolla.com/funding-sources/0094b1b4-e171-4dc8-865b-cb121c2377bb" }, "customer": { "href": "https://api-sandbox.dwolla.com/customers/ad5f2162-404a-4c4c-994e-6ab6c3a13254" }, "with-available-balance": { "href": "https://api-sandbox.dwolla.com/funding-sources/0094b1b4-e171-4dc8-865b-cb121c2377bb" } }, "id": "0094b1b4-e171-4dc8-865b-cb121c2377bb", "status": "verified", "type": "balance", "name": "Balance", "created": "2013-09-07T14:42:52.000Z" }, { "_links": { "self": { "href": "https://api-sandbox.dwolla.com/funding-sources/b5e68264-7d4d-42a9-88d4-5616c77c6baa" }, "customer": { "href": "https://api-sandbox.dwolla.com/customers/ad5f2162-404a-4c4c-994e-6ab6c3a13254" } }, "id": "b5e68264-7d4d-42a9-88d4-5616c77c6baa", "status": "verified", "type": "bank", "name": "ABC Bank Checking", "created": "2014-09-04T23:19:19.543Z" } ] } } ``` ```ruby list_customer_funding_sources.rb theme={"dark"} customer_url = 'https://api.dwolla.com/customers/ad5f2162-404a-4c4c-994e-6ab6c3a13254' # Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby (Recommended) funding_sources = app_token.get "#{customer_url}/funding-sources" funding_sources._embedded['funding-sources'][0].name # => "ABC Bank Checking" ``` ```javascript list_customer_funding_sources.js theme={"dark"} var customerUrl = "https://api-sandbox.dwolla.com/customers/ad5f2162-404a-4c4c-994e-6ab6c3a13254"; dwolla.get(`${accountUrl}/funding-sources`).then(function (res) { res.body._embedded["funding-sources"][0].name; // => 'ABC Bank Checking' }); ``` ```python list_customer_funding_sources.py theme={"dark"} customers_url = 'https://api.dwolla.com/customers/ad5f2162-404a-4c4c-994e-6ab6c3a13254' # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python (Recommended) funding_sources = app_token.get('%s/funding-sources' % customer_url) funding_sources.body['_embedded']['funding-sources'][0]['name'] # => 'ABC Bank Checking' ``` ```php list_customer_funding_sources.php theme={"dark"} getCustomerFundingSources($customerUrl); # Access desired information in response object fields print($fundingSources->_embedded) # => PHP associative array of _embedded contents in schema ?> ``` When the list of funding sources is successfully retrieved, you will receive a `200` HTTP response with the details for the funding sources. After retrieving the funding sources, we recommend storing the full URL for future use as it will be referenced when creating the transfer to this user's bank account. ## Step 4 - Initiating a transfer Now that our user is onboarded and their funding source has been created, we're ready to create a transfer to their bank account. In order to create the transfer, we'll need Funding Source links that represent both the source and destination bank accounts. Your customer's funding source URL should be stored from the previous step and retrieved on demand when creating the transfer. #### Identify Source and Destination Parties Since you are utilizing a `send` funds flow, you will need to ensure that you know exactly who will be receiving these funds. * Source - Your Dwolla Master Account Bank Funding Source * Destination - Your Customer's Bank Funding Source ### Initiate a Transfer To initiate a transfer, we will need to specify the funding source URLs in the `_links` parameter. | Parameter | Required? | Type | Description | | --------- | --------- | ------ | ---------------------------------------------------------------------------------- | | \_links | yes | object | A \_links JSON object describing the desired source and destination of a transfer. | | amount | yes | object | An amount JSON object. | ##### Request and response ```bash Create Transfer theme={"dark"} POST https://api-sandbox.dwolla.com/transfers Accept: application/vnd.dwolla.v1.hal+json Content-Type: application/vnd.dwolla.v1.hal+json Authorization: Bearer 0Sn0W6kzNicvoWhDbQcVSKLRUpGjIdlPSEYyrHqrDDoRnQwE7Q { "_links": { "source": { "href": "https://api-sandbox.dwolla.com/funding-sources/b5e68264-7d4d-42a9-88d4-5616c77c6baa" }, "destination": { "href": "https://api-sandbox.dwolla.com/funding-sources/3152c22b-3d72-442d-a83b-e575df3a043e" } }, "amount": { "currency": "USD", "value": "225.00" } } ... HTTP/1.1 201 Created Location: https://api-sandbox.dwolla.com/transfers/d76265cd-0951-e511-80da-0aa34a9b2388 ``` ```ruby create_transfer.rb theme={"dark"} transfer_request = { :_links => { :source => { :href => "https://api-sandbox.dwolla.com/funding-sources/b5e68264-7d4d-42a9-88d4-5616c77c6baa" }, :destination => { :href => "https://api-sandbox.dwolla.com/funding-sources/3152c22b-3d72-442d-a83b-e575df3a043e" } }, :amount => { :currency => "USD", :value => "225.00" } } # Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby (Recommended) transfer = app_token.post "transfers", transfer_request transfer.response_headers[:location] # => "https://api-sandbox.dwolla.com/transfers/d76265cd-0951-e511-80da-0aa34a9b2388" ``` ```javascript create_transfer.js theme={"dark"} var transferRequest = { _links: { source: { href: "https://api-sandbox.dwolla.com/funding-sources/b5e68264-7d4d-42a9-88d4-5616c77c6baa", }, destination: { href: "https://api-sandbox.dwolla.com/funding-sources/3152c22b-3d72-442d-a83b-e575df3a043e", }, }, amount: { currency: "USD", value: "225.00", }, }; dwolla.post("transfers", transferRequest).then(function (res) { res.headers.get("location"); // => 'https://api-sandbox.dwolla.com/transfers/d76265cd-0951-e511-80da-0aa34a9b2388' }); ``` ```python create_transfer.py theme={"dark"} transfer_request = { '_links': { 'source': { 'href': 'https://api-sandbox.dwolla.com/funding-sources/b5e68264-7d4d-42a9-88d4-5616c77c6baa' }, 'destination': { 'href': 'https://api-sandbox.dwolla.com/funding-sources/3152c22b-3d72-442d-a83b-e575df3a043e' } }, 'amount': { 'currency': 'USD', 'value': '225.00' } } # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python (Recommended) transfer = app_token.post('transfers', transfer_request) transfer.headers['location'] # => 'https://api-sandbox.dwolla.com/transfers/d76265cd-0951-e511-80da-0aa34a9b2388' ``` ```php create_transfer.php theme={"dark"} array ( 'source' => array ( 'href' => 'https://api-sandbox.dwolla.com/funding-sources/b5e68264-7d4d-42a9-88d4-5616c77c6baa', ), 'destination' => array ( 'href' => 'https://api-sandbox.dwolla.com/funding-sources/3152c22b-3d72-442d-a83b-e575df3a043e', ), ), 'amount' => array ( 'currency' => 'USD', 'value' => '225.00', ) ); $transferApi = new DwollaSwagger\TransfersApi($apiClient); $transfer = $transferApi->create($transfer_request); print($transfer); # => https://api-sandbox.dwolla.com/transfers/d76265cd-0951-e511-80da-0aa34a9b2388 ?> ``` When the transfer is created, you will receive a `201` HTTP response with an empty response body. You can refer to the Location header to retrieve a link to the created Transfer resource. All transactions that are sourced from a bank or that are going to a bank will have an initial status of `pending`. We recommend storing the full Transfer URL for future use, as it will be needed for correlating transfer update webhooks that are triggered for the user in the Dwolla system. ### Handle Webhooks A single API call to create a payment transfer can trigger several transfer-related webhook events. The number of webhooks and type of webhook events can vary depending on the Customer type(s) involved in the transfer, as well as the source and destination for the funds transfer. For more information on which webhooks will be fired, refer to our [API Reference Docs](/docs/api-reference/events). ### Simulate ACH Processing To simulate ACH processing in the Dwolla Sandbox environment, navigate to the Sandbox Dashboard. From here, you will want to click the "Process Bank Transfers" button on the top of the screen. Your Sandbox transfer will be moved out of a `pending` status and moved to a `processed` status. process bank transfers ### Verify Status of Transfer Since ACH transactions can take a few days to complete, webhooks are an efficient way to notify you of when a transfer is completed and `processed` to a destination funding source. However, if you want to verify the status of a transfer at any given point in time, you can make a call to the API to retrieve the transfer by its unique id. ```bash Retrieve Transfer Status theme={"dark"} GET https://api-sandbox.dwolla.com/transfers/d76265cd-0951-e511-80da-0aa34a9b2388 Accept: application/vnd.dwolla.v1.hal+json Authorization: Bearer 0Sn0W6kzNicvoWhDbQcVSKLRUpGjIdlPSEYyrHqrDDoRnQwE7Q { "_links": { "cancel": { "href": "https://api-sandbox.dwolla.com/transfers/d76265cd-0951-e511-80da-0aa34a9b2388", "type": "transfer" }, "source": { "href": "https://api-sandbox.dwolla.com/accounts/ad5f2162-404a-4c4c-994e-6ab6c3a13254", "type": "account" }, "funding-transfer": { "href": "https://api-sandbox.dwolla.com/transfers/e73f5b8e-e458-e611-80e5-0aa34a9b2388", "type": "transfer" }, "self": { "href": "https://api-sandbox.dwolla.com/transfers/d76265cd-0951-e511-80da-0aa34a9b2388", "type": "transfer" }, "source-funding-source": { "href": "https://api-sandbox.dwolla.com/funding-sources/b5e68264-7d4d-42a9-88d4-5616c77c6baa", "type": "funding-source" }, "destination": { "href": "https://api-sandbox.dwolla.com/customers/c7f300c0-f1ef-4151-9bbe-005005aa3747", "type": "customer" } }, "id": "d76265cd-0951-e511-80da-0aa34a9b2388", "status": "processed", "amount": { "value": "42.00", "currency": "usd" }, "created": "2015-09-01T19:08:55.500Z" } ``` ```ruby get_transfer_status.rb theme={"dark"} transfer_url = 'https://api.dwolla.com/transfers/d76265cd-0951-e511-80da-0aa34a9b2388' # Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby (Recommended) transfer = app_token.get transfer_url transfer.status # => "processed" ``` ```javascript get_transfer_status.js theme={"dark"} var transferUrl = "https://api.dwolla.com/transfers/d76265cd-0951-e511-80da-0aa34a9b2388"; dwolla.get(transferUrl).then(function (res) { res.body.status; // => 'processed' }); ``` ```python get_transfer_status.py theme={"dark"} transfer_url = 'https://api.dwolla.com/transfers/d76265cd-0951-e511-80da-0aa34a9b2388' # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python (Recommended) fees = app_token.get(transfer_url) fees.body['status'] # => 'processed' ``` ```php get_transfer_status.php theme={"dark"} byId($transferUrl); print($transfer->status); # => "processed" ?> ``` That's it! You've successfully transferred money to a recipient. Please continue to the [Webhooks guide](/docs/working-with-webhooks) for information on implementing notifications for your customers about the status of the transfer. # Testing in the Sandbox Source: https://developers.dwolla.com/docs/testing Test and refine your integration in the Dwolla Sandbox—a free, full-featured environment that simulates real API interactions, allowing you to build, experiment, and validate your application before going live. ## Overview The Sandbox environment is a complete replica of the Dwolla production environment, supporting all of the same API endpoints. Applications should be tested against the Sandbox environment before being used in production. ### Differences from production environment * The Sandbox contains only test data and is completely separate from your production account. * All API endpoints have a base URL of `https://api-sandbox.dwolla.com/` instead of `https://api.dwolla.com/` * Actual money is not sent or received as part of test transactions. Real financial data should never be used in the Sandbox. ### Transfer behavior in the Sandbox Unlike transfers that are sourced from a [Dwolla balance](/docs/balance-funding-source), which are processed instantaneously, bank-sourced transfers exist in the pending state for a few business days until they are `processed`, `failed`, or `cancelled`. The Sandbox environment does not replicate any ACH processes, so a `pending` transfer will not clear or fail automatically after a few business days as it would in production. It will simply remain in the `pending` state indefinitely. Reference the [testing transfers](#testing-transfers) section for more information on how to simulate bank transfer processing in the Sandbox environment. ### Sandbox account setup To set up your Sandbox account, all you will need is a valid email address. Once you agree to the Dwolla Developer Terms and Service, you will receive an email asking to verify your email address. Failure to verify your email will result in a 401 HTTP status for all API calls with an error code of InvalidAccountStatus. After email verification, your Sandbox account will be created and you'll be redirected to our Sandbox Dashboard at `https://dashboard-sandbox.dwolla.com/`. Here you can view your API key and secret and generate an OAuth access token. Dwolla will also create an application for your account, associate a funding source named 'Superhero Savings Bank', and add \$5000 to the account balance for testing. Start testing with a sandbox account # Testing Customers ### Manage Customers in the Dashboard The [Sandbox Dashboard](https://dashboard-sandbox.dwolla.com) allows you to manage Customers, as well as transfers associated with the Customers that belong to your Sandbox account. Once your application has [created its Customers](/docs/api-reference/customers/create-a-customer), you can access the [Sandbox Dashboard](https://dashboard-sandbox.dwolla.com) to validate that the request was recorded properly in our test environment. There are multiple Customer types within the Dwolla API. Use our concept article for a more in-depth overview of each Customer type and its capabilities. ### Simulate identity verification statuses There are various reasons a [Verified Customer](/docs/customer-types#verified-customer) may have a status other than `verified` after the initial Customer creation. You will want your app to be prepared to handle these alternative statuses. In production, Dwolla will place the Verified Customer in either the `retry`, `kba`, `document`, `verified`, or `suspended` state of verification after an initial identity verification check. **For personal Verified Customers**: Reference the guide on [customer verification](/docs/personal-verified-customer) for more information on handling identity verification for Verified Customers. To simulate the various statuses in the Sandbox, supply either `verified`, `retry`, `kba`, `document`, or `suspended` in the **firstName** parameter in order to [create a new Verified Customer](/docs/api-reference/customers/create-a-customer) with that status. **For business Verified Customers**: Reference the guide on [customer verification](/docs/business-verified-customer) that goes over information on properly verifying a business's Controller, the business, and associated Beneficial Owners. Here's how to simulate the different statuses and verification links for business Verified Customers in Sandbox: `retry` status: * For the business - Supply `retry` in the **businessName** parameter. This action will return a `retry-verification` link in the Customer resource. * For both the Controller and business - Supply `retry` in the **controller firstName** parameter. This action will return both a `retry-verification` link and a `retry-with-full-ssn` link in the Customer resource. `document` status: * For the controller - Supply `document` in the **controller firstName** parameter. This action will return a `verify-with-document` linkin the Customer resource. * For the business - Supply `document` in the **businessName** parameter. This action will return a `verify-business-with-document` link in the Customer resource. * For both the Controller and the business - Submit `document` in both the **controller firstName** and the **businessName** parameters. This action will return a `verify-controller-and-business-with-document` link in the Customer resource. `suspended`: * Supply `suspended` in the **controller firstName** parameter to create a new Verified Customer with that status. **For beneficial owners**: To simulate different verification statuses for Beneficial Owners, submit either `incomplete` or `document` in the **beneficial owner firstName** parameter. ### Simulate KBA verified and failed events If a Personal Verified Customer isn't systematically identity-verified after their second attempt to retry their information, the Customer may be placed in a `kba` status and will be required to successfully answer at least three out of four knowledge based authentication (KBA) questions in order to pass verification. More information on KBA status for Personal Verified Customers and the related endpoints. To simulate the `customer_kba_verification_passed` event as the result of KBA success in Sandbox, answer all four questions with either "None of the above" or "I have never been associated with this vehicle". As a result, the Customer will be placed in a verified status and the `customer_verified` event is triggered. To simulate the `customer_kba_verification_failed` event as the result of KBA failure in Sandbox, answer the questions with any answer choices other than "None of the above" or "I have never been associated with this vehicle". As a result, the Customer will be placed in a document status and the `customer_verification_document_needed` event is triggered. ### Simulate document upload approved and failed events If a Verified Customer isn't systematically identity-verified, the Customer may be placed in a `document` status and will require an identifying document to be uploaded and reviewed. Reference either the [personal customer verification](/docs/personal-verified-customer#document-types) or [business customer verification](/docs/business-verified-customer#document-types) guide for acceptable forms of identifying documents for `Verified Customers`. Since the document review process requires interaction from Dwolla, sample test documents can be uploaded in the Sandbox environment to simulate the `customer_verification_document_approved` and `customer_verification_document_failed` events. When downloading a test image, make sure to keep the size, format, and name of the image the same. #### **Sample document approved image** Document upload success example #### **Sample document failed image** Document upload fail example ### Simulate verification directives You can test how your application handles specific embedded error codes (Verification Directives) in the Sandbox environment using the `/sandbox-simulations` endpoint. This allows you to simulate the Dwolla API returning a particular verification directive for a business Verified Customer. This simulation can only be performed for customers that are in either `retry` or `document` status. Learn more about the structure and meaning of these directives in the Understanding Verification Directives section. The following error codes can be triggered: * **PersonalIDRequired** * **POBoxNotAllowed** * **AddressNotAssociatedWithBusiness** * **EINDocumentRequired** To simulate one of these error codes: 1. Create a business Verified Customer in the Sandbox. 2. Make a POST request to the `/sandbox-simulations` endpoint with a request body like the following: ```bash HTTP theme={"dark"} POST https://api-sandbox.dwolla.com/sandbox-simulations Accept: application/vnd.dwolla.v1.hal+json Content-Type: application/vnd.dwolla.v1.hal+json Authorization: Bearer {Your access token} { "type": "customer-verification", "_links": { "customer": { "href": "https://api-sandbox.dwolla.com/customers/{customer-id}" } }, "errorCode": "AddressNotAssociatedWithBusiness" } ``` ```ruby RUBY theme={"dark"} # Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby (Recommended) request_body = { :type => "customer-verification", :_links => { :customer => { :href => "https://api-sandbox.dwolla.com/customers/{customer-id}" } }, :errorCode => "AddressNotAssociatedWithBusiness" } simulation = app_token.post "sandbox-simulations", request_body ``` ```python PYTHON theme={"dark"} # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python (Recommended) request_body = { 'type': 'customer-verification', '_links': { 'customer': { 'href': 'https://api-sandbox.dwolla.com/customers/{customer-id}' } }, 'errorCode': 'AddressNotAssociatedWithBusiness' } simulation = app_token.post('sandbox-simulations', request_body) ``` ```javascript JAVASCRIPT theme={"dark"} var requestBody = { type: "customer-verification", _links: { customer: { href: "https://api-sandbox.dwolla.com/customers/{customer-id}" } }, errorCode: "AddressNotAssociatedWithBusiness" }; dwolla.post("sandbox-simulations", requestBody); ``` ```php PHP theme={"dark"} create([ 'type' => 'customer-verification', '_links' => [ 'customer' => [ 'href' => 'https://api-sandbox.dwolla.com/customers/{customer-id}' ] ], 'errorCode' => 'AddressNotAssociatedWithBusiness' ]); ?> ``` Replace `{customer-id}` with the ID of your Sandbox customer, and set `errorCode` to one of the supported values (e.g., `PersonalIDRequired`, `POBoxNotAllowed`, `AddressNotAssociatedWithBusiness`, or `EINDocumentRequired`). After making this request, retrieve the Customer resource using the API. The specified error code will appear in the `_embedded.errors` array of the response, allowing you to test your application's handling of these verification directives. # Testing Funding Sources ### Test bank account numbers Dwolla requires a valid U.S. routing number and a random account number between 4-17 digits to add a bank account. For testing purposes, you can use the routing number `222222226` or refer to the list of routing numbers from the [Federal Reserve Bank Services](https://www.frbservices.org/EPaymentsDirectory/agreement.html) website. ### Test micro-deposit verification If your application leverages the micro-deposit method of bank verification, Dwolla will transfer two deposits of less than `$0.10` to your customer's linked bank or credit union account after calling the API to initiate micro-deposits. Since the Sandbox environment doesn't replicate any bank transfer processes, any two amounts **below** `$0.10` will allow you to verify the funding source immediately. In Production, when the micro-deposits have finished processing, you will receive a `customer_microdeposits_completed` event. To trigger this event in Sandbox, you need to simulate bank transfer processing. Check out the [testing transfers](#testing-transfers) section for more information on how to simulate bank transfer processing in Sandbox. Use our step-by-step guide to verify a funding-source with micro-deposits. ### Test micro-deposit failed verification attempts When verifying a funding source using the micro-deposit method of bank verification, users are allowed **three attempts** to correctly input the two posted micro-deposit amounts. If the user fails to verify the two posted amounts on the third attempt, an event will be triggered and the funding source will not be verified using those micro-deposit amounts. To simulate the `microdeposits_maxattempts` or `customer_microdeposits_maxattempts` events in the Sandbox, use the amounts `0.09` and `0.09` when calling the API to verify micro-deposits. Reference the [micro-deposit verification](/docs/micro-deposit-verification#handle-failed-verification-attempts) guide for more information on handling failed verification attempts. # Testing Transfers The Sandbox environment does not replicate any bank transfer processes, so a pending transfer will not clear or fail automatically after a few business days as it would in production. The transfer will simply remain in the pending state indefinitely. ### Simulate bank transfer processing There are two options available for processing or failing bank transfers in the Sandbox environment. * **Option 1:** your application will call the "sandbox-simulations" endpoint (referenced below) which will process or fail the last 500 bank transfers that occurred on the authorized application or Sandbox account. * **Option 2:** you'll use the "Process bank transfers button" in the Sandbox Dashboard, which will process or fail the last 500 bank transfers that occurred on your Sandbox account or any API `Customers` you manage. If a bank-to-bank transaction is initiated between two users, you'll want to simulate bank transfer processing twice in order to process both sides of the transaction (debit and credit). Processing for bank transfers will also include initiated micro-deposits. If your application is subscribed to webhooks, notifications will be sent, including all transfer or micro-deposit related events, letting your application know that transfers have processed or failed. #### Sandbox simulations request and response ```bash theme={"dark"} POST https://api-sandbox.dwolla.com/sandbox-simulations Accept: application/vnd.dwolla.v1.hal+json Content-Type: application/vnd.dwolla.v1.hal+json Authorization: Bearer {Your access token} ... { "_links": { "self": { "href": "https://api-sandbox.dwolla.com/sandbox-simulations", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "sandbox-simulation" } }, "total": 8 } ``` ### Process bank transfers button The [Dwolla Sandbox Dashboard](https://dashboard-sandbox.dwolla.com) provides a convenient way to simulate bank transfer processing. In the left-side navigation, locate and click the "Process bank transfers" button. This button offers the same functionality as the "sandbox-simulations" endpoint (mentioned earlier) and allows you to simulate the processing of bank transfers within the Sandbox environment. By clicking the button, Dwolla will process or fail the last 500 bank transfers associated with your Sandbox account or any API Customer accounts you manage. Process bank transfers ### Test bank transfer failures Transfers to or from a bank account can fail for a number of reasons (e.g. insufficient funds, invalid account number, etc.). When a bank transfer fails, the associated financial institution that rejected the transaction assigns an ACH return code and a transfer failure event is then triggered in Dwolla. Dwolla allows you to trigger various bank transfer failures by specifying an “R” code in the funding source `name` parameter when creating or [updating a funding source](https://developers.dwolla.com/api-reference/funding-sources/update) for a Dwolla Account or API Customer. When a [transfer is initiated](https://developers.dwolla.com/api-reference/transfers/initiate) using a funding source that has an “R” code assigned to its name, a transfer failure event will trigger and the status will update to failed when you simulate bank transfer processing (as mentioned above). Dwolla allows you to pass in a few different sentinel values that are used to test different bank transfer failure scenarios. The list of available sentinel values cover the most common uses cases where ACH return codes can be triggered in production. #### List of codes for testing bank transfer failures | Return code | Description | | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | R01 | Insufficient Funds: This value is used to simulate funds failing from the source bank account (ACH debit). | | R03 | No Account/Unable to Locate Account: This value is primarily used to simulate funds failing to the destination bank account (ACH credit). The funding source will be automatically removed from Dwolla when this return code is triggered. | | R01-late | This value is used to simulate funds failing from the source bank account post-settlement. Note: You must click “Process bank transfers” twice in order to test this scenario. | | R03-late | This value is primarily used to simulate funds failing to the destination bank post-settlement. The funding source will be automatically removed from Dwolla when this return code is triggered. Note: You must click “Process bank transfers” twice in order to test this scenario. | Our concept article has more information on bank transfer failures, and a list of common return codes and actions. #### Example of using a sentinel value for testing bank transfer failures This example assumes that a funding source has already been attached to an account. Once the funding source `name` has been updated to reflect the ACH failure scenario you want to test, then you can [initiate a transfer](https://developers.dwolla.com/api-reference/transfers/initiate) to or from that funding source via the API. ```bash HTTP theme={"dark"} POST https://api-sandbox.dwolla.com/funding-sources/692486f8-29f6-4516-a6a5-c69fd2ce854c Accept: application/vnd.dwolla.v1.hal+json Content-Type: application/vnd.dwolla.v1.hal+json Authorization: Bearer pBA9fVDBEyYZCEsLf/wKehyh1RTpzjUj5KzIRfDi0wKTii7DqY ... { "name": "R03" } ``` ```ruby RUBY theme={"dark"} funding_source_url = 'https://api-sandbox.dwolla.com/funding-sources/692486f8-29f6-4516-a6a5-c69fd2ce854c' request_body = { "name" => "R03", } # Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby (Recommended) funding_source = app_token.post "#{funding_source_url}", request_body funding_source.name # => "R03" ``` ```php PHP theme={"dark"} /** * No example for this language yet. Coming soon. **/ ``` ```python PYTHON theme={"dark"} funding_source_url = 'https://api-sandbox.dwolla.com/funding-sources/692486f8-29f6-4516-a6a5-c69fd2ce854c' request_body = { 'name': 'R03' } # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python (Recommended) funding_source = app_token.post(funding_source_url, request_body) funding_source.body['name'] # => 'R03' ``` ```javascript JAVASCRIPT theme={"dark"} var fundingSourceUrl = 'https://api-sandbox.dwolla.com/funding-sources/692486f8-29f6-4516-a6a5-c69fd2ce854c'; var requestBody = { name: 'R03', }; dwolla.post(fundingSourceUrl, requestBody).then((res) => res.body.name); // => "R03" ``` # Testing Virtual Account Numbers Virtual Account Numbers (VANs) allow you to simulate external ACH transactions flowing into and out of Dwolla balances. The Sandbox environment provides specialized endpoints for testing VAN functionality without real money movement. To request Sandbox access to test VANs, please contact [support@dwolla.com](mailto:support@dwolla.com). ### Simulate VAN transfers External transfers can be simulated in the Dwolla Sandbox by using the `sandbox-simulations` endpoint with a request body that includes a `type` field set to `virtual` and a `transfers` field with a list of transfers to process. Up to 10 transfers at a time can be included in one call to the sandbox simulations endpoint. Transfers will be created and processed immediately. #### VAN transfer simulation request ```bash theme={"dark"} POST https://api-sandbox.dwolla.com/sandbox-simulations Accept: application/vnd.dwolla.v1.hal+json Content-Type: application/vnd.dwolla.v1.hal+json Authorization: Bearer {Your access token} { "type": "virtual", "transfers": [ { "_link": { "destination": { "href": "https://api-sandbox.dwolla.com/funding-sources/5880e310-675a-4ce3-87d9-a475cc565e09" } }, "amount": { "currency": "USD", "value": "1.11" } } ] } ... HTTP/1.1 202 Accepted ``` ### Test VAN transfer failures Similar to [testing ACH bank transfer failures](#test-bank-transfer-failures), you can test a transfer failure with a virtual account number by specifying an "R" code in the `name` parameter when creating a VAN (e.g. "R01"). The return code must be at the beginning of the VAN funding source's name. **Note:** the name is not case sensitive. #### List of codes for testing VAN transfer failures | Code | Description | When is the failure triggered? | | ---- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------ | | R01 | Insufficient Funds: This value is used to simulate funds failing from the source bank account (ACH debit). | During processing of pending transactions. | | R02 | No Account/Unable to Locate Account: This value is primarily used to simulate funds failing to the destination bank account (credit). The funding source will be automatically removed from Dwolla when this return code is triggered. | During processing of pending transactions. | # Transfer Failures Source: https://developers.dwolla.com/docs/transfer-failures Familiarize yourself with the unhappy path of transfers and build resilient practices to mitigate ACH returns. Learn about returns, why they happen and recommended actions your application can take based on the return code. ## Overview There are several reasons bank transfers can fail, a few of which are outlined below. When a transfer fails it is usually a result of an ACH failure which is assigned an ACH return code after being rejected from the financial institution. A few common failure examples include: * **Insufficient Funds (R01):** Pending transfers can fail due to insufficient funds from the source bank account. * **No Account/Unable to Locate Account (R03):** The recipient of a transfer has closed their bank account or has incorrectly entered their bank account/routing number when attaching their funding source. * **Customer Advises Not Authorized (R10):** The owner of a bank account has told their bank that this transfer was unauthorized. ### Retrieving the transfer You can check the status of a transfer at any time by [retrieving the transfer via the API](/docs/api-reference/transfers/retrieve-a-transfer). When a bank transfer is unable to be completed, its status will be updated to `failed`. The response from the API when retrieving the transfer should contain a `"failure"` link that your application will follow to [retrieve the transfer failure reason](/docs/api-reference/transfers/retrieve-a-transfer-failure-reason) in the next step. ##### Example failure link ```json theme={"dark"} "failure": { "href": "https://api-sandbox.dwolla.com/transfers/a1e58cd8-11ec-e811-8111-bec1f96924ed/failure", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "failure" } ``` ### Retrieving the reason for a failed bank transfer If your application is subscribed to webhooks, you'll receive either the `transfer_failed` event if the transfer belongs to a Dwolla account or the `customer_transfer_failed`/`customer_bank_transfer_failed`(*Verified Customer only*) event if the transfer belongs to an API Customer. The event contains a links to the associated account as well as the transfer resource. When retrieving the failed bank transfer reason, the response will contain information on the ACH return code and description, as well as `_links` to the Funding Source and Customer that triggered the bank transfer failure. ##### Request and response ```bash HTTP theme={"dark"} GET https://api-sandbox.dwolla.com/transfers/8997ebed-69be-e611-80ea-0aa34a9b2388/failure Accept: application/vnd.dwolla.v1.hal+json Authorization: Bearer pBA9fVDBEyYZCEsLf/wKehyh1RTpzjUj5KzIRfDi0wKTii7DqY { "_links": { "self": { "href": "https://api.dwolla.com/transfers/8997ebed-69be-e611-80ea-0aa34a9b2388/failure", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "failure" }, "failed-funding-source": { "href": "https://api.dwolla.com/funding-sources/285ea6f4-c45d-4e15-ad33-21f51461f437", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "funding-source" }, "customer": { "href": "https://api.dwolla.com/customers/be2d2322-fdee-4361-8722-4289f5601604", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "customer" } }, "code": "R03", "description": "No Account/Unable to Locate Account", "explanation": "The account number does not correspond to the individual identified in the entry or a valid account." } ``` ```ruby retrieve_transfer_failure.rb theme={"dark"} transfer_url = 'https://api-sandbox.dwolla.com/transfers/8997ebed-69be-e611-80ea-0aa34a9b2388' # Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby failure = app_token.get "#{transfer_url}/failure" failure.code # => "R01" ``` ```php retrieve_transfer_failure.php theme={"dark"} failureById($transfer); print($failureReason->code); # => "R01" ?> ``` ```python retrieve_transfer_failure.py theme={"dark"} transfer_url = 'https://api-sandbox.dwolla.com/transfers/8997ebed-69be-e611-80ea-0aa34a9b2388' # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python (Recommended) failure = app_token.get('%s/failure' % transfer_url) failure.body['code'] # => 'R01' ``` ```javascript retrieveTransferFailure.js theme={"dark"} var transferUrl = "https://api-sandbox.dwolla.com/transfers/8997ebed-69be-e611-80ea-0aa34a9b2388"; dwolla.get(`${transferUrl}/failure`).then((res) => res.body.code); // => 'R01' ``` ### What occurs in the Dwolla system when a bank transfer fails? When a bank transfer failure occurs there is a subset of systematic actions Dwolla may take on the Customer and/or the funding source based on the ACH return code. It is recommended to have an active [webhook subscription](/docs/api-reference/webhook-subscriptions/create-a-webhook-subscription), which is used to listen for events relating to any Customer or funding source state change. Please refer to the table below to understand the systematic actions that Dwolla may take for Customer and funding source resources, as well as the events that are created. #### Systematic actions taken against the Customer | Customer action | Description | Webhook Event | | --------------- | -------------------------------------------------------------------------- | ---------------------- | | None | No action taken against the Customer account as result of transfer failure | N/A | | Deactivated | Customer account has been deactivated | `customer_deactivated` | | Suspended | Customer account has been suspended (R10 only) | `customer_suspended` | #### Systematic actions taken against the bank funding source | Funding Source action | Description | Webhook Event | | --------------------- | -------------------------------------------------------- | ------------------------------------ | | None | No action taken against the Customer bank funding source | N/A | | Unverified | A Customer's bank has been unverified, but not removed | `customer_funding_source_unverified` | | Removed | A Customer's bank has been removed | `customer_funding_source_removed` | ### Why does Dwolla automatically take these actions? Being able to catch and take action errors can be beneficial on many levels. For instance, if your Customer initiates a transaction which fails with an R10 (Customer Advises Not Authorized) return code, Dwolla will automatically put the suspected Customer in a `suspended` status, thereby not allowing them to initiate or receive more transfers. This gives you the ability investigate the Customer to determine if they are a valid party without worrying about them sending funds. Other return codes may result in the Customer being `deactivated`, or may only affect bank funding sources in which Dwolla may automatically unverify or remove a bank in response to various return codes. ### List of possible return codes, descriptions, and actions Below are tables of the most common return codes we see involved in transactions, organized by category. For a full list of return codes, you can check out the ACH return code [list on our blog](https://www.dwolla.com/ach/ach-return-codes/). As a best practice, we recommend handling any systematic actions that trigger webhooks as a result of a transfer failure rather than relying on the specific actions as referenced below. We do not recommend building a workflow around each individual return code in the tables below. These tables are solely meant to be a reference for you to be aware of actions Dwolla may take on common transfer failures. #### Administrative Returns Administrative returns occur when there's an issue with the account itself—such as insufficient funds, a closed account, or invalid account information. These are typically returned within **2 banking days** and generally don't indicate fraudulent activity. | Code | Reason & Description | Return Time Frame | Triggered Action(s) | | ---- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | R01 | **Insufficient Funds**
Available balance is not sufficient to cover the dollar value of the debit entry. | 2 banking days | None | | R02 | **Bank Account Closed**
Previously active account has been closed. | 2 banking days |
  • [Funding Source Unverified](/docs/webhook-events#param-customer-funding-source-unverified)
  • [Funding Source Removed](/docs/webhook-events#param-customer-funding-source-removed)
| | R03 | **No Account/Unable to Locate Account**
Account number structure is valid, but does not match individual identified in entry or is not an open account. | 2 banking days |
  • [Funding Source Unverified](/docs/webhook-events#param-customer-funding-source-unverified)
  • [Funding Source Removed](/docs/webhook-events#param-customer-funding-source-removed)
| | R04 | **Invalid Bank Account Number Structure**
Account number structure is not valid. | 2 banking days |
  • [Funding Source Unverified](/docs/webhook-events#param-customer-funding-source-unverified)
  • [Funding Source Removed](/docs/webhook-events#param-customer-funding-source-removed)
| | R08 | **Payment Stopped**
The Receiver has requested the stop payment of a specific ACH debit entry. | 2 banking days |
  • [Funding Source Unverified](/docs/webhook-events#param-customer-funding-source-unverified)
  • [Funding Source Removed](/docs/webhook-events#param-customer-funding-source-removed)
| | R09 | **Uncollected Funds**
Sufficient balance exists, but value of uncollected items brings available balance below amount of debit entry. | 2 banking days | None | | R12 | **Branch Sold to Another DFI**
A financial institution received an entry to an account that was sold to another FI (typically due to a merger). | 2 banking days |
  • [Funding Source Removed](/docs/webhook-events#param-customer-funding-source-removed)
| | R13 | **Invalid ACH Routing Number**
Entry contains a receiving DFI identification or gateway identification that is not a valid ACH routing number. | Next file delivery |
  • [Funding Source Unverified](/docs/webhook-events#param-customer-funding-source-unverified)
  • [Funding Source Removed](/docs/webhook-events#param-customer-funding-source-removed)
  • Funding Source Blocklisted
| | R14 | **Representative Payee Deceased or Unable to Continue in That Capacity**
The representative payee is a person either deceased or no longer able to continue in original capacity (i.e. legally incapacitated adults or minors), while the beneficiary is not deceased. | 2 banking days |
  • [Customer Deactivated](/docs/webhook-events#param-customer-deactivated)
  • [Funding Source Removed](/docs/webhook-events#param-customer-funding-source-removed)
| | R15 | **Beneficiary or Account Holder Deceased**
(1) The beneficiary is deceased, or (2) The account holder is deceased. | 2 banking days |
  • [Customer Deactivated](/docs/webhook-events#param-customer-deactivated)
  • [Funding Source Unverified](/docs/webhook-events#param-customer-funding-source-unverified)
  • [Funding Source Removed](/docs/webhook-events#param-customer-funding-source-removed)
  • Funding Source Blocklisted
| | R16 | **Account Frozen/Entry Returned per OFAC Instruction**
(1) Access to the account is restricted due to specific action taken by the RDFI or by legal action; or (2) OFAC has instructed the RDFI to return the entry. | 2 banking days |
  • [Customer Deactivated](/docs/webhook-events#param-customer-deactivated)
  • [Funding Source Unverified](/docs/webhook-events#param-customer-funding-source-unverified)
  • [Funding Source Removed](/docs/webhook-events#param-customer-funding-source-removed)
  • Funding Source Blocklisted
| | R17 | **File Record Edit Criteria/Entry with Invalid Account Number Initiated Under Questionable Circumstances**
(1) Field(s) cannot be processed by RDFI; or (2) The entry contains an invalid DFI Account Number (account closed/no account/unable to locate account/invalid account number) and is believed by the RDFI to have been initiated under questionable circumstances. | 2 banking days |
  • [Customer Deactivated](/docs/webhook-events#param-customer-deactivated)
  • [Funding Source Unverified](/docs/webhook-events#param-customer-funding-source-unverified)
  • [Funding Source Removed](/docs/webhook-events#param-customer-funding-source-removed)
  • Funding Source Blocklisted
| | R20 | **Non-Transaction Account**
ACH entry to a non-transaction account (typically due to account holder exceeding their monthly withdrawal threshold under Regulation D). | 2 banking days |
  • [Funding Source Removed](/docs/webhook-events#param-customer-funding-source-removed)
  • Funding Source Blocklisted
| | R22 | **Invalid Individual ID Number**
The Receiver has indicated to the RDFI that the number with which the Originator identified is not correct. | 2 banking days |
  • [Funding Source Unverified](/docs/webhook-events#param-customer-funding-source-unverified)
| | R23 | **Credit Entry Refused by Receiver**
Any credit entry that is refused by the Receiver may be returned by the RDFI. | 2 banking days |
  • [Funding Source Unverified](/docs/webhook-events#param-customer-funding-source-unverified)
  • [Funding Source Removed](/docs/webhook-events#param-customer-funding-source-removed)
| #### Unauthorized Returns Unauthorized returns occur when the account holder claims they did not authorize the transaction. These are more serious and can be returned up to **60 calendar days** after the settlement date. Due to the nature of these returns, Dwolla takes protective actions on the Customer account. Unauthorized returns may indicate potential fraud or disputes. Dwolla automatically suspends or deactivates the Customer to protect against further unauthorized activity. | Code | Reason & Description | Return Time Frame | Triggered Action(s) | | ---- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | R05 | **Unauthorized Debit to Consumer Account Using Corporate SEC Code**
A CCD or CTX debit entry was transmitted to a consumer account and was not authorized by the Receiver. Written Statement is required. | 60 calendar days |
  • [Customer Deactivated](/docs/webhook-events#param-customer-deactivated)
  • [Funding Source Unverified](/docs/webhook-events#param-customer-funding-source-unverified)
  • [Funding Source Removed](/docs/webhook-events#param-customer-funding-source-removed)
  • Funding Source Blocklisted
| | R07 | **Authorization Revoked by Customer**
Consumer who previously authorized entries has revoked authorization with the Originator. Written Statement is required. | 60 calendar days |
  • [Customer Deactivated](/docs/webhook-events#param-customer-deactivated)
  • [Funding Source Unverified](/docs/webhook-events#param-customer-funding-source-unverified)
  • [Funding Source Removed](/docs/webhook-events#param-customer-funding-source-removed)
| | R10 | **Customer Advises Originator is Not Known to Receiver and/or Originator is Not Authorized by Receiver to Debit Receiver's Account**
Receiver has no relationship with the Originator or has not authorized the Originator to debit the account. Written Statement is required. | 60 calendar days |
  • [Customer Suspended](/docs/webhook-events#param-customer-suspended)
  • [Funding Source Unverified](/docs/webhook-events#param-customer-funding-source-unverified)
  • [Funding Source Removed](/docs/webhook-events#param-customer-funding-source-removed)
  • Funding Source Blocklisted
| | R11 | **Customer Advises Entry Not in Accordance with the Terms of the Authorization**
The debit entry was inaccurate or improperly initiated. Other reasons include source document was ineligible, notice was not provided to the receiver or amount was inaccurately obtained. Written statement is required. | 60 calendar days |
  • [Customer Deactivated](/docs/webhook-events#param-customer-deactivated)
  • [Funding Source Unverified](/docs/webhook-events#param-customer-funding-source-unverified)
  • [Funding Source Removed](/docs/webhook-events#param-customer-funding-source-removed)
| | R29 | **Corporate Customer Advises Not Authorized**
The RDFI has been notified by the Receiver (non-consumer) that a specific entry has not been authorized by the Receiver. | 2 banking days |
  • [Customer Deactivated](/docs/webhook-events#param-customer-deactivated)
  • [Funding Source Unverified](/docs/webhook-events#param-customer-funding-source-unverified)
  • [Funding Source Removed](/docs/webhook-events#param-customer-funding-source-removed)
  • Funding Source Blocklisted
| #### General Returns These returns are initiated by the originating or receiving financial institution, or relate to specific transaction types like source documents or permissible returns. Return time frames vary. | Code | Reason & Description | Return Time Frame | Triggered Action(s) | | ---- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | R06 | **Returned per ODFI's Request**
The ODFI has requested that the RDFI return an erroneous entry, or a credit entry originated without the authorization of the Originator. | Varies |
  • [Customer Deactivated](/docs/webhook-events#param-customer-deactivated)
  • [Funding Source Unverified](/docs/webhook-events#param-customer-funding-source-unverified)
  • [Funding Source Removed](/docs/webhook-events#param-customer-funding-source-removed)
  • Funding Source Blocklisted
| | R31 | **Permissible Return (CCD and CTX only)**
The RDFI may return a CCD or CTX entry that the ODFI agrees to accept. | Varies |
  • [Customer Deactivated](/docs/webhook-events#param-customer-deactivated)
  • [Funding Source Unverified](/docs/webhook-events#param-customer-funding-source-unverified)
  • [Funding Source Removed](/docs/webhook-events#param-customer-funding-source-removed)
  • Funding Source Blocklisted
| | R37 | **Source Document Presented for Payment**
Source document to which an ARC, BOC, or POP entry relates has been presented for payment. Written Statement is required. | 60 calendar days |
  • [Funding Source Removed](/docs/webhook-events#param-customer-funding-source-removed)
| | R38 | **Stop Payment on Source Document**
The RDFI indicates a stop payment order has been placed on the source document the ARC or BOC entry relates to. | 60 calendar days |
  • [Customer Deactivated](/docs/webhook-events#param-customer-deactivated)
  • [Funding Source Unverified](/docs/webhook-events#param-customer-funding-source-unverified)
  • [Funding Source Removed](/docs/webhook-events#param-customer-funding-source-removed)
  • Funding Source Blocklisted
| | R51 | **Item Related to RCK Entry is Ineligible or RCK Entry is Improper**
The RDFI notifies that the RCK entry is considered ineligible or improper. Written Statement is required. | 60 calendar days |
  • [Customer Deactivated](/docs/webhook-events#param-customer-deactivated)
  • [Funding Source Unverified](/docs/webhook-events#param-customer-funding-source-unverified)
  • [Funding Source Removed](/docs/webhook-events#param-customer-funding-source-removed)
  • Funding Source Blocklisted
| # Transfer Lifecycle Source: https://developers.dwolla.com/docs/transfer-lifecycle Learn about the complete lifecycle of a transfer in Dwolla and find out what webhooks you can expect to receive during each step. ## Overview To initiate a transfer, you can either make an API request or use the Dwolla Dashboard. You will need to specify the source and destination funding sources, which can be either a [bank account](/docs/bank-funding-source) or a [Dwolla Wallet](/docs/balance-funding-source). It's important to note that all account-to-account transfers are two-sided. When a transfer request is received, it is broken down into "hops" or "legs," which describe each step of the process, such as pulling funds into a Dwolla Wallet and pushing funds out of it. ## Transfer Statuses As outlined in our [transfer resource](/docs/api-reference/transfers), transfers can have the following statuses: `pending`, `processed`, `cancelled`, or `failed`. * `pending` - A pending transfer hasn't been sent to the payment network or has been sent but not processed. This means that it may still be cancellable or may result in a transfer failure. * `processed` - The meaning of a "processed" status varies based on the transfer destination. If it's going to a Dwolla Wallet, the funds have cleared successfully. If it's going to a linked bank account, enough time has passed for the funds to clear into that account. * `cancelled` - A transfer can be cancelled in two ways: either Dwolla cancels it systematically, or your application sends an API request. If a funding source is removed during the transfer's journey to that bank account, Dwolla will cancel the transfer. * `failed` - A failed status is associated with an ACH network return, meaning that Dwolla received an ACH return code from the RDFI (Receiving Depository Financial Institution). You can fetch the failure reason via an additional API request. [Learn more on transfer failures](/docs/transfer-failures). ## Flow of Funds It's crucial to understand how funds flow into and out of the Dwolla Network because all transfers require at least one user (either the sender or receiver) to hold a balance in a Dwolla Wallet. In other words, **all transfers flow through a Dwolla Wallet, either explicitly or implicitly**. * With an explicit flow of funds, the source of the transfer is a bank account (funding source) and the destination is a Dwolla Wallet, or vice versa. In this scenario, the funds are explicitly pushed into or pulled out of a Dwolla Wallet when the transfer is initiated. * With an implicit flow of funds, the source and destination of the transfer are both bank accounts (funding sources). In this scenario, the funds first enter the Dwolla Network by flowing through a Dwolla Wallet of either the sender or receiver (or both, depending on the customer type), before ultimately exiting to the destination bank account. ## Tracking Transfers As mentioned previously, all account-to-account payments are two-sided. This can make it difficult to track the status of a transfer at any given time. To address this, Dwolla supports two ways to track transfers throughout their lifecycle: * **Correlation ID**: This value is supplied by your application when the transfer is created, and you can use it to track the transfer through the Dwolla API. * **Response JSON `_links` property**: This property is automatically populated by Dwolla in the response JSON for a transfer. It contains links to other resources that you can use to track the transfer, such as the transfer's status and the transfer's history. ### Correlation ID Many Dwolla resources allow a correlation ID—[Customers](/docs/api-reference/customers), [Mass Payments](/docs/api-reference/mass-payments), and [Transfers](/docs/api-reference/transfers). Through correlation IDs your application can send an ID of your choosing (generally a pseudo-random alphanumeric string) that is attached to the resource once it’s created in the Dwolla system. In particular, transfers are easily traceable in our API, as our transfers endpoint allows listing and searching based on a `correlationId` value. #### Create Transfer with Correlation ID ```javascript createTransfer.js theme={"dark"} // Using dwolla-v2 - https://github.com/Dwolla/dwolla-v2-node const requestBody = { _links: { source: { href: "https://api.dwolla.com/funding-sources/707177c3-bf15-4e7e-b37c-55c3898d9bf4", }, destination: { href: "https://api.dwolla.com/funding-sources/ab443d36-3757-44c1-a1b4-29727fb3111c", }, }, amount: { currency: "USD", value: "10.00", }, correlationId: "8a2cdc8d-629d-4a24-98ac-40b735229fe2", }; dwolla .post("transfers", requestBody) .then((res) => res.headers.get("Location")); // => 'https://api.dwolla.com/transfers/74c9129b-d14a-e511-80da-0aa34a9b2388' ``` ```php create_transfer.php theme={"dark"} create([ '_links' => [ 'source' => [ 'href' => 'https://api.dwolla.com/funding-sources/707177c3-bf15-4e7e-b37c-55c3898d9bf4', ], 'destination' => [ 'href' => 'https://api.dwolla.com/funding-sources/ab443d36-3757-44c1-a1b4-29727fb3111c' ] ], 'amount' => [ 'currency' => 'USD', 'value' => '10.00' ], 'correlationId' => '8a2cdc8d-629d-4a24-98ac-40b735229fe2' ]); $transfer; # => "https://api.dwolla.com/transfers/74c9129b-d14a-e511-80da-0aa34a9b2388" ``` ```ruby create_transfer.rb theme={"dark"} # Using dwolla_v2 - https://github.com/Dwolla/dwolla-v2-ruby request_body = { :_links => { :source => { :href => "https://api.dwolla.com/funding-sources/707177c3-bf15-4e7e-b37c-55c3898d9bf4" }, :destination => { :href => "https://api.dwolla.com/funding-sources/ab443d36-3757-44c1-a1b4-29727fb3111c" } }, :amount => { :currency => "USD", :value => "10.00" }, :correlationId => "8a2cdc8d-629d-4a24-98ac-40b735229fe2" } transfer = app_token.post "transfers", request_body transfer.response_headers[:location] # => "https://api.dwolla.com/transfers/74c9129b-d14a-e511-80da-0aa34a9b2388" ``` ```python create_transfer.py theme={"dark"} # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python request_body = { '_links': { 'source': { 'href': 'https://api.dwolla.com/funding-sources/707177c3-bf15-4e7e-b37c-55c3898d9bf4' }, 'destination': { 'href': 'https://api.dwolla.com/funding-sources/ab443d36-3757-44c1-a1b4-29727fb3111c' } }, 'amount': { 'currency': 'USD', 'value': '10.00' }, 'correlationId': '8a2cdc8d-629d-4a24-98ac-40b735229fe2' } transfer = app_token.post('transfers', request_body) transfer.headers['location'] # => 'https://api.dwolla.com/transfers/74c9129b-d14a-e511-80da-0aa34a9b2388' ``` ```bash HTTP theme={"dark"} POST https://api.dwolla.com/transfers Accept: application/vnd.dwolla.v1.hal+json Content-Type: application/vnd.dwolla.v1.hal+json Authorization: Bearer pBA9fVDBEyYZCEsLf/wKehyh1RTpzjUj5KzIRfDi0wKTii7DqY Idempotency-Key: 19051a62-3403-11e6-ac61-9e71128cae77 { "_links": { "source": { "href": "https://api.dwolla.com/funding-sources/707177c3-bf15-4e7e-b37c-55c3898d9bf4" }, "destination": { "href": "https://api.dwolla.com/funding-sources/ab443d36-3757-44c1-a1b4-29727fb3111c" } }, "amount": { "currency": "USD", "value": "10.00" }, "correlationId": "8a2cdc8d-629d-4a24-98ac-40b735229fe2" } ... HTTP/1.1 201 Created Location: https://api.dwolla.com/transfers/74c9129b-d14a-e511-80da-0aa34a9b2388 ``` #### List (Search) Customer Transfers via Correlation ID ```javascript searchTransfer.js theme={"dark"} // Using dwolla-v2 - https://github.com/Dwolla/dwolla-v2-node const customerUrl = "https://api.dwolla.com/customers/33e56307-6754-41cb-81e2-23a7f1072295"; dwolla .get( `${customerUrl}/transfers?correlationId=8a2cdc8d-629d-4a24-98ac-40b735229fe2` ) .then((res) => res.body.\_embedded["transfers"][0].status); // => "pending" ``` ```php search_transfer.php theme={"dark"} getCustomerTransfers($customerUrl); $transfers->_embedded->{'transfers'}[0]->status; # => "pending" ``` ```ruby search_transfer.rb theme={"dark"} # Using dwolla_v2 - https://github.com/Dwolla/dwolla-v2-ruby customer_url = 'https://api.dwolla.com/customers/33e56307-6754-41cb-81e2-23a7f1072295' transfers = app_token.get "#{customer_url}/transfers?correlationId=8a2cdc8d-629d-4a24-98ac-40b735229fe2" transfers._embedded['transfers'][0].status # => "pending" ``` ```python search_transfer.py theme={"dark"} # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python customer_url = 'https://api.dwolla.com/customers/33e56307-6754-41cb-81e2-23a7f1072295' transfers = app_token.get('%s/transfers?correlationId=%s' % customer_url % '8a2cdc8d-629d-4a24-98ac-40b735229fe2') transfers.body['_embedded']['transfers'][0]['status'] # => 'pending' ``` ```bash HTTP theme={"dark"} GET https://api.dwolla.com/customers/33e56307-6754-41cb-81e2-23a7f1072295/transfers?correlationId=8a2cdc8d-629d-4a24-98ac-40b735229fe2 Accept: application/vnd.dwolla.v1.hal+json Authorization: Bearer pBA9fVDBEyYZCEsLf/wKehyh1RTpzjUj5KzIRfDi0wKTii7DqY ... { "_links": { "self": { "href": "https://api.dwolla.com/customers/33e56307-6754-41cb-81e2-23a7f1072295/transfers" }, "first": { "href": "https://api.dwolla.com/customers/33e56307-6754-41cb-81e2-23a7f1072295/transfers?&limit=25&offset=0" }, "last": { "href": "https://api.dwolla.com/customers/33e56307-6754-41cb-81e2-23a7f1072295/transfers?&limit=25&offset=0" } }, "_embedded": { "transfers": [ { "_links": { "self": { "href": "https://api.dwolla.com/transfers/74c9129b-d14a-e511-80da-0aa34a9b2388" }, "source": { "href": "https://api.dwolla.com/customers/39e21228-5958-4c4f-96fe-48a4bf11332d" }, "source-funding-source": { "href": "https://api.dwolla.com/funding-sources/707177c3-bf15-4e7e-b37c-55c3898d9bf4" }, "destination": { "href": "https://api.dwolla.com/customers/33e56307-6754-41cb-81e2-23a7f1072295" }, "destination-funding-source": { "href": "https://api.dwolla.com/funding-sources/ab443d36-3757-44c1-a1b4-29727fb3111c" } }, "id": "74c9129b-d14a-e511-80da-0aa34a9b2388", "status": "pending", "amount": { "value": "10.00", "currency": "USD" }, "created": "2018-11-29 21:00:59 UTC", "correlationId": "8a2cdc8d-629d-4a24-98ac-40b735229fe2" } ] }, "total": 1 } ``` ### Response JSON Links In addition to using a correlation ID, Dwolla automatically appends two resource `_links` properties in all transfers: funding-transfer and funded-transfer, both of which can be used to traverse programmatically a single transfer chain. * `funding-transfer`: A resource link that points to the previous transfer in the overall chain (if applicable). It identifies the previous transfer that, once processed, funded the current transfer. * `funded-transfer`: A resource link that points to the next transfer in the overall chain (if applicable). It identifies the next transfer that the current transfer will fund, once processed. **Example** Consider a successful account-to-account transfer where a Verified Customer (VCR-1) sends money from their bank account to the bank account of another Verified Customer (VCR-2). The “legs” of the overall transfer lifecycle would look like this: Verified Customer (VCR-1) Bank to Verified Customer (VCR-2) Bank | Name | Transfer ID | Resource Links | | ----------------------------- | ------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------- | | VCR-1 Bank → VCR-1 Balance | a379c863-77f2-4248-8b37-486bc10b3817 | `funding-transfer`: N/A

`funded-transfer`: 5dc959dd-29ec-436f-8cdc-dd7269fd4e7c | | VCR-1 Balance → VCR-2 Balance | 5dc959dd-29ec-436f-8cdc-dd7269fd4e7c | `funding-transfer`: a379c863-77f2-4248-8b37-486bc10b3817

`funded-transfer`: a91f21c1-d9bb-41a6-8aab-981e763bd325 | | VCR-2 Balance → VCR-2 Bank | a91f21c1-d9bb-41a6-8aab-981e763bd325 | `funding-transfer`: 5dc959dd-29ec-436f-8cdc-dd7269fd4e7c

`funded-transfer`: N/A | * The `funding-transfer` link for the first transfer is N/A because there is no previous transfer in the chain. * The `funded-transfer` link for the first transfer points to the second transfer, since the funds from the first transfer funded the second transfer. * The `funding-transfer` link for the second transfer points to the first transfer, which is the transfer that funded it. * The `funded-transfer` link for the third transfer is N/A because there are no further transfers in the chain. ## Interactive Transfer Lifecycle # Transfer Money Between Users Source: https://developers.dwolla.com/docs/transfer-money-between-users Facilitate ACH transfers between two distinct parties, e.g. for marketplace applications that connect buyers with sellers for bank to bank payments. ## Overview The most common scenario for this guide is to facilitate marketplace or peer-to-peer transfers between your customers. Funds Flow Facilitate Transfers In this guide, we'll cover the key points of transferring money: Create a Verified Customer in your application to act as the recipient of the funds. Create an Unverified Customer who will initiate and send the funds. Link and verify a bank or credit union account to the sender's profile to enable sending funds. Link a bank or credit union account to the recipient's profile (verification not required for receiving funds). Initiate a transfer from the sender's verified funding source to the recipient's funding source using the Dwolla API. Certain use cases involving transfers between users, such as peer-to-peer payments, may require additional review to ensure alignment with Dwolla's risk policies. Before you start building, contact our Sales team to confirm that your use case is supported. ## Before you begin You need to have a [Sandbox account](/docs/testing) already set up. ## Verified and Unverified Customers Here are some rules to keep in mind: 1. With a transfer of money, at least one party must complete the [identity verification process](https://www.dwolla.com/updates/guide-customer-identification-program-payments-api/), either the sender or the receiver. It's your decision about which party completes this process, based on your business model, and you may want to have both parties complete the identity verification process. 2. The sender must have a verified funding source. Unverified funding sources can only receive money, not send. In this guide, we'll create two Customers: one to represent a seller and one to represent a buyer. In this scenario, the seller, Jane Merchant, is a `Verified Customer` with an unverified funding source. The buyer, Joe Buyer, is an `Unverified Customer` with a verified funding source. This is a suggested approach and there are other ways you can implement your marketplace transfers. For instance, both the sender and the receiver (or buyer and seller) could be Verified Customers, and both could have verified funding sources. Or, you could have the sender undergo identity verification but not the recipient. Looking to learn more about each Customer type and how it relates to your funds flow? Take a look at our [Customer types](/docs/customer-types) article for more information. # Step 1 - Create a Verified Customer First, we'll create a `Verified Customer` for Jane Merchant. There are two types of Verified Customers you can create; [Personal Verified Customers](/docs/personal-verified-customer) and [Business Verified Customers.](/docs/business-verified-customer) In this example, we use [Business Verified Customers](/docs/business-verified-customer) (sole proprietorship) to represent the merchant who will be receiving funds. ```bash HTTP theme={"dark"} POST https://api-sandbox.dwolla.com/customers Content-Type: application/vnd.dwolla.v1.hal+json Accept: application/vnd.dwolla.v1.hal+json Authorization: Bearer 0Sn0W6kzNic+oWhDbQcVSKLRUpGjIdl/YyrHqrDDoRnQwE7Q { "firstName": "Jane", "lastName": "Merchant", "email": "solePropBusiness@email.com", "ipAddress": "143.156.7.8", "type": "business", "dateOfBirth": "1980-01-31", "ssn": "6789", "address1": "99-99 33rd St", "city": "Some City", "state": "NY", "postalCode": "11101", "businessClassification": "9ed3f670-7d6f-11e3-b1ce-5404a6144203", "businessType": "soleProprietorship", "businessName":"Jane Corp", "ein":"00-0000000" } HTTP/1.1 201 Created Location: https://api-sandbox.dwolla.com/customers/62c3aa1b-3a1b-46d0-ae90-17304d60c3d5 ``` ```php create_verified_customer.php theme={"dark"} create([ 'firstName' => 'Jane', 'lastName' => 'Merchant', 'email' => 'solePropBusiness@email.com', 'ipAddress' => '143.156.7.8', 'type' => 'business', 'dateOfBirth' => '1980-01-31', 'ssn' => '6789', 'address1' => '99-99 33rd St', 'city' => 'Some City', 'state' => 'NY', 'postalCode' => '11101', 'businessClassification' => '9ed3f670-7d6f-11e3-b1ce-5404a6144203', 'businessType' => 'soleProprietorship', 'businessName' => 'Jane Corp', 'ein' => '00-0000000']); ?> ``` ```ruby create_verified_customer.rb theme={"dark"} # Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby (Recommended) request_body = { :firstName => 'Jane', :lastName => 'Merchant', :email => 'solePropBusiness@email.com', :ipAddress => '143.156.7.8', :type => 'business', :dateOfBirth => '1980-01-31', :ssn => '6789', :address1 => '99-99 33rd St', :city => 'Some City', :state => 'NY', :postalCode => '11101', :businessClassification => '9ed3f670-7d6f-11e3-b1ce-5404a6144203', :businessType => 'soleProprietorship', :businessName => 'Jane Corp', :ein => '00-0000000' } customer = app_token.post "customers", request_body customer.response_headers[:location] # => "https://api-sandbox.dwolla.com/customers/62c3aa1b-3a1b-46d0-ae90-17304d60c3d5" ``` ```python create_verified_customer.py theme={"dark"} # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python (Recommended) request_body = { 'firstName': 'Jane', 'lastName': 'Merchant', 'email': 'solePropBusiness@email.com', 'ipAddress': '143.156.7.8', 'type': 'business', 'dateOfBirth': '1980-01-31', 'ssn': '6789', 'address1': '99-99 33rd St', 'city': 'Some City', 'state': 'NY', 'postalCode': '11101', 'businessClassification': '9ed3f670-7d6f-11e3-b1ce-5404a6144203', 'businessType': 'soleProprietorship', 'businessName': 'Jane Corp', 'ein': '00-0000000' } customer = app_token.post('customers', request_body) customer.headers['location'] # => 'https://api-sandbox.dwolla.com/customers/62c3aa1b-3a1b-46d0-ae90-17304d60c3d5' ``` ```javascript create_verified_customer.js theme={"dark"} var requestBody = { firstName: "Jane", lastName: "Merchant", email: "solePropBusiness@email.com", ipAddress: "143.156.7.8", type: "business", dateOfBirth: "1980-01-31", ssn: "6789", address1: "99-99 33rd St", city: "Some City", state: "NY", postalCode: "11101", businessClassification: "9ed3f670-7d6f-11e3-b1ce-5404a6144203", businessType: "soleProprietorship", businessName: "Jane Corp", ein: "00-0000000", }; dwolla .post("customers", requestBody) .then((res) => res.headers.get("location")); // => 'https://api-sandbox.dwolla.com/customers/62c3aa1b-3a1b-46d0-ae90-17304d60c3d5' ``` When the customer is created, you'll receive the customer URL in the location header. There are various reasons a Verified Customer will result in a status other than verified which you will want to account for after the Customer is created. Reference the Customer verification resource article for more information on handling verification statuses. # Step 2 - Create unverified funding source Next, we'll add Jane Merchant's bank or credit union account as an unverified funding source. Unverified funding sources can only receive funds, not send. The example below shows sample bank information, but you will include actual bank name, routing, and account numbers after prompting your customer for this information within your application. Possible values for `bankAccountType` can be either "checking" or "savings". More detail is available in [API docs](/docs/api-reference/funding-sources/create-customer-funding-source). ```bash HTTP theme={"dark"} POST https://api-sandbox.dwolla.com/customers/AB443D36-3757-44C1-A1B4-29727FB3111C/funding-sources Content-Type: application/vnd.dwolla.v1.hal+json Accept: application/vnd.dwolla.v1.hal+json Authorization: Bearer 0Sn0W6kzNicvoWhDbQcVSKLRUpGjIdlPSEYyrHqrDDoRnQwE7Q { "routingNumber": "222222226", "accountNumber": "123456789", "bankAccountType": "checking", "name": "Jane Merchant" } HTTP/1.1 201 Created Location: https://api-sandbox.dwolla.com/funding-sources/375c6781-2a17-476c-84f7-db7d2f6ffb31 ``` ```ruby create_unverified_funding_source.rb theme={"dark"} customer_url = 'https://api-sandbox.dwolla.com/customers/62c3aa1b-3a1b-46d0-ae90-17304d60c3d5' request_body = { routingNumber: '222222226', accountNumber: '123456789', bankAccountType: 'checking', name: 'Jane Merchant' } # Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby (Recommended) funding_source = app_token.post "#{customer_url}/funding-sources", request_body funding_source.response_headers[:location] # => "https://api-sandbox.dwolla.com/funding-sources/375c6781-2a17-476c-84f7-db7d2f6ffb31" ``` ```javascript create_unverified_funding_source.js theme={"dark"} var customerUrl = "https://api-sandbox.dwolla.com/customers/62c3aa1b-3a1b-46d0-ae90-17304d60c3d5"; var requestBody = { routingNumber: "222222226", accountNumber: "123456789", bankAccountType: "checking", name: "Jane Merchant", }; dwolla .post(`${customerUrl}/funding-sources`, requestBody) .then((res) => res.headers.get("location")); // => 'https://api-sandbox.dwolla.com/funding-sources/375c6781-2a17-476c-84f7-db7d2f6ffb31' ``` ```python create_unverified_funding_source.py theme={"dark"} customer_url = 'https://api-sandbox.dwolla.com/customers/62c3aa1b-3a1b-46d0-ae90-17304d60c3d5' request_body = { 'routingNumber': '222222226', 'accountNumber': '123456789', 'bankAccountType': 'checking', 'name': 'Jane Merchant' } # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python (Recommended) customer = app_token.post('%s/funding-sources' % customer_url, request_body) customer.headers['location'] # => 'https://api-sandbox.dwolla.com/funding-sources/375c6781-2a17-476c-84f7-db7d2f6ffb31' ``` ```php create_unverified_funding_source.php theme={"dark"} createCustomerFundingSource(array ( 'routingNumber' => '222222226', 'accountNumber' => '123456789', 'bankAccountType' => 'checking', 'name' => 'Jane Merchant', ), $customer ); print($new_fs); # => https://api-sandbox.dwolla.com/funding-sources/375c6781-2a17-476c-84f7-db7d2f6ffb31 ?> ``` The created funding source URL is returned in the Location header. # Step 3 - Creating an Unverified Customer Now that we've created a customer for Jane Merchant and associated a funding source, we'll do the same for Joe Buyer, but this time we'll create an `Unverified Customer`, and a verified funding source which is capable of sending money. Provide the user's full name, email address, and IP address to create the Customer. More detail is available in [API docs](https://developers.dwolla.com/api-reference/customers). Provide the IP address of the end user accessing your application as the ipAddress parameter. This enhances Dwolla's ability to detect fraud. ```bash HTTP theme={"dark"} POST https://api-sandbox.dwolla.com/customers Content-Type: application/vnd.dwolla.v1.hal+json Accept: application/vnd.dwolla.v1.hal+json Authorization: Bearer 0Sn0W6kzNicvoWhDbQcVSKLRUpGjIdlPSEYyrHqrDDoRnQwE7Q { "firstName": "Joe", "lastName": "Buyer", "email": "jbuyer@mail.net" } HTTP/1.1 201 Created Location: https://api-sandbox.dwolla.com/customers/247B1BD8-F5A0-4B71-A898-F62F67B8AE1C ``` ```ruby create_unverified_customer.rb theme={"dark"} request_body = { :firstName => 'Joe', :lastName => 'Buyer', :email => 'jbuyer@mail.net', :ipAddress => '99.99.99.99' } # Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby (Recommended) customer = app_token.post "customers", request_body customer.response_headers[:location] # => "https://api-sandbox.dwolla.com/customers/247B1BD8-F5A0-4B71-A898-F62F67B8AE1C" ``` ```javascript create_unverified_customer.js theme={"dark"} var requestBody = { firstName: "Joe", lastName: "Buyer", email: "jbuyer@mail.net", ipAddress: "99.99.99.99", }; dwolla .post("customers", requestBody) .then((res) => res.headers.get("location")); // => 'https://api-sandbox.dwolla.com/customers/247B1BD8-F5A0-4B71-A898-F62F67B8AE1C' ``` ```python create_unverified_customer.py theme={"dark"} request_body = { 'firstName': 'Joe', 'lastName': 'Buyer', 'email': 'jbuyer@mail.net', 'ipAddress': '99.99.99.99' } # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python (Recommended) customer = app_token.post('customers', request_body) customer.headers['location'] # => 'https://api-sandbox.dwolla.com/customers/247B1BD8-F5A0-4B71-A898-F62F67B8AE1C' ``` ```php create_unverified_customer.php theme={"dark"} create([ 'firstName' => 'Joe', 'lastName' => 'Buyer', 'email' => 'jbuyer@mail.net', 'ipAddress' => '99.99.99.99' ]); print($new_customer); # => https://api-sandbox.dwolla.com/customers/247B1BD8-F5A0-4B71-A898-F62F67B8AE1C ?> ``` When the customer is created, you'll receive the customer URL in the location header. # Step 4 - Attach a verified funding source Next, you will create and attach a verified funding source to Joe Buyer, which will be done using Dwolla's Open Banking solution with Plaid, a leading Open Banking service provider that Dwolla partners with. This method will give Joe Buyer the ability to add and verify their bank account in a matter of seconds by authenticating using their online banking credentials. Once Joe Buyer reaches the page in your application to add a bank account, you will use Open Banking with Plaid to authenticate their bank account. This involves initiating an Exchange Session with Dwolla, guiding the user through the verification process with their bank, and then using the Exchange details to create a funding source in Dwolla. To integrate Open Banking with Plaid, we recommend checking out our [integration guide](/docs/open-banking/plaid). Additionally, if you would like to see a working example that verifies a bank using Open Banking with Plaid and attaches it as a verified funding source to a Dwolla Customer, please check out our [open-banking/plaid](https://github.com/Dwolla/integration-examples/tree/main/packages/open-banking/plaid) integration example on our GitHub profile. Finally, once an instantly-verified funding source has been created via Open Banking with Plaid, Joe Buyer is now set up and ready to send money! # Step 5 - Initiating a transfer ```bash HTTP theme={"dark"} POST https://api-sandbox.dwolla.com/transfers Accept: application/vnd.dwolla.v1.hal+json Content-Type: application/vnd.dwolla.v1.hal+json Authorization: Bearer 0Sn0W6kzNicvoWhDbQcVSKLRUpGjIdlPSEYyrHqrDDoRnQwE7Q { "_links": { "source": { "href": "https://api-sandbox.dwolla.com/funding-sources/80275e83-1f9d-4bf7-8816-2ddcd5ffc197" }, "destination": { "href": "https://api-sandbox.dwolla.com/funding-sources/375c6781-2a17-476c-84f7-db7d2f6ffb31" } }, "amount": { "currency": "USD", "value": "225.00" } } HTTP/1.1 201 Created Location: https://api-sandbox.dwolla.com/transfers/d76265cd-0951-e511-80da-0aa34a9b2388 ``` ```ruby initiate_transfer.rb theme={"dark"} request_body = { :_links => { :source => { :href => "https://api-sandbox.dwolla.com/funding-sources/80275e83-1f9d-4bf7-8816-2ddcd5ffc197" }, :destination => { :href => "https://api-sandbox.dwolla.com/funding-sources/375c6781-2a17-476c-84f7-db7d2f6ffb31" } }, :amount => { :currency => "USD", :value => "225.00" } } # Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby (Recommended) # For Dwolla API applications, an app_token can be used for this endpoint. (https://developers.dwolla.com/docs/api-reference/tokens/create-an-application-access-token) transfer = app_token.post "transfers", request_body transfer.response_headers[:location] # => "https://api.dwolla.com/transfers/d76265cd-0951-e511-80da-0aa34a9b2388" ``` ```javascript initiate_transfer.js theme={"dark"} var requestBody = { _links: { source: { href: "https://api-sandbox.dwolla.com/funding-sources/80275e83-1f9d-4bf7-8816-2ddcd5ffc197", }, destination: { href: "https://api-sandbox.dwolla.com/funding-sources/375c6781-2a17-476c-84f7-db7d2f6ffb31", }, }, amount: { currency: "USD", value: "225.00", }, }; // For Dwolla API applications, an dwolla can be used for this endpoint. (https://developers.dwolla.com/docs/api-reference/tokens/create-an-application-access-token) dwolla .post("transfers", requestBody) .then((res) => res.headers.get("location")); // => 'https://api.dwolla.com/transfers/d76265cd-0951-e511-80da-0aa34a9b2388' ``` ```python initiate_transfer.py theme={"dark"} request_body = { '_links': { 'source': { 'href': 'https://api-sandbox.dwolla.com/funding-sources/80275e83-1f9d-4bf7-8816-2ddcd5ffc197' }, 'destination': { 'href': 'https://api-sandbox.dwolla.com/funding-sources/375c6781-2a17-476c-84f7-db7d2f6ffb31' } }, 'amount': { 'currency': 'USD', 'value': '225.00' } } # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python (Recommended) # For Dwolla API applications, an app_token can be used for this endpoint. (https://developers.dwolla.com/docs/api-reference/tokens/create-an-application-access-token) transfer = app_token.post('transfers', request_body) transfer.headers['location'] # => 'https://api.dwolla.com/transfers/d76265cd-0951-e511-80da-0aa34a9b2388' ``` ```php initiate_transfer.php theme={"dark"} array ( 'source' => array ( 'href' => 'https://api-sandbox.dwolla.com/funding-sources/80275e83-1f9d-4bf7-8816-2ddcd5ffc197', ), 'destination' => array ( 'href' => 'https://api-sandbox.dwolla.com/funding-sources/375c6781-2a17-476c-84f7-db7d2f6ffb31', ), ), 'amount' => array ( 'currency' => 'USD', 'value' => '225.00', ) ); $transferApi = new DwollaSwagger\TransfersApi($apiClient); $myAccount = $transferApi->create($transfer_request); print($xfer); # => https://api-sandbox.dwolla.com/transfers/d76265cd-0951-e511-80da-0aa34a9b2388 ?> ``` ## Retrieve the status of your transfer You can check the status of the newly created transfer by retrieving the transfer by its URL. ##### Request and response ```bash HTTP theme={"dark"} GET https://api-sandbox.dwolla.com/transfers/d76265cd-0951-e511-80da-0aa34a9b2388 Accept: application/vnd.dwolla.v1.hal+json Authorization: Bearer pBA9fVDBEyYZCEsLf/wKehyh1RTpzjUj5KzIRfDi0wKTii7DqY ... { "_links": { "self": { "href": "https://api-sandbox.dwolla.com/transfers/D76265CD-0951-E511-80DA-0AA34A9B2388" }, "source": { "href": "https://api-sandbox.dwolla.com/customers/AB443D36-3757-44C1-A1B4-29727FB3111C" }, "destination": { "href": "https://api-sandbox.dwolla.com/customers/C7F300C0-F1EF-4151-9BBE-005005AC3747" } }, "id": "D76265CD-0951-E511-80DA-0AA34A9B2388", "status": "pending", "amount": { "value": "225.00", "currency": "USD" }, "created": "2015-09-02T00:30:25.580Z" } ``` ```ruby get_transfer_status.rb theme={"dark"} transfer_url = 'https://api-sandbox.dwolla.com/transfers/d76265cd-0951-e511-80da-0aa34a9b2388' # Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby (Recommended) # For Dwolla API applications, an app_token can be used for this endpoint. (https://developers.dwolla.com/api-reference/authorization/application-authorization) transfer = app_token.get transfer_url transfer.status # => "pending" ``` ```php get_transfer_status.php theme={"dark"} byId($transferUrl); $transfer->status; # => "pending" ?> ``` ```python get_transfer_status.py theme={"dark"} transfer_url = 'https://api.dwolla.com/transfers/d76265cd-0951-e511-80da-0aa34a9b2388' # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python (Recommended) # For Dwolla API applications, an app_token can be used for this endpoint. (https://developers.dwolla.com/api-reference/authorization/application-authorization) transfer = app_token.get(transfer_url) transfer.body['status'] # => 'pending' ``` ```javascript get_transfer_status.js theme={"dark"} var transferUrl = "https://api.dwolla.com/transfers/d76265cd-0951-e511-80da-0aa34a9b2388"; // For Dwolla API applications, an dwolla can be used for this endpoint. (https://developers.dwolla.com/api-reference/authorization/application-authorization) dwolla.get(transferUrl).then((res) => res.body.status); // => 'pending' ``` That's it! You've successfully transferred money from Joe Buyer to Jane Merchant. Please continue to the [Webhooks guide](/docs/working-with-webhooks) for information on implementing notifications for your customers about the transfer. # Transfer Money Me-to-Me Source: https://developers.dwolla.com/docs/transfer-money-me-to-me Move funds between two different bank accounts belonging to a single Verified Customer, e.g. for savings applications that move funds between a Customer's checking and savings accounts. ## Overview This guide is designed to get you up and running quickly through creating a bank to bank transfer between a verified Customer's two bank accounts. In this guide we'll cover the basics of integrating this payment flow by walking through the steps needed to onboard your end user as a verified Customer and create the bank to bank transfer. Funds Flow Me-to-Me In this quickstart guide, you'll learn the key concepts involved with sending money between a Customer's bank accounts. Select the appropriate Customer type and create a verified Customer in your application. Link both a source and a destination bank account to the Customer's profile for account-to-account transfers. Retrieve the list of all funding sources associated with the Customer. Initiate a transfer from the Customer's checking account to their savings account using the Dwolla API. ## Before you begin We encourage you to create a Sandbox account, if you haven't already. This will allow you to follow along with the steps outlined in this guide. Check out our [Sandbox guide](/docs/testing) to learn more. After creating a sandbox account, you'll obtain your API Key and Secret, which are used to obtain an OAuth access token. An access token is required in order to authenticate against the Dwolla API. If you haven't already, run through the [Quickstart](/docs/quickstart) to get your first token, or see the [Authentication guide](/docs/api-reference/api-fundamentals/making-requests-and-authentication) for OAuth details. Lastly, in this sandbox walkthrough, we recommend having an active webhook subscription. This will help notify your application of various events that occur within Dwolla. [Check out our guide to learn more](/docs/working-with-webhooks). **Let's get started!** # Step 1 - Creating your Customer #### Choose the Customer Type for Your Funds Flow Before your end user can send or receive funds to their connected bank account, they must be created as a Customer via the Dwolla API. With this funds flow, however, the only eligible Customer types are: * Verified Personal Customers * Verified Business Customers To learn more on the differences between personal and business verified Customers and the capabilities of each, [check out our developer resource article.](/docs/customer-types) Verified Customers must go through the identity verification process and have a verified status in order to be eligible to transact. In order to verify the identity of the individual or business creating a Dwolla Customer account, you will need to pass information including, but not limited to, social security number (SSN), address, date of birth, and/or Employer Identification Number (EIN). ### Create the Customer While both the Personal and Business verified Customer types are valid in this funds flow, we will be creating a Personal verified Customer in this guide. ##### Request Parameters - Personal Verified Customer | Parameter | Required | Type | Description | | ----------- | -------- | ------ | ------------------------------------------------------------------------------------ | | firstName | yes | string | Individual's legal first name. | | lastName | yes | string | Individual's legal last name. | | email | yes | string | Customer's email address. | | type | yes | string | Type of identity verified Customer. Value of `personal` for individual. | | address1 | yes | string | Street number, street name of individual's physical address. | | address2 | no | string | Apartment, floor, suite, bldg # of individual's physical address. | | city | yes | string | City of individual's physical address. | | state | yes | string | Two-letter US state or territory abbreviation code of individual's physical address. | | postalCode | yes | string | Customer's US five-digit ZIP or ZIP + 4 code. | | dateOfBirth | yes | string | Customer's date of birth. Must be 18 years of age with format of `YYYY-MM-DD`. | | ssn | yes | string | Last four-digits of individual's social security number. | ##### Request and response ```bash HTTP theme={"dark"} POST https://api-sandbox.dwolla.com/customers Content-Type: application/vnd.dwolla.v1.hal+json Accept: application/vnd.dwolla.v1.hal+json Authorization: Bearer pBA9fVDBEyYZCEsLf/wKehyh1RTpzjUj5KzIRfDi0wKTii7DqY { "firstName": "John", "lastName": "Doe", "email": "johndoe@nomail.net", "ipAddress": "10.10.10.10", "type": "personal", "address1": "99-99 33rd St", "city": "Some City", "state": "NY", "postalCode": "11101", "dateOfBirth": "1970-01-01", "ssn": "1234" } HTTP/1.1 201 Created Location: https://api.dwolla.com/customers/FC451A7A-AE30-4404-AB95-E3553FCD733F ``` ```php create_personal_verified_customer.php theme={"dark"} create([ 'firstName' => 'John', 'lastName' => 'Doe', 'email' => 'jdoe@nomail.net', 'type' => 'personal', 'address1' => '99-99 33rd St', 'city' => 'Some City', 'state' => 'NY', 'postalCode' => '11101', 'dateOfBirth' => '1970-01-01', # For the first attempt, only the # last 4 digits of SSN required # If the entire SSN is provided, # it will still be accepted 'ssn' => '1234' ]); $customer; # => "https://api-sandbox.dwolla.com/customers/AB443D36-3757-44C1-A1B4-29727FB3111C" ?> ``` ```ruby create_personal_verified_customer.rb theme={"dark"} # Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby request_body = { :firstName => 'John', :lastName => 'Doe', :email => 'jdoe@nomail.net', :type => 'personal', :address1 => '99-99 33rd St', :city => 'Some City', :state => 'NY', :postalCode => '11101', :dateOfBirth => '1970-01-01', # For the first attempt, only the # last 4 digits of SSN required # If the entire SSN is provided, # it will still be accepted :ssn => '1234' } customer = app_token.post "customers", request_body customer.response_headers[:location] # => "https://api-sandbox.dwolla.com/customers/AB443D36-3757-44C1-A1B4-29727FB3111C" ``` ```python create_personal_verified_customer.py theme={"dark"} # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python request_body = { 'firstName': 'John', 'lastName': 'Doe', 'email': 'jdoe@nomail.net', 'type': 'personal', 'address1': '99-99 33rd St', 'city': 'Some City', 'state': 'NY', 'postalCode': '11101', 'dateOfBirth': '1970-01-01', # For the first attempt, only the # last 4 digits of SSN required # If the entire SSN is provided, # it will still be accepted 'ssn': '1234' } customer = app_token.post('customers', request_body) customer.headers['location'] # => 'https://api-sandbox.dwolla.com/customers/AB443D36-3757-44C1-A1B4-29727FB3111C' ``` ```javascript create_personal_verified_customer.js theme={"dark"} var requestBody = { firstName: "John", lastName: "Doe", email: "jdoe@nomail.net", type: "personal", address1: "99-99 33rd St", city: "Some City", state: "NY", postalCode: "11101", dateOfBirth: "1970-01-01", // For the first attempt, only the // last 4 digits of SSN required // If the entire SSN is provided, // it will still be accepted ssn: "1234", }; dwolla .post("customers", requestBody) .then((res) => res.headers.get("location")); // => 'https://api-sandbox.dwolla.com/customers/FC451A7A-AE30-4404-AB95-E3553FCD733F' ``` When the Customer is successfully created on your application, you will receive a `201` HTTP response with an empty response body. You can reference the Location header to retrieve a link that represents the created Customer resource. We recommend storing the full URL for future use, as it will be needed for actions such as attaching a bank or correlating webhooks that are triggered for the user in the Dwolla system. Providing the IP address of the end user accessing your application as the ipAddress parameter. This enhances fraud detection and tracking. ### Handle Webhooks If you have an active webhook subscription, you will receive the `customer_created` and `customer_verified` webhook immediately after the resource has been created. ### Additional expected behavior #### Customer Statuses Not all Customers will have a `verified` status upon initial Customer creation. In production, you may run into an instance where more information is needed from your end user in order for Dwolla to fully verify their identity. Other statuses your Customer may be placed in include, `retry`, `document`, `deactivated`, or `suspended`. For more information on these statuses, refer to our developer resource article. #### Balance Funding Source On successful Customer verification, Dwolla will also create a [Balance Funding Source](/docs/balance-funding-source) for this Customer. There are two types of Funding Sources available within the Dwolla Platform which include a bank or a balance. A bank account is commonly used as the source or destination for ACH transfers. A balance is a Funding Source that can be utilized like a "wallet" for holding a stored value of funds. The Dwolla balance is made available for Customers that have fully verified their identity within Dwolla. # Step 2 - Adding Funding Sources Within Dwolla, the sending party must always verify their bank account in order to be eligible to create a transfer. #### Bank Addition and Verification Methods There are multiple ways of adding a bank to a Customer with the Dwolla API. A simplified table below outlines the similarities and differences of each method. | Bank Addition Method | Will the bank be verified? | Required Information | | ---------------------------------------------------------- | ----------------------------- | ------------------------------- | | API - Account & Routing Number | Optional - With Microdeposits | Bank Account and Routing Number | | [Dwolla + Open Banking](/docs/open-banking#overview) | Yes | Online banking credentials | | [Drop-in components](/docs/drop-in-components) | Optional - With Microdeposits | Bank Account and Routing Number | | [Dwolla + Secure Exchange solution](/docs/secure-exchange) | Yes | Online banking credentials | | Other Approved Third-party Provider | Yes | Variable | For more information on securely submitting a user's bank details directly to Dwolla from the client-side of your application, reference our Drop-in components . ### Add a Bank to a Verified Personal Customer In this step, we will create and attach a verified funding source to your Customer using Dwolla's Open Banking solution with Plaid, a leading Open Banking service provider that Dwolla partners with. This method will give your Customers the ability to add and verify their bank account in a matter of seconds by authenticating using their online banking credentials. Once your Customer reaches the page in your application to add a bank account, you will use Open Banking with Plaid to authenticate the user's bank account. This involves initiating an Exchange Session with Dwolla, guiding the user through the verification process with their bank, and then using the Exchange details to create a funding source in Dwolla. To integrate Open Banking with Plaid, we recommend checking out our [integration guide](/docs/open-banking/plaid). Additionally, if you would like to see a working example that verifies a bank using Open Banking with Plaid and attaches it as a verified funding source to a Dwolla Customer, please check out our [open-banking/plaid](https://github.com/Dwolla/integration-examples/tree/main/packages/open-banking/plaid) integration example on our GitHub profile. ### Handle Webhooks If you have an active webhook subscription, you should receive both the `customer_funding_source_added` and `customer_funding_source_verified` webhooks immediately following the request to Dwolla to add a funding source using Open Banking. ### Add a Savings Account Once you have implemented Dwolla's Open Banking solution with Plaid, and you have attached a customer's `checking` account, you will want to repeat the same steps outlined above to add the customer's `savings` account as a funding source as well. # Step 3 - Retrieve Funding Sources In order to find your Customer's available bank and balance funding sources, you will need to first retrieve the funding sources from your Customer, via the API. ##### Request and response ```bash HTTP [expandable] theme={"dark"} GET https://api-sandbox.dwolla.com/customers/5b29279d-6359-4c87-a318-e09095532733/funding-sources Accept: application/vnd.dwolla.v1.hal+json Authorization: Bearer pBA9fVDBEyYZCEsLf/wKehyh1RTpzjUj5KzIRfDi0wKTii7DqY ... { "_links": { "self": { "href": "https://api-sandbox.dwolla.com/customers/5b29279d-6359-4c87-a318-e09095532733/funding-sources" }, "customer": { "href": "https://api-sandbox.dwolla.com/customers/5b29279d-6359-4c87-a318-e09095532733" } }, "_embedded": { "funding-sources": [ { "_links": { "self": { "href": "https://api-sandbox.dwolla.com/funding-sources/ab9cd5de-9435-47af-96fb-8d2fa5db51e8" }, "customer": { "href": "https://api-sandbox.dwolla.com/customers/5b29279d-6359-4c87-a318-e09095532733" }, "with-available-balance": { "href": "https://api-sandbox.dwolla.com/funding-sources/ab9cd5de-9435-47af-96fb-8d2fa5db51e8" } }, "id": "ab9cd5de-9435-47af-96fb-8d2fa5db51e8", "status": "verified", "type": "balance", "name": "Balance", "created": "2015-10-02T21:00:28.153Z", "removed": false, "channels": [] }, { "_links": { "self": { "href": "https://api-sandbox.dwolla.com/funding-sources/98c209d3-02d6-4bee-bc0f-61e18acf0e33" }, "customer": { "href": "https://api-sandbox.dwolla.com/customers/5b29279d-6359-4c87-a318-e09095532733" } }, "id": "98c209d3-02d6-4bee-bc0f-61e18acf0e33", "status": "verified", "type": "bank", "bankAccountType": "checking", "name": "Jane Doe's Checking", "created": "2015-10-02T22:03:45.537Z", "removed": false, "channels": [ "ach" ], "fingerprint": "4cf31392f678cb26c62b75096e1a09d4465a801798b3d5c3729de44a4f54c794" }, { "_links": { "self": { "href": "https://api-sandbox.dwolla.com/funding-sources/32eb2e53-d1e8-4b4d-bfc7-9ae7c553969d" }, "customer": { "href": "https://api-sandbox.dwolla.com/customers/5b29279d-6359-4c87-a318-e09095532733" } }, "id": "98c209d3-02d6-4bee-bc0f-61e18acf0e33", "status": "verified", "type": "bank", "bankAccountType": "savings", "name": "Jane Doe's Savings", "created": "2015-10-02T22:03:45.537Z", "removed": false, "channels": [ "ach" ] } ] } } ``` ```php get_funding_sources.php theme={"dark"} getCustomerFundingSources($customerUrl); $fundingSources->_embedded->{'funding-sources'}[0]->name; # => "Jane Doe's Checking" ?> ``` ```ruby get_funding_sources.rb theme={"dark"} # Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby customer_url = 'https://api-sandbox.dwolla.com/customers/5b29279d-6359-4c87-a318-e09095532733' funding_sources = app_token.get "#{customer_url}/funding-sources" funding_sources._embedded['funding-sources'][0].name # => "Jane Doe's Checking" ``` ```python get_funding_sources.py theme={"dark"} # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python customer_url = 'https://api-sandbox.dwolla.com/customers/5b29279d-6359-4c87-a318-e09095532733' funding_sources = app_token.get('%s/funding-sources' % customer_url) funding_sources.body['_embedded']['funding-sources'][0]['name'] # => 'Jane Doe's Checking' ``` ```javascript get_funding_sources.js theme={"dark"} var customerUrl = "https://api-sandbox.dwolla.com/customers/5b29279d-6359-4c87-a318-e09095532733"; dwolla .get(`${customerUrl}/funding-sources`) .then((res) => res.body._embedded["funding-sources"][0].name); // => 'Jane Doe's Checking' ``` When the funding sources are successfully retrieved, you will receive a `200` HTTP response with the details of the funding sources. After retrieving the funding sources, we recommend storing the full URL for future use as it will be referenced when creating the transfer to this user's bank account. # Step 4 - Initiating a Transfer Sending funds from the Customer's checking account to their savings account. #### Identify Source and Destination For Transfer The first step is to determine where the funds are being sourced from and where the funds are going to. * `Source` - Your Customer's Checking Bank Funding Source * `Destination` - Your Customer's Savings Bank Funding Source Since you are utilizing a `me-to-me` funds flow, you will need to know that there are two parts to a transfer, * Source funding source to Balance funding source * Balance funding source to Destination funding source ### Initiate a Transfer To initiate a transfer, we will need to specify the source and destination funding source URLs in the `_links` parameter. ##### Request Parameters | Parameter | Required | Type | Description | | --------- | -------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | \_links | yes | object | A \_links JSON object describing the desired source and destination of a transfer. [Reference the Source and Destination object](/docs/api-reference/transfers) to learn more about possible values for source and destination. | | amount | yes | object | An amount JSON object. [Reference the amount JSON object](/docs/api-reference/transfers) to learn more. | Within a transfer request, Dwolla supports additional optional parameters. These can range from clearing to specify the processing timing for the transfer, or correlationId to help correlate transfers from end-to-end. The object facilitator-fee isn't supported for this funds flow. For more information on all available transfer request parameters, check out our API reference documentation. ##### Request and response ```bash HTTP theme={"dark"} POST https://api-sandbox.dwolla.com/transfers Accept: application/vnd.dwolla.v1.hal+json Content-Type: application/vnd.dwolla.v1.hal+json Authorization: Bearer pBA9fVDBEyYZCEsLf/wKehyh1RTpzjUj5KzIRfDi0wKTii7DqY Idempotency-Key: 19051a62-3403-11e6-ac61-9e71128cae77 { "_links": { "source": { "href": "https://api-sandbox.dwolla.com/funding-sources/707177c3-bf15-4e7e-b37c-55c3898d9bf4" }, "destination": { "href": "https://api-sandbox.dwolla.com/funding-sources/3152c22b-3d72-442d-a83b-e575df3a043e" } }, "amount": { "currency": "USD", "value": "10.00" }, } ... HTTP/1.1 201 Created Location: https://api-sandbox.dwolla.com/transfers/81643da1-b1b3-e911-811b-f08aa77f5aa3 ``` ```php initiate_transfer.php theme={"dark"} array ( 'source' => array ( 'href' => 'https://api-sandbox.dwolla.com/funding-sources/707177c3-bf15-4e7e-b37c-55c3898d9bf4', ), 'destination' => array ( 'href' => 'https://api-sandbox.dwolla.com/funding-sources/3152c22b-3d72-442d-a83b-e575df3a043e', ), ), 'amount' => array ( 'currency' => 'USD', 'value' => '225.00', ) ); $transferApi = new DwollaSwagger\TransfersApi($apiClient); $transfer = $transferApi->create($transfer_request); print($transfer); # => https://api-sandbox.dwolla.com/transfers/81643da1-b1b3-e911-811b-f08aa77f5aa3 ?> ``` ```ruby initiate_transfer.rb theme={"dark"} transfer_request = { :_links => { :source => { :href => "https://api-sandbox.dwolla.com/funding-sources/707177c3-bf15-4e7e-b37c-55c3898d9bf4" }, :destination => { :href => "https://api-sandbox.dwolla.com/funding-sources/3152c22b-3d72-442d-a83b-e575df3a043e" } }, :amount => { :currency => "USD", :value => "225.00" } } # Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby (Recommended) transfer = app_token.post "transfers", transfer_request transfer.response_headers[:location] # => "https://api-sandbox.dwolla.com/transfers/81643da1-b1b3-e911-811b-f08aa77f5aa3" ``` ```python initiate_transfer.py theme={"dark"} transfer_request = { '_links': { 'source': { 'href': 'https://api-sandbox.dwolla.com/funding-sources/707177c3-bf15-4e7e-b37c-55c3898d9bf4' }, 'destination': { 'href': 'https://api-sandbox.dwolla.com/funding-sources/3152c22b-3d72-442d-a83b-e575df3a043e' } }, 'amount': { 'currency': 'USD', 'value': '225.00' } } # Using dwollav2 - https://github.com/Dwolla/dwolla-v2-python (Recommended) transfer = app_token.post('transfers', transfer_request) transfer.headers['location'] # => 'https://api-sandbox.dwolla.com/transfers/81643da1-b1b3-e911-811b-f08aa77f5aa3' ``` ```javascript initiate_transfer.js theme={"dark"} var transferRequest = { _links: { source: { href: "https://api-sandbox.dwolla.com/funding-sources/707177c3-bf15-4e7e-b37c-55c3898d9bf4", }, destination: { href: "https://api-sandbox.dwolla.com/funding-sources/3152c22b-3d72-442d-a83b-e575df3a043e", }, }, amount: { currency: "USD", value: "225.00", }, }; dwolla.post("transfers", transferRequest).then(function (res) { res.headers.get("location"); // => 'https://api-sandbox.dwolla.com/transfers/81643da1-b1b3-e911-811b-f08aa77f5aa3' }); ``` ### Handle Webhooks If you have an active webhook subscription (required in production & optional in Sandbox), you will receive the `customer_bank_transfer_created` webhook immediately after the transfer resource has been created. This denotes that the first part of the transfer has been initiated from the checking bank funding source to the Balance funding source. ### Simulate Bank Transfer Processing To simulate bank transfer processing in the Dwolla Sandbox environment, navigate to the Sandbox Dashboard. From here, you will want to click the "Process Bank Transfers" button on the top of the screen. Your Sandbox transfer will be moved out of a pending status and moved to a processed status. process bank transfers **Production bank transfer processing timing** While pending bank transfers can be processed at any time in the Sandbox, behavior will vary in production depending on if your application has access to expedited transfers versus standard ACH transfer times. Refer to our [developer resource article](/docs/transfer-processing-times) to learn more on transfer timing in production. ### Handle Webhooks If you have an active webhook subscription (required in production & optional in Sandbox), you will receive the `customer_bank_transfer_completed` webhook when the status of the first part of the transfer has changed from `pending` to `processed`. The second part of the transfer is then automatically initiated from the Balance funding source to the savings bank funding-source which triggers another `customer_bank_transfer_created` webhook. Repeat [simulate bank transfer processing](#simulate-bank-transfer-processing) to simulate bank transfer processing again. Once the transfer has cleared into the final destination bank funding source, you will receive another `customer_bank_transfer_completed` webhook denoting that the transfer is now complete. ### Verify Status of Transfer Since ACH transfers in production can take a few days to complete, webhooks are an efficient way to notify you of when a transfer's status has been updated from `pending` to `processed` to a destination funding source. However, if you want to verify the status of a transfer at any given point in time, you can make a call to the API to retrieve the transfer by its unique id. ##### Example response - Transfer 1 ```json theme={"dark"} { "_links": { "self": { "href": "https://api-sandbox.dwolla.com/transfers/81643da1-b1b3-e911-811b-f08aa77f5aa3", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "transfer" }, "source": { "href": "https://api-sandbox.dwolla.com/funding-sources/707177c3-bf15-4e7e-b37c-55c3898d9bf4", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "funding-source" }, "destination": { "href": "https://api-sandbox.dwolla.com/customers/cf3f1ad4-fc48-45d3-8aff-0ef5577b8a17", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "customer" }, "funded-transfer": { "href": "https://api-sandbox.dwolla.com/transfers/a00ff82d-73b4-e911-811b-f08aa77f5aa3", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "transfer" } }, "id": "81643da1-b1b3-e911-811b-f08aa77f5aa3", "created": "2019-08-01T15:32:05.620Z", "status": "processed", "amount": { "value": "225.00", "currency": "USD" }, "individualAchId": "IQ8M922R" } ``` ##### Example response - Transfer 2 ```json theme={"dark"} { "_links": { "self": { "href": "https://api-sandbox.dwolla.com/transfers/a00ff82d-73b4-e911-811b-f08aa77f5aa3", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "transfer" }, "source": { "href": "https://api-sandbox.dwolla.com/customers/cf3f1ad4-fc48-45d3-8aff-0ef5577b8a17", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "customer" }, "destination": { "href": "https://api-sandbox.dwolla.com/funding-sources/3152c22b-3d72-442d-a83b-e575df3a043e", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "funding-source" }, "funding-transfer": { "href": "https://api-sandbox.dwolla.com/transfers/81643da1-b1b3-e911-811b-f08aa77f5aa3", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "transfer" } }, "id": "a00ff82d-73b4-e911-811b-f08aa77f5aa3", "created": "2019-08-01T15:44:06.290Z", "status": "processed", "amount": { "value": "225.00", "currency": "USD" }, "individualAchId": "IQKBPJAY" } ``` # Transfer Processing Times Source: https://developers.dwolla.com/docs/transfer-processing-times Learn more about transfer clearing times, including `standard` ,`expedited` and `instant` clearing. ## Overview The transfer processing time denotes the duration required for a bank transfer to complete. Understanding transfer processing times helps ensure seamless user experience, workflow optimization, and business operations. By managing expectations and designing efficient processes, you can enhance your customer experience and drive business success. ## Bank-to-Bank Transfers Bank-to-bank transfers at Dwolla involve two steps: * Payment In (Debit) - Funds are pulled from the sender's bank account (source) into the Dwolla Network. * Payment Out (Credit) - Funds are pushed into the recipient's bank account (destination) from the Dwolla Network. The transfer processing time depends on the transfer speed chosen for both the debit and credit sides of the transfer. Reference our Transfer Lifecycle, which explains bank-to-bank transfers in full detail. ## Understanding Transfer Processing Times ## Standard ACH Transfers Standard ACH is the default processing option for bank transfers, requiring no additional setup or configuration. Any transfer created will automatically follow the standard clearing time. It applies to both ACH payment in (debit) and ACH payment out (credit) transfers. #### Key Points to Note for Standard ACH Transfers: * **Export deadline**: * 4 p.m. CT * **Transfer processing time**: * Debit transfers: 3-4 business days * Credit transfers: 1-2 business days * **Processing schedule**: Business days only (unavailable on weekends and banking holidays) ### Standard ACH Debit (Payment In) A Standard ACH debit from a bank to the Dwolla Network processes within 3-4 business days. This extended processing time accounts for ACH returns and processing delays, and checks on [common return codes](https://www.dwolla.com/resources/ach-return-codes/) like insufficient funds and invalid bank accounts. ##### Standard ACH Debit Transfer Schedule All times in Central Time (CT) (\*approximate deadlines) | Transfer created between | Transfer exported | Transfer processed | | ----------------------------------- | ----------------- | ------------------ | | Friday 4 p.m. to Monday 4 p.m. | Monday 4 p.m. | Thursday morning | | Monday 4 p.m. to Tuesday 4 p.m. | Tuesday 4 p.m. | Friday morning | | Tuesday 4 p.m. to Wednesday 4 p.m. | Wednesday 4 p.m. | Monday morning | | Wednesday 4 p.m. to Thursday 4 p.m. | Thursday 4 p.m. | Tuesday morning | | Thursday 4 p.m. to Friday 4 p.m. | Friday 4 p.m. | Wednesday morning | *\*Dwolla cannot guarantee an exact deadline for transmission of ACH files. Please be mindful that these are target cut-off times for transfer processing.* ### Standard ACH Credit (Payment Out) A Standard ACH credit from the Dwolla Network to a bank processes within 1-2 business days. ##### Standard ACH Credit Transfer Schedule All times in Central Time (CT) (\*approximate deadlines) | Transfer created between | Transfer exported | Transfer processed | | ----------------------------------- | ----------------- | ------------------ | | Friday 4 p.m. to Monday 4 p.m. | Monday 4 p.m. | Tuesday morning | | Monday 4 p.m. to Tuesday 4 p.m. | Tuesday 4 p.m. | Wednesday morning | | Tuesday 4 p.m. to Wednesday 4 p.m. | Wednesday 4 p.m. | Thursday morning | | Wednesday 4 p.m. to Thursday 4 p.m. | Thursday 4 p.m. | Friday morning | | Thursday 4 p.m. to Friday 4 p.m. | Friday 4 p.m. | Monday morning | *\*Dwolla cannot guarantee an exact deadline for transmission of ACH files. Please be mindful that these are target cut-off times for transfer processing.* ## Next Day ACH Transfers Dwolla offers expedited clearing into the Dwolla Network from a bank account in the form of Next Day ACH. Funds are made available in the Dwolla Network 1-2 business days after a Next Day ACH debit transfer is created. Next Day ACH transfers can be enabled on an account-level basis for you and users that send funds on your application. Once obtaining approval from us, you will gain capability for Next Day ACH transfers into the Dwolla Network. Once activated, Next Day ACH is the default processing speed for all ACH debits unless you specify otherwise using the clearing object. Refer to the example clearing object below to slow down the processing time from Next Day to Standard. Example `clearing` Object (to slow down Next Day ACH): ```raw theme={"dark"} { "clearing": { "source": "standard" } } ``` #### Key Points to Note for Next Day ACH Transfers: * **Available for**: Debit transfers only * **Export deadline**: * 4 p.m. CT * **Transfer processing time**: * Debit transfers: 1-2 business days * **Processing schedule**: Business days only (unavailable on weekends and banking holidays) ##### Next Day ACH Debit Transfer Schedule All times in Central Time (CT) (\*approximate deadlines) | Transfer created between | Transfer exported | Transfer processed | | ----------------------------------- | ----------------- | ------------------ | | Friday 4 p.m. to Monday 4 p.m. | Monday 4 p.m. | Tuesday morning | | Monday 4 p.m. to Tuesday 4 p.m. | Tuesday 4 p.m. | Wednesday morning | | Tuesday 4 p.m. to Wednesday 4 p.m. | Wednesday 4 p.m. | Thursday morning | | Wednesday 4 p.m. to Thursday 4 p.m. | Thursday 4 p.m. | Friday morning | | Thursday 4 p.m. to Friday 4 p.m. | Friday 4 p.m. | Monday morning | *\*Dwolla cannot guarantee an exact deadline for transmission of ACH files. Please be mindful that these are target cut-off times for transfer processing.* ## Same Day ACH Transfers We can enable [Same Day ACH](/docs/same-day-ach) transfers on an account-level basis for transfers within your application. With Same Day ACH, you can apply it to the ACH debit, the ACH credit, or both. After receiving approval from Dwolla to enable Same Day ACH for your account, you can create a Same Day debit and/or credit on a per-transfer basis by specifying it in the clearing object of an API transfer request. Transfer processing is expedited with Same Day ACH, but checks for common return codes may experience delays, which could increase the risk of losses incurred from return codes. Reference our Concept article, which explains Same Day debits and credits in full detail. #### Key Points to Note for Same Day ACH Transfers * **Available for**: Debit and Credit transfers * \*\*Export deadlines: * 9 a.m. CT * 1 p.m. CT * 3 p.m. CT * **Processing Time**: Once a Same Day ACH transfer is submitted before the deadline, it typically takes 0-1 business day to settle, regardless of whether it's a debit or credit. **Important Note**: The [\$1 million transaction limit](https://www.dwolla.com/updates/access-faster-payments-with-dwolla-same-day-ach/) is enforced by Nacha, the governing body for ACH transfers. ### Same Day ACH Debit (Payment In) A Same Day ACH debit from the Dwolla Network to a bank processes within 0-1 business days. To initiate a debit transfer with Same Day ACH processing, you can include the following clearing object in your API request: Example `clearing` Object: ```raw theme={"dark"} { "clearing": { "source": "next-available" } } ``` The "next-available" value indicates that the transfer will be exported in the next available Same Day ACH export window. Refer to the table below for a more detailed schedule of Same Day debit transfers. ##### Same Day ACH Debit Transfer Schedule All times in Central Time (CT) (\*approximate deadlines) | Transfer created between | Transfer exported | Transfer processed | | ------------------------ | ----------------- | ------------------ | | 3 p.m. to 9 a.m. | 9 a.m. | 11:30 a.m. | | 9 a.m. to 1 p.m. | 1 p.m. | 5 p.m. | | 1 p.m. to 3 p.m. | 3 p.m. | 5 p.m. | *\*Dwolla cannot guarantee an exact deadline for transmission of ACH files. Please be mindful that these are target cut-off times for transfer processing.* ### Same Day ACH Credit (Payment Out) A Same Day ACH credit allows for expedited processing of funds from the Dwolla Network to a bank account within 0-1 business days. To initiate a credit transfer with Same Day ACH processing, you can include the following clearing object in your API request: Example `clearing` Object: ```raw theme={"dark"} { "clearing": { "destination": "next-available" } } ``` The "next-available" value indicates that the transfer will be exported in the next available Same Day ACH export window. Refer to the table below for a more detailed schedule of Same Day credit transfers. ##### Same Day ACH Credit Transfer Schedule All times in Central Time (CT) (\*approximate deadlines) | Transfer created between | Transfer exported | Transfer processed | | ------------------------ | ----------------- | ------------------ | | 3 p.m. to 9 a.m. | 9 a.m. | 11:30 a.m. | | 9 a.m. to 1 p.m. | 1 p.m. | 5 p.m. | | 1 p.m. to 3 p.m. | 3 p.m. | 5 p.m. | *\*Dwolla cannot guarantee an exact deadline for transmission of ACH files. Please be mindful that these are target cut-off times for transfer processing.* ## Instant Payments (RTP and FedNow) [Instant Payments](/docs/instant-payments) is a payment method that allows for near-instantaneous transfer processing through two US-based payment networks: * **RTP® Network** - Operated by The Clearing House (TCH) * **FedNow® Service** - Operated by the Federal Reserve Bank (FRB) When you initiate an Instant Payment, Dwolla routes the transfer to the appropriate network based on availability and configuration. The specific network used (RTP or FedNow) is indicated in the transfer response through either an `rtpDetails` object or a `fedNowDetails` object. #### Key Points to Note for Instant Payment Transfers * **Available for**: Credit transfers only * **Export deadline**: None * **Transfer processing time**: Funds available within minutes * **Processing schedule**: 24/7/365 (business days, weekends and banking holidays) To initiate a credit transfer with Instant Payment processing, you can include the following processingChannel object in your API request: Example `processingChannel` Object: ```json theme={"dark"} "processingChannel": { "destination": "instant" } ``` For backward compatibility you can also use `real-time-payments` as an alternative value for `processingChannel.destination`. Both values are functionally equivalent and will route to either RTP or FedNow based on availability. However, we recommend using `instant`. ## Transfer Processing Timeline Scenarios This table outlines the expected processing times for different transfer scenarios: | Bank Account to Dwolla Network Clearing | Dwolla Network to Bank Account Clearing | Time to Dwolla Network (Debit) | Time to Destination Bank Account (Credit) | Total time to processed | | --------------------------------------- | --------------------------------------- | ------------------------------ | ----------------------------------------- | ----------------------- | | Standard | Standard | 3-4 business days | 1-2 business days | 4-6 business days | | Standard | Same Day | 3-4 business days | 0-1 business days | 3-5 business days | | Standard | Instant Payments | 3-4 business days | Instant | 3-4 business days | | Next Day | Standard | 1-2 business days | 1-2 business days | 2-4 business days | | Next Day | Same Day | 1-2 business days | 0-1 business days | 1-3 business days | | Next Day | Instant Payments | 1-2 business days | Instant | 1-2 business days | | Same Day | Standard | 0-1 business days | 1-2 business days | 1-3 business days | | Same Day | Same Day | 0-1 business days | 0-1 business days | 1-2 business days | | Same Day | Instant Payments | 0-1 business days | Instant | 0-1 business days | ## Best Practices and Considerations **Canceling a transfer:** Transfers can be canceled up until they are exported out of the Dwolla Network. To help you determine if a transfer is eligible for cancellation, Dwolla will return a cancel link on the transfer resource. **Export cut-off:** Please note that while there's a cut-off time for transfer processing, it's not a strict deadline. In some cases, the export out of the Dwolla Network may experience delays, causing transfers created slightly after the cut-off time to be included in the same batch. As a best practice, we recommend using the [cancel link](https://developers.dwolla.com/docs/balance/api-reference/transfers#transfer-links) returned on the transfer resource to determine if a transfer is cancellable instead of relying solely on the export timing. **ACH return:** Next Day ACH and Same Day ACH expedites the transfer processing time; however, checks for common return codes might experience delays which may increase the risk of losses incurred from return codes. # Virtual Account Numbers Source: https://developers.dwolla.com/docs/virtual-account-numbers A Virtual Account Number (VAN) provides a dedicated routing path for external transactions to flow into and out of a Dwolla balance. Each VAN generates unique account and routing numbers that enable you to credit or debit a Dwolla balance through ACH transfers. # Overview Virtual accounts are virtual subledgers within one physical depository account that can be utilized in a variety of ways. Virtual Account Numbers (VANs) allow clients to provide a routing and account number that can be used to allow ACH transactions to and from a [Dwolla Balance](/docs/balance-funding-source). The routing and account number is tied to a Dwolla Client's Master Balance, or one of their [Verified Customer's](/docs/customer-types#verified-customer) balances. Virtual accounts hold no funds—they are simply a mechanism to route funds into a single Dwolla Balance. Dwolla's Virtual Account Number feature performs two crucial functions: * Eliminate the need to deposit funds into an intermediary depository account before transferring funds in or out of your Dwolla Balance. * Keep transaction data separate and organized within the Dwolla Balance. VANs are a premium feature and count toward a total Funding Source limit. For existing Dwolla clients, you will be required to implement additional requirements in order to be approved for production access. To learn more about pricing and enabling this feature, please contact Sales. Send funds directly from a Virtual Account Number #### Items To Note * VANs cannot be used as a source or destination when calling the [/transfers endpoint](/docs/api-reference/transfers/initiate-a-transfer). * Users that are enabled for VANs will currently not have access to RTP or wire features. * Many virtual account numbers can route to a single Dwolla Balance. If you need to designate a portion of funds within a balance, reference the [Labels API](/docs/api-reference/labels). * Dwolla has no transaction limit for externally initiated transfers. ## Interacting With Virtual Accounts The Dwolla API supports methods for [creating a VAN for a Verified Customer](/docs/virtual-account-numbers#create-a-van-funding-source-for-a-customer) and [creating a VAN for a Master Account](/docs/virtual-account-numbers#create-a-van-funding-source-for-an-account), as well as [retrieving routing details](/docs/api-reference/funding-sources/retrieve-van-account-and-routing-numbers) and [removing a virtual account](/docs/api-reference/funding-sources/update-or-remove-a-funding-source). Once a virtual account is created, a unique account and routing number pair will be available for use to create external transactions (reference example below). A virtual account will be represented as a [Funding Source](/docs/api-reference/funding-sources) in the Dwolla API, however, it cannot be used when calling the Dwolla API to initiate transfers. Your Dwolla Balance is the only funding source you can use for transfers using the funds in that account within the Dwolla system, and you'll only be able to use the VAN to initiate transfers affecting the funds in that account from a third party system. Upon creation of a virtual account as a Funding Source, it will immediately be available for use with a `verified` status. A VAN can be [removed](/docs/api-reference/funding-sources/update-or-remove-a-funding-source) at any time via the API. A new VAN can be created in its place if needed. #### Example Routing Details Response: ```bash theme={"dark"} { "_links": { "self": { "href": "https://api-sandbox.dwolla.com/funding-sources/e6d68efb-c49b-43db-8867-e1ca58c6ee8c/ach-routing", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "ach-routing" }, "funding-source": { "href": "https://api-sandbox.dwolla.com/funding-sources/e6d68efb-c49b-43db-8867-e1ca58c6ee8c", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "funding-source" } }, "accountNumber": "9619991490430833", "routingNumber": "084106768" } ``` ## Tracking Virtual Account Transfers Externally initiated transactions will be represented by transfers that are automatically created and available in the transaction listing endpoint when Dwolla is notified of the transaction by the ACH Network. Adding funds into a balance will be represented by a new transfer where the **source** is the VAN funding source and the **destination** is the Customer. #### Example Response Object ```bash theme={"dark"} { "_links": { "self": { "href": "https://api-sandbox.dwolla.com/transfers/c7149132-c552-ec11-813a-ebf9f1240ece", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "transfer" }, "source": { "href": "https://api-sandbox.dwolla.com/funding-sources/533e2de1-bc59-4301-a081-a431ef023fbd", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "customer" }, "destination": { "href": "https://api-sandbox.dwolla.com/customers/af6eb65c-ab64-4510-8ce3-56076b6ab3a9", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "funding-source" } }, "id": "c7149132-c552-ec11-813a-ebf9f1240ece", "status": "processed", "amount": { "value": "10.00", "currency": "USD" }, "created": "2021-12-01T16:39:06.200Z", "achDetails": { "source": { "addenda": { "values": [ "addenda" ] }, "beneficiaryName": "Fake name", "companyName": "Fake company name", "companyEntryDescription": "PAYMENT", "effectiveDate": "2021-12-01", "postingData": "Fake company name:Fake discretionary data:Fake name", "routingNumber": "222222226", "traceId": "222222225926346" }, "destination": {} } } ``` Withdrawals from the Dwolla Balance will be represented by a new transfer where the **source** is the Customer and the **destination** is the VAN funding source. #### Example Response Object ```bash theme={"dark"} { "_links": { "self": { "href": "https://api-sandbox.dwolla.com/transfers/c7149132-c552-ec11-813a-ebf9f1240ece", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "transfer" }, "source": { "href": "https://api-sandbox.dwolla.com/customers/af6eb65c-ab64-4510-8ce3-56076b6ab3a9", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "customer" }, "destination": { "href": "https://api-sandbox.dwolla.com/funding-sources/533e2de1-bc59-4301-a081-a431ef023fbd", "type": "application/vnd.dwolla.v1.hal+json", "resource-type": "funding-source" } }, "id": "c7149132-c552-ec11-813a-ebf9f1240ece", "status": "processed", "amount": { "value": "10.00", "currency": "USD" }, "created": "2021-12-01T16:39:06.200Z", "achDetails": { "source": {}, "destination": { "addenda": { "values": [ "addenda" ] }, "beneficiaryName": "Fake name", "companyName": "Fake company name", "companyEntryDescription": "PAYMENT", "effectiveDate": "2021-12-01", "postingData": "Fake company name:Fake discretionary data:Fake name", "routingNumber": "222222226", "traceId": "222222225926346" } } } ``` Each new transfer will trigger a `bank_transfer_created` or `customer_bank_transfer_created` webhook. The creation webhooks will be triggered approximately 30 minutes prior to any completed webhook event of `bank_transfer_completed` or `customer_bank_transfer_completed`. Transfers will be created in a `pending` or `failed` status depending on whether the customer record or VAN has been deactivated, suspended, or removed. Additional details about an external transaction will be available in the achDetails object when [retrieving the transfer](/docs/api-reference/transfers/retrieve-a-transfer) from the API. Either the source or the destination will be present in the object depending on the direction of the funds transfer. #### ACH Details Optional Fields | Name | Description | | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | addenda | Contains addenda information for the transfer. See [addenda object](#addenda-object) for details. | | beneficiaryName | Beneficiary of the transaction's name. In general, should match the user onboarded to the Platform's name. | | companyEntryDescription | Describes the purpose of the transaction. Values can include but are not limited to: `REVERSAL`, `RECLAIM`, `NO CHECK`, `AUTOENROLL`, `REDEPCHECK`, `RETURN FEE`, `RETRY PMNT`, and `HEALTHCARE`. | | companyId | Numeric identifier of originator. | | companyName | Name of the originator. | | effectiveDate | The date when the ACH transaction becomes effective, formatted as `YYYY-MM-DD`. This is typically the settlement date for the transaction. | | postingData | Suggested memo line format for bank statements, structured as `companyName:companyDiscretionaryData:beneficiaryName`. Provides additional context for transaction identification. | | routingNumber | Routing number of Originating Depository Financial Institution (ODFI). Identifies the financial institution that originated the ACH transaction. | | traceId | A unique identifier for tracing the ACH transaction through the banking network. Used for transaction tracking and reconciliation purposes. | #### Example `achDetails` Object ```bash theme={"dark"} { ... "achDetails": { "source": { "addenda": { "values": [ "string" ] }, "beneficiaryName": "string", "companyId": "string", "companyName": "string", "companyEntryDescription": "PAYMENT", // will differ depending on originator "effectiveDate": "YYYY-MM-DD", "postingData": "string:string:string", // suggested memo line, fields are companyName:companyDiscretionaryData:beneficiaryName "routingNumber": "string", // of originating bank "traceId": "string" }, "destination": { // same fields as source } } } ``` ## API Operations Virtual Account Numbers integrate seamlessly with Dwolla's existing funding source endpoints, requiring only VAN-specific parameters to enable external ACH routing capabilities. Each operation leverages the standard funding source architecture while providing unique account and routing numbers for external transaction processing. Create a Virtual Account Number for a Dwolla Master Account using the funding source endpoint with `type` set to `virtual`. Create a Virtual Account Number for a Verified Customer using the customer funding source endpoint with `type` set to `virtual`. Retrieve VAN funding source details including `ach-routing` link for accessing unique account and routing numbers. Remove a Virtual Account Number by setting `removed` to `true`. A new VAN can be created if needed. ## Testing Before deploying to production, validate your Virtual Account Number implementation using Dwolla's Sandbox environment. The sandbox environment provides comprehensive simulation tools for external ACH transfers, failure scenarios, and edge cases to ensure your integration handles real-world payment flows reliably. Learn how to simulate VAN transfers and test failure scenarios in the Sandbox environment. # Webhook Events Source: https://developers.dwolla.com/docs/webhook-events Real-time event notifications. Determine why a webhook subscription is vital to the experience of your application. ## Overview When an action occurs within the Dwolla system on a resource (Customers, Transfers, etc.), an Event object is created to record a change to the state of the resource. All created Webhook Events follow the same format and include high level details such as: an event topic, a resource URL that identifies the specific resource that changed states, and a timestamp. If your application has an active [webhook subscription](/docs/working-with-webhooks), all Events relevant to your integration will trigger a webhook notification each time they occur. The following resource article will provide guidance on the structure of webhook events, which will assist with handling incoming webhooks. Dwolla strongly encourages all applications to establish a webhook subscription in production. It's a vital tool for receiving timely notifications about crucial actions within your Dwolla account, ensuring seamless updates and notifications for your end users. ## Webhook request details When your webhook subscription is configured, events will be created and sent asynchronously via webhooks as they occur. The webhook notification from Dwolla is a POST request that contains a JSON-encoded payload as well as HTTP headers, which are both used when consuming the webhook. Webhook payloads are designed to be lightweight with only minimum details regarding the triggered event. Dwolla returns links within the Event object pointing to relevant resources in the API which are used to lookup more detailed information on the resource that changed states. ## Webhook headers There are a few HTTP headers that are useful for your application when consuming the webhook request. `X-Dwolla-Topic` lets your app know, at a high level, the type of event being sent in the payload. `X-Request-Signature-SHA-256` contains an HMAC SHA-256 hash based on the webhook payload and a key which is your webhook secret. The webhook signature should be [processed and validated](/docs/working-with-webhooks#step-2-processing-and-validating-webhooks) prior to parsing the webhook payload. * `X-Dwolla-Topic` - customer\_created * `X-Request-Signature-SHA-256` - ed551cfb4acb48d31e14886bffa33aa417dfa4a3d3778f6141a7f7f92ee64861 ## Webhook payload All webhook payloads will include an Event object which will follow the same format as outlined in the [API reference docs](/docs/api-reference/events). An Event contains `_links` to: the relevant resource that caused the Event to be triggered, the Customer that the Event belongs to, and a `self` link to identify the unique Event. In addition to relevant `_links`, the payload will include attributes such as a `created` timestamp, event `topic`, `resourceId`, and conditionally a `correlationId` (see table below for more information). | Attribute | Description | | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | \_links | An object that contains relevant links to resources in the Dwolla API. Possible links can include: `self`, `account`, `resource`, and `customer`.
`self` - a link to the unique event
`account` - a link to the Dwolla account that the application belongs to
`resource` - a link to the resource that changed states. Used to lookup additional details returned on the resource itself
`customer` - a link to the customer the Event belongs to | | id | Unique Event ID. An Event ID is used along with the created timestamp for idempotent event processing. | | created | ISO-8601 timestamp when event was created. | | topic | Type of action that occurred with Dwolla. | | resourceId | Unique ID of the resource that triggered the Event. | | correlationId | Unique ID that was specified, if any, when a [transfer was created](/docs/api-reference/transfers/initiate-a-transfer).
**This value is only present for [transfer and transfer-related webhooks](#customers---transfers).** | ### Example webhook payload ```json theme={"dark"} { "_links": { "account": { "href": "https://api-sandbox.dwolla.com/accounts/0ee84069-47c5-455c-b425-633523291dc3", "resource-type": "account", "type": "application/vnd.dwolla.v1.hal+json" }, "customer": { "href": "https://api-sandbox.dwolla.com/customers/a6f09251-c2de-4833-94a8-5c70916cebbc", "resource-type": "customer", "type": "application/vnd.dwolla.v1.hal+json" }, "resource": { "href": "https://api-sandbox.dwolla.com/customers/a6f09251-c2de-4833-94a8-5c70916cebbc", "type": "application/vnd.dwolla.v1.hal+json" }, "self": { "href": "https://api-sandbox.dwolla.com/events/29a82d20-a703-41cb-9b3c-bd409c499925", "resource-type": "event", "type": "application/vnd.dwolla.v1.hal+json" } }, "created": "2019-05-30T18:21:11.490Z", "id": "29a82d20-a703-41cb-9b3c-bd409c499925", "resourceId": "a6f09251-c2de-4833-94a8-5c70916cebbc", "topic": "customer_created" } ``` ## List of Customer related events by resource Customer related webhooks are available for the list of Events shown below. As API enhancements are made, Dwolla may add new Events at any point in the future. A complete list of supported Events can be found in the [API docs](/docs/api-reference/events/list-events). The Event topic for all events triggered for your end users that are created as Customers will be prepended with customer\_\*. Events that represent actions being triggered on your primary Dwolla Account are not shown below. Refer to the API docs for the complete list of Events. ### Customers #### `customer_created` A Customer was created. **Timing:** Occurs upon a POST request to the [Create a Customer](/docs/api-reference/customers/create-a-customer) endpoint. #### `customer_kba_verification_needed` The retry identity verification attempt failed due insufficient scores on the submitted data. The end user will have a single KBA attempt to answer a set of "out of wallet" questions about themselves for identity verification. **Timing:** Occurs after a failed attempt to verify a Verified Customer Record. #### `customer_kba_verification_failed` The end user failed KBA verification and was unable to correctly answer at least three KBA questions. **Timing:** Triggered after a single attempt at verifying a Verified Customer Record using KBA. #### `customer_kba_verification_passed` The end user was able to correctly answer at least three KBA questions. **Timing:** Triggered after a Verified Customer Record successfully passes KBA requirements. #### `customer_reverification_needed` Incomplete information was received for a Customer; updated information is needed to verify the Customer. **Timing:** Occurs upon a POST request to the [Update a Customer](/docs/api-reference/customers/update-a-customer) endpoint, or when Dwolla places a Customer into retry status. #### `customer_verification_document_needed` Additional documentation is needed to verify a Customer. **Timing:** Occurs when a second attempt to re-verify a Customer fails, which systematically places the Customer in document status immediately after a POST request to the [Update a Customer](/docs/api-reference/customers/update-a-customer) endpoint. #### `customer_verification_document_uploaded` A verification document was uploaded for a Customer. **Timing:** Occurs upon a POST request to the [Create a Document](/docs/api-reference/documents/create-a-document-for-a-customer) endpoint. #### `customer_verification_document_failed` A verification document was rejected for a Customer. **Timing:** Occurs when a document uploaded for a Customer is reviewed by Dwolla, and rejected with a document failure reason, usually within 1-2 business days of uploading a document. #### `customer_verification_document_approved` A verification document was approved for a Customer. **Timing:** Occurs when a document uploaded for a Customer is reviewed by Dwolla, and approved, usually within 1-2 business days of uploading a document. #### `customer_verified` A Customer was verified. **Timing:** Occurs when a Customer is verified by Dwolla upon a POST request to the [Create a Customer](/docs/api-reference/customers/create-a-customer) endpoint. In a case where the Customer isn't instantly verified upon creation, this event occurs when the Customer is verified after a `retry` attempt, or after a document is approved. #### `customer_suspended` A Customer was suspended. **Timing:** Occurs when Dwolla systematically places a Customer in `suspended` status as a result of uploading fraudulent document, or upon receiving certain ACH return codes when a transfer fails. #### `customer_activated` A Customer moves from deactivated or suspended to an active status. **Timing:** Occurs upon reactivating a Customer that has a `deactivated` status by making a POST request to the [Update a Customer](/docs/api-reference/customers/update-a-customer) endpoint, or when Dwolla reactivates a Customer that has a `suspended` status. #### `customer_deactivated` A Customer was deactivated. **Timing:** Occurs upon deactivation of a Customer by making a POST request to the [Update a Customer](/docs/api-reference/customers/update-a-customer) endpoint, or when Dwolla systematically deactivates a Customer upon receiving certain ACH return codes when a transfer fails. ### Customers - Beneficial Owners #### `customer_beneficial_owner_created` A Beneficial owner was successfully created. **Timing:** Occurs upon a POST request to the [Create a beneficial owner](/docs/api-reference/beneficial-owners/create-beneficial-owner) endpoint. #### `customer_beneficial_owner_removed` An individual beneficial owner has been successfully removed from the Customer. **Timing:** Occurs upon a POST request to the [Remove a beneficial owner](/docs/api-reference/beneficial-owners/delete-beneficial-owner) endpoint. #### `customer_beneficial_owner_verification_document_needed` Additional documentation is needed to verify an individual beneficial owner. **Timing:** Occurs when a second attempt to re-verify a beneficial owner fails, which systematically places the beneficial owner in document status immediately after a POST request to the [Update a beneficial owner](/docs/api-reference/beneficial-owners/update-beneficial-owner) endpoint. #### `customer_beneficial_owner_verification_document_uploaded` A verification document was uploaded for beneficial owner. **Timing:** Occurs upon a POST request to the [Create a document for a beneficial owner](/docs/api-reference/documents/create-a-document-for-beneficial-owner) endpoint. #### `customer_beneficial_owner_verification_document_failed` A verification document was rejected for a beneficial owner. **Timing:** Occurs when a document uploaded for a beneficial owner is reviewed by Dwolla, and rejected with a document failure reason, usually within 1-2 business of uploading a document. #### `customer_beneficial_owner_verification_document_approved` A verification document was approved for a beneficial owner. **Timing:** Occurs when a document uploaded for a Customer is reviewed by Dwolla, and approved, usually within 1-2 business days of uploading a document. #### `customer_beneficial_owner_reverification_needed` A previously verified beneficial owner status has changed due to either a change in the beneficial owner's information or at request for more information from Dwolla. The individual will need to verify their identity within 30 days. **Timing:** Occurs upon a POST request to the [Update a beneficial owner](/docs/api-reference/beneficial-owners/update-beneficial-owner) endpoint, or when Dwolla places the beneficial owner into incomplete status. #### `customer_beneficial_owner_verified` A beneficial owner has been verified. **Timing:** Occurs when a Beneficial Owner is verified by Dwolla upon a POST request to the [Create a beneficial owner](/docs/api-reference/beneficial-owners/create-beneficial-owner) endpoint. In a case where the Beneficial Owner isn't instantly verified upon creation, this event occurs when the Beneficial Owner is verified after an update, or after a document is approved. ### Customers - Exchanges #### `customer_exchange_reauth_required` An exchange has been deactivated (or is pending deactivation) and requires reauthentication. **Timing:** Occurs when access to a user's connected bank account has been interrupted. This interruption could be due to changes on the bank's end, such as a password update, multi-factor authentication reset or revoked consent. This event signals that a user's bank connection needs to be refreshed by [creating a reauth exchange session](/docs/api-reference/exchange-sessions/create-re-authentication-exchange-session). ### Customers - Funding Sources #### `customer_funding_source_added` A funding source was added to a Customer. **Timing:** Occurs upon a POST request to the [Create a funding source for a customer](/docs/api-reference/funding-sources/create-customer-funding-source) endpoint, or when a funding source is added via drop-in components or a third party bank verification method. #### `customer_funding_source_removed` A funding source was removed from a Customer. **Timing:** Occurs upon a POST request to the [Remove a funding source](/docs/api-reference/funding-sources/update-or-remove-a-funding-source) endpoint, or when Dwolla systematically removes a funding source upon receiving certain ACH return codes when a transfer fails. #### `customer_funding_source_verified` A Customer's funding source was marked as verified. **Timing:** Occurs upon a POST request to the [Verify micro-deposits](/docs/api-reference/funding-sources/initiate-or-verify-micro-deposits) endpoint with the correct amounts, or when a funding source is added + verified via a third-party bank verification method. Also occurs in cases where Dwolla manually marks a funding source as verified. #### `customer_funding_source_unverified` A funding source has been systematically unverified. This is generally a result of a transfer failure. **Timing:** Occurs when Dwolla systematically marks a funding source unverified upon receiving certain ACH return codes when a transfer fails. #### `customer_funding_source_negative` A Customer's balance has gone negative. You are responsible for ensuring a zero or positive Dwolla balance for Customer accounts created by your application. If a Customer's Dwolla balance has gone negative, you are responsible for making the Dwolla Customer account whole. Dwolla will notify you via a webhook and separate email of the negative balance. **Timing:** Occurs upon a POST request to the [Initiate a transfer](/docs/api-reference/transfers/initiate-a-transfer) endpoint that causes a funding source balance to go negative. #### `customer_funding_source_updated` A Customer's funding source has been updated. This can also be fired as a result of a correction after a bank transfer processes. **Timing:** Occurs upon a POST request to the [Update a funding source](/docs/api-reference/funding-sources/update-or-remove-a-funding-source) endpoint. For example, a financial institution can issue a correction to change the bank account type from checking to savings. #### `customer_microdeposits_added` Two less than or equal to ten cent transfers to a Customer's linked bank account were initiated. **Timing:** Occurs upon a POST request to the [Initiate micro-deposits](/docs/api-reference/funding-sources/initiate-or-verify-micro-deposits) endpoint. #### `customer_microdeposits_failed` The two less than or equal to ten cent transfers to a Customer's linked bank account failed to clear successfully. **Timing:** Occurs when micro-deposits fail to clear into a bank account, usually within 1-2 business days of initiating them. #### `customer_microdeposits_completed` The two less than or equal to ten cent transfers to a Customer's linked bank account were successful. **Timing:** Occurs when micro-deposits are successful, usually within 1-2 business days of initiating them. #### `customer_microdeposits_maxattempts` The Customer has reached their max verification attempts, limit of three. The Customer can no longer verify their funding source with the completed micro-deposit amounts. **Timing:** Occurs upon the third POST request to the [Verify micro-deposits](/docs/api-reference/funding-sources/initiate-or-verify-micro-deposits) endpoint with incorrect micro-deposit amounts. ### Customers - Transfers For transfer webhooks, in addition to the [default payload keys](#webhook-payload), a `correlationId` key-value pair *may* be present if a value was specified when the [transfer was created](/docs/api-reference/transfers/initiate-a-transfer). The event descriptions below reflect ACH behavior. For **Instant Payments**, the recipient always receives `customer_bank_transfer_*` events regardless of whether the recipient is a Receive-only User, an Unverified Customer, or a Verified Customer. See [Instant Payments webhook notifications](/docs/instant-payments#webhook-notifications) for details. #### `customer_bank_transfer_created` A bank transfer was created for a Customer. Represents funds moving either from a verified Customer's bank to the Dwolla Platform or from the Dwolla Platform to a verified Customer's bank. **Timing:** Occurs upon a POST request to the [Initiate a transfer](/docs/api-reference/transfers/initiate-a-transfer) endpoint when sending funds from a Verified Customer's bank, or when funds move from a receiving Verified Customer's balance to their bank. #### `customer_bank_transfer_cancelled` A pending Customer bank transfer has been cancelled, and will not process further. Represents a cancellation of funds either transferring from a verified Customer's bank to the Dwolla Platform or from the Dwolla Platform to a verified Customer's bank. **Timing:** Occurs upon a POST request to the [Cancel a transfer](/docs/api-reference/transfers/cancel-a-transfer) endpoint, or when Dwolla manually cancels a transfer. #### `customer_bank_transfer_failed` A Customer bank transfer failed. Usually, this is a result of an ACH failure (insufficient funds, etc.). Represents a failed funds transfer either from a verified Customer's bank to the Dwolla Platform or from the Dwolla Platform to a verified Customer's bank. **Timing:** Occurs when Dwolla marks a transfer as failed. #### `customer_bank_transfer_creation_failed` Transfers initiated to a verified Customer's bank must pass through the verified Customer's balance before being sent to a receiving bank. Dwolla will fail to create a transaction intended for a verified Customer's bank if the funds available in the balance are less than the transfer amount. **Timing:** Occurs when a transfer to a verified Customer's bank fails to be created. #### `customer_bank_transfer_completed` A bank transfer that was created for a Customer was successful. Represents a successful funds transfer either from a verified Customer's bank to the Dwolla Platform or from the Dwolla Platform to a verified Customer's bank. **Timing:** Occurs when a funds transfer into the Dwolla Platform or a verified Customer's bank is successful, based on the transfer processing timing used. #### `customer_transfer_created` A transfer was created for a Customer. Represents funds transferring from a verified Customer's balance or unverified Customer's bank. **Timing:** Occurs upon a POST request to the [Initiate a transfer](/docs/api-reference/transfers/initiate-a-transfer) endpoint when sending funds from a verified Customer's balance, or to/from an unverified Customer's bank. #### `customer_transfer_cancelled` A pending transfer has been cancelled, and will not process further. Represents a cancellation of funds transferring either to an unverified Customer's bank or to a verified Customer's balance. **Timing:** Occurs upon a POST request to the [Cancel a transfer](/docs/api-reference/transfers/cancel-a-transfer) endpoint to cancel a transfer initiated from a verified Customer's balance, or to/from an unverified Customer's bank. #### `customer_transfer_failed` A Customer transfer failed. Represents a failed funds transfer either to an unverified Customer's bank or to a verified Customer's balance. **Timing:** Occurs when Dwolla marks a transfer as failed. #### `customer_transfer_completed` A Customer transfer was successful. Represents a successful funds transfer either to an unverified Customer's bank or to a verified Customer's balance. **Timing:** Occurs when a funds transfer into an unverified Customer's bank or a verified Customer's balance is successful, based on the transfer processing timing used. ### Customers - Mass Payments #### `customer_mass_payment_created` A verified Customer's mass payment was created. **Timing:** Occurs upon a POST request to the [Initiate a mass-payment](/docs/api-reference/mass-payments/initiate-a-mass-payment) endpoint. #### `customer_mass_payment_completed` A verified Customer's mass payment was completed. However, this doesn't mean that each mass payment item's transfer was successful. **Timing:** Occurs when a mass payment job completes. #### `customer_mass_payment_cancelled` A Verified Customer's created and deferred mass payment was cancelled. **Timing:** Occurs upon a POST request to the Update a mass-payment endpoint when cancelling a mass payment job. #### `customer_balance_inquiry_completed` Upon checking a Customer's bank balance, Dwolla will immediately return an HTTP 202 with response body that includes a status of `processing`. **Timing:** This event will be triggered when the bank balance check has completed processing. ### Customers - Labels #### `customer_label_created` A Verified Customer's label was created. **Timing:** Occurs upon a POST request to the [Create a label](/docs/api-reference/labels/create-a-label-for-a-customer) endpoint. #### `customer_label_ledger_entry_created` A ledger entry for a Verified Customer's label was created. **Timing:** Occurs upon a POST request to the [Create a label ledger entry](/docs/api-reference/labels/create-a-label-ledger-entry) endpoint. #### `customer_label_removed` A Verified Customer's label was removed. **Timing:** Occurs upon a POST request to the [Remove a label](/docs/api-reference/labels/remove-a-label) endpoint. # What is Dwolla? Source: https://developers.dwolla.com/docs/what-is-dwolla Leverage Dwolla's existing financial institution relationship to connect your business to the U.S. banking infrastructure. ## Overview Dwolla offers a white labeled product experience powered by an API that enables you to embed account-to-account payments into a web or mobile application. Dwolla connects your business to the U.S. banking infrastructure through an established relationship with one of Dwolla's financial institution partners. The platform revolves around the idea of stored-value/digital wallet functionality, enabling a range of funds flow models such as sending funds (payouts), receiving funds (pay-ins), facilitating transfers between users, and facilitating transfers between a user's payment accounts (Me-to-Me). ## Audience and Technical Level Dwolla is intended for businesses of all sizes, from small startups to large enterprises. The technical level required to integrate can vary depending on the specific needs of your business and which aspects of the product you'll be utilizing. In general, you will need to have a basic understanding of HTTP requests and responses, JSON data format, and security best practices. Dwolla requires skills in both front-end and back-end web and/or mobile development. Since Dwolla offers a REST API, you can integrate the API into your own platform using standard programming languages such as Node.js, Python, PHP, C-Sharp, Java/Kotlin, and Ruby. To expedite your integration, low code Drop-in Components are available for a wide range of functions which can be customized to match your brand. ## Benefits The benefits of using Dwolla include: * Fast and secure payments: Dwolla uses the Automated Clearing House (ACH) network to process payments, which is a secure and reliable way to transfer money. * Low fees: Dwolla's transaction fees are lower than those of traditional payment processors; such as credit cards. * Flexibility: Dwolla's API is flexible and can be customized to meet the needs of businesses of all sizes. Real-time webhooks enable your application to be in sync with all events that occur on the Dwolla platform. * Scalability and reliability: Dwolla is scalable and can handle large volumes of transactions. In addition, the API maintains 99.9% uptime so you can count on reliability as your business grows. ## Features The following key features help you diversify the way in which you embed account-to-account payments into your own platform: * Same-day ACH payments: Allows you to expedite the way you send and receive money via ACH by opening up additional windows for processing transactions on the same day. * Real-time payments: Dwolla also supports real-time payments, which allow businesses to send instantly. You can pre-fund your [Dwolla Balance](/docs/balance-funding-source) to enable near real-time push payments 24/7/365. * Reporting and analytics: Dwolla provides businesses with detailed reporting and analytics data, which can be used to track payments and improve cash flow management. ## Limitations Some of the limitations of Dwolla include: * Not available in all countries: Dwolla is currently only available for businesses registered to operate within the United States. * Integration complexity: For complex payment flows, Dwolla can be challenging to set up for businesses that are not familiar with APIs. # Working with Webhooks Source: https://developers.dwolla.com/docs/working-with-webhooks Implement simple, real-time, event notifications regarding account and transaction status, and more. ## Overview A webhook is a means of notifying your application of the occurrence of an event with some relevant information. [Events](/docs/api-reference/events) are created each time a resource is created or updated in Dwolla. For example, when a [Customer is created](/docs/api-reference/customers/create-a-customer) or a [Funding Source is removed](/docs/api-reference/funding-sources/update-or-remove-a-funding-source), a `customer_created` and `customer_funding_source_removed` event will be created, respectively. These events are what trigger HTTP webhook requests to your subscribed URL if you have an active webhook subscription. It is important to note that a single API request can trigger multiple webhooks to be fired, e.g. [initiating a transfer](/docs/api-reference/transfers/initiate-a-transfer) from an Account to Customer can create the events `transfer_created` and `customer_transfer_created`. Check out our API reference for an exhaustive list of all possible events. ## Webhook Event Each webhook contains an [Event](/docs/api-reference/events) with `_links` to the following resources: * The unique event itself * The [Dwolla Account](/docs/api-reference/accounts) associated with the event * The Resource associated with the event * The [Customer](/docs/api-reference/customers) that the resource relates to (if applicable) Example webhook payload: ```json theme={"dark"} { "id": "80d8ff7d-7e5a-4975-ade8-9e97306d6c15", "resourceId": "36E9DCB2-889B-4873-8E52-0C9404EA002A", "topic": "customer_created", "timestamp": "2015-10-22T14:44:11.407Z", "_links": { "self": { "href": "https://api-sandbox.dwolla.com/events/80d8ff7d-7e5a-4975-ade8-9e97306d6c15" }, "account": { "href": "https://api-sandbox.dwolla.com/accounts/b4cdac07-eeca-4059-a29c-48900e453d54" }, "resource": { "href": "https://api-sandbox.dwolla.com/customers/36E9DCB2-889B-4873-8E52-0C9404EA002A" }, "customer": { "href": "https://api-sandbox.dwolla.com/customers/36E9DCB2-889B-4873-8E52-0C9404EA002A" } } } ``` For detailed information on Dwolla's webhook request structure, refer to the Webhook Events resource. ## What to Know About Dwolla Webhooks * Each application can have multiple webhook subscriptions associated with it. While one subscription is sufficient, you can create up to **five** in Production and **ten** in Sandbox for redundancy. * Webhooks are sent asynchronously and are not guaranteed to be delivered in order. We recommend that applications protect against duplicated events by [making event processing idempotent](/docs/working-with-webhooks#check-for-duplicate-events). * Your application will need to respond to Dwolla webhook requests with a 200-level HTTP status code within 10 seconds of receipt. Otherwise, the attempt will be counted as a failure and Dwolla will retry sending the webhook according to the [back-off schedule](/docs/api-reference/webhook-subscriptions). * If there are 400 consecutive failures, and it has been 24 hours since your last success, your webhook subscription will be automatically paused and an email will be sent to the Admin of the Dwolla account. After fixing the issue that is causing the failures, you can unpause the webhook subscription either via your Dashboard or [Dwolla's API](/docs/api-reference/webhook-subscriptions/update-a-webhook-subscription) in order to continue receiving new webhooks and to [retry failed webhooks](/docs/api-reference/webhooks/retry-a-webhook). ## Getting Started In this guide we will walk through creating a webhook subscription along with validating and processing webhook requests. You will need to have a [Sandbox account](https://accounts-sandbox.dwolla.com/sign-up) already set up. Although not required, this guide assumes that you have some familiarity with Amazon Web Services (AWS); specifically [Lambda](https://aws.amazon.com/lambda/) and [SQS](https://aws.amazon.com/sqs/), or other similar services. # Step 1 - Create a Webhook Subscription First, you will need to have a URL that is publicly accessible where Dwolla can send webhooks in the form of HTTP requests. This also means that anyone on the Internet can hit your endpoint. As such, here are some security concerns: * Your webhook endpoint should only be accessible over [TLS (HTTPS)](https://www.dwolla.com/updates/improving-transport-layer-security/) and your server should have a valid SSL certificate. * Your subscription should include a random, secret key, only known by your application. This secret key should be securely stored and used later when [validating the authenticity of the webhook request](#authentication) from Dwolla. #### Request Parameters | Parameters | Required | Type | Description | | ---------- | -------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | url | yes | string | The publicly-accessible URL where Dwolla should deliver the webhook notification. | | secret | yes | string | A random, secret key, only known by your application. This secret key should be securely stored and used later when [validating the authenticity of the webhook](#authentication) from Dwolla. | ##### Request and Response ```raw HTTP theme={"dark"} POST https://api-sandbox.dwolla.com/webhook-subscriptions Accept: application/vnd.dwolla.v1.hal+json Content-Type: application/vnd.dwolla.v1.hal+json Authorization: Bearer 0Sn0W6kzNicvoWhDbQcVSKLRUpGjIdlPSEYyrHqrDDoRnQwE7Q { "url": "https://myapplication.com/webhooks", "secret": "sshhhhhh" } ... HTTP/1.1 201 Created Location: https://api-sandbox.dwolla.com/webhook-subscriptions/077dfffb-4852-412f-96b6-0fe668066589 ``` When the webhook subscription is created, you will receive a `201 Created` HTTP response with an empty response body. You can refer to the `Location` header to retrieve a link to the newly-created subscription. ## Webhook Subscription Resource | Parameter | Description | | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | id | Unique webhook subscription identifier assigned by Dwolla. | | url | Subscribed URL where Dwolla will deliver webhook notifications. | | paused | A boolean `true` or `false` value indicating if a webhook subscription is paused. A webhook subscription will be automatically paused after 400 consecutive failures. In addition, a subscription can be paused or unpaused by calling [this endpoint](/docs/api-reference/webhook-subscriptions/update-a-webhook-subscription) in our API. | | created | [ISO-8601](https://en.wikipedia.org/wiki/ISO_8601) timestamp this webhook subscription was created | ### Request and Response ```raw theme={"dark"} GET https://api-sandbox.dwolla.com/webhook-subscriptions/077dfffb-4852-412f-96b6-0fe668066589 Accept: application/vnd.dwolla.v1.hal+json Authorization: Bearer pBA9fVDBEyYZCEsLf/wKehyh1RTpzjUj5KzIRfDi0wKTii7DqY ... { "_links": { "self": { "href": "https://api-sandbox.dwolla.com/webhook-subscriptions/077dfffb-4852-412f-96b6-0fe668066589" }, "webhooks": { "href": "https://api-sandbox.dwolla.com/webhook-subscriptions/077dfffb-4852-412f-96b6-0fe668066589/webhooks" } }, "id": "077dfffb-4852-412f-96b6-0fe668066589", "url": "https://myapplication.com/webhooks", "created": "2022-01-20T16:20:47+00:00" } ``` # Step 2 - Processing and Validating Webhooks Before we begin, although not required, please note that many of the snippets we use include Amazon Web Services (AWS) as a dependency — specifically, [Lambda](https://aws.amazon.com/lambda/) and [SQS](https://aws.amazon.com/sqs/). We will use AWS' [v2 Node SDK](https://www.npmjs.com/package/aws-sdk); however, [v3 is also currently available](https://github.com/aws/aws-sdk-js-v3). Now that you have created a webhook subscription in the previous step, we will work on creating a webhook listener/handler. In order to asynchronously process webhooks, we recommend implementing the four following steps: [**ingestion**](#ingestion), [**authentication**](#authentication), [**queueing**](#queueing) and [**processing**](#processing). ## Ingestion ### Receive the Webhook At its core, a webhook listener is an HTTP endpoint that Dwolla will call. As such, your endpoint must: * Be publicly accessible (Dwolla cannot send requests to `localhost`) * Be able to receive `POST` HTTP requests (Dwolla will not send `GET`, `PUT`, `PATCH`, etc.) * Have TLS enabled (with a valid SSL certificate issued to your domain) * Be able to handle at least 10 concurrency requests, unless a lower value has otherwise been configured for your application by Dwolla ### Return HTTP 2xx Status Code Once a webhook has fired to your endpoint (and you have received it), in order to fully "ingest" the webhook, you must respond to Dwolla with a 200-level status code (e.g., `200 OK`). Additionally, Dwolla must receive your response within 10,000 milliseconds (or, in other words, 10 seconds). If a response is not received in the allotted time, Dwolla will retry the request up to 8 times over the next 72 hours, according to our [back-off schedule](/docs/api-reference/webhook-subscriptions). Finally, if your application fails to response after 400 consecutive attempts and 24 hours has passed since the last success, [your webhook subscription will automatically pause](/docs/api-reference/webhook-subscriptions). ## Authentication When you [set up your webhook subscription](#step-1-create-a-webhook-subscription), you sent over a `secret` value to Dwolla. This value is a shared secret that only your application and Dwolla should have access to. Before Dwolla sends a webhook, the JSON-encoded payload is used in conjunction with the shared `secret` to create the `X-Request-Signature-SHA-256` header value, which is sent with the webhook request. When a webhook request is received, Dwolla recommends creating a SHA-256 HMAC signature of the JSON-encoded payload that your application receives with the shared `secret` as the key, and checking its value against the signature that Dwolla generated and supplied in the `X-Request-Signature-SHA-256` header. ```javascript theme={"dark"} const crypto = require("crypto"); const isSignatureValue = (body, signature) => signature === crypto .createHmac("sha256", process.env.DWOLLA_WEBHOOK_SECRET) .update(body) .digest("hex"); ``` ### Considerations and Limitations When implementing webhook authentication in your application, please consider the following: * Dwolla uses a highly dynamic and wide range of IP addresses, meaning that your application cannot use IP whitelisting to authenticate webhook requests. * The request body is already JSON-encoded prior to being sent. As such, the JSON body should never be re-encoded! If it is, the signatures will not match and authentication will fail, even if the request came from Dwolla. * Your webhook endpoint must have access to the request body in order to authenticate the request. This means that some services, such as AWS Lambda Authorizers, are unable to authenticate a webhook Dwolla. ## Queueing Once you ingest the webhook and authenticate that it is from Dwolla, instead of handling the business logic within the listener, we recommend passing the webhook off to a queue to be picked up by another Lambda function. By doing this, your webhook listener takes on the minimum amount of responsibility necessary, enabling your application to scale quickly and easily based on the volume of concurrent requests. Which messaging broker you use is up to you; however, we recommend using [AWS SQS](https://aws.amazon.com/sqs/) or [GCP Pub/Sub](https://cloud.google.com/pubsub) if your application resides in a serverless environment. If your application is self-hosted, [Redis](https://redis.io/) is also a good alternative to the options listed above. As an example, the following snippet will demonstrate how a webhook could be placed on an AWS SQS queue. In it `QueueUrl` would be the URL of your SQS queue, whereas `MessageBody` would be the "stringified" body of the webhook that Dwolla sends. (For more information on sending a message using AWS SQS, we recommend checking out their [official documentation](https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/sqs-examples-send-receive-messages.html#sqs-examples-send-receive-messages-sending).) ```javascript theme={"dark"} const { SQS } = require("aws-sdk"); const sqs = new SQS(); try { await sqs .sendMessage({ QueueUrl: "AWS_SQS_URL", MessageBody: JSON.stringify("WEBHOOK_JSON_BODY"), }) .promise(); } catch (e) { console.error("Failed to place webhook in SQS queue", e); } ``` ## Processing Now that the webhook has been ingested, authenticated, and queued, once the webhook has been fetched from the queue, we recommend checking for duplicate events and then handling any application-specific business logic. ### Check for Duplicate Events Although Dwolla tries to only send a webhook once, it is possible that the same webhook may be sent more than once. Because of this, it is recommended to check an internal database against the webhook's [Event](/docs/api-reference/events) ID to ensure that the same business logic is not processed multiple times. In other words, we recommend maintaining an internal database that keeps track of all of the events that are processed. Then, when a new webhook is received, your application ensures that the event (checked against its ID) has not already been processed previously. Additionally, once your application finishes processing an event, its ID is appended to your database, ensuring that if your application receives another webhook with the same event ID, it will not get processed a second time. Finally, when checking if an event has already been processed, it's important to note that multiple events can fire for the same resource while still remaining unique. For example, when a transfer is created, there may be cases where you receive `customer_transfer_created` twice with a different event ID but the same resource ID. This is because when a transfer is created, an event is triggered for both the sender and the receiver. It is for this reason that we recommend checking webhooks against their event ID, not by their resource ID or topic. When your application detects that you received an event that has already been processed, it is imperative that it returns with a 200-level status, such as 200 OK. If it responds with another value like 409 Conflict, this simply propagates irrelevant information and may result in your webhook getting automatically paused. ### Handle Business Logic Once you reach this point, you're finally ready to handle any business logic related to the webhook — for example, creating or updating a database entry, sending a notification email or triggering an alert. Although business logic will vary case by case based on your application's specific needs, in this final section, we will demonstrate how you can use an AWS Lambda function to pull the webhook off an SQS queue, and print it to the console. ```javascript theme={"dark"} module.exports.queueHandler = async (event) => { try { event.Records.forEach((record) => { const webhook = JSON.parse(record.body); console.log( `Received ${webhook.topic}, body=${JSON.stringify(webhook, null, 2)}` ); }); } catch (e) { console.error( "An unexpected error occurred while processing the queue.", e ); } }; ``` ## Conclusion We hope that this guide is helpful in getting your webhook listener/handler set up and configured for use with Dwolla! If you would like to take a closer look at the code that we used (or give it a try using your own account), check out our [webhook-receiver](https://github.com/Dwolla/webhook-receiver) repository on GitHub. # Frequently Asked Questions
  • Event — An event is a unique resource that gets created whenever an action occurs in Dwolla that changes the state of an API resource like a Customer being created or a funding source being verified.
  • Webhook Subscription — A webhook subscription is a resource in the API that you can create in order to subscribe to Dwolla webhooks.
  • Webhook — A webhook is an HTTP request that Dwolla sends to your subscribed URL to notify your app of an event. In order to get webhook notifications, you will need to have an active webhook subscription.

While a webhook subscription is not required for you to integrate with the API, Dwolla requires all applications to have one in production for automated notifications of events to your application. Webhooks provide automatic near real-time status updates to your application versus polling the API which causes unnecessary load on your application and the API.

Dwolla includes a X-Request-Signature-SHA-256 header on each webhook request which is a SHA-256 HMAC hash of the request body with the key being the webhook secret you passed in when you created the webhook subscription. As a best practice, we recommend validating webhooks by generating the same SHA-256 HMAC hash and comparing it to the signature sent with the payload.

We do not recommend nor support relying on IP whitelisting as a method of validating webhooks. Dwolla's IPs are dynamically allocated with no defined range and are subject to change. Refer to the Processing/Validating section for a more detailed guide.

Dwolla sends webhooks for all events that occur in your platform and there isn't a way to filter what events you subscribe to.

Dwolla automatically pauses a webhook subscription after 400 consecutive failed delivery attempts and sends an email to notify the Admin of the Dwolla account. While it's paused, Dwolla isn't able to send webhooks for new events to your URL. To resume webhooks, you need to address the issue that's causing failures , unpause the subscription and retry missed webhooks .

No. If your webhook subscription was previously paused or unavailable, Dwolla will not attempt to re-deliver webhooks. In this case, you can list all webhooks by subscription, filtering all that have zero attempts (in other words, the `attempts` array is empty), and retrying each webhook individually .

When listing all webhooks by webhook subscription , an embedded attempts array will include a response object that includes a statusCode property indicating the HTTP status code that Dwolla received when the webhook attempt was made. If the status code that Dwolla received was >=300, then the attempt was considered to have failed.

Yes. You can create up to 5 webhook subscriptions in Production and 10 in Sandbox. While only one subscription is needed to be notified of all events, you can have multiple in case one or more of your URLs becomes unreachable.

Dwolla's platform uses asynchronous processing to ensure high availability and performance at scale. During periods of elevated activity, there can be brief intervals where webhook notifications are delivered slightly ahead of when the updated status becomes available via the API.

To build a resilient integration, we recommend the following when processing webhooks:

  • Retry Logic — If an API call returns a status different from what the webhook indicated, implement retries with exponential backoff.
  • Retry Window — Retry for up to 5 minutes using exponential delays (e.g., 5 seconds, 15 seconds, 45 seconds, etc.).
  • Graceful Handling — Design your system to handle brief delays between webhook delivery and API consistency. This could be a short wait before retrying, or rejecting the webhook request so that Dwolla retries it according to its retry schedule.
# Dwolla Developers Source: https://developers.dwolla.com/index Build, test, and launch payment solutions with Dwolla's comprehensive API documentation and developer tools.
New: Dwolla-TypeScript SDK is now available Learn more →

The Unified API

Build, test, and launch payment solutions with Dwolla's comprehensive API documentation and developer tools.

Infrastructure
API Layer
Your Brand
ACH
RTP
FedNow
D YOUR BRAND
Payment Sent
\$225.00 Just now
ACH Same-Day
Recent Transfers
\$1,250
\$850
\$3,400
99.9% uptime
126M+ annual transaction volume
ACH, Same-Day ACH, RTP, FedNow, Push to Card
16+ years of payment experience
Platform capabilities

Everything you need to build powerful payment experiences

Dwolla's API provides a complete toolkit for account-to-account transfers, customer verification, and payment management.

Account-to-Account Transfers

Initiate money movement between bank accounts with a unified API.

ACH, Instant payments, and Wire transfers Flexible configuration of payment speed Real-time transfer status updates

Flexible End-user Types

Support various customer profiles with different verification levels.

Personal and Business — Identity Verified, Unverified, and Receive-only Users Tailored onboarding requirements by end-user type Progressive KYC based on user needs

Open Banking Services

Connect to bank accounts securely with open banking APIs.

Instant account verification Real-time bank account balance check Enhanced user experience

Webhooks

Real-time event notifications.

Transfer status notifications Customer verification updates Secure signature verification

Security & Compliance

Enterprise-grade security features.

SOC 2 Type 2 compliant Tokenization for sensitive data API key & OAuth authentication

Developer Tools

Resources to accelerate development.

SDKs for popular languages Sandbox testing environment API explorer & request builder

Payment Rails

Compare rails
ACH
1–3 business days
Same-Day ACH
Same business day
RTP
Seconds
FedNow
Seconds
Instant Payments
Seconds
Financial institutions play an important role in our network. All funds transfers made using the Dwolla Platform are performed by a financial institution partner, and any funds held in a Dwolla Balance are held by a financial institution partner. Learn more about our financial institution partners.