Missed medical appointments cost the U.S. healthcare system an estimated $150 billion annually. For the 3.6 million Americans who miss or delay care due to transportation barriers, the consequences extend beyond dollars—delayed treatments, worsening conditions, and preventable hospitalizations.
Non-Emergency Medical Transportation (NEMT) bridges this gap. But building software that coordinates thousands of daily trips while meeting Medicaid compliance requirements demands careful architecture decisions.
The NEMT Market Landscape
NEMT is one of the fastest-growing segments in healthcare technology. The market is projected to reach $15.6 billion by 2028, driven by aging populations, Medicaid expansion, and value-based care models that penalize missed appointments.
Who Needs NEMT Software?
The NEMT ecosystem includes multiple stakeholders, each with distinct requirements:
| Stakeholder | Primary Needs |
|---|---|
| Transportation Providers | Dispatch, driver management, billing |
| Medicaid Managed Care Organizations | Broker management, cost control, compliance |
| Healthcare Systems | Appointment integration, no-show reduction |
| State Medicaid Agencies | Oversight, fraud prevention, reporting |
Most NEMT software focuses on one stakeholder. The opportunity lies in platforms that connect all four.
Core Architecture for NEMT Platforms
Scalable NEMT software requires real-time coordination across three domains: scheduling, routing, and compliance. Each domain has unique technical challenges.
Real-Time Trip Management
NEMT trips follow a predictable lifecycle, but edge cases dominate daily operations:
NEMT Trip Lifecycle
// NEMT Trip State Machine
type TripStatus =
| 'requested' // Member or facility requested transport
| 'verified' // Eligibility confirmed
| 'scheduled' // Assigned to driver/vehicle
| 'dispatched' // Driver en route to pickup
| 'arrived' // Driver at pickup location
| 'in_transit' // Member in vehicle
| 'completed' // Member delivered to destination
| 'no_show' // Member not present at pickup
| 'cancelled' // Trip cancelled before completion
| 'billing_ready'; // Ready for claim submission
interface Trip {
id: string;
memberId: string;
status: TripStatus;
pickupLocation: Location;
dropoffLocation: Location;
appointmentTime: Date;
pickupWindow: { start: Date; end: Date };
serviceLevel: 'ambulatory' | 'wheelchair' | 'stretcher' | 'bariatric';
assignedVehicle?: string;
assignedDriver?: string;
actualTimes: {
dispatched?: Date;
arrivedPickup?: Date;
departed?: Date;
arrivedDropoff?: Date;
};
evvData?: EVVRecord;
}
The state machine must handle concurrent modifications. A driver marking "arrived" while dispatch reassigns the trip creates race conditions that corrupt data if not handled properly.
Multi-Modal Transportation Support
NEMT encompasses multiple service levels with different vehicle, driver, and billing requirements:
NEMT Service Levels
Each service level has distinct constraints:
- Ambulatory: Standard vehicles, no special equipment
- Wheelchair: ADA-compliant vehicles with ramps or lifts
- Stretcher: Medical transport vehicles with securing equipment
- Bariatric: Vehicles rated for higher weight capacity
Your scheduling algorithm must match members to appropriate vehicles while optimizing route efficiency.
Appointment Integration
The most effective NEMT platforms integrate directly with healthcare appointment systems:
// Example: Healthcare appointment webhook handler
interface AppointmentEvent {
eventType: 'scheduled' | 'rescheduled' | 'cancelled';
appointmentId: string;
patientId: string;
facilityId: string;
appointmentTime: Date;
appointmentType: string;
provider: string;
}
async function handleAppointmentEvent(event: AppointmentEvent): Promise<void> {
const member = await memberService.findByPatientId(event.patientId);
if (!member?.transportationBenefit) {
return; // Member doesn't have NEMT benefit
}
switch (event.eventType) {
case 'scheduled':
// Check if member needs transportation
const needsTransport = await assessTransportationNeed(member, event);
if (needsTransport) {
await tripService.createFromAppointment(member, event);
}
break;
case 'rescheduled':
// Update existing trip or create new one
await tripService.rescheduleForAppointment(member, event);
break;
case 'cancelled':
// Cancel associated trip
await tripService.cancelForAppointment(event.appointmentId);
break;
}
}
Proactive trip creation—scheduling transportation when appointments are booked—reduces no-shows significantly compared to member-initiated requests.
Route Optimization at Scale
Route optimization is where NEMT software succeeds or fails economically. A 10% improvement in route efficiency directly impacts profitability.
The Vehicle Routing Problem
NEMT routing is a variant of the Vehicle Routing Problem with Time Windows (VRPTW), complicated by:
- Pickup and delivery pairs: Each trip has two stops that must be served by the same vehicle
- Time windows: Appointments have fixed times; pickups must occur within windows
- Service level constraints: Vehicles can only serve compatible service levels
- Driver hours: DOT regulations limit driving time
- Return trips: Members need rides home after appointments
This is NP-hard. Exact solutions don't scale beyond a few dozen trips. Practical systems use heuristics and metaheuristics.
Optimization Approaches
1. Insertion Heuristics (Fast, Good Enough)
For real-time dispatch, insertion heuristics provide acceptable solutions in milliseconds:
// Cheapest insertion heuristic
function findBestInsertion(
trip: Trip,
routes: Route[]
): { route: Route; pickupIndex: number; dropoffIndex: number; cost: number } | null {
let bestInsertion = null;
let bestCost = Infinity;
for (const route of routes) {
if (!isVehicleCompatible(route.vehicle, trip.serviceLevel)) {
continue;
}
// Try all valid insertion positions
for (let pickupIdx = 0; pickupIdx <= route.stops.length; pickupIdx++) {
for (let dropoffIdx = pickupIdx + 1; dropoffIdx <= route.stops.length + 1; dropoffIdx++) {
const insertionCost = calculateInsertionCost(route, trip, pickupIdx, dropoffIdx);
if (insertionCost < bestCost && isTimeWindowFeasible(route, trip, pickupIdx, dropoffIdx)) {
bestCost = insertionCost;
bestInsertion = { route, pickupIndex: pickupIdx, dropoffIndex: dropoffIdx, cost: insertionCost };
}
}
}
}
return bestInsertion;
}
2. Metaheuristics (Better Solutions, More Time)
For batch optimization (overnight scheduling), metaheuristics like Simulated Annealing or Genetic Algorithms find better solutions:
// Simulated annealing for route optimization
function optimizeRoutes(
initialSolution: Solution,
config: {
initialTemp: number;
coolingRate: number;
iterations: number;
}
): Solution {
let current = initialSolution;
let best = initialSolution;
let temperature = config.initialTemp;
for (let i = 0; i < config.iterations; i++) {
// Generate neighbor solution via random move
const neighbor = generateNeighbor(current);
const delta = neighbor.cost - current.cost;
// Accept better solutions, or worse solutions with probability
if (delta < 0 || Math.random() < Math.exp(-delta / temperature)) {
current = neighbor;
if (current.cost < best.cost) {
best = current;
}
}
temperature *= config.coolingRate;
}
return best;
}
3. Machine Learning Enhancement
ML models can improve optimization by predicting:
- Trip duration based on traffic patterns, weather, and historical data
- No-show probability to inform overbooking strategies
- Demand patterns for proactive vehicle positioning
The practical approach: use ML for predictions, feed those into traditional optimization algorithms.
Real-Time Reoptimization
Static routes break on contact with reality. Cancellations, delays, and new trip requests require continuous reoptimization.
Design for incremental updates rather than full recalculation:
// Event-driven reoptimization
async function handleTripEvent(event: TripEvent): Promise<void> {
switch (event.type) {
case 'cancelled':
// Remove trip from route, check if subsequent trips can be served earlier
await removeAndReoptimize(event.tripId);
break;
case 'delayed':
// Propagate delay through route, potentially reassign downstream trips
await propagateDelay(event.tripId, event.newEstimate);
break;
case 'new_request':
// Find best insertion without disrupting confirmed pickups
await insertWithMinimalDisruption(event.trip);
break;
}
}
Medicaid Compliance Requirements
NEMT is a Medicaid benefit, which means strict compliance requirements govern every aspect of operations.
Electronic Visit Verification (EVV)
The 21st Century Cures Act mandates EVV for NEMT services. Your system must capture and transmit:
EVV Required Data Elements
EVV data must be captured at the point of service—not entered later from driver logs. GPS, timestamps, and member verification create an auditable record.
// EVV record creation at trip completion
interface EVVRecord {
tripId: string;
memberId: string;
driverId: string;
serviceDate: string;
pickupTime: string;
pickupLocation: {
latitude: number;
longitude: number;
address: string;
captureMethod: 'gps' | 'manual_override';
};
dropoffTime: string;
dropoffLocation: {
latitude: number;
longitude: number;
address: string;
captureMethod: 'gps' | 'manual_override';
};
memberVerification: {
method: 'signature' | 'pin' | 'biometric' | 'telephonic';
timestamp: string;
verificationData: string; // encrypted
};
mileage: number;
serviceLevel: string;
}
async function createEVVRecord(trip: Trip, driver: Driver): Promise<EVVRecord> {
// Validate all required data is present
if (!trip.actualTimes.arrivedPickup || !trip.actualTimes.arrivedDropoff) {
throw new EVVError('Missing required timestamps');
}
const record: EVVRecord = {
tripId: trip.id,
memberId: trip.memberId,
driverId: driver.id,
serviceDate: formatDate(trip.actualTimes.arrivedPickup),
pickupTime: formatTime(trip.actualTimes.arrivedPickup),
pickupLocation: await captureLocation(driver, 'pickup'),
dropoffTime: formatTime(trip.actualTimes.arrivedDropoff),
dropoffLocation: await captureLocation(driver, 'dropoff'),
memberVerification: await captureMemberVerification(trip),
mileage: calculateMileage(trip),
serviceLevel: trip.serviceLevel,
};
// Submit to state EVV aggregator
await evvAggregator.submit(record);
return record;
}
Eligibility Verification
Before scheduling any trip, verify the member's Medicaid eligibility and NEMT benefit:
// Real-time eligibility check
interface EligibilityResult {
eligible: boolean;
nemtBenefit: boolean;
serviceLevel: string[];
limitations: string[];
priorAuthRequired: boolean;
remainingTrips?: number; // if limited
}
async function verifyEligibility(
memberId: string,
serviceDate: Date
): Promise<EligibilityResult> {
// Query state Medicaid system (e.g., ProviderOne in Washington)
const response = await medicaidClient.checkEligibility({
memberId,
serviceDate,
benefitType: 'NEMT',
});
if (!response.eligible) {
await auditLog.info('ELIGIBILITY_DENIED', { memberId, reason: response.reason });
}
return response;
}
Billing and Claims
NEMT billing uses standard healthcare claim formats (837P) with procedure codes specific to transportation:
| HCPCS Code | Description | Typical Use |
|---|---|---|
| A0080 | Non-emergency transportation, per mile | Mileage billing |
| A0090 | Non-emergency transport, per trip | Flat rate billing |
| A0100 | Non-emergency transport, taxi | Taxi/rideshare |
| A0120 | Non-emergency wheelchair van | Wheelchair transport |
| A0130 | Non-emergency ambulance, wheelchair | Stretcher transport |
Billing accuracy requires precise mileage calculation and proper service level coding:
// Generate billing record from completed trip
function generateClaimData(trip: Trip, evv: EVVRecord): ClaimData {
const procedureCode = mapServiceLevelToHCPCS(trip.serviceLevel);
const mileage = evv.mileage;
return {
memberId: trip.memberId,
serviceDate: evv.serviceDate,
procedureCode,
units: procedureCode === 'A0080' ? mileage : 1,
placeOfService: '99', // Other
diagnosis: trip.diagnosisCode,
pickupAddress: evv.pickupLocation.address,
dropoffAddress: evv.dropoffLocation.address,
// Attach EVV record as supporting documentation
evvReference: evv.tripId,
};
}
Fraud Prevention
Medicaid fraud in NEMT is a documented problem. Build fraud detection into your platform:
Common fraud patterns to detect:
- Trips billed but never provided (ghost trips)
- Inflated mileage
- Services billed at higher service levels than provided
- Trips for ineligible members
- Duplicate billing
// Fraud detection rules
const fraudRules: FraudRule[] = [
{
name: 'excessive_mileage',
check: (trip) => {
const straightLine = calculateDistance(trip.pickup, trip.dropoff);
const billed = trip.billedMileage;
return billed > straightLine * 1.5; // Flag if >50% over direct route
},
severity: 'high',
},
{
name: 'missing_evv',
check: (trip) => !trip.evvRecord && trip.status === 'completed',
severity: 'critical',
},
{
name: 'impossible_timing',
check: (trip) => {
const tripDuration = trip.dropoffTime - trip.pickupTime;
const minimumTime = calculateMinimumTravelTime(trip);
return tripDuration < minimumTime * 0.8;
},
severity: 'high',
},
];
Scaling for High Volume Operations
Large NEMT operations handle thousands of trips daily. Architecture decisions made early determine whether the system scales.
Database Design
Trip data grows fast. Design for time-series queries from the start:
-- Partitioned trips table
CREATE TABLE trips (
id UUID PRIMARY KEY,
member_id UUID NOT NULL,
service_date DATE NOT NULL,
status VARCHAR(20) NOT NULL,
-- ... other columns
created_at TIMESTAMP DEFAULT NOW()
) PARTITION BY RANGE (service_date);
-- Create partitions by month
CREATE TABLE trips_2025_10 PARTITION OF trips
FOR VALUES FROM ('2025-10-01') TO ('2025-11-01');
-- Indexes for common queries
CREATE INDEX idx_trips_member_date ON trips (member_id, service_date);
CREATE INDEX idx_trips_status_date ON trips (status, service_date);
CREATE INDEX idx_trips_driver_date ON trips (assigned_driver, service_date);
Event-Driven Architecture
NEMT operations are inherently event-driven. Design your system around events:
// Event types
type NEMTEvent =
| { type: 'trip.requested'; trip: Trip }
| { type: 'trip.scheduled'; tripId: string; vehicleId: string; driverId: string }
| { type: 'trip.status_changed'; tripId: string; from: TripStatus; to: TripStatus }
| { type: 'vehicle.location_updated'; vehicleId: string; location: Location }
| { type: 'driver.status_changed'; driverId: string; status: DriverStatus };
// Event handlers are independent and can scale horizontally
const eventHandlers: Record<string, EventHandler[]> = {
'trip.status_changed': [
updateTripRecord,
notifyMember,
updateDriverApp,
checkForReoptimization,
updateDashboards,
],
'vehicle.location_updated': [
updateVehiclePosition,
checkGeofences,
updateETAs,
],
};
Real-Time Updates
Drivers, dispatchers, and members all need real-time updates. WebSockets scale better than polling:
// WebSocket room management
class TripUpdatesService {
private rooms: Map<string, Set<WebSocket>> = new Map();
subscribeToTrip(tripId: string, socket: WebSocket): void {
if (!this.rooms.has(tripId)) {
this.rooms.set(tripId, new Set());
}
this.rooms.get(tripId)!.add(socket);
}
broadcastTripUpdate(tripId: string, update: TripUpdate): void {
const subscribers = this.rooms.get(tripId);
if (!subscribers) return;
const message = JSON.stringify(update);
for (const socket of subscribers) {
if (socket.readyState === WebSocket.OPEN) {
socket.send(message);
}
}
}
}
Key Takeaways
-
NEMT is operationally complex - Multi-modal transport, time windows, and paired pickups/deliveries create challenging optimization problems
-
Compliance is non-negotiable - EVV, eligibility verification, and proper billing are required for Medicaid reimbursement
-
Route optimization drives economics - Invest in optimization algorithms; 10% efficiency gains directly impact profitability
-
Real-time is essential - Static schedules fail on contact with reality; design for continuous reoptimization
-
Fraud prevention protects everyone - Build detection into the platform from day one
Building NEMT Software That Scales
The NEMT market is growing, but many existing platforms struggle with scale, compliance, or both. The opportunity exists for well-architected solutions that solve real operational problems.
PEW Consulting has experience building healthcare transportation platforms that handle thousands of daily trips while maintaining compliance with Medicaid requirements. Our RouteOps platform demonstrates the architecture patterns described in this guide.
Schedule a consultation to discuss your NEMT software needs.
Sources
- HCI Group: Missed Appointments Cost Healthcare $150B
- Grand View Research: NEMT Market Analysis
- CMS Electronic Visit Verification
- HHS OIG: Medicaid Fraud Reports
Related reading: HIPAA Compliance Automation: A Technical Guide for Healthcare Software Teams
