Expense audit has always been sampling because checking every claim by hand was never affordable. AI removes that constraint. You score every line for policy breaches, duplicates and fraud signals, then route only the ranked exceptions to a reviewer. The audit becomes full-population coverage with a human queue on top rather than a five-percent spot check.
Picture the old process. A reviewer pulls five percent of claims, checks receipts against policy, eyeballs a few amounts, and signs off on the rest by inference. Full scoring changes the economics, but it does not change where the difficulty lives. The inputs arrive as photographed receipts, PDF invoices, card-network feeds and free-text descriptions, and none of them agree on how to spell a merchant’s name. Get the extraction and entity resolution wrong and every downstream flag inherits the error. So the work starts there, not with the model.
Extraction and entity resolution come first
Every claim has to become a set of fields you can reason about: amount, currency, date, merchant, category, tax component, payment instrument, employee, cost centre. Receipts are the messy end. A crumpled restaurant slip photographed at an angle gives you a total, a tip, a tax line and a date, and any of them can be misread by a digit. You extract with confidence scores per field and treat low-confidence amounts as unresolved rather than trusting them silently. A wrong amount that reads like a right one is the failure mode that costs you.
Merchant identity is the harder problem. “AMZN Mktp DE”, “Amazon.de”, and “Amazon EU Sarl” on an invoice are one vendor for audit purposes and three strings on the page. Entity resolution against a canonical vendor and merchant table is what lets you compare a card-feed line to its receipt and to the eventual invoice. Without it, duplicate detection and three-way reconciliation both fall apart, because the join key is a name nobody standardised.
- Card-network merchant category codes give you a category you did not have to infer, and a cross-check against the category the employee claimed.
- Last-four card digits and authorisation timestamps let you tie a receipt to a specific transaction rather than guessing from amount and date.
- Currency and FX date matter: a claim converted at the wrong day’s rate is a real leak, and you can only catch it once the date is trustworthy.
Policy violations are rules, not models
Most of what an expense audit catches is deterministic. Per-diem exceeded, alcohol claimed where it is disallowed, a hotel over the city cap, a taxi claimed on a day the employee also claimed mileage, a missing receipt above the threshold, VAT reclaimed on a non-reclaimable category. These are rules run against clean fields. Do not reach for a model where a rule is correct and explainable to the employee whose claim you rejected.
The engineering discipline is keeping the rule engine honest as policy changes. Caps move, categories get added, a country changes its per-diem quarterly. You version the policy set and evaluate each claim against the policy in force on the transaction date, not the policy in force today. Score last quarter’s expenses under this quarter’s caps and you generate breaches that never existed. That is a point-in-time correctness problem, the same one that bites feature pipelines, and it shows up here the moment someone audits a prior period at quarter-end.
Rules also need a false-positive budget. A cap breach of eleven cents is technically a violation and practically noise. You set materiality thresholds per rule, suppress trivial breaches, and keep them in the audit trail so a reviewer can still see them if a pattern emerges. The goal is a queue a human can actually clear.
Duplicates and fraud need scoring, and a memory of what happened
Duplicates are the highest-yield category and the one pure rules miss. The same dinner submitted twice, once from the receipt and once from the card statement. An invoice paid on the PO line and again as a one-off. A contractor who bills the same work across two months with the dates nudged. Exact-match dedup catches the lazy cases. The rest need fuzzy matching:
- Block candidates by amount band, date window and resolved vendor so you are not comparing every claim to every other claim.
- Score each pair on normalised amount, date proximity, merchant identity, and shared card or invoice identifiers.
- Flag pairs above a distance threshold, and weight recent submissions by the same employee more heavily.
Fraud scoring sits on top. Round-number amounts clustered just under approval thresholds, a spike in claims near quarter-end, receipts whose stated tax does not reconcile to the stated total, image metadata that says the photo was taken after the claimed transaction, or the same receipt image reused across two claims. Where you have confirmed fraud cases, supervised scoring helps; where you do not, outlier detection against an employee’s and a cost centre’s own history does most of the work. Either way you are ranking by expected recovery, not raw anomaly, so the review queue is ordered by what is worth chasing.
Two things keep the flags defensible over time. First, an audit trail: every flag records the fields it fired on, the policy version, the threshold, and the model score, so a rejected employee gets a reason and an auditor gets lineage. Second, drift monitoring. Spend patterns move with travel policy, headcount and the calendar, and a threshold tuned in January quietly over- or under-fires by June. You hold out an eval set of adjudicated claims, track precision and recall on it each month, and recalibrate before the queue either floods or goes quiet. A model nobody rechecks is a model that has already stopped matching the business it audits.
FAQ
Do you need labelled fraud cases to build an expense audit model?
For policy breaches, no. Those are deterministic rules run against extracted fields. For fraud and duplicate scoring you want confirmed cases where you have them, but most of the early lift comes from unsupervised duplicate detection and outlier scoring that need no labels at all.
How do you catch a duplicate when the two claims do not match exactly?
Blocking plus fuzzy matching. You group candidates by amount band, date window and vendor, then compare each pair on normalised amount, date proximity, merchant identity and last-four card digits. Near-duplicates that survive a distance threshold get flagged, not just byte-identical resubmissions.
Will full-population scoring bury the audit team in flags?
Only if you skip calibration. You set a false-positive budget per category, tune thresholds so the flag rate matches review capacity, and rank by expected recovery. Scoring everything does not mean reviewing everything.