Modern Treasury API Documentation

Welcome to Modern Treasury. You'll find comprehensive guides and documentation to help you start working with Modern Treasury APIs as quickly as possible, as well as support if you get stuck.

Virtual Accounts

Overview

The Virtual Accounts API offers an easy way to create and manage virtual accounts at your bank. A virtual account, also commonly referred to as a subaccount, is an account within a bank account. They can be used to manage transactions for individual customers or vendors, or to segment funds for different use cases or parts of your company. In some cases, virtual accounts can be a replacement for the need to have multiple DDA or FBO bank accounts.

To use the virtual accounts API, your bank must be able to create virtual account numbers (VAN) for your bank account. A VAN is an account number than can be used when transacting with your bank account. For example, let's say you have a bank account with an account number of 12345 and routing number 121141822. Your bank would allocate you a range of virtual account numbers that would map to your bank account. Let's say the range is 200xxxx. This means you can create a virtual account with a VAN of 2000001 and another with a VAN of 2000002.

These virtual accounts can be used to initiate or receive payments. When initiating payments, you would simply specify which virtual account to originate the payment order from. When receiving payments, your counterparty would use your virtual account number and routing number.

Creating a virtual account

The first step to using virtual accounts is to create one using our API.

curl --request POST \
  -u ORGANIZATION_ID:API_KEY \
  --url https://app.moderntreasury.com/api/virtual_accounts \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "Funds on behalf of Alice Jones",
    "internal_account_id": "c743edb7-4059-496a-94b8-06fc081156fd",
    "account_details": [
      {
        "account_number": "2000001",
        "account_number_type": "other"
      }
    ]
  }'

You will get a response which includes the full details of the virtual account, including the routing details which are inherited from the internal account.

{
  "id": "2e0296c1-1daf-4b3e-954d-fb9ec7be56f6",
  "object": "virtual_account",
  "name": "Funds on behalf of Alice Jones",
  "description": null,
  "active": true,
  "counterparty_id": null,
  "internal_account_id": "c743edb7-4059-496a-94b8-06fc081156fd",
  "debit_ledger_account_id": "f5cc945c-ac46-4f05-b96b-f13f9c6a1cdd",
  "credit_ledger_account_id": "c39d893d-4944-419e-816e-1266387f2ec5",
  "account_details": [
    {
      "id": "5668c0cf-972d-49c6-970f-b32591f3e8a6",
      "object": "account_detail",
      "account_number": "2000001",
      "account_number_type": "other"
    }
  ],
    "routing_details": [
    {
      "id":"5ceb251f-0235-48a2-81cb-0c668f5ee81b",
      "object": "routing_detail",
      "payment_type": null,
      "routing_number": "121141822",
      "routing_number_type": "aba",
      "bank_name": "BANK OF AMERICA CALIFORNIA, NA",
      "bank_address": {
        "id": "2f1e12dd-de80-44aa-92cd-f0e4101b8e54",
        "object": "address",
        "line1": "PO BOX 27025",
        "line2": null,
        "locality": "RICHMOND",
        "region": "VA",
        "postal_code": "23261-7025",
        "country": "US",
        "live_mode": true,
        "created_at": "2019-11-09T00:11:07Z",
        "updated_at": "2019-11-09T00:11:07Z"
      },
      "live_mode": true,      
      "created_at": "2019-11-09T00:11:07Z",
      "updated_at": "2019-11-09T00:11:07Z"
    }
  ],
  "metadata": {},
  "live_mode": true,
  "created_at": "2020-11-09T00:11:07Z",
  "updated_at": "2020-11-09T00:11:07Z"
}

Creating a virtual account for a counterparty

Our API supports linking a virtual account to a counterparty. This may be helpful if you are holding funds on behalf of someone, or if you want to link a set of transactions to a counterparty. Your bank may require you to associate the virtual account to a counterparty as well. The API call for creating a virtual account linked to a counterparty would look like this.

curl --request POST \
  -u ORGANIZATION_ID:API_KEY \
  --url https://app.moderntreasury.com/api/virtual_accounts \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "Funds on behalf of Alice Jones",
    "internal_account_id": "c743edb7-4059-496a-94b8-06fc081156fd",
    "counterparty_id": "b4313c66-3892-416d-995f-f5b6044b5c7a",
    "account_details": [
      {
        "account_number": "2000001",
        "account_number_type": "other"
      }
    ]
  }'

Receiving payments in a virtual account

Virtual accounts can be helpful when you want a counterparty to send money to you or pull funds from your account. Before a payment like this, you would provide the counterparty with your virtual account number, 2000001, and your routing number, 121141822.

For this example, we will assume the counterparty has pulled $10 from your account. There are three events that can happen. As you build out your integration, you can decide which events to process.

First, you may receive an Incoming Payment Detail webhook. The event will be incoming_payment_detail.created. The incoming payment detail (IPD) will include details about the transaction that is about to hit your bank account. IPDs may arrive days before the transaction actually hits your account, like in the case of payroll. The object's data will include information like the expected settlement date.

Second, when the transaction hits your account, you will initially receive a transaction.created webhook for the pending transaction. Pending transactions usually come via intraday data from the bank.

Finally, you will receive another transaction.created webhook for the posted transaction. This is usually the following banking day after when the pending transaction arrived.

The transaction webhook body will include the virtual_account_id. A sample transaction webhook would look like this.

{
  "event": "created",
  "data": {
    "amount": 1000,
    "as_of_date": "2019-12-12",
    "as_of_time": null,
    "created_at": "2019-12-12T23:01:17Z",
    "currency": "USD",
    "details": {},
    "direction": "debit",
    "id": "30dd4826-732f-4fe5-ab15-fc903f85ffdd",
    "internal_account_id": "b9fc1ae0-d493-4f01-a7b3-b39104e802b5",
    "metadata": {},
    "object": "transaction",
    "posted": true,
    "reconciled": true,
    "updated_at": "2019-12-12T23:01:32Z",
    "vendor_code": "469",
    "vendor_code_type": "bai2",
    "vendor_customer_id": null,
    "vendor_description": "ACH PAYMENT",
    "vendor_id": null,
    "virtual_account_id": "880813d6-1253-4650-8a09-c4a55ba5a430"
  }
}

There may be cases where you want to initiate a return against a transaction you've received, such as if the transaction was unexpected or for too much money. In that case, you can originate an ACH return using the incoming payment detail's ID.

Initiating payment orders from a virtual account

To initiate a payment order from a virtual account, you include the virtual_account_id in the request payload for the payment order. Note that the virtual account must be a part of the internal account that is used as the originating bank account.

curl --request POST \
  -u ORGANIZATION_ID:API_KEY \
  --url https://app.moderntreasury.com/api/payment_orders \
  -H 'Content-Type: application/json' \
  -d '{
    "type": "ach",
    "amount": 1000,
    "direction": "credit",
    "currency": "USD",
    "originating_account_id": "0f8e3719-3dfd-4613-9bbf-c0333781b59f",
    "virtual_account_id": "86b9b625-e148-4cbe-9761-83dd108a8f1e",
    "receiving_account_id": "5acec2ef-987b-4260-aa97-b719eeb0a8d5"
  }'

The transaction that is ultimately created because of this payment order will be linked to the virtual account, in the same way that a transaction received in a virtual account is linked to it.

Supported Banks

We currently support virtual accounts at Metropolitan Commercial Bank. We plan to add support for additional banks soon, including Perfect Receivables at Wells Fargo, Virtual ACH Accounts at Silicon Valley Bank, and Virtual Accounts at JP Morgan Chase.

Updated 3 months ago


Virtual Accounts


Suggested Edits are limited on API Reference Pages

You can only suggest edits to Markdown body content, but not to the API spec.