How it works
The audit, in plain English
This page describes exactly what the engine does. It's kept in sync with the live rules — if you spot a row whose behaviour disagrees with what you read here, that's a bug: click ⚠ Report on the row and we'll fix it.
Engine: v0.9.0 — May 6, 2026
1. The one question
For every line on every carrier bill: "Is this phone number in our Salesforce, and what should we do about it?"
Then the reverse: "For every active Salesforce contract, did it appear on any uploaded bill?"That second pass is the "Unbilled assets" bucket.
2. The eight buckets
Every billed line — and every active Salesforce contract — ends up in exactly one bucket. Listed here in the priority order they appear in the audit results page (most actionable first).
Ghost devices
Hardware appears dead. Cancel + retrieve.
All these must be true
- •Billed on the carrier bill
- •Phone matches Salesforce active
- •Bill shows zero or no usage
- •InControl shows device offline 30+ days (configurable in Settings)
Action
Cancel the SIM at the carrier and retrieve the hardware.
Add to Salesforce
Billed and confirmed working — but missing from CRM.
All these must be true
- •Billed on the carrier bill
- •Phone is NOT in Salesforce
- •InControl confirms the device is ONLINE
Action
Add the Asset to Salesforce. We own it and we know it works.
Not on Salesforce
Billed line, missing from CRM, no InControl confirmation.
All these must be true
- •Billed on the carrier bill
- •Phone is NOT in Salesforce
- •InControl can't confirm (offline 60+ days OR no device record)
Action
Manual search (tickets / emails / order history) before canceling. If you find a record we own → add to SF; otherwise → cancel.
Unbilled assets (reverse audit)
Active in Salesforce but NOT on any carrier bill.
All these must be true
- •Salesforce status is Active or Deployed
- •The phone (or ICCID, for Frontier) doesn't appear on any uploaded carrier bill
Action
Verify line status with the carrier. May be canceled at the carrier without CRM update — risky for failover.
SIM swaps & failover
MTN/ICCID drift OR healthy failover SIMs.
All these must be true
- •(swap) Phone matches SF, but bill ICCID differs from SF ICCID
- •(missing-iccid) Phone matches SF, SF has NO ICCID on file → add the bill's ICCID to SF
- •(failover) Zero usage but InControl shows device online
Action
Verify, then either update Salesforce SIM ID, add the ICCID to SF, or leave alone (failover is doing its job).
No device attached
Billed line in CRM but no hardware in InControl.
All these must be true
- •Billed on the carrier bill
- •Phone matches Salesforce active
- •Bill shows zero or no usage
- •InControl has NO device record for this SIM
Action
Confirm: SIM-only sale, cold spare, or on-site backup SIM? If none of those, treat as ghost candidate.
Manual audit needed
True edge cases — kept as a safety net.
All these must be true
- •The rules engine couldn't fit this row into a clearer category
- •(in practice currently rare; any future rule branch that can't decide routes here)
Action
Human review of bill + Salesforce + InControl context.
Working SIMs
Healthy. No action needed.
All these must be true
- •Phone is in Salesforce active
- •Carrier bill reports usage > 0 MB, OR
- •Bill has no usage column AND InControl confirms the device is online
Action
Leave it alone.
3. The decision tree
For every billed line the engine walks the forward audit; then for every Salesforce active contract it runs the reverse audit.
Forward audit (per billed line)
Reverse audit (per Salesforce active contract)
4. 📞 Phone number is the master key
The audit matches by phone for every carrier (one exception below — Frontier). Phones are normalized so all of these point to the same line:
(555) 123-4567+1 555-123-45671.555.123.45675551234567MTN: 555-123-4567sub: +1 (555) 123-45675551234567 ext 22555.123.4567 x100Column names in carrier files come in many flavors — all of these are recognized as the phone column:MTN · MDN · MSISDN · sub · sub: · subscriber · PTN · Wireless Number · Mobile Number · Phone · Line Number · Contact
5. 🟡 Frontier — the one exception
Frontier doesn't put real phone numbers on their bills. Their Service_TN column actually holds the SIM ICCID (a 19-digit number starting with 891030…).
So for Frontier — and only Frontier — we match by ICCID instead of by phone. Salesforce records for Frontier supplier rows must have the SIM ID populated for matching to work.
6. ⛔ InControl is verification only
The audit list comes from carrier bills. InControl is never a source of audit candidates — only a witness for SIMs already on a bill.
- ✅ Bill says X is billed → look up X in InControl → use status as evidence
- ✅ Salesforce says contract Y is active → check whether Y appears on any bill
- ❌ NEVER: take an InControl SIM and ask "is this in Salesforce?" — that direction is forbidden
7. ICCID verification (only when phone matches)
Once a phone is matched to Salesforce, we check whether the bill's ICCID matches what SF has. There are three states:
match
Bill ICCID is in SF's contract ICCIDs (or bill has no ICCID). No action needed.
missing_in_sf
Phone matches SF but SF has no ICCID on file. Add the bill's ICCID to the SF Asset.
mismatch_in_sf
SF has a different ICCID than the bill. Verify SIM swap with the carrier.
We never recommend ICCID actions when the phone isn't in Salesforce — first deal with the missing phone record; the SIM ID is downstream.
8. How files are auto-classified
Filenames don't matter — every file is classified by its column headers. Salesforce's "fake .xls" (HTML masquerading as Excel) is also handled.
Salesforce export
Recognized by: Service Account · SIM ID · Supplier · MTN · Assets: Assets
InControl device list
Recognized by: S/N · Online Status · Online/Offline
InControl SIM info
Recognized by: SIM Slot · IMSI · ICCID
Verizon bill
Recognized by: Filename has 'Verizon' OR 'Quick Bill Summary' header
Frontier bill
Recognized by: Service_TN · Service_Circuit_Number · Service_Status
AT&T bill
Recognized by: BAN · FAN · Wireless Number columns OR filename
T-Mobile bill
Recognized by: MSISDN · Subscriber Number OR filename has 'T-Mobile' / 'T-Mo'
Data2Go bill
Recognized by: Filename has 'Data2Go' + 'Carrier Name' column (reseller — provider per row)
RH bill
Recognized by: Filename has 'RH' (AT&T reseller)
Generic carrier
Recognized by: Catch-all if a file has identifier + usage/amount columns
9. Confidence levels
Every row carries a confidence badge. Color matches what you see in the audit results.
high
Strong, unambiguous evidence. Multiple data sources agree. Act on it.
medium
Rule fired but evidence has known false-positive modes. Verify before acting.
low
Edge case the rules engine couldn't decide. Human review required.
10. 🤖 AI assist on edge cases
Everything above runs as deterministic JavaScript — no AI involved. AI only enters the picture for rows the rules engine couldn't confidently categorize (specifically: manualAuditNeeded rows or those flagged low confidence).
- ✅ Gemini sees the same evidence the rules engine saw and returns a refined bucket + plain-English narrative.
- ✅ Every AI verdict is cached in SQLite by a hash of the row's evidence — same input gives same output forever (deterministic).
- ✅ Off by default unless
GEMINI_API_KEYis set in Settings AND the "Use AI on edge cases" toggle is on. - ❌ AI is never used for the deterministic rules above. The bulk of the audit is byte-identical between runs even with AI off.
11. 💵 How leakage is calculated
The dollar number on the dashboard is computed from the categorized output — never AI-estimated.
| Bucket | Counts toward leakage? | Fallback when amount missing |
|---|---|---|
| Ghost devices | Yes | $25 / mo |
| Add to Salesforce | Yes — we're paying for it | $25 / mo |
| Not on Salesforce | Yes | $25 / mo |
| Unbilled assets | No (not on a bill) | — |
| SIM swaps & failover | No (inventory issue) | — |
| No device attached | No (ambiguous) | — |
| Manual audit needed | No (no claim without justification) | — |
| Working SIMs | No (healthy) | — |
12. ⚠ Spotted a row that's wrong?
Click ⚠ Report on any row in the audit results. A modal opens with the full row context. Describe what's wrong — we get an email with everything: the row's data, the engine's evidence, the recommendation it gave, and your description.
Then we tune the rule and add a regression test so the same false positive can never silently return. The engine learns from real use; this page updates to reflect the new behaviour. Every change to the rules updates this page in the same release.
13. 🚨 Salesforce-cancelled detection
When a row is matched to a Salesforce contract whose status reads Cancelled, Disconnected, Suspended, Terminated, Decommissioned, Inactive (or any close variant), it lands in Ghost devices with the highest possible confidence — no InControl heuristic needed. Salesforce is already telling us the line should be off; the only remaining action is the carrier-side cancellation.
The status vocabulary is intentionally broad to catch operator typos and carrier-specific words. "Cancelled", "cancelled ", "Disconnected", "Inactive" all map to the same canceled state.
Limitation worth knowing: this rule looks at the asset-level status. If your Salesforce export includes a separate parent Service Account status column (e.g. account-level cancellation), it isn't currently joined in. Add it as a column to the export and we'll wire it into the rule.
14. ☁︎ Direct Salesforce links
Every row whose ICCID/MTN matched a Salesforce record now shows an ☁︎ Open in SF chip under the SIM ICCID. Click it to open the live record in a new tab — no searching, no copy-paste.
The link is built from the Assets: ID column in your Salesforce export (values like a1ZHs00000OIVssMAH) and points at: metrowireless.lightning.force.com/lightning/r/Assets__c/<id>/view.
The asset ID is also included in CSV exports (column Salesforce Asset ID) and in the error-report email so anyone reviewing a flagged row can jump straight to the SF record.
Rows in the Not on Salesforce bucket don't have a SF link (by definition — they're missing from CRM). Rows in Unbilled assets (reverse audit) DO have links since they came from a SF record in the first place.
MW Audit · automated reconciliation · v0.9.0 — May 6, 2026