Payments Quickstart

This guide walks you through authenticating with the Modern Treasury API, setting up an account, and sending your first payment.

1. Retrieve your API Key

Log in to Modern Treasury and navigate to your API Keys page. You'll find your Organization ID and API keys there.

All requests authenticate via HTTP Basic Auth, with your Organization ID as the username and API key as the password. The cURL examples below pass these with the -u flag.

If you're using a server-side SDK, you can pass these values directly or let the library read them from 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. Retrieve your Internal Account ID

Payment orders originate from one of your internal accounts. That's the account money leaves when you send, and the account money lands in when you charge.

You'll need an internal account ID for the next steps. Copy one from the Internal Accounts page in the dashboard, or retrieve it programmatically via the List Internal Accounts endpoint.

Since this example sends an ACH payment in USD, filter by payment_type=ach and currency=USD to return only accounts that support this combination:

curl https://app.moderntreasury.com/api/internal_accounts?payment_type=ach&currency=USD \
  --header 'content-type: application/json' \
  --user $ORGANIZATION_ID:$API_KEY 

Reference the id from the internal account object for step 4.

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

3. Create a Counterparty

Before you can send a payment, you need to create a counterparty — an external party (person or business) who will send or receive funds. Each counterparty includes one or more bank accounts.

The following request creates a counterparty named Harry Potter with a checking account:

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

Reference the id from the external account object created for step 4.

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

4. Fund the Account

Now that you have both account IDs, you can create a Payment Order to pull funds into your internal account via ACH debit. Amounts are specified in the smallest currency unit (cents for USD), so 10000 represents $100.00.

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'll receive the payment order object:

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

In the Sandbox, payment orders settle within minutes. Subscribe to webhooks to receive status updates in real time:

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

Once the payment order reaches completed status, your internal account is funded and you're ready to originate outbound payments. Note that this can take up to 5 minutes for an ACH payment in the Sandbox.