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 transaction fails, the National Automated Clearing House Association (Nacha) assigns a return code. Understanding these codes—and acting on them programmatically—is critical for any developer building payout infrastructure. A mishandled return can leave a user's payment stuck, your reconciliation broken, and your support queue full.
This guide covers the most common ACH return codes, what triggers them, and how to implement detection and recovery logic.
The Most Common ACH Return Codes
| Code | Reason | Typical Cause | Retry? |
|---|---|---|---|
| R01 | Insufficient funds | Account balance too low | Yes (after funds available) |
| R02 | Account closed | Receiver closed the account | No |
| R03 | No account/unable to locate | Account number invalid or closed | No |
| R04 | Invalid account number | Routing or account malformed | No |
| R05 | Reserved (not used) | — | — |
| R06 | Returned per customer request | User asked bank to block it | No |
| R07 | Authorization revoked | Recurring payment authorization canceled | No |
| R08 | Payment stopped | Payer initiated stop-payment | No |
| R09 | Uncollected funds | Funds not yet available in account | Yes (wait & retry) |
| R10 | Subject to offset | Debt offset or legal hold | No (escalate) |
R01 (Insufficient Funds) fires when the receiver's account doesn't have enough balance. This is retriable—wait 1–3 business days and retry. Many users receive paychecks or transfers that settle overnight.
R02, R03, R04 indicate permanent account issues. Don't retry these; instead, prompt the user to verify their bank details or add an alternate payout method.
R06–R08 are user-initiated blocks. These require manual intervention or customer support escalation.
R09 (Uncollected Funds) is subtle: the account exists and has some funds, but not enough immediately available (e.g., a recent deposit on hold). Retry after 2–3 days.
Detecting and Decoding Returns Programmatically
ACH returns arrive via your processor's webhook or API. Most modern payout platforms (Stripe, PayPal, Wise, Payoneer, and direct ACH providers like Dwolla or Plaid) deliver return notifications with a standardized return code field.
Here's a minimal example using a hypothetical webhook payload:
{
"event": "payout.returned",
"payout_id": "po_abc123",
"return_code": "R01",
"return_reason": "Insufficient funds",
"recipient_account": "****5678",
"amount_cents": 50000,
"timestamp": "2024-01-15T14:32:00Z"
}
And a simple handler in Node.js:
async function handleAchReturn(webhook) {
const { payout_id, return_code, amount_cents } = webhook;
// Log for reconciliation
console.log(`Return: ${payout_id} code=${return_code}`);
// Determine retry eligibility
const retriableCodes = ['R01', 'R09'];
const isRetriable = retriableCodes.includes(return_code);
if (isRetriable) {
// Schedule retry after 2–3 business days
await scheduleRetry(payout_id, 3);
await notifyUser(payout_id, 'Your payout failed. We'll retry in 3 days.');
} else {
// Permanent failure: flag for manual review
await flagPayoutForReview(payout_id, return_code);
await notifyUser(payout_id, 'Please verify your bank details and try again.');
}
// Update payout status
await updatePayoutStatus(payout_id, 'returned', { return_code });
}
Handling Returns Without Breaking Your Flow
Retry strategy: For R01 and R09, implement exponential backoff. Retry once after 2 days, then again after 5 days. If it fails twice, escalate to customer support.
Alternate rails: If ACH fails, offer Visa Direct or RTP as fallbacks. These settle faster and have different failure modes.
Dunning: Track return frequency per user. If a user receives 3+ returns in 30 days, require them to verify their account or switch payment methods.
Reconciliation: Store the return code and timestamp in your database. At end-of-month, reconcile returned amounts against your processor's settlement reports.
Key Takeaways
- **R
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)