ACH payment blocked or returned R10 unauthorized? Why you must not just retry
ACH failures don't behave like card declines. When a debit comes back blocked — bank_account_unusable, an HTTP 402 on your next Payment Intent — or returns with a NACHA code like R10 (unauthorized), the rules are explicit: you can't just try again. Re-debiting a blocked or unauthorized account isn't a retry, it's a compliance problem.
Why the account got blocked
When an ACH debit is returned for a reason other than insufficient funds — the account is closed or frozen, the customer disputed it as unauthorized, or a risk rule fired — NACHA rules require the originator to review and confirm the account before initiating another debit. Payment processors enforce this by blocking the account until the root cause is resolved:
- In the Dashboard, the failed debit is labelled
blockedwith an error message. - Confirming a Payment Intent or Setup Intent against a blocked account returns
HTTP 402withbank_account_unusable. - The payment method exposes a
status_details.blockedfield with anetwork_code(the raw ACH return code) and areasonsummary.
Why R10 and other unauthorized returns must never be retried
Unauthorized returns — R10, R29, and the rest of the unauthorized set — are different from a soft return like R01 (insufficient funds). Re-debiting after an unauthorized return violates NACHA rules. Worse, these returns are asynchronous: an R10 can arrive days or even up to 60 days after the original debit settled, long after your code has moved on. A blind retry loop doesn't just fail — it compounds a rules violation.
The critical distinction: R01 (insufficient funds) can be retried within limits. R10/R29 (unauthorized) must NOT be retried — resolve the authorization first. Treating all ACH returns the same is how integrations end up in violation.
How to handle it
- Read the return code / block reason. Distinguish insufficient-funds returns (retryable within limits) from unauthorized returns (not retryable) and administrative returns (closed/frozen — needs a new account).
- For unauthorized returns, contact the customer. Resolve the authorization issue and obtain a fresh mandate before any new debit.
- For a blocked account, resolve the root cause, then have the customer unblock or switch accounts. Listen for the
payment_method.automatically_updatedevent to know when a block is applied or lifted. - Never loop. An async unauthorized return that arrives 60 days later should stop the flow and alert a human, not trigger an automatic re-debit.
For agents moving money
An agent that treats every payment failure as retryable is dangerous with ACH, where a retry can be a rules violation. diagnose_infra_error returns the ach-plaid family with the correct handling per return code, so the agent knows which returns to retry, which to never retry, and when to escalate to a human.
Machine-readable guide: https://snapback.sh/llms.txt · Tool discovery: https://snapback.sh/.well-known/mcp.json · MCP endpoint: https://api.snapback.sh/mcp. Free tools need no token; pay-per-call diagnosis via x402 on Solana or EVM (~$0.0032 USDC).