An invoice can match its purchase order line for line and still charge the wrong price. That sentence sounds like a contradiction until you separate the two things an invoice is checked against. Accounts payable checks the document in front of it: supplier known, goods received, quantities right, total plausible. The contract is a different document, often a different system, and it is the one that says what the price was supposed to be.

Building a price variance check meant joining those two datasets and living with everything that makes the join awkward.

The shape of the drift

Contract pricing rarely fails in a dramatic way. It drifts in small, dated steps:

An increase agreed for January gets applied in October, because a billing system took the announcement date as the effective date. A price rises above a cap that was negotiated precisely to stop that happening. A volume tier the business qualified for is never switched on, so every invoice in the period bills at the standard rate. A temporary surcharge, introduced for one quarter, is still present three quarters later because nothing in the workflow owns removing it.

Each gap is a few percent. None of it trips a tolerance check tuned for large errors. Repeated across a year of orders, a two or three percent gap becomes a material overpayment, and worse, it becomes the base that the next increase is calculated from.

What was built

The check itself is simple to state. For each major supplier, hold the contract terms as structured data, not as a PDF someone remembers reading: the agreed price, the date any increase takes effect, the cap, and the tier and discount rules. Then compare those terms against every paid invoice in the period and flag the mismatches.

Three flags earned their place first, because they are provable from documents alone: an increase billed before its agreed date, a unit price above the contractual cap, and an invoice that ignores a tier or discount the order volume qualified for. Each flag points at a specific invoice, a specific contract clause, and a specific difference. That specificity is what makes a credit request hard to argue with.

Sequencing mattered as much as detection. Starting with the five largest suppliers sounds like a sampling shortcut, but it is really a prioritisation rule: variance found there pays for the attention the rest of the ledger needs, and variance absent there is genuine evidence the billing is disciplined.

What worked

Treating the contract as data worked. The moment terms lived in fields rather than in a scanned agreement, the comparison became mechanical and repeatable, and a check that ran once could run every month. Variance stopped being an annual forensic project and became a routine diff.

Date logic carried more weight than expected. A surprising share of findings were not wrong prices but right prices applied at the wrong time. Effective dates, surcharge end dates, and tier review points are all just dates, and billing systems are quietly careless with all three.

Small percentages deserved respect. A checker tuned to ignore anything under five percent ignored almost everything that mattered, because contract drift lives at two and three percent by design.

What broke, or nearly did

Tier logic nearly broke the model. Volume tiers depend on what counts toward the volume, over which window, and whether returns net off. Two reasonable readings of the same clause produced different expected prices, and the checker had to record which reading it used rather than pretend the contract was unambiguous.

Amendments broke the single contract assumption. Prices change by side letter and email agreement as well as by formal renewal, so the terms table needed a history, not a snapshot. An invoice from March has to be judged against the terms as they stood in March.

The honest limit is that detection is not recovery. The checker can prove a gap. Turning the proof into a credit still depends on the contract and the order history being current, which is why the same check gets harder, and less recoverable, the longer it is left.

Lessons for builders

If a rule lives in a document, extract it into fields before you build anything that depends on it. Comparisons against prose are manual forever.

Check dates as carefully as amounts. A correct value at the wrong time is still an error, and it is the kind tolerance checks wave through.

Rank findings by provability. A short list where every item names its clause, invoice, and difference beats a long list of suspicions.

The project behind these lessons is ERPSav, a cost recovery product from CloudVerg that runs rules based checks across bills a business has already paid, including duplicate supplier payments, lease overcharges, utility and telecom errors, and price variance against contract terms.

Discussion welcome: for anyone building reconciliation or audit tooling, where did the messiest mismatch between two supposedly matching records turn up in your system?