An ACH return occurs when the receiving bank rejects or reverses an ACH transaction. Unlike card chargebacks, ACH returns follow NACHA rules and have specific return reason codes.
| Code | Name | Description | Timing |
|---|---|---|---|
| R01 | Insufficient funds | Not enough money in the account | 2 business days |
| R02 | Account closed | The account has been closed | 2 business days |
| R03 | No account / unable to locate | Account number is invalid | 2 business days |
| R04 | Invalid account number | Account number structure is wrong | 2 business days |
| R08 | Payment stopped | Customer requested a stop payment | 2 business days |
| R10 | Customer advises not authorized | Customer claims they did not authorize the debit | 60 calendar days |
| R29 | Corporate customer advises not authorized | Corporate account disputes authorization | 2 business days |
When an ACH transaction is returned, the transaction_state changes to failed. Check the transaction periodically or use batch status polling.
curl https://api.dev.paradisegateway.net/v1/transactions/TRANSACTION-01KEW32V6YNV11T33XGDR7TGWC \
-H "APIKEY: YOUR-API-KEY"A returned transaction looks like:
{
"id": "TRANSACTION-01KEW32V6YNV11T33XGDR7TGWC",
"type": "sale",
"amount": 250000,
"transaction_state": "failed",
"response_status": "declined",
"response_message": "ACH return: R01 - Insufficient funds",
"correlation_id": "e5f6a7b8-c9d0-1234-ef01-23456789abcd"
}import time, requests, os
API_KEY = os.environ["PARADISE_API_KEY"]
BASE = "https://api.dev.paradisegateway.net"
def monitor_ach_transaction(txn_id, max_checks=10, interval=3600):
"""Poll an ACH transaction until it settles or fails."""
for _ in range(max_checks):
resp = requests.get(
f"{BASE}/v1/transactions/{txn_id}",
headers={"APIKEY": API_KEY},
)
txn = resp.json()
state = txn["transaction_state"]
if state in ("completed", "settled"):
return {"status": "success", "transaction": txn}
if state == "failed":
return {
"status": "returned",
"message": txn.get("response_message", "ACH return"),
"transaction": txn,
}
time.sleep(interval)
return {"status": "timeout", "message": "Transaction still pending"}| Return code | Action |
|---|---|
| R01 (Insufficient funds) | Wait a few days and retry, or contact the customer for an alternative payment method |
| R02/R03/R04 (Bad account) | Request updated bank details; delete the invalid token |
| R08 (Stop payment) | Contact the customer to resolve the dispute |
| R10 (Unauthorized) | Do not retry — investigate the authorization. This may indicate fraud |
- Verify bank accounts — use micro-deposit verification before processing large debits
- Get authorization — for WEB entries, ensure you have the customer's online consent
- Validate routing numbers — check the routing number format before tokenizing
- Communicate clearly — tell customers when debits will occur and for how much
- ACH for invoices — collect payments via ACH
- ACH payouts — send funds to vendors
- Handle payment failures — broader failure patterns
- About ACH payments