ACH Return Codes Explained: R01, R03, R10 & How to Handle Them
ACH Return Codes Explained: R01, R03, R10 & How to Handle Them
When you build a payout system, ACH returns are inevitable. A transaction that looked solid at submission can bounce back days later with a cryptic two-character code. Understanding what those codes mean—and how to respond—is the difference between a resilient payment system and one that leaks money and user trust.
The National Automated Clearing House Association (Nacha) defines 85 possible return codes (R01 through R85). Most fall into a few categories: insufficient funds, account issues, authorization problems, and format errors. Let's walk through the most common ones you'll encounter in production.
R01: Insufficient Funds
What it means: The receiver's account doesn't have enough money to cover the debit.
When it fires: 1–2 business days after the ACH originates, during the bank's settlement window.
How to handle it:
- Log the return immediately with a timestamp and reason code.
- Notify the recipient (if you're a disbursement platform) or the sender (if you're collecting).
- Decide: retry in 2–3 days (funds may arrive), or escalate to manual review.
- Don't retry automatically more than 2–3 times; each retry costs you a return fee (~$0.25–$1.50 per return, depending on your processor).
{
"return_code": "R01",
"description": "Insufficient funds",
"amount_cents": 50000,
"receiver_account": "****1234",
"original_trace_number": "123456789012345",
"return_timestamp": "2024-01-16T14:32:00Z",
"action": "notify_and_queue_retry",
"retry_after_days": 3
}
R03: No Account / Account Closed
What it means: The account number doesn't exist, or the account was closed before the ACH arrived.
When it fires: Usually within 1 business day, sometimes same-day.
How to handle it:
- This is a terminal failure. Do not retry.
- Verify the account number and routing number with the user immediately.
- If it's a payout (e.g., contractor payment), ask for a new bank account or offer an alternate rail (wire, check, card).
- If it's an inbound ACH (e.g., customer deposit), contact the customer to confirm their account details.
- Log as a data quality issue in your monitoring system.
if (returnCode === 'R03') {
await markPayoutAsFailed(payoutId, 'ACCOUNT_CLOSED_OR_INVALID');
await notifyUser(userId, {
subject: 'Payment failed: account not found',
action: 'Please verify your bank account details',
offeredAlternatives: ['wire_transfer', 'card', 'check']
});
// Do not retry
}
R10: Unauthorized / Customer Advises Not Authorized
What it means: The account holder claims they didn't authorize the debit. This is a dispute.
When it fires: 1–60 days after the ACH posts (customer-initiated).
How to handle it:
- Treat this as a chargeback. You have limited time to respond with proof of authorization.
- Retrieve the original authorization (signed agreement, API consent, email confirmation).
- If you have proof, respond to your bank with documentation within the dispute window (usually 10 days).
- If you don't have proof, accept the loss and update your authorization collection process.
- Flag the user account for review; R10s may indicate fraud or a pattern of disputes.
if (returnCode === 'R10') {
const dispute = await createDispute(payoutId, 'R10_UNAUTHORIZED');
const auth = await fetchAuthorizationProof(payoutId);
if (auth && auth.timestamp < payoutDate) {
await submitDisputeResponse(dispute.id, {
proof_type: 'api_authorization',
proof_document: auth.signedConsent,
submitted_at: new Date()
});
} else {
await markPayoutAsLost(payoutId);
await flagUserAccount(userId, 'DISPUTE_PATTERN');
}
}
Other Common Codes
| Code | Meaning | Retry? | Action |
|---|---|---|---|
| R02 | Account closed | No | Update user account details |
| R04 | Invalid account number | No | Verify routing & account |
| R05 | Unauthorized user / account | No | Investigate authorization |
| R07 | Authorization revoked | No | Contact user |
| R09 | Uncollected funds | Yes (1–2 times) | Retry after 3 |
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)