Regulation E Error Resolution for ACH Disputes

When a consumer notifies you that an ACH debit was unauthorized, duplicated, or posted in the wrong amount, Regulation E stops being a policy document and becomes a stopwatch. Under 12 CFR 1005.11 you have ten business days to investigate, and if you cannot finish in that window you must issue provisional credit to keep investigating out to forty-five days — or ninety for a defined set of transactions — followed by a results notice within three business days of conclusion. Within the broader Reg E, NACHA and audit controls framework, this reference specifies those obligations at the subsection level and shows how to implement them as guarded, attributable state rather than a spreadsheet of due dates. A dispute is itself a reconciliation exception, so it flows through the same exception lifecycle state machine as any other break, with the Reg E clock attached as metadata.

The engineering hazard is that the clock rarely runs on the calendar an untrained implementation assumes. The ten-, forty-five-, and ninety-day counts are business days, they exclude Federal Reserve holidays, and they start from the date of notice rather than the date of the transaction — while provisional credit, once the ten-day mark passes without resolution, becomes a mandatory action whose own reversal has a deadline. That provisional-credit obligation is a guarded transition in its own right; the state-machine mechanics of it are worked through in encoding provisional-credit state transitions, and the two dedicated references below make the timeline and the credit decision copy-paste concrete.

The scope of §1005.11 is specific: it governs consumer accounts and electronic fund transfers. A commercial ACH dispute or a wire falls under UCC Article 4A and its own contractual timelines, not Reg E — misclassifying a commercial account as consumer is one of the four failure modes catalogued below, and it starts a clock the statute never required.

The Statutory Timeline at Subsection Level

12 CFR 1005.11 lays out the machinery in a fixed order, and each step has a citable subsection. A notice of error (§1005.11(b)) is timely only if the institution receives it within sixty days of transmitting the periodic statement showing the alleged error; a notice outside that window does not trigger the mandatory error-resolution procedures. Once a timely notice arrives, §1005.11(c)(1) sets the baseline: investigate and determine whether an error occurred within ten business days of receiving the notice, then correct any error within one business day of determining it occurred and report the results within three business days of finishing the investigation.

The extension is conditional, not automatic. Section 1005.11(c)(2) permits taking up to forty-five calendar days to investigate — but only if the institution provisionally credits the disputed amount (plus, where applicable, any related fees and interest) within ten business days of the notice and tells the consumer of the credit within two business days of providing it. For new accounts (opened within thirty days before the disputed transaction), point-of-sale debit-card transactions, and foreign-initiated transfers, §1005.11(c)(3) extends those figures to twenty business days for the base investigation and ninety calendar days for the provisional-credit-backed extension. The results notice under §1005.11(d) is due within three business days of concluding the investigation, and if no error (or a different error) is found, §1005.11(d)(2) requires notice of the reversal and continued honoring of items for five business days.

Regulation E error-resolution timeline from notice to results A horizontal timeline runs from Day 0 to Day 90. Day 0 is the consumer notice of error, which must arrive within 60 days of the statement. A ten-business-day investigation window runs from Day 0. If the investigation concludes within ten days, a results notice is due within three business days. If it does not conclude, provisional credit must be issued by Day 10 to unlock the extended investigation window — 45 calendar days for a standard account, or 90 days for new accounts, point-of-sale, and foreign-initiated transfers. At resolution, a results notice is due within three business days; if no error is found, provisional credit is reversed with a five-business-day honor period. Two parallel tracks are shown: the standard 45-day track and the 90-day extended track for the special transaction classes. Day 0 notice → provisional credit by Day 10 → resolution by Day 45 / 90 → results within 3 days Day 0 Notice of error within 60 days of statement · §1005.11(b) Day 10 Investigation due 10 business days §1005.11(c)(1) else → provisional credit Provisional credit unlocks extension Day 45 Standard resolution 45 calendar days §1005.11(c)(2) Day 90 Extended resolution new account · POS · foreign-initiated 90 days · §1005.11(c)(3) Results notice — within 3 business days of conclusion §1005.11(d): if no error, reverse credit & honor items 5 days notice 10-day / credit trigger standard 45-day track extended 90-day track (new acct / POS / foreign)
The Reg E clock: a notice on Day 0 opens a ten-business-day investigation; missing it forces provisional credit by Day 10, which unlocks the 45-day standard or 90-day extended window, each closed by a results notice within three business days.

Phase 1 — Model the Dispute as Guarded State

A dispute record must carry everything the clocks depend on: the notice date, the account classification, the transaction class, the disputed amount in cents, and whether provisional credit has been issued. Model it as a typed record whose fields the deadline calculator reads, never as loose columns an analyst edits by hand.

python
from __future__ import annotations

from dataclasses import dataclass
from datetime import date
from enum import Enum


class AccountType(Enum):
    CONSUMER = "consumer"
    COMMERCIAL = "commercial"   # NOT covered by Reg E — routes to UCC 4A


class TxnClass(Enum):
    STANDARD = "standard"
    NEW_ACCOUNT = "new_account"       # opened <=30 days before the txn
    POINT_OF_SALE = "pos"
    FOREIGN_INITIATED = "foreign"


@dataclass(frozen=True, slots=True)
class Dispute:
    dispute_id: str
    txn_id: str
    account_type: AccountType
    txn_class: TxnClass
    amount_cents: int
    notice_date: date               # date the institution RECEIVED notice
    statement_date: date            # periodic statement showing the error
    provisional_credit_issued: bool = False

    def is_reg_e(self) -> bool:
        """Reg E error resolution applies only to consumer EFTs."""
        return self.account_type is AccountType.CONSUMER

    def within_60_day_bar(self) -> bool:
        """§1005.11(b): notice must arrive within 60 days of the statement."""
        return (self.notice_date - self.statement_date).days <= 60

Freezing the record matters more than it looks. A dispute's classification is the input every deadline depends on, so an analyst who quietly flips txn_class from STANDARD to NEW_ACCOUNT after credit has issued has silently moved four deadlines and invalidated the audit trail that justified them. Reclassification should be a new, attributed event that supersedes the prior record, never an in-place mutation — which is exactly why Dispute is frozen=True. The intake step that constructs it is also where the two derived predicates belong: is_reg_e() decides whether the statute applies at all, and within_60_day_bar() decides whether the mandatory procedures are even triggered. Both should be evaluated and logged the moment the dispute is created, not recomputed ad hoc downstream.

Phase 2 — Compute the Deadlines on a Banking-Day Calendar

The calculator converts the classification into concrete dates. Business-day counts skip weekends and Federal Reserve holidays; calendar-day counts (the 45/90 extension caps) do not. The full annotated banking-day implementation lives in encoding Reg E dispute timelines in Python; the deadline shape it returns is below.

python
from __future__ import annotations

from dataclasses import dataclass
from datetime import date, timedelta
from typing import Callable


@dataclass(frozen=True, slots=True)
class RegEDeadlines:
    investigation_due: date          # 10 (or 20) business days from notice
    provisional_credit_due: date     # same as investigation_due if unresolved
    extended_resolution_due: date    # 45 or 90 calendar days from notice
    results_notice_due: date         # 3 business days after conclusion


def compute_deadlines(
    dispute: Dispute,
    add_business_days: Callable[[date, int], date],
) -> RegEDeadlines:
    """Derive every Reg E deadline from the notice date and classification.

    `add_business_days` must honor weekends and Fed holidays; passing naive
    calendar arithmetic here is the most common cause of a blown deadline.
    """
    special = dispute.txn_class in (
        TxnClass.NEW_ACCOUNT, TxnClass.POINT_OF_SALE, TxnClass.FOREIGN_INITIATED,
    )
    base_business_days = 20 if special else 10
    extension_calendar_days = 90 if special else 45

    investigation_due = add_business_days(dispute.notice_date, base_business_days)
    return RegEDeadlines(
        investigation_due=investigation_due,
        provisional_credit_due=investigation_due,
        extended_resolution_due=dispute.notice_date + timedelta(days=extension_calendar_days),
        results_notice_due=add_business_days(investigation_due, 3),
    )

Note that provisional_credit_due deliberately coincides with investigation_due: the credit is what buys the extension, so it must be on the ledger by the same date the base investigation would otherwise have had to conclude. The extended_resolution_due is a calendar-day figure and therefore ignores the banking-day helper entirely — passing forty-five or ninety through add_business_days would stretch the window by roughly a third and hand you a deadline the statute never granted. Keeping the two arithmetics in visibly separate expressions is not stylistic; it is the guardrail that stops a maintainer from "consistency-fixing" the calendar cap into a business-day count. The results_notice_due chains off investigation_due because the three-day notice window runs from the conclusion of the investigation, which in the unresolved-and-extended case is bounded by the extension cap, not the base window.

Phase 3 — Gate Provisional Credit and the Results Notice

Provisional credit becomes mandatory the instant the ten-business-day investigation lapses without a determination. Whether to issue it — and the amount and reversal deadline — is a decision with its own guardrails, developed fully in automating provisional-credit decisions under Reg E. The gate below is the minimal rule: if today is past the investigation due date and the dispute is unresolved, credit is owed; issuing it unlocks the extended clock, and every transition writes to the append-only audit trail.

python
def provisional_credit_required(
    dispute: Dispute, deadlines: RegEDeadlines, today: date, resolved: bool,
) -> bool:
    """Credit is mandatory once the investigation window lapses unresolved."""
    if not dispute.is_reg_e() or resolved:
        return False
    return today > deadlines.investigation_due and not dispute.provisional_credit_issued

Failure Modes & Guardrails

Failure scenario Root cause Mitigation
Deadline lands on a weekend or Fed holiday Business-day count implemented as notice_date + timedelta(days=10) Use a banking-day calendar that skips Saturdays, Sundays, and the ten Federal Reserve holidays; never mix calendar and business-day arithmetic
Provisional credit never issued Investigation silently ran past ten business days with no gate Fire provisional_credit_required on every tick past investigation_due; block auto-close of an unresolved dispute past that date
Wrong clock for a new account txn_class defaulted to STANDARD when the account was opened within 30 days Derive NEW_ACCOUNT from account-open date at dispute intake, not from analyst entry; apply 20 business-day / 90-day figures
60-day bar miscalculated Bar measured from transaction date instead of statement date Measure §1005.11(b) from the periodic-statement date that showed the error; a late notice does not trigger mandatory procedures but must still be logged
Commercial dispute run under Reg E Account type misclassified as consumer Gate on is_reg_e(); route commercial disputes to the UCC 4A path with its contractual timelines, not the §1005.11 clocks

Compliance & Auditability

Every deadline and every credit is a regulated control surface, so cite the subsection in the audit record, not just the date. §1005.11(b) — the timeliness of the notice (60 days from statement) — determines whether the mandatory procedures apply at all; log the statement date and notice date so the bar is reconstructable. §1005.11(c)(1) governs the ten-business-day base investigation and the one-business-day correction; §1005.11(c)(2) the 45-calendar-day extension conditioned on provisional credit within ten business days; §1005.11(c)(3) the 20-business-day / 90-calendar-day figures for new accounts, POS, and foreign-initiated transfers. §1005.11(d) covers the three-business-day results notice and, on a no-error finding, the reversal notice and five-business-day honor period. Each state transition — dispute opened, credit issued, investigation concluded, results sent, credit reversed — must append an event carrying the actor, timestamp, and governing subsection so an examiner can replay the full lifecycle against the calendar.

The subsection tag is what makes an audit defensible rather than merely detailed. A due date on its own tells an examiner when you acted; the citation tells them which obligation you were discharging, and the two together let them verify the date was computed under the right rule. This matters most in the extension case: a 45-day resolution that cites §1005.11(c)(2) implicitly asserts that provisional credit was issued by the ten-business-day mark, because the extension is not available otherwise. If the audit trail shows the extended deadline being relied upon but no credit-issued event before investigation_due, that is a self-evident compliance gap an examiner will spot immediately — which is precisely the kind of contradiction an append-only, subsection-tagged log surfaces automatically instead of hiding. Retain these records for the Reg E minimum of two years, and longer where a legal hold or a broader SOX retention policy applies.

Testing & Verification

Deadline logic is exactly the kind of code that passes a naive unit test and fails in production on a holiday week. Test against known-hard dates: a Friday notice, a notice the week of a Federal Reserve holiday, and the new-account boundary.

python
import pytest
from datetime import date


def test_new_account_uses_20_business_days(add_business_days):
    dispute = Dispute(
        dispute_id="D1", txn_id="T1",
        account_type=AccountType.CONSUMER, txn_class=TxnClass.NEW_ACCOUNT,
        amount_cents=25_00, notice_date=date(2026, 3, 2),
        statement_date=date(2026, 2, 20),
    )
    d = compute_deadlines(dispute, add_business_days)
    # 20 business days, not 10, for a new account
    assert d.investigation_due == add_business_days(date(2026, 3, 2), 20)
    # 90 calendar days for the extended resolution
    assert d.extended_resolution_due == date(2026, 3, 2) + __import__("datetime").timedelta(days=90)


def test_provisional_credit_required_after_investigation_lapses(add_business_days):
    dispute = Dispute(
        dispute_id="D2", txn_id="T2",
        account_type=AccountType.CONSUMER, txn_class=TxnClass.STANDARD,
        amount_cents=140_00, notice_date=date(2026, 3, 2),
        statement_date=date(2026, 2, 25),
    )
    d = compute_deadlines(dispute, add_business_days)
    after = d.investigation_due + __import__("datetime").timedelta(days=1)
    assert provisional_credit_required(dispute, d, today=after, resolved=False)


def test_commercial_account_is_not_reg_e(add_business_days):
    dispute = Dispute(
        dispute_id="D3", txn_id="T3",
        account_type=AccountType.COMMERCIAL, txn_class=TxnClass.STANDARD,
        amount_cents=500_00, notice_date=date(2026, 3, 2),
        statement_date=date(2026, 2, 25),
    )
    assert not dispute.is_reg_e()

Frequently Asked Questions

When does the Reg E clock actually start?

On the date the institution receives the consumer's notice of error, not the transaction date and not the statement date. Section 1005.11(c) counts the ten (or twenty) business days from that receipt. The statement date matters only for the separate §1005.11(b) timeliness bar — a notice must arrive within sixty days of the statement showing the error for the mandatory procedures to apply.

Is provisional credit always required?

No — it is required only when the institution cannot complete its investigation within ten business days and wants to use the extended 45- or 90-day window. If you resolve within ten business days, you correct the error (if any) within one business day and never issue provisional credit. Once you pass the ten-day mark unresolved, though, the credit becomes mandatory to keep the extension available.

What makes a transaction eligible for the 90-day window instead of 45?

Three classes under §1005.11(c)(3): transfers on accounts opened within thirty days before the disputed transaction (new accounts), point-of-sale debit-card transactions, and foreign-initiated transfers. These also carry a twenty-business-day base investigation rather than ten. Everything else uses the ten-business-day / forty-five-calendar-day standard track.

Does Reg E cover commercial ACH disputes or wires?

No. Regulation E governs consumer electronic fund transfers only. A commercial account dispute or a wire transfer falls under UCC Article 4A and the contractual timelines in your account agreement, which are different from the §1005.11 clocks. Misclassifying a commercial account as consumer starts a statutory clock the law never imposed and can distort your compliance metrics.

How do business days and calendar days differ in the deadlines?

The investigation windows (ten or twenty days) and the results notice (three days) are business days — they skip weekends and Federal Reserve holidays. The extension caps (forty-five or ninety days) are calendar days measured from the notice date. Mixing the two, or running business-day counts on a naive calendar, is the single most common way institutions miscompute a Reg E deadline.