fintech

ACH Is Not an API Call

ACH is a 50-year-old batch system. It will break every assumption you bring to it.

By Alex Kugell ·

You've built APIs. You've sent HTTP requests, waited for a response, and moved on. Moving money feels like it should work the same way: call the endpoint, get a 200, done.

ACH doesn't work that way. The 200 means your request was received. The money hasn't moved. It might not move for days, and even after it does, it can come back.

ACH - the system that handles direct deposits, bill payments, and bank-to-bank transfers in the United States - processes nearly 9 billion transactions a quarter. It's the plumbing under almost every fintech product that moves dollars. And it was designed in 1972.

The answer to "why is my payment integration so complicated" isn't that your code is bad. It's that you're building on top of a system that was designed for magnetic tape reels, and treating it like it's a REST API.

What Engineers Expect vs. What ACH Actually Does
What you expect
Send
Response
Done
What ACH actually does
Initiate
Batch
Wait
Settle
returned (2–60 days later)

The batch window problem

A post office that only sends mail three times a day. You can drop a letter in the box at 7am, but it won't leave until the 10:30am truck. Drop one at 11am, and it sits until the 2:45pm truck. Miss the 4:45pm truck, and your letter waits until tomorrow morning.

That's how ACH works. There is no "send this payment now." There are settlement windows. Three of them per business day for same-day ACH. Your transaction sits in a queue until the next window opens. Traditional ACH - the kind most systems default to - settles in one to two business days.

Same-Day ACH Settlement Windows
6:00am
10:30am
2:45pm
4:45pmWindow 1Window 2Window 3Tomorrow

When your bank's API returns "accepted," it means "we put your letter in the mailbox." It does not mean "the letter arrived." It does not mean "the recipient read it." It certainly does not mean "the money moved."

This distinction matters because your status model depends on it. If you treat "accepted" as "complete," your ledger is wrong. Your customer sees a balance that doesn't reflect reality. Your reconciliation breaks the moment a return comes in.

A payment that succeeds, then fails

A settled ACH transfer can come back.

An ACH return can arrive up to two business days after settlement for most reasons - wrong account number, insufficient funds, account closed. But for unauthorized transactions - where the account holder says "I never approved this" - the return window is 60 calendar days.

A $500 Payment That Reverses
Mon
Initiated$500 sent
Tue
Customerspends $500
Wed
Settled
Thu
Returned–$500
60-day unauthorized return window

You moved $500 on Monday. Your customer spent that $500 on Tuesday. On Thursday, the originating bank returns the transaction. You now have a negative balance, a customer who already spent the money, and a reconciliation problem that no amount of retry logic will fix.

When you're building payments for the first time, it's natural to design for the happy path: initiate, process, complete. ACH demands you build for a fourth state: completed-then-reversed. If your state machine doesn't have that transition, you're going to discover it in production.

The code you're required to pick

ACH transactions carry a three-letter Standard Entry Class code. This is not metadata. This is a regulatory classification that determines what rules apply to your transfer.

The two that matter most for fintech:

PPD - Prearranged Payment and Deposit. Consumer transactions where the account holder gave prior authorization. If your product pulls from or pushes to a consumer's bank account, this is the code.

CCD - Corporate Credit or Debit. Business-to-business transfers. If you're disbursing to a vendor, a charity, or another company, this is the code.

Using the wrong code doesn't cause a technical failure. The transaction processes normally. The money moves. Everything looks fine. But you've violated NACHA rules, and the consequences - returns, fines, or losing your ACH origination privileges entirely - don't show up until an audit or a pattern of returns triggers review.

It's like driving through a red light at 3am on an empty road. Nothing happens immediately. But the camera recorded it.

Same-day doesn't mean real-time

"Same-day ACH" sounds like it solves the speed problem. It doesn't. Same-day ACH means your transaction settles within the same business day, but only if you hit one of the three daily windows. Submit at 3pm, and it settles in the end-of-day window. Submit at 5pm on a Friday, and "same-day" means Monday.

Same-day ACH caps individual transactions at $1 million, though NACHA approved an increase to $10 million in 2026. Even at the higher limit, anything above the cap falls back to traditional ACH - one to two business days.

The distinction matters because your customers don't know any of this. They see "instant transfer" from their bank and assume that's what's happening in your system. It's not. Your product needs to manage that expectation gap in its UI, its notifications, and its status tracking.

The notification nobody handles

When an account holder's information changes - new account number, new routing number, name correction - the receiving bank sends a Notification of Change entry back through the ACH network.

You are required by NACHA rules to update your records before the next transaction to that account. Required. Not "should." Not "best practice."

Most systems ignore Notifications of Change entirely. The transaction went through, the money moved, why would we read some administrative message? Then the next transaction to that account gets returned because the old account number is now invalid. Then the next one. Then someone investigates and discovers there were NOC entries sitting in a queue that nobody built a handler for.

What your status model actually needs

If you're building on ACH, your payment states aren't "pending" and "complete." They're:

ACH Payment State Machine
Initiated
Submitted
Settled
Corrected
Returned
NACHA requires record update before next transaction

Initiated - you've created the transaction record in your system.

Submitted - you've sent the transaction to your bank or payment processor. The batch file has been accepted.

Settled - the settlement window has passed and the funds have been credited or debited. This is not final.

Returned - the transaction was reversed after settlement. Your system needs to handle this as a normal event, not an error.

Corrected - a Notification of Change was received. The original transaction may have processed, but your stored account information needs to be updated.

Each of these states has its own timing. Initiated to submitted might be instant. Submitted to settled is hours to days. Settled to returned can be two days or sixty days, depending on the return reason code. Your status machine, your webhooks, your customer-facing UI, and your reconciliation pipeline all need to account for this timeline.

ACH is not a request-response protocol. It's a multi-day, asynchronous, batch-processed, reversible system with regulatory rules that vary by transaction type. Infrastructure from 1972 that moves trillions of dollars because everyone agreed to follow the same rulebook.

The NACHA Operating Rules are the authoritative source. Dense, expensive, and the only document that will tell you exactly what happens when something goes wrong. If your company is originating ACH transactions and your engineering team hasn't read the relevant sections, your payment system has bugs you haven't found yet.

Sources

Built by Trio, a fintech-native engineering partner helping teams build the next generation of financial technology and infrastructure.

Subscribe to Ledger Drift for high-signal insights into how modern fintech is built, from systems to code to teams.

Keep reading

fintechWhat Agent Authentication Actually RequiresThree approaches to the same problem are shipping simultaneously. One already processed a real transaction.
analysisCircle Bought Its Own Network MemberCircle promised it would never compete with its CPN partners. Then it paid $400 million for one of them.
analysisFintechs Are Done Renting BanksChime bought Stride Bank for $590 million. Revolut got its US charter. Block applied for one. Three in three months.
View more ›