The client came to us with a problem they had lived with for years: a core reconciliation process that touched five systems, required three FTEs to run daily, and had a failure rate that meant every Friday afternoon was a crisis drill.
The Problem
The process ran like this: data exports from two source systems were emailed to a shared mailbox. An analyst downloaded both files, ran a macro-heavy Excel model to reconcile them, manually corrected mismatches by logging into a third system, and then produced a summary report that was emailed up the chain. Any mismatch that could not be auto-resolved triggered a ticket in a fourth system.
Three FTEs. Five systems. Two hours minimum per run. Four runs per day. Zero auditability beyond "ask Karen what happened on Tuesday."
The Solution
We built a self-serve reconciliation platform on Azure that replaced the entire manual workflow:
- Azure Logic Apps ingested the source data on a schedule, eliminating the email dependency
- A custom reconciliation engine (TypeScript + PostgreSQL) applied the matching rules that had previously lived in the Excel macro
- Exception handling surfaced mismatches in a React dashboard with a one-click resolution workflow
- All decisions were logged with timestamps and user attribution for audit purposes
- The summary report was generated automatically and pushed to the right people via Microsoft Teams
The Outcome
At go-live, run time dropped from two hours to eleven minutes. The three FTEs were redeployed to higher-value work. Exception volume dropped by 67% in the first month as the matching rules improved with better data.
The platform cost roughly 8% of the annual labor cost it replaced in year one. By year two, with maintenance factored in, it is under 4%.
What Made It Work
The technical decisions were straightforward. What made the project succeed was the discovery process. We spent the first two weeks doing nothing but watching the current process run and talking to the analysts who ran it. They knew things about the edge cases that were not in any document.
// Reconciliation engine core matching logic (simplified)
async function reconcileRecords(
source: RecordSet,
target: RecordSet
): Promise<ReconciliationResult> {
const exact = matchExact(source, target)
const fuzzy = matchFuzzy(source.unmatched, target.unmatched, THRESHOLD)
const exceptions = source.unmatched.filter(
(r) => !fuzzy.matched.includes(r.id)
)
return { exact, fuzzy, exceptions }
}
If you have a process like this โ one that everyone knows is broken but no one has fixed โ we would be glad to spend an hour understanding it with you.