How ACH works: A developer perspective - Part 2

At Gusto, we rely heavily on the ACH network to pay employees and to remit various payroll taxes to federal and state agencies on behalf of our clients.

In part 1 of this post, I outlined the basics of how we originate ACH debits and credits. About 95% of the time, things go exactly as planned. This post will explain the other 5% cases (called ACH returns) and how they are handled.

Here's a few of the common reasons an ACH file we originate can get returned:

  1. Non-sufficient funds (or NSF) -- The bank account we're trying to debit does not have enough monies in it.
  2. Invalid account number -- The routing/account number we're trying to credit or debit does not exist (a routing number identifies the Bank to the Federal Reserve, and an account number identifies the customer's account to the Bank)
  3. Payment stopped -- The owner of the bank account we're trying to debit/credit told their bank that they did not authorize this transaction and wants it reversed.

In all, there are 69 different reasons an ACH file can get returned. I've posted the full list of ACH return codes.

How Returns are Handled

For the rest of this post, I'll talk specifically about returns on ACH debits (that is, us debiting money from a customer's account). If you want to know how returns on ACH credits work, you need only to switch the word "debit" with "credit" in this post.

If you recall from part 1 of this post, in order to debit a customer's account by $100.00, we need to SFTP an ACH file to our ODFI's servers. Our ODFI will send this file to the Federal Reserve that evening and and will increment our balance by $100.

A normal ACH transaction

It's important to note that our ODFI has incremented our balance without even knowing if Alice has the funds in our account, or if the account even exists. This is why finding a bank willing to be your ODFI is no easy task -- the bank takes on a small amount of risk by letting you originate ACH transfers through them.

When Alice's bank receives the ACH file from the Federal Reserve the following day, they generally have 24 hours after that to tell the Federal Reserve whether or not the payment should be returned. Depending on the return reason, the RDFI may or may not take the full 24 hours to response. For example, if the bank is returning the ACH file because of an invalid account number, they may return the ACH file immediately. If the bank returns the ACH file because of a NSF, the bank may wait for the entire 24 hours to see if their customer deposits money into their account before then.

The RDFI notifies the Federal Reserve that they are rejecting an ACH file by uploading an "ACH Return" file to the Federal Reserve. The contents of the return file say (1) which part of the ACH file they are rejecting (since an ACH file can contain multiple credits/debits in it) and (2) the reason for rejection (one of the Rxx) codes in the list of ACH return codes).

RDFI returns an ACH file

That evening, our ODFI will check in with the Federal Reserve to see if there are any ACH return files waiting for them. If there are, our ODFI will download the files, and then forward them to us by placing the file in the same SFTP server we originated the ACH file from. At the same time our ODFI will decrement our account by $100 to take back the money they originally credited us for. For record keeping, we'll see a debit of $100 on our bank statement with the description "ACH Return".

ODFI returns an ACH file

It's our job to to check the SFTP server every day, download and parse all the ACH response files, and update our systems when an ACH file gets returned. For example, at Gusto, if we originated an ACH debit because a company ran payroll, we'll cancel that payroll if the ACH file gets returned.

How Change Requests are Handled

If ACH returns are the developer's equivalent of an "error", ACH change requests are "warnings".

Sometimes, we will send an ACH file that isn't quite right, but is close enough for the RDFI to understand and process. In this case, the ACH transaction will go through, but the RDFI will upload an "ACH Change Request" file to the Federal Reserve, which then gets placed into our SFTP server through our ODFI. The change request file contains the correct information that we should have used in the ACH file. It's our job to check the SFTP server, parse the change request file, and update our system so that the next time we submit an ACH file to the RDFI, it contains the correct information.

For example, at Gusto, we'll automatically correct the customer's information in our system and shoot an email to our customer notifying them of the changes that their bank requested.

ACH change request

Here's a few of the common reasons an ACH file we originate can result in a change request:

  1. Incorrect name of account holder -- We might have used "Allie" instead of "Alice" as the name of the account we tried to debit.
  2. Savings/Checking selection switched -- We said we wanted to debit a checking account, when in fact the account is a savings account.
  3. Incorrect account number -- We used the wrong bank account number, but we were close enough that the bank knew which account we actually meant to use (perhaps through a checksum).

It's important to note that ACH is not a real-time system. Rather, things are processed in batches at 6:30pm EST, 12:30am EST, and 3:00am EST. As a result, funds can take days to settle. For a payroll company moving more than a billion dollars annually through the ACH network, it's critical that we account for these edge cases correctly and in an automated way. If there's interest in the comments, I'll write a part 3 of this post, detailing these timelines and how we account for them properly.

Comments on Hacker News


Provided below are ACH return codes and change codes.

ACH Return Codes

R00 Manually Cancelled
R01 Insufficient Funds
R02 Account Closed
R03 No Account/Unable to Locate Account
R04 Invalid Account Number Structure
R05 Unauthorized Debit to Consumer Account Using Corporate SEC Code
R06 Return per ODFI's Request
R07 Authorization Revoked by Customer
R08 Payment Stopped
R09 Uncollected Funds
R10 Customer Advises Not Authorized, Improper, or Ineligible
R11 Check Truncation Entry Return
R12 Account Sold to Another DFI
R13 Invalid ACH Routing Number
R14 Representative Payee Deceased or Unable to Continue in That Capacity
R15 Beneficiary or Account Holder Deceased
R16 Account Frozen
R17 File Record Edit Criteria
R18 Improper Effective Entry Date
R19 Amount Field Error
R20 Non-Transaction Account
R21 Invalid Company Identification
R22 Invalid Individual ID Number
R23 Credit Entry Refused by Receiver
R24 Duplicate Entry
R25 Addenda Error
R26 Mandatory Field Error
R27 Trace Number Error
R28 Routing Number Check Digit Error
R29 Corporate Customer Advises Not Authorized
R30 RDFI Not Participant in Check Truncation Program
R31 Permissible Return Entry
R32 RDFI Non-Settlement
R33 Return of XCK Entry
R34 Limited Participation DFI
R35 Return of Improper Debit Entry
R36 Return of Improper Credit Entry
R37 Source Document Presented for Payment
R38 Stop Payment on Source Document
R39 Improper Source Document/Source Document Presented for Payment
R40 Return of ENR Entry by Federal Government Agency
R41 Invalid Transaction Code
R42 Routing Number/Check Digit Error
R43 Invalid DFI Account Number
R44 Invalid Individual ID Number
R45 Invalid Individual Name/Company Name
R46 Invalid Representative Payee Indicator
R47 Duplicate Enrollment
R50 State Law Affecting RCK Acceptance
R51 Item related to RCK Entry is Ineligible or RCK Entry is Improper
R52 Stop Payment on Item Related to RCK Entry
R53 Item and RCK Entry Presented for Payment
R61 Misrouted Return
R67 Duplicate Return
R68 Untimely Return
R69 Field Error(s)
R70 Permissible Return Entry Not Accepted/Return Not Requested by ODFI
R71 Misrouted Dishonored Return
R72 Untimely Dishonored Return
R73 Timely Original Return
R74 Corrected Return
R75 Return Not a Duplicate
R76 No Errors Found
R80 IAT Entry Coding Error
R81 Non-Participant in IAT Program
R82 Invalid Foreign Receiving DFI Identification
R83 Foreign Receiving DFI Unable to Settle
R84 Entry Not Processed by Gateway
R85 Incorrectly Coded Outbound International Payment

ACH Change Codes

C01 Incorrect DFI Account Number
C02 Incorrect Routing Number
C03 Incorrect Routing Number and Incorrect DFI Account Number
C04 Incorrect Individual Name/Receiving Company Name
C05 Savings/Checking Selection is Wrong
C06 Incorrect DFI Account Number and Incorrect Transaction Code
C07 Incorrect Routing Number, Incorrect DFI Account Number, and Incorrect Transaction Code
C08 Incorrect Receiving DFI Identification
C09 Incorrect Individual Identification Number
C13 Addenda Format Error
C14 Incorrect SEC Code for Outbound International Payment
C61 Misrouted Notification of Change
C62 Incorrect Trace Number
C63 Incorrect Company Identification Number
C64 Incorrect Individual Identification Number/Identification Number
C65 Incorrectly Formatted Corrected Data
C66 Incorrect Discretionary Data
C67 Routing Number Not From Original Entry Detail Record
C68 DFI Account Number Not From Original Entry Detail Record
C69 Incorrect Transaction Code