ACH Return Codes Explained: R01–R85 and How to Handle Them in Production
ACH Return Codes Explained: R01–R85 and How to Handle Them in Production
When an ACH transfer fails, your payout system receives a return code. Understanding what that code means—and how to act on it—is the difference between a graceful retry and a stuck transaction.
The National Automated Clearing House Association (Nacha) defines 85 standardized return codes (R01 through R85). Each one tells you why a debit or credit failed, and each demands a different handling strategy. This guide covers the most common codes you'll encounter in production and how to decode them programmatically.
The Big Three: R01, R03, R10
R01: Insufficient Funds
The account exists, the routing number is correct, but there's not enough money to cover the debit.
- When it fires: During the settlement window, typically 1–2 business days after initiation.
- What to do: Flag the transaction as failed. Retry after 3–5 days (account may have been topped up), or route to an alternate rail (Visa Direct, RTP) if speed matters. Don't retry immediately; it will fail again.
R03: No Account / Unable to Locate Account
The routing number is valid, but the account number doesn't exist or has been closed.
- When it fires: During settlement, sometimes within 24 hours.
- What to do: This is terminal. Mark the transaction failed permanently. Contact the recipient to verify their account details. No retry will succeed.
R10: Unauthorized / Customer Advises Not Authorized
The account holder claims they didn't authorize this debit.
- When it fires: Usually 10–60 days after initiation (ACH allows disputes in this window).
- What to do: Investigate immediately. Check your authorization records. If legitimate, respond with documentation. If not, accept the chargeback and reverse the payout in your system.
Other Critical Codes
| Code | Meaning | Retry? | Action |
|---|---|---|---|
| R02 | Account Closed | No | Verify account; request new details |
| R04 | Invalid Routing Number | No | Correct routing number or request new account |
| R05 | Unauthorized User / Account Type Mismatch | No | Verify account holder and account type |
| R07 | Authorization Revoked | No | Account holder revoked permission; get new auth |
| R08 | Payment Stopped | No | Recipient stopped this specific payment |
| R09 | Uncollected Funds | Yes | Retry after 3–5 days |
| R16 | Duplicate Entry | No | Check for duplicate in your system; don't resubmit |
| R20 | Improper Effective Entry Date | No | Correct date and resubmit (rare in modern systems) |
| R29 | Corporate Customer Advises Not Authorized | No | Same as R10; investigate |
Building a Return Handler
Here's a minimal pattern for handling returns programmatically:
async function handleAchReturn(returnCode, transactionId) {
const transaction = await db.getTransaction(transactionId);
// Terminal failures: no retry
const terminalCodes = ['R03', 'R02', 'R04', 'R05', 'R07', 'R08', 'R16'];
if (terminalCodes.includes(returnCode)) {
await transaction.markFailed('permanent');
await notifyUser(transaction.recipientId,
`Payment failed. Please verify account details.`);
return;
}
// Retryable: insufficient funds, uncollected funds
const retryableCodes = ['R01', 'R09'];
if (retryableCodes.includes(returnCode)) {
transaction.retryCount++;
if (transaction.retryCount < 3) {
const retryDate = addDays(new Date(), 5);
await transaction.scheduleRetry(retryDate);
return;
}
// Max retries exceeded
await transaction.markFailed('retries_exhausted');
return;
}
// Disputes: investigate
if (['R10', 'R29'].includes(returnCode)) {
await transaction.markPending('dispute');
await escalateToCompliance(transaction);
return;
}
// Unknown code
await transaction.markPending('unknown_return');
}
Timing Matters
ACH returns arrive in batches, typically 1–2 business days after settlement. Your reconciliation must account for this lag. If you're tracking payout status in real time, don't mark a transaction "complete" until you've confirmed no return arrived within the settlement window (usually 5 business days for most codes, up to 60 for disputes).
Decoding ACH return codes programmatically? The ACH Return Codes API returns the full Nacha R01–R85 set with plain-language descriptions and handling guidance.
Top comments (0)