DEV Community

Payout Rail
Payout Rail

Posted on

ACH Return Codes Explained: R01, R03, R10 & When to Retry vs. Reroute

ACH Return Codes Explained: R01, R03, R10 & When to Retry vs. Reroute

When a payout fails, the reason matters. ACH return codes tell you why a transfer bounced—and whether you should retry, escalate to customer support, or switch payment rails entirely.

If you're building a fintech product, marketplace, or payroll system, understanding the Nacha return code set (R01–R85) is non-negotiable. A mishandled return can orphan funds, frustrate users, and trigger compliance headaches.

The Most Common ACH Return Codes

R01: Insufficient Funds

The account exists and is accessible, but the balance is too low. This is temporary and reversible.

  • When it fires: Receiver's bank rejects the debit during posting.
  • Developer action: Flag the transaction as INSUFFICIENT_FUNDS. Retry after 1–2 business days (the user may have deposited more). Don't immediately fail the payout.
  • Example: A gig worker's bank account has $50; you're trying to pay out $200.

R03: No Account / Unable to Locate Account

The account number or routing number doesn't match any account at the receiver's bank.

  • When it fires: Bank validation fails during the ACH file processing cycle.
  • Developer action: Mark as ACCOUNT_INVALID. Do not retry the same account. Prompt the user to re-enter banking details. This is a data entry error, not a temporary issue.
  • Example: User transposed two digits in their account number.

R10: Customer Advises Unauthorized

The account holder disputes the transaction. Often a fraud claim or authorization mismatch.

  • When it fires: Receiver's bank receives a customer complaint within the ACH return window (typically 2 business days post-settlement).
  • Developer action: Escalate to compliance. Don't retry. Investigate whether the user authorized the payout. If legitimate, contact the receiver's bank to dispute the return (within your bank's window). If fraudulent, block the account and file a report.
  • Example: A user's spouse claims they didn't authorize a transfer.

Other High-Impact Codes

Code Reason Retry? Action
R02 Bank account closed No Collect new account details
R04 Invalid account number format No Validate format before submission
R05 Unauthorized user / account holder No Verify authorization; escalate if needed
R07 Authorization revoked No Contact receiver; investigate
R08 Payment stopped No Check with originator (your customer)
R16 Account frozen / legal hold No Escalate to compliance
R20 Non-transaction account No Collect valid checking/savings account
R29 Corporate account restriction No Confirm account type with receiver

Building Retry Logic Around Returns

Here's a pseudocode pattern for handling returns programmatically:

async function handleAchReturn(returnCode, payout) {
  const retryable = ['R01'];  // Insufficient funds only
  const reroutable = ['R01', 'R02'];  // Try RTP or Visa Direct

  if (retryable.includes(returnCode)) {
    // Retry after 2 business days
    payout.status = 'PENDING_RETRY';
    payout.nextRetryAt = addBusinessDays(new Date(), 2);
    await db.savePayout(payout);
    await notifyUser('Payment delayed; will retry soon.');
  } else if (reroutable.includes(returnCode)) {
    // Attempt alternate rail (e.g., RTP for faster settlement)
    const rtpResult = await initiateRtp(payout);
    if (rtpResult.success) {
      payout.status = 'ROUTED_TO_RTP';
      await db.savePayout(payout);
    } else {
      payout.status = 'MANUAL_REVIEW';
      await escalateToSupport(payout);
    }
  } else {
    // Non-retryable: prompt user for action
    payout.status = 'ACTION_REQUIRED';
    await notifyUser(`Payment failed (${returnCode}). Please update your banking details.`);
  }
}
Enter fullscreen mode Exit fullscreen mode

Key Takeaways

  1. R01 is your only true retry candidate. Most other codes indicate user error or fraud—retrying won't help.
  2. Return timing matters. ACH returns arrive 2–5 business days after settlement. Plan your reconciliation loop accordingly.
  3. Reroute strategically. If ACH fails for a time-sensitive payout, consider RTP (Real-Time Payments) or Visa Direct—both settle in minutes, though at higher cost.
  4. Log everything. Store the return

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)