Skip to content

Part 2 of 7 · Invoice dispute triager series ~5 min read

How a dispute gets recognised

An accounts inbox is mostly not disputes. It is remittance advices, out-of-office replies, requests for a copy invoice, and people asking when something is due. Treating all of it as a dispute would pause half your ledger; treating none of it as a dispute is the current situation. This post is about the line between them, and about the one action that has to happen the instant the line is crossed.

Key takeaways

  • Most accounts mail is not a dispute. The first job is telling them apart.
  • The chaser is paused the moment a message is judged a dispute, before it is classified.
  • The invoice number is found in the body, the subject, the thread, or by asking.
  • A remittance or an auto-reply is recognised and ignored without a model call.
  • A dispute on a paid invoice is a different and more urgent thing.

What arrives at an accounts inbox

How an incoming message is recognised as an invoice disputeA vertical chain of six steps inside the AWS account, entered by a box labelled Message arrives at the accounts inbox. Step one asks whether it is an auto-reply, judged from mail headers rather than wording; if so it exits to Ignore with no model call. Step two asks whether it is a remittance advice, recognisable from amounts and references only; if so it exits to File it and match to payments. Step three asks whether it is about an invoice, found by a number or by the thread, checking against invoice records; if not it exits to General enquiry, sent to the inbox owner. Step four asks whether it is a complaint, using a single Bedrock call; if not it exits to Question, not dispute, which is answered without pausing anything. Step five pauses the chaser immediately, before anything else. Step six hands the message to the classifier with the invoice attached. A note says three of the four filters cost nothing and only the last one calls a model.AWS ACCOUNTMessage arrivesaccounts inboxAuto-reply?headers, not wordingIgnoreno model callyesRemittance?amounts and refs onlyFile itmatch to paymentsyesAbout an invoice?a number, or a threadInvoice recordsnumbers and totalsGeneral enquiryto the inbox ownernoIs it a complaint?one Bedrock callQuestion, not disputeanswer it, do not pausenoPause the chaserimmediately, beforeanything elseHand to the classifierwith the invoiceThree of the four filters cost nothing. Only the last one calls a model.
Fig 1. How a dispute is separated from everything else that arrives. The cheap structural filters run first, and the chaser is stopped the moment the answer is yes.
  • Database
  • App integration
  • Machine learning
  • Security & identity
  • Front-end & mobile
  • People

Why the cheap filters come first

Auto-replies are identifiable from headers with complete certainty — Auto-Submitted, X-Autoreply, a precedence of bulk. There is no reason to ask a model whether an out-of-office is a dispute, and a business with one chatty customer on annual leave can generate dozens of them.

Remittance advices are nearly as easy: a message that is mostly a table of invoice numbers and amounts, with a total, and no sentences. They are frequent, they are structurally distinctive, and misclassifying one as a dispute would pause several invoices that have just been paid.

Finding the invoice

  • In the body. The common case. An invoice number in a recognisable format, matched against invoices for that customer — not against all invoices, because customer-scoped matching removes almost every false positive.
  • In the subject or the thread. A reply to the original invoice email carries the number in the subject or in the quoted text. Threading by In-Reply-To is more reliable than any parsing.
  • By amount. “Your invoice for £3,240 is wrong” identifies an invoice uniquely often enough to be worth trying, but only when exactly one invoice for that customer matches. Two candidates is not a match.
  • By asking. If none of the above works, the dispute is still real and the chaser for that customer is paused across the board until it is resolved — which is cautious, and correct, because chasing a customer who is currently disputing something is the exact failure this system exists to prevent.

Pausing the chaser

This happens before classification, before gathering, and before any human sees anything. It is a single conditional write setting a disputed flag on the invoice, and whatever sends your payment reminders checks that flag. If your reminders are sent by a person, the pause is a line on a list they check; if they are automated, it is a field.

It is worth being explicit about why this is first. A dunning email that arrives while a customer is waiting for a reply about a genuine problem does more relationship damage than the original error, and it is the one part of the whole sequence that is irreversible — you cannot unsend it. Everything else in this system can afford to take thirty seconds. This cannot.

A dispute on an already-paid invoice

Rarer and more urgent. It usually means either a duplicate payment, which the customer wants back, or a genuine service failure that has escalated after the money went. Either way the chaser is irrelevant and the routing is different: it goes straight to whoever owns the relationship rather than into the accounts queue, and it is marked so.

Next: how a recognised dispute gets sorted into one of the six reasons.

All posts