Voyage π
A comprehensive Flutter plugin for the Kruzr 360 SDK, providing advanced driving analytics, trip monitoring, and user behavior analysis capabilities.
π Table of Contents
- Overview
- Features
- Installation
- Getting Started
- Configuration
- Usage
- API Reference
- Permissions
- Examples
- Support
π Overview
Voyage is a Flutter plugin that integrates with the Kruzr 360 SDK to provide comprehensive driving analytics and trip monitoring capabilities. It enables developers to build applications with advanced telematics features including real-time trip tracking, driving behavior analysis, user ranking systems, and detailed performance metrics.
β¨ Features
- π SDK Integration - Easy initialization and platform version detection
- π± Trip Monitoring - Automatic trip detection and real-time monitoring
- π€ User Management - Registration, authentication, and profile management
- π Analytics & Metrics - Comprehensive driving behavior analysis
- π Leaderboards - User rankings and performance comparison
- ποΈ Achievements - Track progress across driving behavior categories
- π Rewards System - Earn and redeem rewards based on driving performance
- π OTP Authentication -
DEPRECATEDSecure phone number verification - π Data Synchronization - Automatic and manual data sync capabilities
- π― Trip Control - Manual trip start/stop functionality
- πΊοΈ Trip Route Visualization β Retrieve full route GeoJSON for rendering
- π€ Trip Sharing - Generate shareable trip URLs
- πΆ Bluetooth Support - Device scanning and management
π¦ Installation
Add this to your pubspec.yaml file:
dependencies:
voyage: ^0.0.4-canary.14
Then run:
flutter pub get
π Getting Started
1. Initializes the Kruzr 360 SDK with the provided configuration.
This method must be called before using any other functionality of the Kruzr 360 SDK. It establishes the connection with the underlying native SDK and configures various SDK behaviors and settings.
Parameters:
kruzr360InitConfig: Configuration object containing all required initialization settings:licenseKey: Valid license key provided by Kruzr for authenticationappName: Name of your application for identificationnotificationChannelId: Id for the notification channel (Android)shouldTripAutoEnd: Whether trips should automatically end (default: true)shouldTripAutoStart: Whether trips should automatically start (default: true)allowEventSyncRealTime: Whether to sync events in real-time (default: true)
Usage:
try {
final config = Kruzr360InitConfig(
licenseKey: 'your-license-key-here',
appName: 'My Driving App',
notificationChannelId: 'co.mycompany.myapp.notification.CHANNEL_ID_TELEMATICS',
shouldTripAutoEnd: true,
shouldTripAutoStart: true,
allowEventSyncRealTime: false,
);
await Kruzr360Communicator.initializeSDK(config);
print('SDK initialized successfully');
} catch (e) {
print('Failed to initialize SDK: $e');
}
Throws:
Future.error("Unable to initialize SDK"): When the SDK fails to initialize due to platform-specific issues, invalid license key, or other initialization errors.
Platform Exceptions Handled:
PlatformException: Catches platform-specific errors from native Android/iOS codeException: Catches any other general exceptions during initialization
Important Notes:
- This is a static method and should be called before creating any instance of Kruzr360Communicator
- The method will log detailed error information in debug mode for troubleshooting purposes
- Initialization should typically be done in your main.dart file or during app startup
- Configuration settings affect SDK behavior throughout the app lifecycle
Example Implementation:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
try {
final config = Kruzr360InitConfig(
licenseKey: 'your-license-key',
appName: 'My App',
notificationChannelId: 'co.mycompany.myapp.notification.CHANNEL_ID_TELEMATICS',
);
await Kruzr360Communicator.initializeSDK(config);
runApp(MyApp());
} catch (e) {
// Handle initialization failure
print('SDK initialization failed: $e');
// Show error dialog or fallback UI
}
}
2. Create a Communicator Instance
final communicator = Kruzr360Communicator(
companyName: 'your-company-name',
accountName: 'your-account-name',
);
3. Set Up Location Services
communicator.setupLocationStuffs();
βοΈ Configuration
Android Setup
Add the following permissions to your android/app/src/main/AndroidManifest.xml:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
π± Usage
User Registration and Authentication
// Register User
final userId = await communicator.registerUserToKruzr(
driverId: 'unique_driver_123',
name: 'John Doe',
countryCode: '+1',
phoneNumber: '1234567890',
email: 'john.doe@example.com',
);
Trip Monitoring
// Initiate trip monitoring
await communicator.initiateTripMonitoring();
// Start a trip manually
final started = await communicator.startTrip();
// Get current trip data
final currentTrip = await communicator.getCurrentTripData();
// Stop a trip
final stopped = await communicator.stopTrip();
Enables or disables automatic trip start detection
Controls whether the SDK should automatically detect and start recording trips based on vehicle movement patterns. When enabled, the SDK will automatically begin trip recording when driving behavior is detected.
Parameters:
shouldAutoStart:trueto enable automatic trip start,falseto disable
Usage:
try {
// Enable automatic trip start
await communicator.setTripAutoStartEnabled(true);
print('Automatic trip start enabled');
// Disable automatic trip start (manual start only)
await communicator.setTripAutoStartEnabled(false);
print('Automatic trip start disabled - manual start required');
} catch (e) {
print('Failed to change auto start setting: $e');
}
Throws:
Future.error("Unable to change trip auto start pref"): When setting update fails
Important Notes:
- Setting persists across app sessions
- When disabled, trips must be started manually using startTrip
- Overrides the initial configuration from Kruzr360InitConfig
- Changes take effect immediately for new trip detection
Use Cases:
- User preference settings
- Fleet management policies
- Testing and debugging scenarios
- Power management optimization
Retrieves the current automatic trip start setting.
Returns whether the SDK is currently configured to automatically detect and start recording trips based on vehicle movement patterns. Returns:
bool:trueif automatic trip start is enabled,falseotherwise Usage:
try {
final autoStartEnabled = await communicator.getTripAutoStartEnabled();
if (autoStartEnabled) {
print('Automatic trip start is enabled');
// Show UI indicating automatic mode
} else {
print('Manual trip start required');
// Show manual start button
}
} catch (e) {
print('Failed to get auto start setting: $e');
}
Throws:
Future.error("Unable to get trip auto start pref"): When retrieval fails Use Cases:- Updating UI based on current settings
- Validating configuration state
- Settings screen initialization
- Debugging trip detection issues
Retrieves the current automatic trip end setting.
Returns whether the SDK is currently configured to automatically detect and stop recording trips when the vehicle stops moving. Returns:
bool:trueif automatic trip end is enabled,falseotherwise Usage:
try {
final autoEndEnabled = await communicator.getTripAutoEndEnabled();
if (autoEndEnabled) {
print('Automatic trip end is enabled');
// Trips will end automatically when stopped
} else {
print('Manual trip end required');
// Show manual stop button
}
} catch (e) {
print('Failed to get auto end setting: $e');
}
Throws:
Future.error("Unable to get trip auto end pref"): When retrieval fails Use Cases:- Updating UI based on current settings
- Validating configuration state
- Settings screen initialization
- Debugging trip termination issues
Enables or disables automatic trip end detection.
Controls whether the SDK should automatically detect and stop recording trips when the vehicle stops moving for a sufficient duration. When enabled, the SDK will automatically end trip recording based on movement patterns. Parameters:
shouldAutoEnd:trueto enable automatic trip end,falseto disable Usage:
try {
// Enable automatic trip end
await communicator.setTripAutoEndEnabled(true);
print('Automatic trip end enabled');
// Disable automatic trip end (manual end only)
await communicator.setTripAutoEndEnabled(false);
print('Automatic trip end disabled - manual end required');
} catch (e) {
print('Failed to change auto end setting: $e');
}
Throws:
Future.error("Unable to change trip auto end pref"): When setting update fails Important Notes:- Setting persists across app sessions
- When disabled, trips must be stopped manually using stopTrip
- Overrides the initial configuration from Kruzr360InitConfig
- Changes take effect immediately for active trips Use Cases:
- User preference settings
- Fleet management policies
- Long stop scenarios (gas stations, shopping)
- Testing and debugging scenarios
Enables or disables real-time event synchronization.
Controls whether driving events and trip data should be synchronized with Kruzr servers immediately as they occur, or batched for later synchronization. Real-time sync provides immediate data availability but may impact battery life and data usage.
Parameters:
allowEventSyncRealTime:trueto enable real-time sync,falsefor batch sync
Usage:
try {
// Enable real-time synchronization
await communicator.shouldEventSyncRealTime(true);
print('Real-time sync enabled - events sync immediately');
// Disable real-time sync (batch mode)
await communicator.shouldEventSyncRealTime(false);
print('Batch sync enabled - events sync periodically');
} catch (e) {
print('Failed to change sync setting: $e');
}
Throws:
Future.error("Unable to change event sync real time pref"): When setting update fails Important Notes:- Setting persists across app sessions
- Real-time sync requires active internet connection
- Overrides the initial configuration from Kruzr360InitConfig
- Affects battery life and data usage patterns
- May be subject to network conditions and server availability
Performance Considerations:
- Real-time enabled: Immediate data availability, higher battery/data usage
- Real-time disabled: Better battery life, periodic data updates
Use Cases:
- Fleet monitoring requiring immediate updates
- Battery optimization for extended trips
- Network-constrained environments
- User preference settings
Retrieves the current real-time event synchronization setting.
Returns whether the SDK is currently configured to synchronize driving events and trip data immediately as they occur, or to batch them for later synchronization.
Returns:
bool:trueif real-time sync is enabled,falsefor batch sync
Usage:
try {
final realTimeSyncEnabled = await communicator.isEventSyncRealTimeEnabled();
if (realTimeSyncEnabled) {
print('Real-time sync active - immediate data updates');
// Show real-time indicator in UI
} else {
print('Batch sync active - periodic data updates');
// Show batch sync indicator
}
} catch (e) {
print('Failed to get sync setting: $e');
}
Throws:
Future.error("Unable to get event sync real time pref"): When retrieval fails
Use Cases:
- Updating UI sync status indicators
- Validating configuration state
- Settings screen initialization
- Performance monitoring and optimization
- Debugging data synchronization issues
Retrieve User Driving Streak
Returns information about the userβs current and longest continuous safe-driving streak.
A streak represents consecutive days with at least one valid trip that meets safety criteria defined by the Kruzr scoring engine.
Return
UserStreakcurrent: Current ongoing streak (days)longest: Longest streak achieved (days) Usage:
try {
final streak = await communicator.getUserStreak();
print('Current streak: ${streak.current}');
print('Longest streak: ${streak.longest}');
} catch (e) {
print('Failed to get user streak: $e');
}
Throws:
- Future.error("Unable to get user streak")
Important Notes:
- Streaks are calculated server-side
- A day counts only if at least one valid trip is completed
- Data may be delayed if trips are not synced
Prerequisites:
- User must be logged in
- At least one completed trip synced to server
Use Cases:
- Gamification and streak UI
- Daily engagement nudges
- Safe-driving habit tracking
π Retrieve Aggregated Trips Count
Returns the cumulative driving duration across all recorded trips. Returns:
int:Total driving duration in seconds
Usage:
try {
final duration = await communicator.getAggregatedDrivingDuration();
print('Total driving duration (seconds): $duration');
} catch (e) {
print('Failed to get driving duration: $e');
}
Throws:
Future.error("Unable to get aggregated driving duration")
Important Notes:
- Duration is calculated using trip start/end timestamps
- Idle time outside active trips is excluded Use Cases:
- Usage analytics
- Insurance risk analysis
- Fleet utilization insights
π Data Synchronization
Manually Sync Trip Data
Forces synchronization of locally stored trip data with Kruzr servers. Useful when trips were recorded offline or during poor network conditions.
Returns:
Future<void>
Usage:
try {
await communicator.syncTripData();
print('Trip data sync triggered');
} catch (e) {
print('Failed to sync trip data: $e');
}
Throws:
Future.error("Unable to sync trip data")
Important Notes:
- Sync runs in background
- Safe to call multiple times
- Does not block UI thread
Use Cases:
- Manual βSync Nowβ button
- Offline-first applications
- Debugging upload issues
Retrieve Pending Upload Count
Returns the number of trip files currently waiting to be uploaded to the server.
Returns:
int:Number of pending uploads
Usage:
try {
final pendingCount = await communicator.getPendingFilesCount();
print('Pending uploads: $pendingCount');
} catch (e) {
print('Failed to get pending files count: $e');
}
print('Failed to sync trip data: $e');
}
Throws:
- Future.error("Unable to get pending files count")
Important Notes:
- Count decreases automatically as sync completes
- High count may indicate connectivity issues Use Cases:
- Upload progress indicators
- Network health diagnostics
- Warning users before logout
Analytics and Metrics
// Get driving summary
final summary = await communicator.getDrivingSummary(
DateTime.now().subtract(Duration(days: 30)),
DateTime.now(),
KruzrPerioicType.weekly,
);
// Get user ranking
final userRank = await communicator.getCurrentUserRank();
// Get leaderboard
final topDrivers = await communicator.getLeaderboardTop10();
// Get user achievements
final achievements = await communicator.fetchMyAchievements();
if (achievements != null) {
// Check overall achievement progress
print('Current tier: ${achievements.overall?.currentTier?.value}');
print('Progress: ${achievements.overall?.count}');
// Check specific category achievements
if (achievements.speeding != null) {
print('Speeding tier: ${achievements.speeding!.currentTier?.value}');
print('Trips to next tier: ${achievements.speeding!.remainingTrips}');
}
}
// Get available rewards
final availableRewards = await fetchAvailableRewards();
for (final reward in availableRewards) {
print('Reward: ${reward.title}');
print('Cost: ${reward.points} points');
print('Description: ${reward.description}');
}
// Get earned rewards history
final earnedRewards = await fetchEarnedRewards();
if (earnedRewards.isNotEmpty) {
print('You have earned ${earnedRewards.length} rewards!');
for (final reward in earnedRewards) {
print('Earned: ${reward.title} on ${reward.earnedOn}');
}
}
// Get user profile tier requirements
final tierRequirements = await communicator.fetchUserProfileTierRequirements();
for (final tier in tierRequirements) {
print('Tier: ${tier.name}');
print('Required trips: ${tier.requiredTrips}');
print('Required score: ${tier.requiredScore}');
print('Description: ${tier.description}');
}
// Get user details including profile tier
final userDetails = await communicator.userDetails();
if (userDetails != null) {
print('User name: ${userDetails.name}');
print('Current profile tier: ${userDetails.profileTier.value}');
print('Average trip score: ${userDetails.averageTripScore}');
print('Total trips: ${userDetails.totalTripCount}');
print('Total distance: ${userDetails.totalDistanceTravelled} km');
}
Retrieves possible intervention events that occurred during a specific trip.
Analyzes trip data to identify segments where driving interventions could have been beneficial, such as areas with poor driving behavior, potential safety issues, or coaching opportunities. Each intervention includes location data, timing, and associated scoring statistics.
Parameters:
appTripId: Unique identifier for the trip to analyze
Returns:
List<PossibleIntervention>: List of intervention events with detailed information
Usage:
try {
final interventions = await communicator.getPossibleInterventionsForAppTripId('trip-123');
for (final intervention in interventions) {
print('Intervention type: ${intervention.scoringType}');
print('Start time: ${intervention.startTime}');
print('Location: ${intervention.startAddress}');
// Check scoring statistics
if (intervention.scoreStats != null) {
intervention.scoreStats!.forEach((scoreType, stats) {
if (stats != null) {
print('$scoreType - Average: ${stats.average}, Max: ${stats.max}');
}
});
}
}
} catch (e) {
print('Failed to get interventions: $e');
}
Throws:
Future.error("Unable to get possible interventions"): When retrieval fails
Intervention Data Includes:
- Timing: Start and end timestamps in ISO 8601 format
- Location: GPS coordinates and resolved addresses for start/end points
- Scoring: Statistical data for various driving behavior categories
- Type: Classification of the intervention (e.g., "SPEEDING", "HARD_BRAKING", etc.)
Common Intervention Triggers:
- Hard braking events
- Rapid acceleration
- Sharp cornering
- Speeding incidents
- Distracted driving patterns
- Drowsy driving detection
Use Cases:
- Driver coaching and feedback
- Safety analysis and reporting
- Insurance risk assessment
- Fleet management insights
- Training program development
Prerequisites:
- Trip must be completed and processed
- Trip data must be synchronized with servers
- Valid app trip ID is required
πΊοΈ Fetching Trip Route (GeoJSON)
Fetch the complete trip route including start/end points, path, and event highlights.
try {
final route = await communicator.fetchRoute("2985_1764285633333");
if (route != null) {
for (final feature in route.collection!) {
print("Type: ${feature.type}");
print("Properties: ${feature.properties}");
print("Geometry: ${feature.geometry?.type}");
}
}
} catch (e) {
print("Failed to fetch route: $e");
}
Route Contains:
-
Route LineString (full trip path)
-
Start and end points
-
Speeding / braking / acceleration event markers
-
Highlight segments with styling metadata
-
Event metadata (startTime, scoreStats, etc.)
Works with any map render engine: MapLibre, Mapbox, flutter_map, Leaflet, or custom painter.
π Vehicle Management
Manage Bluetooth vehicle associations for automatic trip detection.
// Scan for nearby Bluetooth devices
final nearbyDevices = await communicator.scanForNearbyDevices();
for (final device in nearbyDevices) {
print('Device: ${device.name} (${device.id})');
}
// Save a vehicle for automatic trip detection
final selectedVehicle = nearbyDevices.first; // User selects their car
communicator.saveVehicle(selectedVehicle);
print('Vehicle saved: ${selectedVehicle.name}');
// Get all saved vehicles
final savedVehicles = await communicator.getAllSavedVehicles();
for (final vehicle in savedVehicles) {
print('Saved: ${vehicle.name} (${vehicle.id})');
}
// Remove a specific vehicle
final vehicleToRemove = savedVehicles.first;
communicator.removeSavedVehicle(vehicleToRemove);
print('Removed: ${vehicleToRemove.name}');
// Delete all saved vehicles (factory reset)
communicator.removeAllSavedVehicles();
print('All vehicles deleted');
Vehicle Management Features:
- Automatic Detection: Saved vehicles enable automatic trip start/end
- Multi-Vehicle Support: Save multiple vehicles for households/fleets
- Granular Control: Remove specific vehicles or reset all
- Persistent Storage: Vehicle associations survive app restarts
π Trip Sharing
Generate Shareable Trip URL
Generates a publicly accessible URL for a completed trip. The URL can be viewed without authentication.
Parameters:
appTripId: Unique identifier of the completed trip
Returns:
String: Shareable trip URL
Usage:
try {
final shareUrl =
await communicator.generateShareableURL('trip_2985_1764285633333');
print('Shareable URL: $shareUrl');
} catch (e) {
print('Failed to generate shareable URL: $e');
}
Throws:
Future.error("Unable to generate shareable URL")
Important Notes:
- Trip must be completed and synced
- URL is publicly accessible
Prerequisites:
- Valid appTripId
- Trip data available on server
Use Cases:
- Social sharing
- Driver coaching reviews
- Customer support investigations
π€ User Account Management
Delete User Account
Deletes the currently logged-in user from Kruzr systems and clears all local SDK state.
Returns:
Future<void>
Usage:
try {
await communicator.deleteUser();
print('User account deleted successfully');
} catch (e) {
print('Failed to delete user: $e');
}
Throws:
Future.error("Unable to delete user")
Important Notes:
- Action is irreversible
- All trips and analytics become inaccessible
- SDK must be re-initialized for new user
Prerequisites:
- User must be logged in
Use Cases:
- Account deletion flows
- Data privacy compliance (GDPR)
- User off-boarding
Update User Profile
Updates editable user profile information on the server.
Parameters:
name(optional): Updated display nameemail(optional): Updated email address
Returns:
Future<void>
Usage:
try {
await communicator.updateUserProfile(
name: 'John Doe',
email: 'john.doe@example.com',
);
print('User profile updated');
} catch (e) {
print('Failed to update user profile: $e');
}
Throws:
Future.error("Unable to update user profile")
Use Cases:
- Profile edit screen
- Account settings
- Data correction
π API Reference
Core SDK Methods
| Method | Description |
|---|---|
initializeSDK(String licenseKey) |
Initialize the SDK with license key |
getPlatformVersion() |
Get platform version information |
setupLocationStuffs() |
Set up location services |
Trip Monitoring
| Method | Description |
|---|---|
initiateTripMonitoring() |
Enable automatic trip detection |
startTrip() |
Manually start a trip |
stopTrip() |
Manually stop a trip |
getCurrentTripData() |
Get real-time trip information |
User Management
| Method | Description |
|---|---|
registerUserToKruzr() |
Register a new user |
logout() |
Log out current user |
isLoggedIn() |
Check login status |
userDetails() |
Get user information |
Analytics & Metrics
| Method | Description |
|---|---|
getUserStreak() |
Get driving streak information |
getCurrentUserRank() |
Get user ranking |
leaderboard() |
Get paginated leaderboard |
getAggregatedDistanceTravelled() |
Get distance metrics |
getDrivingSummary() |
Get comprehensive driving overview |
getAggregatedDrivingScore() |
Get driving performance scores |
fetchMyAchievements() |
Get user achievements across all categories |
fetchTripDetailsByAppTripId() |
Get historical trip details |
fetchTripStatsByAppTripId() |
Get trip statistical analysis |
getPossibleInterventionsForAppTripId() |
Get possible intervention events for a trip |
fetchRoute() |
Fetch Trip Route (GeoJSON) |
fetchAvailableRewards() |
Get all available rewards in the catalog |
fetchEarnedRewards() |
Get user's earned rewards history |
fetchUserProfileTierRequirements() |
Get user profile tier requirements and progression metadata |
Authentication (OTP) DEPRECATED
| Method | Description |
|---|---|
generateOtp() |
Generate OTP for phone verification |
generateOtpAlternate() |
Alternative OTP generation method |
verifyOtp() |
Verify OTP code |
Data Management
| Method | Description |
|---|---|
syncTripData() |
Manually sync trip data |
getPendingFilesCount() |
Get count of pending uploads |
Sharing & Bluetooth
| Method | Description |
|---|---|
generateShareableURL() |
Create shareable trip URLs |
bluetoothScanEventStream() |
Get Bluetooth event stream |
scanForNearbyDevices() |
Scan for nearby Bluetooth devices |
saveVehicle() |
Save a Bluetooth device as known vehicle |
getAllSavedVehicles() |
Get all saved vehicle devices |
editSavedVehicle() |
Edit specific saved vehicle |
removeSavedVehicle() |
Remove specific saved vehicle |
removeAllSavedVehicles() |
Remove all saved vehicles |
π Permissions
This plugin requires the following permissions:
Android
ACCESS_FINE_LOCATION- For accurate location trackingACCESS_COARSE_LOCATION- For general location servicesACCESS_BACKGROUND_LOCATION- For background trip monitoringBLUETOOTH&BLUETOOTH_ADMIN- For Bluetooth device management
iOS
- Location permissions will be requested automatically
- Bluetooth permissions for device scanning
π‘ Examples
See the example/ directory for a complete Flutter application demonstrating the usage of the Voyage plugin.
π Issues and Support
If you encounter any issues or have questions:
- Check the documentation
- Search existing issues
- Create a new issue with detailed information
π License
This project is licensed under the terms specified in the LICENSE file.
π€ Contributing
Contributions are welcome! Please read the contributing guidelines before submitting pull requests.
Made with β€οΈ by the Kruzr Team
Libraries
- kruzr_360_communicator
- kruzr_comunication
- model/achievements/achievement_progress
- model/achievements/achievement_tier
- model/achievements/my_achievments
- model/achievements/tier_anchor
- model/create_user_request
- model/create_user_response
- model/current_trip_data_response
- model/custom_bluetooth_device
- model/custom_file_sync_event
- model/driving_behaviour_score
- model/driving_behaviour_score_change
- model/driving_summary
- model/generate_otp_request
- model/generate_otp_response
- model/get_user_detail_response
- model/get_user_detail_using_jwt_request
- model/get_user_detail_using_number_request
- model/kruzr_360_init_config
- model/kruzr_historic_data
- model/kruzr_historic_double_data_response
- model/kruzr_perioic_type
- model/leaderboard_driver
- model/possible_interventions/possible_intervention_address
- model/possible_interventions/possible_intervention_score_stats
- model/possible_interventions/possible_interventions
- model/register_user_request
- model/registered_driver
- model/rewards/available_reward
- model/rewards/earned_reward
- model/single_trip_response
- model/trip_stats_response
- model/user_rank
- model/user_streak
- model/userProfileTier/user_profile_tier
- model/userProfileTier/user_profile_tier_requirement
- model/vehicle/nearby_device
- model/verify_otp_request
- model/verify_otp_response