Skip to main content
ACH is not a real-time, final network. An ACH debit can be returned after it settles: the account holder’s bank sends the money back, sometimes days or weeks later, for reasons like insufficient funds or a claim that the debit wasn’t authorized. Because a linked bank on-ramp delivers crypto against an ACH pull, a return can mean funds you already moved need to be reconciled. This page explains how returns work and how Spritz surfaces them.

When returns arrive

Returns land within windows set by the ACH rules, which vary by reason:
  • Administrative returns (wrong or closed account, invalid number) usually arrive within about two banking days.
  • Unauthorized returns (the account holder disputes the debit) can arrive up to 60 calendar days after settlement.
Spritz groups returns into three buckets, unauthorized, administrative, and other, so you can treat them differently.

How Spritz surfaces a return

When a debit is returned:
  • The deposit’s status and debitStatus become returned, and the deposit carries a returnCode and returnReason.
  • The funding source becomes disabled, with statusReason: "returned" and permanent: true. Read the ACH code from the deposit or return record; private source-disablement inputs are not returned.
  • The achDebitReturn.created webhook fires. achDebitReturn.updated can fire as details change. Delivery order is not guaranteed.
A deposit can move through completed before its bank later returns the debit. Keep processing deposit and return webhooks after completion. The aggregate deposit can also show failed or refunded because of its crypto-release path while its debitStatus is already settled; a later ACH return still moves it to returned. Use debitStatus and releaseStatus to explain the current state. None of these aggregate states makes a settled ACH debit permanently immune to a later return.
You can also list returns directly:
Fetch one return by the public dr_... ID from its webhook:
Each return record ties the return back to its deposit and quantifies your exposure: cryptoStateAtReturn is the key exposure signal: a return before the crypto is released costs you nothing, while a return after fully_confirmed delivery is a realized loss. Store returnCode for reconciliation and support, but branch the user experience only on userAction. Treat returnReason as supporting text for logs and support tooling, not as a stable machine-readable value.

User and frontend handling

Every return disables the bank account that produced it. Cache invalidation can make a funding-source read briefly lag the returned deposit, so reconcile it with bounded polling after either achDebitReturn.* webhook. Never offer the source again once it is disabled. userAction is the authoritative result for the user’s wider ACH access. Spritz’s escalation policy is intentionally not public; do not derive or reproduce it from return codes or return counts. Use direct, action-oriented copy: Do not show a retry button for the disabled source. For none, offer another bank. For every other action, show the support path.

Common return codes

ACH return codes follow the NACHA standard (R01, R02, and so on). The ones you’ll see most: This is a subset. The full set of codes is defined by NACHA, and returnReason carries the human-readable text for whatever code applies.

Preventing avoidable returns

Only debit accounts the user linked and clearly authorized. Treat a blocked deposit as final for that attempt: do not vary amounts or retry in a loop. Reconcile every return promptly and follow userAction; Spritz may pause a source, user, or integration before any new money moves.

Reconciling

1

Listen for the webhook

Subscribe to achDebitReturn.created and achDebitReturn.updated so you learn about returns and synchronize current state. Subscribe to achDebit.returned separately when your integration sends push notifications. Never infer a return push from a generic return update.
2

Persist the return

Key your record by the return id. Fetch GET /v1/integrator/ach-debit/returns/{id} for authoritative state whenever either event arrives. Reconcile the paginated GET /v1/integrator/ach-debit/returns feed after outages or missed deliveries.
3

Assess exposure

Use cryptoStateAtReturn and lossAmountUsd to decide whether the return is a real loss or was caught before delivery.
4

Update the user experience

Remove the disabled source from the payment picker. Branch on userAction to offer a different bank or the support path.