← Back to projects
Product case study · B2B SaaS · EV charging
Trust at Scale
Anomaly detection and real-time reconciliation — a billing-integrity engine that catches and
corrects charger data anomalies before they ever reach a customer's bill.
30–50%fewer anomaly support calls
2–3 hrs → 0manual resolution per case
~5–10%revenue recovered from disputes
10+enterprise clients
Problem
When I took on the billing system, it was reactive by design. Transactions ran in real
time, but error resolution happened post-incident — after a customer had already been billed
incorrectly, after they'd called support, and after a tech engineer had spent 2–3 hours
manually tracing OCPP packets to find where the charger had sent bad data.
EV chargers are IoT devices, and they misbehave. A charger's meter starts counting when a
session begins and stops when energy delivery ends — ideally, meter-stop − meter-start
equals what's billed. In practice, chargers sent packets with stop values lower than start,
incorrect timestamps, or out-of-sequence data — any of which corrupts the record and bills
the customer wrong.
And the silent ones — customers who didn't call, just left a bad review — were never captured
at all.
Solution
A silent auditor that sits inside the CMS and validates every OCPP packet as it flows in —
detecting anomalies, reconstructing the correct value, and writing it back before billing
is triggered.
The shift: from post-incident firefighting to proactive integrity. By the time the invoice is
generated, the data is already clean — most customers never have to notice, because the bill
is right the first time.
Real-time · Packet-level validation · Right-the-first-time billing · Zero manual tracing
The trigger
An enterprise client with roughly 1,500 chargers was losing around 5% of monthly
revenue to billing anomalies — money stuck in dispute, transactions not processing, or
consumers overpaying and demanding refunds. About 70% of their support calls to us were
anomaly-related. They were angry. That made it a priority.
How I scoped it
I scoped and owned the problem end to end, breaking a hard, ambiguous problem into four
sequential steps — each de-risking the next. The hardest was the middle: catching a bad packet
in real time, before it touched billing.
01 · Identify
Root cause
Confirmed the source was upstream — incorrect data coming off the chargers at the packet level.
02 · Flag
Real-time flagging
Catch bad packets as they enter the system, before they touch billing.
03 · Correct
Correction logic
Determine what the correct value should be, and write it back.
04 · Automate
Reconciliation
No human in the loop for standard cases — resolved before a ticket exists.
How the system works
The auditor sits silently inside the CMS and watches every OCPP packet as it flows in. For each
packet, it checks three things:
Three checks on every packet
- Timestamp validity — if a packet's timestamp is clearly wrong (in the past, out of sequence), the system overrides it with the absolute server-receipt time rather than the charger-reported time.
- Meter value consistency — if meter-stop is less than meter-start, or otherwise inconsistent with surrounding packets, the system decides whether it's a one-off or a pattern. A one-off is flagged and the correct consumption is interpolated from neighbouring packets; a persistent mismatch is recalculated from the charger's rated capacity and rounded to the nearest valid value.
- Write-back — corrections happen before billing is triggered. The session record is updated in real time, so by the time the invoice is generated, the data is already clean.
Key decisions & trade-offs
| Decision | Why |
| Real-time correction over a human review loop |
The initial design had a human approval step before any automated correction, as a safety check — and we launched it that way. It didn't work: it introduced delays, so billing was still held up and revenue sat unprocessed. We removed the human loop for standard anomaly types and kept it only for edge cases outside defined thresholds. |
| Static tariffs first, dynamic later |
We built and validated reconciliation against flat-rate tariffs first. Dynamic tariffs add a time-dependency layer — the correct tariff depends on the correct timestamp, which itself might be the thing that's wrong. Prove the simple case, then extend. |
| Descoped: real-time anomaly notifications |
We deliberately didn't alert operators on every anomaly. Many minor irregularities don't affect the final figure and auto-correct without consequence; flooding operators with alerts would cause panic and erode confidence without being actionable. The right signal was "did the session bill correctly?" — not "was there noise in the packet stream?" |
| Held the line on launch speed |
There was pressure to ship faster. I held the timeline: this system writes corrected values directly to live billing data in real time. Getting that wrong in production is worse than getting it slowly right — so we tested extensively before launch. |
Outcomes
Before
Resolution per case2–3 hours, manual
Anomaly share of support calls~70%
Revenue stuck in dispute~5–10%
Anomalous chargers (top client)~500–600 of 1,500
After
Resolution per case0 (auto, pre-ticket)
Anomaly support calls30–50% lower
Disputed transactionsResolving cleanly
RolloutA platform differentiator
The more meaningful signal is the one that's hard to measure: customers who didn't call but
also didn't churn. Renewal conversations got easier, and the system became a product
differentiator — we showed it to other clients as proof the platform self-corrects, which no
competitor was claiming at the time. For a client with ~1,500 chargers (500–600 historically
anomalous), recovering the 5–10% of transactions previously stuck in dispute is material
revenue.
Reflection
What I'd do differently
Scope the guardrails earlier. Too much engineering time went to edge cases we hadn't mapped
in advance — what happens if the meter value and the timestamp are wrong at once,
what's the fallback. A proper failure-mode analysis up front would have moved those
conversations into a doc, not a sprint.
I'd also ship the operator analytics layer alongside the correction system, not
after. Clients could see billing was correct — but had no visibility into how many anomalies
were being caught, so the system was doing work they couldn't see.
What it taught me
In financial systems, trust is the product — features sit on top of it. A platform
can have the best UX, app, and dashboard, and one persistent billing error undoes all of it.
An overcharged customer doesn't think "the charger glitched" — they think "this company
can't be trusted with my money." Getting the data right before it touches the customer isn't
a technical problem; it's the core product commitment.