Bring Your Own Bank Quickstart

This guide demonstrates how to use our APIs to initiate payments out of your business bank account that is integrated with Modern Treasury.

When you bring your own bank, Modern Treasury connects to accounts that you already own and orchestrates payments, reconciliation, and reporting against those accounts. In the Sandbox you will create two fake banks Gringotts Wizarding Bank (GWB) and The Iron Bank of Braavos (IBB) so you can exercise the full flow without waiting on a real bank implementation.

1. Create a Sandbox and Retrieve your API Key

If you don’t already have a Modern Treasury organization, sign up for a free Sandbox at https://app.moderntreasury.com/sign_up. You’ll verify your email, name your organization, and be dropped into a sandbox environment.

Once you’re in, head to your API Keys page. There you will find your Organization ID and API keys.

Authentication with the Modern Treasury API is done using HTTP Basic authentication, with your Organization ID as the username and the API Key as the password. When using curl, you can use the -u option to pass these values directly.

If using one of our server side SDKs, you can either pass these values in directly or the library will pull from defined environment variables.

Start with a simple ping request to test your credentials:

curl --user $ORGANIZATION_ID:$API_KEY https://app.moderntreasury.com/api/ping
from modern_treasury import ModernTreasury

modern_treasury = ModernTreasury(
  # defaults to os.environ.get("MODERN_TREASURY_API_KEY")
  api_key="your-api-key",
  organization_id="your-organization-id",
)

pong = modern_treasury.ping()
print(pong)
import ModernTreasury from 'modern-treasury';

const modernTreasury = new ModernTreasury({
  apiKey: 'your-api-key', // defaults to process.env["MODERN_TREASURY_API_KEY"]
  organizationId: 'your-organization-id',
});

async function main() {
  const pong = await modernTreasury.ping()
  console.log(pong);
}

main().catch(console.error);

2. Create a Bank Connection

Every Internal Account lives on a Connection to a partner bank. In Production, Modern Treasury provisions this connection as part of your bank integration with our Bank Integrations team. In the Sandbox, you can create a simulated connection yourself.

There are two built-in sandbox banks you can choose from:

  • Gringotts Wizarding Bank (GWB) — Payment Orders are relayed to the bank every minute and Transactions are posted immediately after. Use GWB for end-to-end integration testing (originating payments, receiving webhooks, automatic reconciliation).
  • The Iron Bank of Braavos (IBB) — Does not process any Payment Orders. Expected Payments only reconcile when you upload a matching Transaction. Use IBB for reconciliation rule and exception testing.

For this quickstart we’ll use GWB so we can originate a real payment end-to-end.

curl https://app.moderntreasury.com/api/connections \
  --header 'content-type: application/json' \
  --user $ORGANIZATION_ID:$API_KEY \
  --data '{
    "entity_id": "example1",
    "nickname": "GWB"
  }'

In response you will receive the following:

{
  "id": "<Connection ID>",
  "object": "connection",
  "live_mode": false,
  "vendor_id": "example1",
  "vendor_name": "Gringotts Wizarding Bank"
}

Grab the id — you will need it to open an Internal Account on this connection.

📘

Going to Production

In Production, you do not create connections via the API. Reach out to your CSM or [email protected] to kick off the bank integration process. Once your partner bank connection is provisioned, the steps below work identically against your real accounts.

3. Open an Internal Account

With a Connection in place, you can open an Internal Account on it. This account represents one of your actual bank accounts.

curl https://app.moderntreasury.com/api/internal_accounts \
  --header 'content-type: application/json' \
  --user $ORGANIZATION_ID:$API_KEY \
  --data '{
    "connection_id": "<Connection ID>",
    "name": "Operating Account",
    "party_name": "Your Company",
    "currency": "USD"
  }'

In response you will receive the following:

{
  "id": "<Internal Account ID>",
  "object": "internal_account",
  "connection": {
    "id": "<Connection ID>",
    "vendor_name": "Gringotts Wizarding Bank"
  },
  "routing_details": [
    {
      "routing_number_type": "aba",
      "routing_number": "021214891"
    }
  ],
  "account_details": [
    {
      "account_number": "<Unique Account Number>"
    }
  ]
}

4. Create a Counterparty

Before you can move money, create a Counterparty with an External Account that represents the bank account on the other side of the payment. Additional information on counterparty testing can be found here

curl https://app.moderntreasury.com/api/counterparties \
  --header 'content-type: application/json' \
  --user $ORGANIZATION_ID:$API_KEY \
  --data '{
    "name": "Kenner, Bach and Ledeen",
    "accounts": [
      {
        "account_type": "checking",
        "routing_details": [
          {
            "routing_number_type": "aba",
            "routing_number": "121141822"
          }
        ],
        "account_details": [
          {
            "account_number": "123456789"
          }
        ]
      }
    ]
  }'

In response you will receive the following:

{
  "id": "<Counterparty ID>",
  "object": "counterparty",
  "accounts": [
    {
      "id": "<External Account ID>",
      "object": "external_account"
    }
  ]
}

5. Fund the Account

Now that all necessary objects have been created, you can execute your first ACH Debit to fund the Internal Account. Amounts are given in the smallest unit of the currency, so for $100.00, the amount field is 10000.

curl https://app.moderntreasury.com/api/payment_orders \
  --header 'content-type: application/json' \
  --user $ORGANIZATION_ID:$API_KEY \
  --data '{
    "type": "ach",
    "amount": 10000,
    "direction": "debit",
    "currency": "USD",
    "originating_account_id": "<Internal Account ID>",
    "receiving_account_id": "<External Account ID>"
  }'

In response you will receive the following:

{
  "id": "<Payment Order ID>",
  "object": "payment_order",
  "status": "approved"
}

Because the Internal Account is on a GWB Connection, the Payment Order will be relayed to the sandbox bank within a minute and a Transaction will be posted shortly after. You can subscribe to webhooks to receive status changes such as the following:

{
  "event": "completed",
  "data": {
    "id": "<Payment Order ID>",
    "object": "payment_order",
    "status": "completed"
  }
}

When the [Payment Order reaches a completed state, your account has been successfully funded. You can now originate outbound payments using these funds.