Skip to main content
POST
/
v1
/
locations
/
single
Upload Single Location
curl --request POST \
  --url https://api.bookovia.com/v1/locations/single \
  --header 'Content-Type: application/json' \
  --header 'X-API-Key: <api-key>' \
  --data '
{
  "trip_id": "<string>",
  "latitude": 123,
  "longitude": 123,
  "timestamp": "<string>",
  "speed_kmh": 123,
  "heading": 123,
  "accuracy_meters": 123,
  "altitude_meters": 123,
  "battery_level": 123,
  "network_type": "<string>",
  "device_info": {
    "device_id": "<string>",
    "signal_strength": 123,
    "satellites_count": 123
  }
}
'
{
  "error": {
    "code": "invalid_location",
    "message": "Location validation failed",
    "details": {
      "latitude": "Must be between -90 and 90",
      "accuracy_meters": "Must be a positive number"
    }
  }
}

Documentation Index

Fetch the complete documentation index at: https://docs.bookovia.com/llms.txt

Use this file to discover all available pages before exploring further.

Overview

The Upload Single Location endpoint allows you to submit individual GPS location points for active trips with immediate processing. This endpoint is ideal for real-time tracking applications where locations need to be processed as they are received.
Single uploads provide immediate feedback including safety event detection and trip analytics updates.

Authentication

This endpoint requires authentication via API key in the X-API-Key header. Required permissions: locations:write

Request

trip_id
string
required
Unique identifier for the active trip to upload the location to
latitude
number
required
Latitude coordinate in decimal degrees (-90 to 90)
longitude
number
required
Longitude coordinate in decimal degrees (-180 to 180)
timestamp
string
required
ISO 8601 timestamp when the location was recorded
speed_kmh
number
Vehicle speed in kilometers per hour at this location
heading
number
Direction of travel in degrees (0-360, where 0 is North)
accuracy_meters
number
GPS accuracy in meters (lower values indicate better accuracy)
altitude_meters
number
Altitude above sea level in meters
battery_level
number
Device battery level percentage (0-100)
network_type
string
Network connection type: “wifi”, “cellular”, “offline”
device_info
object
Device information for this location point

Request Example

curl -X POST https://api.bookovia.com/v1/locations/single \
  -H "X-API-Key: bkv_test_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "trip_id": "trip_1234567890abcdef",
    "latitude": 40.7128,
    "longitude": -74.0060,
    "timestamp": "2024-04-13T10:30:00Z",
    "speed_kmh": 45.2,
    "heading": 87,
    "accuracy_meters": 5.2,
    "altitude_meters": 15.8,
    "battery_level": 85,
    "network_type": "cellular",
    "device_info": {
      "device_id": "device_abc123",
      "signal_strength": -72,
      "satellites_count": 12
    }
  }'

Response

trip_id
string
Unique identifier for the trip that received the location update
location_id
string
Unique identifier for the uploaded location point
processed_at
string
ISO 8601 timestamp when the location was processed
events_detected
array
Array of safety events detected from this location update
trip_analytics
object
Updated real-time trip analytics
quality_assessment
object
Assessment of the location data quality

Success Response

{
  "trip_id": "trip_1234567890abcdef",
  "location_id": "loc_9876543210fedcba",
  "processed_at": "2024-04-13T10:30:02Z",
  "events_detected": [
    {
      "event_id": "evt_speed_001",
      "event_type": "speeding",
      "severity": "moderate",
      "confidence": 0.92,
      "metrics": {
        "speed_limit_kmh": 40,
        "actual_speed_kmh": 52,
        "excess_speed_kmh": 12,
        "duration_seconds": 15
      }
    }
  ],
  "trip_analytics": {
    "distance_km": 15.8,
    "duration_minutes": 76,
    "avg_speed_kmh": 32.3,
    "current_safety_score": 87,
    "locations_count": 190,
    "max_speed_kmh": 52,
    "idle_time_minutes": 3.2
  },
  "quality_assessment": {
    "accuracy_rating": "excellent",
    "signal_quality": "strong",
    "deviation_from_route": 2.1,
    "gps_fix_type": "3D",
    "hdop": 1.2
  },
  "processing_time_ms": 23
}

Error Responses

{
  "error": {
    "code": "invalid_location",
    "message": "Location validation failed",
    "details": {
      "latitude": "Must be between -90 and 90",
      "accuracy_meters": "Must be a positive number"
    }
  }
}

SDK Examples

import Bookovia from '@bookovia/javascript-sdk';

const client = new Bookovia('bkv_test_your_api_key');

// Simple location upload
const result = await client.locations.uploadSingle('trip_1234567890abcdef', {
  latitude: 40.7128,
  longitude: -74.0060,
  timestamp: new Date().toISOString(),
  speed_kmh: 45.2,
  heading: 87
});

console.log(`Location processed: ${result.location_id}`);

// Advanced upload with geolocation API
if (navigator.geolocation) {
  navigator.geolocation.getCurrentPosition(async (position) => {
    const result = await client.locations.uploadSingle('trip_123', {
      latitude: position.coords.latitude,
      longitude: position.coords.longitude,
      timestamp: new Date().toISOString(),
      speed_kmh: position.coords.speed * 3.6, // Convert m/s to km/h
      heading: position.coords.heading,
      accuracy_meters: position.coords.accuracy,
      altitude_meters: position.coords.altitude
    });
    
    // Handle detected events
    result.events_detected.forEach(event => {
      if (event.severity === 'high' || event.severity === 'critical') {
        showSafetyAlert(event);
      }
    });
  });
}

// Continuous tracking with error handling
const startLocationTracking = (tripId) => {
  const watchId = navigator.geolocation.watchPosition(
    async (position) => {
      try {
        await client.locations.uploadSingle(tripId, {
          latitude: position.coords.latitude,
          longitude: position.coords.longitude,
          timestamp: new Date().toISOString(),
          speed_kmh: (position.coords.speed || 0) * 3.6,
          accuracy_meters: position.coords.accuracy
        });
      } catch (error) {
        console.error('Failed to upload location:', error);
      }
    },
    (error) => console.error('GPS error:', error),
    { enableHighAccuracy: true, timeout: 10000, maximumAge: 0 }
  );
  
  return watchId;
};

Use Cases

Mobile Application Tracking

// Continuous GPS tracking for mobile apps
class MobileGPSTracker {
  constructor(client, tripId) {
    this.client = client;
    this.tripId = tripId;
    this.watchId = null;
    this.lastUpload = 0;
    this.uploadInterval = 5000; // 5 seconds
  }
  
  startTracking() {
    if (!navigator.geolocation) {
      throw new Error('Geolocation not supported');
    }
    
    this.watchId = navigator.geolocation.watchPosition(
      (position) => this.handlePosition(position),
      (error) => this.handleError(error),
      {
        enableHighAccuracy: true,
        timeout: 10000,
        maximumAge: 0
      }
    );
  }
  
  async handlePosition(position) {
    const now = Date.now();
    
    // Rate limiting
    if (now - this.lastUpload < this.uploadInterval) {
      return;
    }
    
    try {
      const result = await this.client.locations.uploadSingle(this.tripId, {
        latitude: position.coords.latitude,
        longitude: position.coords.longitude,
        timestamp: new Date().toISOString(),
        speed_kmh: (position.coords.speed || 0) * 3.6,
        heading: position.coords.heading,
        accuracy_meters: position.coords.accuracy,
        altitude_meters: position.coords.altitude,
        device_info: {
          device_id: this.getDeviceId(),
          signal_strength: this.getNetworkStrength()
        }
      });
      
      this.lastUpload = now;
      
      // Update UI with real-time analytics
      this.updateDashboard(result.trip_analytics);
      
      // Handle safety alerts
      result.events_detected.forEach(event => {
        this.showSafetyAlert(event);
      });
      
    } catch (error) {
      console.error('Location upload failed:', error);
    }
  }
  
  stopTracking() {
    if (this.watchId) {
      navigator.geolocation.clearWatch(this.watchId);
      this.watchId = null;
    }
  }
}

IoT Fleet Monitoring

# Real-time fleet monitoring system
import asyncio
import json
from datetime import datetime, timedelta

class FleetMonitor:
    def __init__(self, client):
        self.client = client
        self.active_vehicles = {}
        self.alert_thresholds = {
            'speed_limit': 80,  # km/h
            'harsh_event_count': 3,  # per hour
            'low_battery': 20  # percentage
        }
    
    async def process_vehicle_location(self, vehicle_id, trip_id, gps_data):
        """Process incoming GPS data from vehicle"""
        try:
            result = await self.client.locations.upload_single(
                trip_id=trip_id,
                latitude=gps_data['lat'],
                longitude=gps_data['lng'],
                timestamp=datetime.utcnow().isoformat() + 'Z',
                speed_kmh=gps_data['speed'],
                heading=gps_data['heading'],
                battery_level=gps_data.get('battery_level', 100),
                device_info={
                    'device_id': vehicle_id,
                    'signal_strength': gps_data.get('signal_strength', -70)
                }
            )
            
            # Update vehicle status
            self.active_vehicles[vehicle_id] = {
                'last_update': datetime.utcnow(),
                'location': (gps_data['lat'], gps_data['lng']),
                'speed': gps_data['speed'],
                'safety_score': result.trip_analytics.current_safety_score,
                'battery_level': gps_data.get('battery_level', 100)
            }
            
            # Check for alerts
            await self.check_vehicle_alerts(vehicle_id, result)
            
            return result
            
        except Exception as e:
            print(f"Error processing location for vehicle {vehicle_id}: {e}")
    
    async def check_vehicle_alerts(self, vehicle_id, location_result):
        """Check for various alert conditions"""
        vehicle_status = self.active_vehicles[vehicle_id]
        
        # Speed alerts
        if vehicle_status['speed'] > self.alert_thresholds['speed_limit']:
            await self.send_alert('speeding', vehicle_id, {
                'speed': vehicle_status['speed'],
                'limit': self.alert_thresholds['speed_limit']
            })
        
        # Safety event alerts
        for event in location_result.events_detected:
            if event.severity in ['high', 'critical']:
                await self.send_alert('safety_event', vehicle_id, {
                    'event_type': event.event_type,
                    'severity': event.severity,
                    'location': vehicle_status['location']
                })
        
        # Battery alerts
        if vehicle_status['battery_level'] < self.alert_thresholds['low_battery']:
            await self.send_alert('low_battery', vehicle_id, {
                'battery_level': vehicle_status['battery_level']
            })
    
    async def send_alert(self, alert_type, vehicle_id, data):
        """Send alert to fleet manager"""
        alert = {
            'type': alert_type,
            'vehicle_id': vehicle_id,
            'timestamp': datetime.utcnow().isoformat(),
            'data': data
        }
        
        print(f"🚨 FLEET ALERT: {json.dumps(alert, indent=2)}")
        # Integration with notification systems would go here

# Usage
monitor = FleetMonitor(client)

# Simulate incoming vehicle data
vehicle_data = {
    'lat': 40.7128,
    'lng': -74.0060,
    'speed': 85.0,  # Over speed limit
    'heading': 90,
    'battery_level': 15  # Low battery
}

await monitor.process_vehicle_location('vehicle_123', 'trip_456', vehicle_data)

Emergency Response Integration

// Emergency response system with location tracking
package main

import (
    "context"
    "fmt"
    "log"
    "time"
    "github.com/bookovia/go-sdk"
)

type EmergencyResponse struct {
    client          *bookovia.Client
    emergencyTrips  map[string]*EmergencyTrip
    alertChannel    chan SafetyAlert
}

type EmergencyTrip struct {
    TripID       string
    VehicleID    string
    StartTime    time.Time
    LastLocation *bookovia.Location
    EventCount   int
}

type SafetyAlert struct {
    TripID    string
    VehicleID string
    EventType string
    Severity  string
    Location  *bookovia.Location
    Timestamp time.Time
}

func NewEmergencyResponse(client *bookovia.Client) *EmergencyResponse {
    return &EmergencyResponse{
        client:         client,
        emergencyTrips: make(map[string]*EmergencyTrip),
        alertChannel:   make(chan SafetyAlert, 100),
    }
}

func (e *EmergencyResponse) TrackEmergencyVehicle(ctx context.Context, tripID, vehicleID string, locations <-chan *bookovia.Location) {
    emergencyTrip := &EmergencyTrip{
        TripID:    tripID,
        VehicleID: vehicleID,
        StartTime: time.Now(),
    }
    
    e.emergencyTrips[tripID] = emergencyTrip
    
    for location := range locations {
        result, err := e.client.Locations.UploadSingle(ctx, &bookovia.UploadSingleRequest{
            TripID:         tripID,
            Latitude:       location.Latitude,
            Longitude:      location.Longitude,
            Timestamp:      location.Timestamp,
            SpeedKmh:       location.SpeedKmh,
            AccuracyMeters: location.AccuracyMeters,
            DeviceInfo: &bookovia.DeviceInfo{
                DeviceID: vehicleID,
            },
        })
        
        if err != nil {
            log.Printf("Failed to upload emergency vehicle location: %v", err)
            continue
        }
        
        emergencyTrip.LastLocation = location
        
        // Monitor for critical safety events
        for _, event := range result.EventsDetected {
            if event.Severity == "critical" {
                alert := SafetyAlert{
                    TripID:    tripID,
                    VehicleID: vehicleID,
                    EventType: event.EventType,
                    Severity:  event.Severity,
                    Location:  location,
                    Timestamp: time.Now(),
                }
                
                select {
                case e.alertChannel <- alert:
                case <-ctx.Done():
                    return
                }
            }
        }
        
        emergencyTrip.EventCount += len(result.EventsDetected)
    }
}

func (e *EmergencyResponse) ProcessAlerts(ctx context.Context) {
    for {
        select {
        case alert := <-e.alertChannel:
            e.handleEmergencyAlert(alert)
        case <-ctx.Done():
            return
        }
    }
}

func (e *EmergencyResponse) handleEmergencyAlert(alert SafetyAlert) {
    log.Printf("🚨 EMERGENCY VEHICLE ALERT: %s - %s at (%.6f, %.6f)",
        alert.VehicleID, alert.EventType, 
        alert.Location.Latitude, alert.Location.Longitude)
    
    // Notify dispatch center
    // Send alerts to supervisors
    // Update incident management system
}

func main() {
    client := bookovia.NewClient("bkv_test_your_api_key")
    emergency := NewEmergencyResponse(client)
    
    ctx := context.Background()
    
    // Start alert processing
    go emergency.ProcessAlerts(ctx)
    
    // Simulate emergency vehicle tracking
    locationChannel := make(chan *bookovia.Location)
    go emergency.TrackEmergencyVehicle(ctx, "emergency_trip_001", "ambulance_01", locationChannel)
    
    // Send test locations
    testLocations := []*bookovia.Location{
        {Latitude: 40.7128, Longitude: -74.0060, SpeedKmh: 80, Timestamp: time.Now()},
        {Latitude: 40.7135, Longitude: -74.0055, SpeedKmh: 90, Timestamp: time.Now().Add(time.Second * 5)},
    }
    
    for _, loc := range testLocations {
        locationChannel <- loc
        time.Sleep(time.Second * 5)
    }
    
    close(locationChannel)
}

Best Practices

Rate Limiting

  • Maximum 10 uploads per second per trip
  • Use batch uploads for high-frequency data (>1 Hz)
  • Implement exponential backoff on rate limit errors
const uploadWithRetry = async (tripId, location, maxRetries = 3) => {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await client.locations.uploadSingle(tripId, location);
    } catch (error) {
      if (error.code === 'rate_limit_exceeded') {
        await sleep(error.retry_after * 1000);
        continue;
      }
      throw error;
    }
  }
};

Data Quality

  • Reject locations with accuracy > 50 meters
  • Validate speed and heading values are reasonable
  • Check for temporal consistency
  • Queue locations during network outages
  • Use compression for payload optimization
  • Implement offline storage with sync capabilities

Next Steps

Batch Upload

Upload multiple locations efficiently in batches

Get Route Data

Retrieve and analyze route information

Safety Analytics

Deep dive into safety event analysis

Real-time Streaming

Set up WebSocket connections for live tracking