How a response gets filed on time
Filing the right packet is worth nothing if it lands a day late, so this post is about time. It walks through the deadline watch — the one-off scheduled job armed the moment the dispute is caught — the owner-approval and auto-file modes that decide how much happens without you, and the guard underneath both: a hard cutoff that files the assembled, real-evidence packet before the deadline so a forgotten tap can never turn a winnable case into a default loss.
Key takeaways
- The deadline watch is a one-off EventBridge Scheduler job, armed the moment the dispute was caught.
- Two modes: auto-file (strong packets file themselves) or owner-approval (nothing files until you tap).
- Underneath both sits a deadline guard that fires a safe margin before the bank’s due-by.
- If nothing has been filed and you haven’t chosen to accept the loss, the guard files the assembled packet.
- It never files late, and it never files a packet you’ve told it to drop — the deadline can’t beat you.
The watch that was armed at the front door
Back in Part 2, the very first thing the intake did after recording the deadline was arm a one-off EventBridge Scheduler job — an at(...) rule timed for a safe margin before the processor’s due_by, targeting the deadline-guard Lambda. That job is the spine of this whole post. It exists from the second the dispute is caught, before any evidence is gathered, before any packet is built. Everything else in the filing step is about giving the owner a chance to decide — but the watch is the promise that whatever the owner does or doesn’t do, the case will be filed before the cutoff or deliberately dropped, never simply forgotten.
Two modes: how much happens without you
How a case reaches the processor depends on a single setting. In owner-approval mode, the assemble step emails the owner a summary — amount, reason code, the winnability read, and the buttons — and nothing is filed until they tap File. This is the default for businesses that want a human eye on every response. In auto-file mode, a packet the assembler rated strong is filed automatically as soon as it’s ready, and the owner’s email becomes a heads-up rather than a gate; only mixed and weak cases wait for a decision. This suits higher volumes where most disputes are clear-cut and the owner only wants to be pulled in on the judgement calls. Either mode can be set per-account and overridden per dispute.
The owner’s three buttons land on a Function URL, the same signed, single-use-token pattern used across the series so a forwarded email link can’t be replayed. File submits the assembled packet now. Hold pauses for a closer look — useful when the owner wants to add a note or check something — but, crucially, Hold does not disarm the deadline guard. Accept loss records the decision not to fight and disarms the guard, so the case is closed cleanly and nothing files.
The deadline guard: a backstop, not a guess
When the one-off Scheduler job fires at the safety cutoff, the deadline-guard Lambda doesn’t blindly submit something. It reads the case state and decides: if the dispute is already filed, it does nothing. If the owner chose accept loss, it does nothing — that was a deliberate decision, and the guard never overrides it. If the assembler rated the case weak and auto-file isn’t on, it does nothing but raise a last-call alert, because filing a hopeless packet just burns the fee. But if the case is winnable, assembled, and simply un-actioned — the owner was busy, on holiday, asleep — the guard files the assembled packet. The reasoning is blunt: a real-evidence packet filed at the last safe moment beats a guaranteed default loss every time. Before the cutoff the guard also triggers a re-gather if a piece (like a delivery confirmation) was still pending earlier, so it files the freshest evidence available.
Filing through the API, exactly once
Filing itself is the cbr-file Lambda calling the processor’s evidence-submission API with the key from Secrets Manager — either the assembled fields and attachments or the packet PDF, whichever this processor takes. Two cases must not file the same dispute twice — the owner tapping File a moment before the guard fires, say — so filing is guarded by an idempotency key tied to the dispute id and a conditional state transition: the case moves to filed only from ready, and a second attempt finds it already filed and stops. On a successful submit, the state, the submission id the processor returns, and the timestamp are written to the disputes table; the deadline-guard job, if it hasn’t already fired, is deleted so it can’t act on a closed case.
Tracking the outcome
Weeks later the processor posts charge.dispute.closed with the result. That webhook comes in the same verified front door, and the system records won or lost against the case — closing the loop so every dispute has a final state, and feeding the monthly summary that reports your win rate by reason code. A lost case isn’t a system failure: some disputes are genuinely the customer’s to win, which is exactly why the winnability read and the accept-loss path exist. What the system guarantees isn’t that you win every chargeback — it’s that you never lose one to a deadline you forgot.
Design rules for filing on time
- The deadline guard is armed at intake and is the backstop for everything — the case can’t lapse.
- Auto-file or owner-approval is a setting; in both, a winnable case still gets filed before the cutoff.
- Hold doesn’t disarm the guard; only an explicit Accept-loss does. Inaction never becomes a silent loss.
- Filing is idempotent — a conditional state transition means a dispute is never submitted twice.
- The guard never files a case marked accept-loss, and never files a hopeless packet just to file something.