DEV Community

Payout Rail
Payout Rail

Posted on

ACH Return Codes Explained: R01 to R85 and How to Handle Each One

ACH Return Codes Explained: R01 to R85 and How to Handle Each One

ACH Return Codes Explained: R01 to R85 and How to Handle Each One

When an ACH transaction fails, your payout doesn't just disappear—it returns with a reason code. The National Automated Clearing House Association (NACHA) defines 85 standardized return codes (R01–R85) that tell you exactly why a debit or credit entry was rejected. Understanding these codes is critical for building reliable payment systems.

Unlike credit card declines, which often come back as a single "declined" message, ACH returns are granular. Each code maps to a specific failure reason, and your handling logic should differ based on the code. Let's walk through the most common ones and how to respond.

The Big Three: R01, R03, R10

R01 – Insufficient Funds

The account exists, routing is correct, but the account holder doesn't have enough money to cover the debit.

  • When it fires: 1–5 business days after the ACH entry reaches the bank.
  • What to do: This is temporary. Retry after 3–5 business days, or notify the user to add funds. Don't immediately fall back to a credit card—many users will fund their account and retry.
  • Recovery rate: ~40–60% on retry.

R03 – No Account / Unable to Locate Account

The routing number exists, but the account number doesn't match any account at that bank.

  • When it fires: Same day or next business day.
  • What to do: Don't retry. This is permanent. Ask the user to verify their account details. If they provide a corrected number, treat it as a new transaction.
  • Recovery rate: ~5% (only if user corrects the account number).

R10 – Customer Advises Not Authorized

The account holder claims they didn't authorize the debit.

  • When it fires: 1–60 days after the entry posts (can come back very late).
  • What to do: Investigate immediately. Check your payment authorization logs. If the user did authorize it, respond to the bank within the dispute window (typically 10 business days). If they didn't, refund and flag for fraud review.
  • Recovery rate: Depends on your authorization proof; if you have signed consent, ~70%.

Common Operational Codes

R02 – Account Closed

The account was closed before the ACH entry arrived.

  • Retry? No. Permanent.
  • Action: Route to alternate payment method or ask for updated banking details.

R04 – Invalid Routing Number

The routing number doesn't exist or is formatted incorrectly.

  • Retry? No.
  • Action: Validate routing numbers against the FedACH directory before submission.

R05 – Unauthorized Use of Routing Number

The originating company (ODFI) is not authorized to use this routing number.

  • Retry? No. This is a configuration error on your end.
  • Action: Contact your ACH processor immediately.

R07 – Authorization Revoked

The account holder revoked authorization for recurring entries.

  • Retry? No for that specific authorization.
  • Action: If this is a subscription or recurring payment, ask the user to re-authorize or switch to an alternate method.

R29 – Corporate Account Closed

Similar to R02, but for business accounts.

  • Retry? No.
  • Action: Request updated business banking details.

Less Common but Important Codes

Code Meaning Retry? Action
R06 Returned per ODFI request No Investigate with processor
R08 Payment stopped No Permanent block by account holder
R11 Duplicate entry No Check your batch logic; don't resubmit
R14 Representative payee deceased No Escalate; may be probate issue
R16 Account frozen No Contact bank; may be temporary
R20 Non-transaction account No Account type doesn't support ACH
R23 Entry amount exceeds limit Maybe Reduce amount and retry, or split entry

Building Retry Logic


javascript
function handleAchReturn(returnCode, transactionId) {
  const retryable = ['R01', 'R23'];
  const permanent = ['R03', 'R04', 'R05', 'R29'];
  const escalate = ['R10', 'R08', 'R16'];

  if (retryable.includes(returnCode)) {
    scheduleRetry(transactionId, 3); // retry in 3 days
  } else if (permanent.includes(returnCode)) {
    markFailed(transactionId);
    notifyUser('Please update your banking details');
  } else if (escalate.includes(returnCode)) {
    flagForReview(transactionId, returnCode

---

*Decoding ACH return codes programmatically? The [ACH Return Codes API](https://rapidapi.com/payoutrail-ach-return-codes/api/ach-return-codes-api?utm_source=nichestream&utm_medium=devto&utm_campaign=payoutrail-ach-returns) returns the full Nacha R01–R85 set with plain-language descriptions and handling guidance.*
Enter fullscreen mode Exit fullscreen mode

Top comments (0)