The NEMT industry has a fraud problem—and states are responding aggressively. Colorado imposed a moratorium on new NEMT provider enrollments until March 2026, citing "significant potential for fraud, waste, or abuse." North Carolina is transitioning brokers amid managed care pressure. Texas is switching to SafeRide Health in 2026.
For legitimate NEMT providers, this creates both risk and opportunity. Risk, because increased scrutiny means more audits. Opportunity, because providers who can prove their legitimacy through technology will win contracts while competitors struggle.
The Fraud Landscape
NEMT fraud costs Medicaid billions annually. The HHS Office of Inspector General has made transportation fraud a priority enforcement area.
Common Fraud Patterns
Common NEMT Fraud Patterns
| Fraud Type | Description | Detection Difficulty |
|---|---|---|
| Ghost trips | Billing for trips never provided | Medium |
| Upcoding | Billing wheelchair when ambulatory provided | Medium |
| Mileage inflation | Claiming more miles than traveled | Low |
| Duplicate billing | Same trip billed multiple times | Low |
| Ineligible members | Transporting non-Medicaid beneficiaries | Medium |
| Kickbacks | Payments to facilities for referrals | High |
Why Fraud Persists
Traditional NEMT operations rely on paper logs, driver attestations, and after-the-fact verification. This creates opportunity:
- No real-time verification: Trips claimed days or weeks after alleged service
- GPS data gaps: Location captured sporadically or not at all
- Member verification weak: Signatures easily forged
- Mileage estimates: Not based on actual routes traveled
Compliance by Design Principles
Rather than detecting fraud after the fact, build systems that make fraud structurally difficult.
Principle 1: Continuous Location Verification
Every trip should have a complete GPS trail from dispatch to completion.
// Real-time trip verification
interface LocationCapture {
tripId: string;
timestamp: string;
latitude: number;
longitude: number;
accuracy: number; // meters
speed: number; // mph
heading: number; // degrees
captureMethod: 'gps' | 'cell_tower' | 'wifi';
deviceId: string;
batteryLevel: number;
}
class TripVerificationService {
private readonly captureIntervalSeconds = 30;
private readonly maxAccuracyMeters = 50;
async verifyTrip(tripId: string): Promise<TripVerification> {
const locations = await this.getLocationHistory(tripId);
const trip = await this.getTripDetails(tripId);
return {
tripId,
gpsTrailComplete: this.hasCompleteTrail(locations, trip),
pickupVerified: this.verifyLocation(locations, trip.pickup, 'pickup'),
dropoffVerified: this.verifyLocation(locations, trip.dropoff, 'dropoff'),
actualMileage: this.calculateActualMileage(locations),
billedMileage: trip.billedMileage,
mileageVariance: this.calculateVariance(locations, trip),
routeReasonable: this.isRouteReasonable(locations, trip),
speedAnomalies: this.detectSpeedAnomalies(locations),
gaps: this.findLocationGaps(locations)
};
}
private hasCompleteTrail(
locations: LocationCapture[],
trip: Trip
): boolean {
// Check for gaps > 2 minutes
for (let i = 1; i < locations.length; i++) {
const gap = new Date(locations[i].timestamp).getTime() -
new Date(locations[i-1].timestamp).getTime();
if (gap > 120000) return false;
}
return true;
}
private verifyLocation(
locations: LocationCapture[],
expected: Location,
type: 'pickup' | 'dropoff'
): LocationVerification {
const nearbyLocations = locations.filter(loc =>
this.distanceMeters(loc, expected) < 100
);
if (nearbyLocations.length === 0) {
return { verified: false, reason: `No GPS data near ${type} location` };
}
// Check dwell time (vehicle should stop for pickup/dropoff)
const dwellTime = this.calculateDwellTime(nearbyLocations);
if (dwellTime < 30) { // Less than 30 seconds
return { verified: false, reason: `Insufficient dwell time at ${type}` };
}
return { verified: true, dwellTimeSeconds: dwellTime };
}
}
Principle 2: Geofence Enforcement
Define valid service areas and alert on trips outside boundaries.
// Geofence validation
interface Geofence {
id: string;
name: string;
type: 'service_area' | 'facility' | 'exclusion';
geometry: GeoJSON.Polygon;
rules: GeofenceRule[];
}
interface GeofenceRule {
condition: 'enter' | 'exit' | 'dwell';
action: 'allow' | 'alert' | 'block';
parameters?: {
minDwellSeconds?: number;
maxDwellSeconds?: number;
timeRestrictions?: TimeWindow[];
};
}
class GeofenceService {
async validateTrip(trip: Trip, locations: LocationCapture[]): Promise<GeofenceValidation> {
const violations: GeofenceViolation[] = [];
// Check pickup is at valid facility or member address
const pickupValid = await this.isValidPickupLocation(trip.pickup, trip.member);
if (!pickupValid) {
violations.push({
type: 'invalid_pickup',
location: trip.pickup,
reason: 'Pickup location not associated with member or approved facility'
});
}
// Check dropoff is at approved medical facility
const dropoffFacility = await this.findFacility(trip.dropoff);
if (!dropoffFacility) {
violations.push({
type: 'unrecognized_destination',
location: trip.dropoff,
reason: 'Dropoff not at recognized medical facility'
});
}
// Check route stayed within service area
const outsideServiceArea = locations.filter(loc =>
!this.isWithinServiceArea(loc)
);
if (outsideServiceArea.length > 0) {
violations.push({
type: 'outside_service_area',
locations: outsideServiceArea,
reason: 'Vehicle traveled outside authorized service area'
});
}
return {
valid: violations.length === 0,
violations,
confidence: this.calculateConfidence(violations, locations)
};
}
}
Principle 3: Real-Time Eligibility Verification
Verify member eligibility before every trip, not after.
// Pre-trip eligibility verification
interface EligibilityCheck {
memberId: string;
serviceDate: Date;
serviceType: 'ambulatory' | 'wheelchair' | 'stretcher' | 'bariatric';
pickupAddress: string;
destination: MedicalFacility;
}
async function verifyEligibilityBeforeDispatch(
check: EligibilityCheck
): Promise<EligibilityResult> {
// 1. Check Medicaid eligibility
const medicaidStatus = await medicaidClient.checkEligibility({
memberId: check.memberId,
serviceDate: check.serviceDate
});
if (!medicaidStatus.eligible) {
return {
eligible: false,
reason: 'Member not eligible for Medicaid on service date',
canProceed: false
};
}
// 2. Check NEMT benefit specifically
if (!medicaidStatus.benefits.includes('NEMT')) {
return {
eligible: false,
reason: 'Member does not have NEMT benefit',
canProceed: false
};
}
// 3. Verify no other transportation available
// (Medicaid NEMT is payor of last resort)
const otherTransport = await checkAlternativeTransportation(check.memberId);
if (otherTransport.available) {
return {
eligible: false,
reason: 'Member has access to alternative transportation',
alternative: otherTransport.type,
canProceed: false
};
}
// 4. Verify appointment exists
const appointment = await verifyAppointment(
check.memberId,
check.destination,
check.serviceDate
);
if (!appointment.verified) {
return {
eligible: true,
warning: 'Unable to verify appointment at destination',
canProceed: true, // Allow but flag for review
requiresAttestation: true
};
}
return {
eligible: true,
canProceed: true,
appointmentConfirmed: true
};
}
Principle 4: Biometric Member Verification
Signatures can be forged. Biometrics can't.
// Member verification options
type VerificationMethod =
| 'facial_recognition'
| 'voice_verification'
| 'pin_code'
| 'telephonic_ivr'
| 'digital_signature';
interface MemberVerification {
tripId: string;
memberId: string;
verificationMethod: VerificationMethod;
timestamp: string;
location: LocationCapture;
confidence: number;
rawData?: string; // Encrypted biometric data
attestation: string;
}
async function verifyMemberPresence(
trip: Trip,
method: VerificationMethod
): Promise<MemberVerification> {
switch (method) {
case 'facial_recognition':
// Compare photo to member's photo on file
const photo = await capturePhoto();
const match = await facialRecognition.compare(
photo,
trip.member.photoOnFile
);
return {
tripId: trip.id,
memberId: trip.member.id,
verificationMethod: method,
timestamp: new Date().toISOString(),
location: await getCurrentLocation(),
confidence: match.confidence,
attestation: `Member identity verified via facial recognition with ${match.confidence}% confidence`
};
case 'telephonic_ivr':
// Call member's phone on file, verify via IVR
const ivrResult = await telephonic.verifyMember(
trip.member.phoneOnFile,
trip.id
);
return {
tripId: trip.id,
memberId: trip.member.id,
verificationMethod: method,
timestamp: new Date().toISOString(),
location: await getCurrentLocation(),
confidence: ivrResult.verified ? 0.95 : 0,
attestation: `Member verified via telephonic IVR to ${maskPhone(trip.member.phoneOnFile)}`
};
// ... other methods
}
}
Principle 5: Immutable Audit Trail
Every action must be logged in a way that cannot be modified.
// Blockchain-backed audit trail
interface AuditRecord {
recordId: string;
tripId: string;
eventType: string;
timestamp: string;
data: Record<string, unknown>;
previousHash: string;
hash: string;
signature: string; // Signed by device/driver
}
class ImmutableAuditLog {
private lastHash: string = 'GENESIS';
async record(event: Omit<AuditRecord, 'recordId' | 'previousHash' | 'hash' | 'signature'>): Promise<AuditRecord> {
const record: Omit<AuditRecord, 'hash' | 'signature'> = {
recordId: crypto.randomUUID(),
previousHash: this.lastHash,
...event
};
const hash = await this.computeHash(record);
const signature = await this.signRecord(record, hash);
const fullRecord: AuditRecord = {
...record,
hash,
signature
};
// Write to immutable storage
await this.writeToBlockchain(fullRecord);
await this.writeToDatabase(fullRecord); // Backup
this.lastHash = hash;
return fullRecord;
}
async verifyChain(tripId: string): Promise<ChainVerification> {
const records = await this.getRecordsForTrip(tripId);
for (let i = 1; i < records.length; i++) {
// Verify hash chain
if (records[i].previousHash !== records[i-1].hash) {
return { valid: false, brokenAt: i, reason: 'Hash chain broken' };
}
// Verify individual hashes
const expectedHash = await this.computeHash(records[i]);
if (expectedHash !== records[i].hash) {
return { valid: false, brokenAt: i, reason: 'Record hash mismatch' };
}
}
return { valid: true, recordCount: records.length };
}
}
EVV Integration for Compliance
Electronic Visit Verification (EVV) is mandated by the 21st Century Cures Act for NEMT services.
Required Data Elements
EVV Required Data Elements
Automated EVV Record Generation
// Generate compliant EVV record at trip completion
async function generateEVVRecord(trip: CompletedTrip): Promise<EVVRecord> {
const locations = await locationService.getTripLocations(trip.id);
const verification = await getMemberVerification(trip.id);
const evvRecord: EVVRecord = {
// Member identification
memberId: trip.member.medicaidId,
memberName: trip.member.name,
// Provider identification
providerId: trip.provider.npi,
providerName: trip.provider.name,
driverId: trip.driver.id,
driverName: trip.driver.name,
// Service details
serviceType: mapToEVVServiceCode(trip.serviceLevel),
serviceDate: trip.serviceDate,
// Location data
pickupAddress: trip.pickup.address,
pickupLatitude: locations.pickup.latitude,
pickupLongitude: locations.pickup.longitude,
pickupTime: locations.pickup.timestamp,
dropoffAddress: trip.dropoff.address,
dropoffLatitude: locations.dropoff.latitude,
dropoffLongitude: locations.dropoff.longitude,
dropoffTime: locations.dropoff.timestamp,
// Verification
memberVerificationMethod: verification.method,
memberVerificationTime: verification.timestamp,
// Calculated fields
totalMileage: calculateMileageFromGPS(locations.trail),
totalDuration: calculateDuration(locations.pickup.timestamp, locations.dropoff.timestamp)
};
// Submit to state EVV aggregator
await evvAggregator.submit(evvRecord);
return evvRecord;
}
Fraud Detection Analytics
Even with prevention controls, anomaly detection catches what slips through.
Real-Time Alerts
| Alert Type | Trigger | Response |
|---|---|---|
| Impossible trip | Travel time < minimum possible | Block billing, investigate |
| Duplicate trip | Same member, same day, same destination | Flag for review |
| Pattern anomaly | Driver billing 3x average | Audit driver's trips |
| Mileage variance | Billed > GPS × 1.3 | Adjust to GPS mileage |
| Location mismatch | GPS not near claimed addresses | Block billing, investigate |
Retrospective Analysis
// Monthly fraud analytics
interface FraudAnalyticsReport {
period: string;
totalTrips: number;
flaggedTrips: number;
flagRate: number;
categories: {
category: string;
count: number;
totalValue: number;
}[];
highRiskDrivers: {
driverId: string;
riskScore: number;
flaggedTrips: number;
reasons: string[];
}[];
recommendations: string[];
}
async function generateFraudReport(month: string): Promise<FraudAnalyticsReport> {
const trips = await getTripsForMonth(month);
const analysis = await fraudModel.analyze(trips, {
compareToBaseline: true,
identifyOutliers: true,
clusterAnalysis: true
});
return {
period: month,
totalTrips: trips.length,
flaggedTrips: analysis.flagged.length,
flagRate: analysis.flagged.length / trips.length,
categories: analysis.categoryBreakdown,
highRiskDrivers: analysis.driverRiskScores.filter(d => d.riskScore > 0.7),
recommendations: analysis.generateRecommendations()
};
}
Key Takeaways
-
Build prevention, not just detection - Make fraud structurally difficult through technology
-
GPS is your foundation - Continuous location capture eliminates most ghost trip fraud
-
Verify before service - Real-time eligibility checks prevent billing for ineligible members
-
Biometrics beat signatures - Member presence verification should be unforgeable
-
Immutable records protect everyone - Audit trails that can't be modified prove legitimacy
Building Trust Through Technology
Legitimate NEMT providers have an opportunity to differentiate through compliance excellence. When states audit, providers with comprehensive EVV data, GPS trails, and biometric verification have nothing to fear—and everything to gain.
PEW Consulting builds fraud-resistant NEMT platforms that protect providers while ensuring compliance with state and federal requirements.
Schedule a compliance architecture review to assess your current systems and identify improvement opportunities.
Sources
- Colorado HCPF: NEMT Moratorium
- CMS: Electronic Visit Verification
- HHS OIG: Medicaid Fraud
- UHC: Texas NEMT Changes
Related reading: NEMT Software Solutions: Building Scalable Medical Transportation Systems
