ACH Transfers

An Automated Clearing House (ACH) transfer is an electronic funds transfer between two accounts at different banks. It allows for for credits (sending money) and debits (receiving money), and is generally cheaper when compared to other available methods, but also most-often slower. This guide will examine the process of sending money to another account using ACH (also known as a credit), though you can also receive money from an account (a debit) using this same method.

This guide uses the following endpoints

To create an ACH transfer you will need to

  1. Get the account ID
  2. Get the Counterparty ID
  3. Create the ACH transfer
  4. Get status updates

1. Get the Account ID

The first step in creating an ACH transfer is to obtain the ID of the account the funds will be transferred from.

⚠️

Both a savings and checking account have been pre-created in the Developer Sandbox for testing purposes; however, you can also create and use your own bank accounts if you prefer.

To retrieve an account ID, make a GET request to the account endpoint.

curl -u $API_KEY_ID:$API_SECRET_KEY https://api.treasuryprime.com/account

This will return a list of of Account objects. In order to make an ACH transfer, you will need the account ID which is stored in the id property of each account object.

{
  "data": [
    {
      "account_type": "savings",
      "bank_id": "bank_treasuryprime",
      "updated_at": "2021-02-01T16:32:34Z",
      "currency": null,
      "routing_number": "000000000",
      "account_number": "123000012345",
      "id": "acct_qda4pJZfpzn4fc",
      "created_at": "2021-01-13T15:05:14Z",
      "userdata": null
    },
    {
      "account_type": "checking",
      "bank_id": "bank_treasuryprime",
      "updated_at": "2021-02-01T16:32:34Z",
      "currency": null,
      "routing_number": "000000000",
      "account_number": "123000067890",
      "id": "acct_wVwR87rxhMRdwD",
      "created_at": "2021-01-13T15:05:13Z",
      "userdata": null
    }
  ],
  "total_estimated": 10
}

2. Get the Counterparty ID

The next piece of data required for an ACH is the ID of the Counterparty (the person or entity on the other side of that transaction) or put more simply: Who the funds are being transferred to. If you have not yet created a Counterparty in your Developer Sandbox, read the next section for instructions on how to accomplish this. Otherwise, feel free to skip ahead to Retrieve an existing Counterparty.

Create a Counterparty

Creating a Counterparty requires making a POST request to the counterparty endpoint, and passing along the name of person or entity who owns the account, as well as three pieces of data required for ACH:

  1. The ACH specific bank account number
  2. The account type (checking or savings)
  3. The routing number
curl -u $API_KEY_ID:$API_SECRET_KEY https://api.treasuryprime.com/counterparty \
  -H 'Content-Type: application/json' \
  -d '{
        "name_on_account": "Adam Smith",
        "ach": {
          "account_number": "12345678",
          "account_type": "checking",
          "routing_number": "87654321"
        }
      }'

Assuming no errors are encountered, the response will contain a Counterparty object containing an id property representing the counterparty ID.

{
  "created_at": "2021-02-25T01:23:24Z",
  "updated_at": "2021-02-25T01:23:24Z",
  "ach": {
    "account_number": "12345678",
    "account_type": "checking",
    "routing_number": "87654321"
  },
  "wire": null,
  "name": "Adam Smith",
  "name_on_account": "Adam Smith",
  "id": "cp_v1p3ay8w4b0x",
  "userdata": null
}

Retrieve an Existing Counterparty

To find the IDs of any existing Counterparty objects, make a GET request to the counterparty endpoint

curl -u $API_KEY_ID:$API_SECRET_KEY https://api.treasuryprime.com/counterparty

This will return a list of all existing Counterparty objects you have created (or empty if none are found). Each Counterparty object contains an id property representing the counterparty ID.

{
  "data": [
    {
      "created_at": "2021-02-25T01:23:24Z",
      "updated_at": "2021-02-25T01:23:24Z",
      "ach": {
        "account_number": "12345678",
        "account_type": "checking",
        "routing_number": "87654321"
      },
      "wire": null,
      "name": "Adam Smith",
      "name_on_account": "Adam Smith",
      "id": "cp_v1p3ay8w4b0x",
      "userdata": null
    },
    ...
  ]
}

3. Create the ACH Transfer

With the Account ID, and Counterparty ID, the ACH transfer can now be made. But first a final note on two additional properties. The direction property specifies whether this transfer is a credit or debit (in this case a credit since money is being sent), and the sec_code which specifies the type of transfer according to the ACH Network. More details on those topics can be found in the ACH documentation.

The protocol that underlies the ACH network is batch-oriented and does not provide success responses (only error responses). As a result, ACH transfers follow a particular workflow as they are processed by the network. You can track a transfer's progress by checking the status attribute on the ACH object.

Enough details, let's make the transfer!

curl -u $API_KEY_ID:$API_SECRET_KEY https://api.treasuryprime.com/ach \
    -H 'Content-Type: application/json' \
    -d '{
          "account_id": "acct_1234567890",
          "amount": "100.00",
          "counterparty_id": "cp_0987654321",
          "direction": "credit",
          "sec_code": "ppd"
        }'

If no errors were encountered, a new ACH Object is returned. To find out when the transfer is complete requires watching for status updates which will be covered next.

{
  "description": null,
  "amount": "100.00",
  "service": "standard",
  "counterparty_id": "cp_0987654321",
  "bankdata": null,
  "bank_id": "bank_treasuryprime",
  "account_id": "acct_1234567890",
  "addenda": [],
  "org_id": "org_treasuryprime",
  "batch_key": null,
  "effective_date": "2021-02-25",
  "updated_at": "2021-02-25T01:32:57Z",
  "status": "pending",
  "id": "ach_5merj00eudnp",
  "error": null,
  "sec_code": "ppd",
  "direction": "credit",
  "created_at": "2021-02-25T01:32:57Z",
  "userdata": null
}

4. Get Status Updates

There are two ways to find out when the status of the ACH transfer has changed:

Manually Checking for Status Updates

By making a GET request to the ach endpoint and passing in the id of the ACH transfer you'd like the status for. This will return an ACH object with the latest status.

curl -u $API_KEY_ID:$API_SECRET_KEY https://api.treasuryprime.com/ach/:id

Note the status has been changed to "sent".

{
  "description": null,
  "amount": "100.00",
  "service": "standard",
  "counterparty_id": "cp_0987654321",
  "bankdata": null,
  "bank_id": "bank_treasuryprime",
  "account_id": "acct_1234567890",
  "addenda": [],
  "org_id": "org_treasuryprime",
  "batch_key": null,
  "effective_date": "2021-02-25",
  "updated_at": "2021-01-04T20:30:27Z",
  "status": "sent",
  "id": "ach_5merj00eudnp",
  "error": null,
  "sec_code": "ppd",
  "direction": "credit",
  "created_at": "2021-02-25T01:32:57Z",
  "userdata": null
}

Listening for Status Updates with Webhooks

By registering a webhook to respond to the ach.update event, you can be notified of changes in the status of the ACH transfer when they occur.

curl https://api.treasuryprime.com/webhook \\
    -u "$API_KEY_ID:$API_SECRET_KEY" \\
    -H 'Content-Type: application/json' \\
    -d '{
           "event": "ach.update",
           "url": "https://example.application.com/notify"
        }'

Once the ach.update webhook notification is received, follow the steps outlined above to obtain the latest status of the transfer, or simply make a GET request to the URL specified in the url property of the data object.

{
  "event": "ach.update",
  "op": "update",
  "id": "ach_01123456789",
  "url": "https://api.treasuryprime.com/ach/ach_01123456789"
}

What’s Next

Once the ACH Transfer object reaches a status of "sent", then the transfer is complete! Congratulations on making your first ACH transfer. If you'd like to dig deeper, check out the ACH API documentation. And since you're already on a hot-streak, why not try out Issuing Debit Cards?