Skip to main content
With Trips, you can track trips and calculate live ETAs from an origin to a destination. Use Trips for use cases like pickup or delivery tracking. Trips illustration Trips provides the following user context:
{
  "trip": {
    "_id": "5f3e50491c2b7d005c81f5d9",
    "live": true,
    "status": "started",
    "externalId": "299",
    "metadata": {
      "Customer Name": "Jacob Pena",
      "Car Model": "Green Honda Civic"
    },
    "mode": "car",
    "destinationGeofenceTag": "store",
    "destinationGeofenceExternalId": "123",
    "destinationLocation": {
      "coordinates": [
        -105.06119826017914,
        39.7793665
      ],
      "type": "Point"
    },
    "eta": {
      "duration": 5.5, // minutes
      "distance": 1331 // meters
    },
    "createdAt": "2020-08-20T10:27:55.830Z",
    "updatedAt": "2020-08-20T10:30:55.837Z",
    "endedAt": "2020-08-20T10:30:55.837Z"
  }
}
For multi-leg trips, the trip object includes currentLegId and legs fields. The top-level destinationGeofenceTag, destinationGeofenceExternalId, and destinationLocation fields are not present, as destinations are defined on each leg instead:
{
  "trip": {
    "_id": "5f3e50491c2b7d005c81f5d9",
    "live": true,
    "status": "started",
    "externalId": "multi-stop-route-123",
    "metadata": {
      "Customer Name": "Jacob Pena",
      "Car Model": "Green Honda Civic"
    },
    "mode": "car",
    "currentLegId": "6f4e60591d3c8e006d9206ea",
    "legs": [
      {
        "_id": "6f4e60591d3c8e006d9206ea",
        "status": "started",
        "destinationGeofenceTag": "stop",
        "destinationGeofenceExternalId": "stop-1",
        "stopDuration": 5,
        "metadata": {
          "stopName": "Customer A",
          "orderId": "order-101"
        },
        "eta": {
          "duration": 8.2,
          "distance": 3200
        }
      },
      {
        "_id": "705f71602e4d9f117ea307fb",
        "status": "pending",
        "destinationGeofenceTag": "stop",
        "destinationGeofenceExternalId": "stop-2",
        "stopDuration": 5,
        "metadata": {
          "stopName": "Customer B",
          "orderId": "order-102"
        },
        "eta": {
          "duration": 15.7,
          "distance": 6100
        }
      },
      {
        "_id": "806082713f5ea0228fb4a80c",
        "status": "pending",
        "destinationGeofenceTag": "store",
        "destinationGeofenceExternalId": "store-1",
        "metadata": {
          "type": "return-to-base"
        },
        "eta": {
          "duration": 22.3,
          "distance": 8900
        }
      }
    ],
    "eta": {
      "duration": 22.3,
      "distance": 8900
    },
    "createdAt": "2020-08-20T10:27:55.830Z",
    "updatedAt": "2020-08-20T10:30:55.837Z"
  }
}
Trips also provides the following events:
  • user.started_trip
  • user.updated_trip
  • user.approaching_trip_destination
  • user.arrived_at_trip_destination
  • user.stopped_trip

Quickstart

First, sign up for Radar and get an API key. Then, create a destination geofence. From there, integrate the SDK and call Radar.startTrip(). Radar will generate trip events with ETAs to the destination geofence.

Starting trips

When it’s time to start a trip (e.g., when a user taps “I’m on my way” for a pickup, or when a delivery starts), start a trip with trip options (including an external ID for the trip, a destination geofence, a travel mode, and any custom metadata) and tracking options to be used during the trip (usually RadarTrackingOptions.CONTINUOUS). As the trip progresses, the status will update from started to approaching (based on the Approaching threshold on the Settings page), then arrived (on destination geofence entry).
let metadata = [
  "Customer Name": "Jacob Pena",
  "Car Model": "Green Honda Civic"
]
let tripOptions = RadarTripOptions(externalId: "299", destinationGeofenceTag: "store", destinationGeofenceExternalId: "123")
tripOptions.mode = .car
tripOptions.metadata = metadata

Radar.startTrip(options: tripOptions, trackingOptions: .presetContinuous) { (status: RadarStatus, trip: RadarTrip?, events: [RadarEvent]?) in
  if status == .success {
    // do something
  } else {
    // handle error
  }
}

Updating trips

As the trip progresses, you can update the trip metadata (e.g., when a parking space is assigned for a pickup) or the trip status (e.g., to manually advance the status to approaching or arrived). This can be done either using the SDK or the API.
let tripOptions = Radar.getTripOptions()
tripOptions.metadata = [
  "parkingSpot": "1",
]

Radar.updateTrip(options: tripOptions, status: RadarTripStatus.ARRIVED) { status, trip, events ->
  if (status == Radar.RadarStatus.SUCCESS) {
    // do something
  } else {
    // handle error
  }
}

Stopping trips

Finally, when the trip is complete (e.g., when an order is picked up or a delivery is dropped off), stop the trip. This can be done either using the SDK or the API. You can set trips to expire after a set period if not manually stopped using the Expiration threshold on the Settings page. If tracking was started before the trip was started, the previous tracking options will be restored. Otherwise, tracking will be stopped.
// trip complete
Radar.completeTrip()

// trip cancelled
Radar.cancelTrip()

Trip events

Radar calculates ETAs and generates events when trips are updated:
  • user.started_trip
  • user.updated_trip
  • user.approaching_trip_destination (configurable ETA threshold)
  • user.arrived_at_trip_destination (destination geofence entered)
  • user.stopped_trip
ETAs are calculated based on travel mode using the same routing engine that powers our distance API. You can receive events client-side via the SDK or server-side via event integrations, including webhooks.
{
  "events": [
    {
      "_id": "56db1f4613012711002229f6",
      "type": "user.updated_trip",
      "createdAt": "2020-08-20T10:30:55.837Z",
      "live": true,
      "trip": {
        "_id": "5f3e50491c2b7d005c81f5d9",
        "live": true,
        "externalId": "299",
        "metadata": {
          "Customer Name": "Jacob Pena",
          "Car Model": "Green Honda Civic"
        },
        "mode": "car",
        "destinationGeofenceTag": "store",
        "destinationGeofenceExternalId": "123",
        "destinationLocation": {
          "coordinates": [
            -105.061198,
            39.779366
          ],
          "type": "Point"
        },
        "eta": {
          "duration": 5.5,
          "distance": 1331
        },
        "createdAt": "2020-08-20T10:27:55.830Z",
        "updatedAt": "2020-08-20T10:30:55.837Z",
        "status": "started"
      },
    },
  ],
  "user": {
    "_id": "56db1f4613012711002229f4",
    "live": true,
    "userId": "1",
    "deviceId": "C305F2DB-56DC-404F-B6C1-BC52F0B680D8",
    ...
  }
}

Multi-leg trips

Multi-leg trips require additional configuration. Contact your customer success manager or reach out to us at radar.com/support to enable this feature.
Multi-leg trips allow you to define a sequence of destination stops within a single trip. Use multi-leg trips for use cases like multi-stop delivery routes, ride-sharing with multiple pickups and dropoffs, or field service visits.

Creating a multi-leg trip

To create a multi-leg trip, define RadarTripLeg objects for each stop and attach them to your RadarTripOptions. Set a stopDuration (in minutes) for intermediate stops to improve ETA accuracy for subsequent legs and the overall trip. Omit stopDuration for the final destination. Each leg’s destination can be defined in one of three ways:
  • Geofence — by tag and external ID, or by geofence ID
  • Address — by a street address string
  • Coordinates — by latitude and longitude
When using address or coordinate destinations, you can set an arrivalRadius (in meters) on the leg to define the arrival zone. The examples below use geofence-based destinations. The first leg is automatically set to started when the trip begins.
let leg1 = RadarTripLeg(destinationGeofenceTag: "stop",
                        destinationGeofenceExternalId: "stop-1")
leg1.stopDuration = 5
leg1.metadata = ["stopName": "Customer A", "orderId": "order-101"]

let leg2 = RadarTripLeg(destinationGeofenceTag: "stop",
                        destinationGeofenceExternalId: "stop-2")
leg2.stopDuration = 5
leg2.metadata = ["stopName": "Customer B", "orderId": "order-102"]

let leg3 = RadarTripLeg(destinationGeofenceTag: "stop",
                        destinationGeofenceExternalId: "stop-3")
leg3.stopDuration = 5
leg3.metadata = ["stopName": "Customer C", "orderId": "order-103"]

let leg4 = RadarTripLeg(destinationGeofenceTag: "store",
                        destinationGeofenceExternalId: "store-1")
leg4.metadata = ["type": "return-to-base"]

let tripOptions = RadarTripOptions(externalId: "multi-stop-route-123")
tripOptions.mode = .car
tripOptions.legs = [leg1, leg2, leg3, leg4]

Radar.startTrip(options: tripOptions) { status, trip, events in
    if status == .success {
        print("Trip started with \(trip?.legs?.count ?? 0) legs")
    }
}

Completing a trip leg

When a stop is completed, update the current trip leg status to completed. The SDK provides several methods depending on how much control you need:
  • updateCurrentTripLeg(status:) — completes the current leg automatically; the SDK resolves the trip and leg IDs for you.
  • updateTripLeg(legId:status:) — targets a specific leg by ID; the SDK resolves the trip ID automatically.
  • updateTripLeg(tripId:legId:status:) — provides full manual control over both trip and leg IDs.
All of these methods automatically advance the next leg to started.
Radar.updateCurrentTripLeg(status: .completed) { status, trip, leg, events in
    if status == .success {
        if let nextLegId = trip?.currentLegId {
            print("Next leg: \(nextLegId)")
        } else {
            print("All legs complete")
        }
    }
}

// Target a specific leg by ID
Radar.updateTripLeg(legId: someLegId, status: .completed) { status, trip, leg, events in
    // SDK resolves the trip ID automatically
}

// Full manual control
Radar.updateTripLeg(
    tripId: tripId,
    legId: legId,
    status: .completed
) { status, trip, leg, events in
    // ...
}
When the final leg is completed, the trip is automatically finished. The SDK clears the stored trip and restores any previous tracking options.
Radar.updateCurrentTripLeg(status: .completed) { status, trip, leg, events in
    if trip?.status == .completed {
        print("Final leg completed — trip is now finished")
    }
}
You can also explicitly complete or cancel a multi-leg trip at any time using Radar.completeTrip() or Radar.cancelTrip() as described in Stopping trips.

Reordering trip legs

If the stop order needs to change mid-trip, call reorderTripLegs() with the leg IDs in the desired order. ETAs for each leg will update on the next track call. Call Radar.trackOnce() immediately after reordering to refresh ETAs right away. If a previously completed leg is placed after the current leg in the new order, its status will change back to started once it becomes the current leg.
let legIds = ["legId1", "legId2", "legId3"]
Radar.reorderTripLegs(legIds: legIds) { status, trip, events in
    if status == .success {
        Radar.trackOnce()
    }
}

// Reorder legs of a specific trip (not necessarily the active one)
Radar.reorderTripLegs(
    tripId: tripId,
    legIds: ["legId1", "legId2", "legId3"]
) { status, trip, events in
    if status == .success {
        Radar.trackOnce()
    }
}

Accessing trip state

The SDK stores the active trip locally, so you can access the trip and its legs at any time.
if let trip = Radar.getTrip() {
    print("Trip ID: \(trip._id)")
    print("Status: \(Radar.stringForTripStatus(trip.status))")
    print("Current leg: \(trip.currentLegId ?? "none")")

    for leg in trip.legs ?? [] {
        let isCurrent = leg._id == trip.currentLegId ? " ← current" : ""
        print("Leg \(leg._id ?? "?"): \(RadarTripLeg.string(for: leg.status))\(isCurrent)")
        print("  Destination: \(leg.destinationGeofenceExternalId ?? "unknown")")
    }
}

Anomaly detection

Wrong destination arrival detection

The wrong destination arrival detection feature identifies when a user on a trip may have entered a different destination than the one intended. Specifically, it triggers an event when the user enters a geofence that shares the same tag as the destination geofence, but possesses a different externalId. To enable this feature, navigate to the Settings page and enable the user.arrived_at_wrong_trip_destination event under Trips section.

Delay detection

Delay detection identifies trips at risk of being delayed or incomplete. To enable this feature, configure a Delay threshold on the Settings page under Trips. If scheduledArrivalAt is set for a trip, delay information is captured in the trip’s delay field. The scheduledArrivalTimeDelay is the predicted arrival delay from the scheduledArrivalAt time in minutes. The delayed flag is true when scheduledArrivalTimeDelay exceeds the delay threshold.

Display live trips and ETAs

In the Radar trip tracking dashboard

The trip tracking dashboard is available on the Enterprise plan.
The Radar trip tracking dashboard displays active trips for a specific destination geofence, including ID, custom metadata, and ETA. Ask your customer success manager for a distribution of the app specific to your organization.

In your own UI

Poll the list trips API to retrieve active trips for a specific destination geofence, including ID, custom metadata, and ETA. For example, you might poll the list trips API from an internal dashboard. Or, listen for trip events sent to a webhook. From there, forward trip information, including ID, custom metadata, and ETA, to other systems. For example, you might set up a webhook that sends ETA to an order management or kitchen display system.

Analytics

Radar offers analytics on trip data and location opt-in rates from users in your project. Analytics on trip data can be found on the Analytics page and grouped under the Trips tab. Analytics on location permission opt-in rates is grouped under the Permissions tab. Read more about Permissions here.

How to filter your data

The Filters button at the top of the page offers ways to look at more granular views of your trip data. The filters will apply to the statistics cards and graphs as well as the data in the By Destination table. Date range: A date range can be selected from the drop-down date picker. Make sure that the date in the left-hand box falls before the date in the right-hand box. When accessing the Analytics page, the default date range encompasses all trip data in your project, meaning that you’ll see a default range from the date of your earliest trip to yesterday. All trip analytics are updated nightly and trip analytics from today will not be shown in the Analytics page until tomorrow. Destination Geofence tag: Each geofence has a geofence tag. You can find the tag to a specific geofence in the “Tag” column on the Geofences page in the sidebar, or you can navigate to the trips page in the sidebar and click on the external ID listed under “Destination”, which will show the details of the destination geofence. This field can be used alone or in tandem with the Destination Geofence External ID field. Destination Geofence external ID: Each geofence has an external ID. You can find the external ID for a specific geofence in the “External ID” column on the Geofences page in the sidebar, or you can navigate to the Trips page and click on the external ID listed under “Destination”. This field can be used alone or in tandem with the Destination Geofence Tag field. Travel mode: Select a specific mode of travel to filter all trip data in the Analytics view to only a specific mode. If there is no mode selected, all trip data, regardless of travel mode, will be shown. Trip analytics apply to the environment in which you view the metrics. For example, when viewing the metrics in the test environment, the metrics only include trip data from the test environment.

Trips overview

  • Median wait time: the median duration between the moment when a user arrives at a destination (user.arrived_at_trip_destination) and the moment when the trip is marked complete (completed). This statistic omits trips which are missing an arrival timestamp.
  • Number of trips: the total count of all live trips for a specific project which have a status of completed.
  • Median trip time: the median duration between the moment when the user starts a trip (user.started_trip) and the moment when they arrive at the destination (user.arrived_at_trip_destination). This statistic omits trips which are missing an arrival timestamp.
  • Repeat customers: the total number of your users who have completed more than one trip.
The graphs for each card show the change over time in each statistic. The time windows of each graph are automatically determined by the dates set in the Filters button. To see data grouped by hour, set the date range to encompass 2 days or fewer. To see data grouped by day, set the date range to encompass 21 days or fewer. To see data grouped by weeks, set the date range to encompass 60 days or fewer. To see data grouped by month, the date range must encompass more than 60 days of data.
Number of daysTime grouping
2Hour
21Day
60Week
60+Month

By destination table

The By Destination table groups your trip data by individual destination geofence and allows you to identify your slowest and fastest stores by wait time as well as your most popular stores by the number of trips made to the store. By default, the By Destination table is sorted alphabetically based on the Destination column. To toggle the sort order of the column, click the column header. The table has 6 sortable headers:
  • Destination: the description of the individual destination geofence.
  • Tag: the geofence tag associated with the unique geofence destination listed in the destination column. Clicking on a tag filters the entire Analytics page for only geofences that contain that tag.
  • Geofence ID: the geofence ID of the unique geofence destination listed in the destination column.
  • Wait Time: the median wait time of all qualifying trips to that destination, given the filters applied.
  • Trips Completed: the number of completed trips to that destination, given the filters applied.
  • Trip Time: the median trip time of all qualifying trips to that destination, given the filters applied.
By destination table filter box: The text box in the By Destination table on the Analytics page also allows you to filter only the data in the table. You can filter by Destination, Tag, or Geofence ID. (The filters set in text box only apply to data in the By Destination table and do not apply to the overall trip metrics at the top of the page.) Additional options in the table: On the right side of each row, there is an icon with three dots that allows viewers to select additional options. Clicking on this icon offers two actions:
  • Filter by destination: This will filter the entire analytics page to only include this single destination.
  • View destination: This will bring you to the destination geofence’s detail page.

Calculations

Wait time and trip time are only calculated for trips that have a status of completed and have timestamp entries for started_at, arrived_at, and completed_at. Reminder that all trip analytics are updated nightly and trip analytics from today will not be shown in the Analytics page until tomorrow.

Data retention settings and frequency of updates

Data retention settings (a setting on the project level) apply to the data in Analytics as well. The default data retention settings for trip data is 90 days. Median wait time, trip count, and median trip duration metrics are all updated nightly, so all calculations do not include the current day’s data. If your project does not have any past trips, you will not see any data in the analytics section of your dashboard. Take your first trip to see the statistics populate!

Keep-alive

With silent push notifications enabled, Radar will attempt to initiate track requests on users that have lost tracking updates. To enable trip keep-alive via silent push, configure the platform specific push notification settings in dashboard Settings page. Then set up silent push in your app by following the silent push guide for your platform. If the trip is active but has not received any updates within 3 minutes, Radar will send a silent push notification to the device which will attempt to restart tracking. Radar will continue to send silent push notifications every 3 minutes if the trip has not been updated. For iOS devices with location extension push enabled, Radar will attempt to send a background location push after 5 minutes of no updates. Location extension will be able provide a location update in some cases where a regular silent push does not have permission to do so. However, location extension push notifications cannot restart tracking for the app.

Support

Have questions? We’re here to help! Contact us at radar.com/support.