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:
- Non-sufficient funds (or NSF) -- The bank account we're trying to debit does not have enough monies in it.
- 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)
- 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.
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).
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".
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.
Here's a few of the common reasons an ACH file we originate can result in a change request:
- Incorrect name of account holder -- We might have used "Allie" instead of "Alice" as the name of the account we tried to debit.
- Savings/Checking selection switched -- We said we wanted to debit a checking account, when in fact the account is a savings account.
- 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
Appendix
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 |