Payments Quickstart
This quickstart guide will demonstrate how you can use Modern Treasury's API to send an ACH payment to a new counterparty.
1. Retrieve your API Key
Once you have access to Modern Treasury, log in and go to your API Keys page. There you will find your Organization ID and API keys.
Authentication with the Modern Treasury API is done by 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.
curl --request GET \
-u ORGANIZATION_ID:API_KEY \
--url https://app.moderntreasury.com/api/pingfrom 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
When you create a payment order, you must specify which account you want the transaction to originate from. If you are sending someone money, this is the account that the money will be taken out of. If you are charging someone, this is the account that the money will be deposited into.
To get an internal account ID, you can pick one from your internal accounts page, which is here. Any of the IDs will work for testing, although you can refer to Internal Accounts to see the differences in how the test banks process payments.
Or, if you want to hit our API to get all the internal accounts, issue the following API request. You can see the documentation for internal accounts here.
In this example, we are planning to send a payment with the ACH payment type, so let us query for internal accounts that have the capability to send this type of payment.
curl --request GET \
-u ORGANIZATION_ID:API_KEY \
--url https://app.moderntreasury.com/api/internal_accounts?payment_type=achfrom 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",
)
internal_accounts = modern_treasury.internal_accounts.list({
"payment_type": "ach"
})
print(internal_accounts.items[0].id)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 internalAccounts = await modernTreasury.internalAccounts.list({
payment_type: 'ach'
});
console.log(internalAccounts.items[0].id);
}
main().catch(console.error);You should have received an array of all your internal accounts that can send payment orders with the payment type "ach". For more information on how we've set up your accounts, see the Sandbox section.
For this quickstart, we'll just use one of the account IDs to originate the payment order from. In this response, we'll use the first one, 0f8e3719-3dfd-4613-9bbf-c0333781b59f.
[
{
"id": "0f8e3719-3dfd-4613-9bbf-c0333781b59f",
"object": "internal_account",
...
},
...
][
{
"id": "0f8e3719-3dfd-4613-9bbf-c0333781b59f",
"object": "internal_account",
"account_type": null,
"party_name": "Modern Treasury",
"party_type": null,
"party_address": null,
"account_details": [
{
"id": "aaf74f7e-d697-4a73-95a3-05bede2edce6",
"object": "account_details",
"account_number": "23174905882992971",
"account_number_type": null,
"created_at": "2019-11-09T00:11:07Z",
"updated_at": "2019-11-09T00:11:07Z"
}
],
"routing_details": [
{
"id": "a3649136-f8d2-46e8-8b41-327bd7da3110",
"object": "routing_details",
"payment_type": null,
"routing_number": "021000021",
"routing_number_type": "aba",
"created_at": "2019-11-09T00:11:07Z",
"updated_at": "2019-11-09T00:11:07Z"
}
],
"connection": {
"id": "05487db0-e234-4ae8-914a-c627f76c987f",
"object": "connection",
"vendor_id": "example1",
"vendor_name": "Gringotts Wizarding Bank",
"created_at": "2019-11-09T00:11:07Z",
"updated_at": "2019-11-09T00:11:07Z"
},
"created_at": "2019-11-09T00:11:07Z",
"updated_at": "2019-11-09T00:11:07Z"
}
]3. Create a Counterparty
Let's onboard your first counterparty. For the purpose of this example, we can assume you are trying to send them money over ACH.
For more examples, see the section on Counterparty Example Requests.
curl --request POST \
-u ORGANIZATION_ID:API_KEY \
--url https://app.moderntreasury.com/api/counterparties \
-H 'Content-Type: application/json' \
-d '{
"name": "Kenner, Bach and Ledeen",
"accounts": [
{
"account_type": "checking",
"routing_details": [
{
"routing_number_type": "aba",
"routing_number": "121141822"
}
],
"account_details": [
{
"account_number": "123456789"
}
]
}
]
}'from modern_treasury import ModernTreasury
modern_treasury = ModernTreasury(
# defaults to os.environ.get("MODERN_TREASURY_API_KEY")
api_key="my-api-key",
organization_id="my-organization-id",
)
counterparty = modern_treasury.counterparties.create(
name="Kenner, Bach and Ledeen",
accounts=[
{
"account_type": "checking",
"routing_details": [
{
"routing_number_type": "aba",
"routing_number": "121141822"
}
],
"account_details": [
{
"account_number": "123456789"
}
]
}
]
)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 counterparty = await modernTreasury.counterparties.create({
"name": "Kenner, Bach and Ledeen",
"accounts": [
{
"account_type": "checking",
"routing_details": [
{
"routing_number_type": "aba",
"routing_number": "121141822"
}
],
"account_details": [
{
"account_number": "123456789"
}
]
}
]
});
console.log(counterparty.accounts[0].id);
}
main().catch(console.error);Note that the response returns a new Counterparty object with 1 attached external account. You will need the ID of the external account (5acec2ef-987b-4260-aa97-b719eeb0a8d5) for the next step.
{
"id":"37ba4454-dd33-4aa0-8906-0e2e4103e45c",
"object": "counterparty",
...
}{
"id":"37ba4454-dd33-4aa0-8906-0e2e4103e45c",
"object": "counterparty",
"name": "Kenner, Bach and Ledeen",
"email": null,
"send_remittance_advice": false,
"metadata": {},
"accounts": [
{
"id":"5acec2ef-987b-4260-aa97-b719eeb0a8d5",
"object": "external_account",
"account_type":"checking",
"party_name": "Kenner, Bach and Ledeen",
"party_type": null,
"party_address": null,
"account_details": [
{
"id":" 81a7cd32-39f5-4f0c-873f-4d9137ec9cd9",
"object": "account_detail",
"account_number_safe": "6789",
"account_number_type": "other",
"live_mode": true,
"created_at": "2019-11-09T00:11:07Z",
"updated_at": "2019-11-09T00:11:07Z"
}
],
"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"
}
],
"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"
}4. Create a Payment Order
Now we can create a Payment Order to send the Counterparty $10 over ACH. As we mentioned, you will use your internal account ID as the originating_account_id and the counterparty's external account ID as the receiving_account_id.
You can see additional sample requests for payment orders here.
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",
"receiving_account_id": "5acec2ef-987b-4260-aa97-b719eeb0a8d5"
}'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",
)
payment_order = modern_treasury.payment_orders.create(
type="ach",
amount=1000,
direction="credit",
currency="USD",
originating_account_id="0f8e3719-3dfd-4613-9bbf-c0333781b59f",
receiving_account_id="5acec2ef-987b-4260-aa97-b719eeb0a8d5"
)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 paymentOrder = await modernTreasury.paymentOrders.create({
"type": "ach",
"amount": 1000,
"direction": "credit",
"currency": "USD",
"originating_account_id": "0f8e3719-3dfd-4613-9bbf-c0333781b59f",
"receiving_account_id": "5acec2ef-987b-4260-aa97-b719eeb0a8d5"
})
console.log(paymentOrder);
}
main().catch(console.error);This is the response you will see. It contains the ID of the Payment Order, c5f4009c-bdd6-4cc1-84b2-17974ac9e77a, as well as additional details about it.
{
"id": "c5f4009c-bdd6-4cc1-84b2-17974ac9e77a",
"object": "payment_order",
...
}{
"id": "c5f4009c-bdd6-4cc1-84b2-17974ac9e77a",
"object": "payment_order",
"type": "ach",
"amount": 1000,
"direction": "credit",
"originating_account_id": "0f8e3719-3dfd-4613-9bbf-c0333781b59f",
"receiving_account": {
"id": "5acec2ef-987b-4260-aa97-b719eeb0a8d5",
"object": "external_account",
"account_type": "checking",
"party_name": "Kenner, Bach & Ledeen",
"party_type": null,
"party_address": null,
"account_details": [
{
"id": "81a7cd32-39f5-4f0c-873f-4d9137ec9cd9",
"object": "account_detail",
"account_number_safe": "6789",
"account_number_type": "other",
"created_at": "2019-11-09T00:11:07Z",
"updated_at": "2019-11-09T00:11:07Z"
}
],
"routing_details": [
{
"id": "5ceb251f-0235-48a2-81cb-0c668f5ee81b",
"object": "routing_detail",
"payment_type": null,
"routing_number": "121141822",
"routing_number_type": "aba",
"created_at": "2019-11-09T00:11:07Z",
"updated_at": "2019-11-09T00:11:07Z"
}
],
"created_at": "2019-11-09T00:11:07Z",
"updated_at": "2019-11-09T00:11:07Z"
},
"receiving_account_id": "5acec2ef-987b-4260-aa97-b719eeb0a8d5",
"receiving_account_type": "external_account",
"accounting_category_id": null,
"currency": "USD",
"effective_date": "2018-11-19",
"priority": "normal",
"description": null,
"statement_descriptor": null,
"remittance_information": null,
"metadata": {},
"status": "approved",
"counterparty_id": "37ba4454-dd33-4aa0-8906-0e2e4103e45c",
"transaction_ids": [],
"charge_bearer": null,
"foreign_exchange_indicator": null,
"foreign_exchange_contract": null,
"nsf_protected": false,
"created_at": "2019-11-09T00:11:07Z",
"updated_at": "2019-11-09T00:11:07Z"
}5. Monitor payment status
To stay updated about your account, you may either poll our API via requests or receive webhooks. You may configure a URL to use for webhook testing on your webhooks page. We recommend using a tool such as ngrok for this.
Payment Orders initially have a status of approved. As a Payment Order progresses through its lifecycle, you will receive updates on its status. See the section on payment order webhooks for more details.
Expected Payments initially have a status of unreconciled. When an Expected Payment is automatically completed in the sandbox, it will become reconciled. If you archive an Expected Payment, it will become archived.
Updated 13 days ago