MOVE TIMELINE API

MOVE Backend APIs

rieving Timeline Items

For all Timeline API calls, the projectId needs to be specified as a query parameter.

What is a Timeline Item?

A timeline item represents an activity of a given user, it consists of a projectId, a userId, a start timestamp, an end timestamp, a type and a set of features. Which are described in the following sections.

Each timeline item is uniquely identified by the combination of projectId, userId and start time.

Example Timeline Item

{
    "projectId": 1000, // an integer
    "userId": "123456789", // a string
    "startTs": "2020-10-16T10:13:00Z", // time formatted according to RFC 3339, e.g. "2020-07-10T06:59:44.377Z"
    "endTs": "2020-10-16T10:20:00Z", // time formatted according to RFC 3339, e.g. "2020-07-10T06:59:44.377Z"
    "type": "CAR", // a string, valid types are UNKNOWN, DRIVING, TRAIN, TRAM, CAR, METRO, FAKETRIP, WALKING, IDLE, CYCLING 
    "features": {
        ... // an object of features
    }
}

New feature types may be added in the future, a client should be able to deal with additional/unknown types of features.

Start Location

The GPS coordinates where the item started. Included with DRIVING and CYCLING trips.

"startLocation": {
    "lat": 48.1234, // latitude
    "lon": 48.1234  // longitude
}

End Location

The GPS coordinates where the item ended. Included with DRIVING and CYCLING trips.

"endLocation": {
    "lat": 48.1234, // latitude
    "lon": 48.1234  // longitude
}

Transport

The type of transport that was used for the given trip. The Mode of Transport recognition is multi-stage and is mostly based on GPS coordinates and data about the position taken from OpenStreetMap data (e.g. speed, proximity to streets and public transport stations). In general, the algorithms are tuned towards distinguishing trips in car-like vehicles (including buses and taxi cabs) from non-car vehicles (e.g. bicycles, trains, metro). Note: Quality metrics reported in the following have a maximum variability of +- 3%-points.

  • FAKETRIP - GPS sensors in mobile phones are not always accurate. Jumps of GPS data around the actual "real-world" position of the device may occur at all times; particularly in dense cities with bad visibility of the GPS satellites, when the phone is stacked e.g. beneath a laptop computer in the backpack, or even sometimes when weather conditions are bad. In rare situations, such GPS jumps may follow a pattern that makes our first stage SDK modules in the SDK frontends believe that the device is in an actual trip. Later stages in the SDK backend will label these trips as FAKETRIP.

  • CAR - users of our apps usually have a preference for using cars in their day-to-day activities. In this (slightly biased) population, the detection of car trips (including car-like vehicles such as taxi cabs and buses) has a true positive rate of about 98% and a false positive rate of less than 5%.

  • PUBLIC_TRANSPORT - The recognition of public transport has multiple vectors: e.g. we may detect a tram trip through GPS like a CAR trip, or we may see a person moving from subway station to subway station and infer (since there are no GPS signals underground) that the person must have taken the subway. Since this contains an element of inference and heuristics, the detection/classification of public transport cannot be 100% perfect. Our tests show a true positive rate for railroad-based vehicles of more than 90%. However, bad GPS may further reduce the overall detection of public transport trips. Note that currently we are unable to include buses or taxi into this category; although we can achieve this through hardware (e.g. cheap Bluetooth beacons) in the user's private car. Please get in touch for a more detailed discussion.

  • BICYCLE - Cycling detection is based not only on GPS data but relies heavily on the activities recognized by the OS of the mobile device. Its quality may vary depending on the hardware of the mobile device, as well as the OS version used (e.g. Google Pixel and Huawei Android phones differ quite substantially in their sensitivity and update frequency of activity recognition).

  • UNKNOWN - Whenever the user moves in ways that we cannot categorize, this will be UNKNOWN. For example, users on a boat trip on the sea, which is too far from the road network; or skiing in the mountains - these activities are fast and stable enough to be recorded as a trip, but cannot be verified/classified as a trip.

We are constantly working on improving these algorithms. Therefore, please be advised to use the "probabilities" merely as a rough indicator, but never base any filtering mechanisms on these data, as our machine learning algorithms may improve/change these numbers at any time.

"transport": {
    "type": "TRAIN",  // possible types are CAR, METRO, TRAIN, TRAM, BICYCLE, FAKETRIP
    "probabilities": { // how probable each transport type is
      "TRAIN": 0,
      "TRAM": 0.064,
      "CAR": 0.832,
      "METRO": 0.002,
      "FAKETRIP": 0.08
    }
},

Phone Distractions

Times where the user used their phone. Included with DRIVING and CYCLING trips.

"phoneDistractions": {
  "distractions": [
    {
      "type": "PHONE_CALL_HANDS_FREE", // the type of distractions, possible distractions are SWIPE_AND_TYPE (the user touched their phone), PHONE_CALL (the user made a phone call without any hands free equipment), PHONE_CALL_HANDS_FREE (the user made a phone call with hands free equipment)
      "start": "2021-05-27T12:57:10.234Z",  // time formatted according to RFC 3339, e.g. "2020-07-10T06:59:44.377Z"
      "end": "2021-05-27T12:57:10.234Z"  // time formatted according to RFC 3339, e.g. "2020-07-10T06:59:44.377Z"
    }
  ],
  "secondsPerType": {
    "PHONE_CALL": 0,
    "PHONE_CALL_HANDS_FREE": 0,
    "SWIPE_AND_TYPE": 0
  }
},

Driving Behavior Events

Returns a list of driving behavior events. Valid types of events are: CORNERING, ACCELERATION, BRAKING

"drivingBehaviorEvents": {
  "events": [
    {
      "time": 0, // UNIX timestamp of event
      "type": "CORNERING", 
      "strength": 0, value between 1 (soft) to 3 (hard)
      "lat": 0, latitude of event
      "lon": 0, long event of vent
      "rawAccel": 0 // raw acceleration of event
    }
  ],
  "validTime": 0 // how many seconds of the trip could be analyse
}

GPS Stats

Provides basic information about the gps points for this timeline item.

distance contains the distance traveled in meters, maxSpeed the maximum speed in kph, averageSpeed contains the average speed in kph.

distancePerWayTypeMeters contains the distance that was traveled for each way type. A way type classifies the road that a trip point lies on into one of 3 categories: motorway, city or other. When the way type for a point is motorway it is on a major highway such as a Freeway or the Autobahn. When it is classified as city it lies on a residential road with a speed limit of less than 50 kph. All other roads are classified as other.

 "gpsStats": {
    "distance": 0,
    "maxSpeed": 0,
    "averageSpeed": 0,
    "distancePerWayTypeMeters": {
       "motorway": 1540,
       "city": 507,
       "other": 11513
    }
}

Trip Metadata

It is possible to add custom metadata to every trip. Keys may be at most 255 characters long, values may be at most 1000 characters long.

"metas": {
  "metaItem1": "example",
  "metaItem2": "123",
  "metaItem3": "example 2"
}

Dangerous Areas

The dangerous areas the user passed through in this timeline item. Inside a dangerous area speeding is punished more severely. Every project defines its own dangerous areas in the Move Dashboard.

The Dangerous Area Id is also found in the points request.

"dangerous_areas": {
  "areas": [
    {
      "id": "d0bd9509-20b1-43d8-8b19-1c56b2db1fdb"
      "name": "name"
      "lat": 48.1770551 // longitude of the dangerous area position
      "lon": 16.3752482 // latitude of the dangerous area position
      "radius": 200 // radius of the danngerous area
    }
  ]
}

Scores

The individual features above give you the raw data for characterizing and scoring your trip. For example. Based on this, you can calculate scores for your users.

  • distraction-free driving (DFD): score is reduced for usage of phone or swipe-and-type during trip as specified in the feature phone_distractions (in minutes)

  • speed: the score is diminished when driving faster than the the speed limit, based on the speed and the maximum allowed speed in the separate API for GPS points

  • driving behavior events (DBE): sensors will identify events where driving was not smooth for Acceleration, Braking or Cornering (in 3 levels of severity)

  • eco-sensors: awards a better score for an eco-friendly driving-style, and assumes that a driver who constantly brakes and accelerates uses more fuel than a driver who manages to drive very smoothly; similar to an aggregation of DBE, but more sensitive. Note, that this insufficient to define a comprehensive Eco-score, which also addresses type of car/fuel, overall transport mix, distance/minutes driven on a daily basis, or other factors you may want to include

  • you can come up with your own scoring mechanisms, such as:

    • Trip duration (short): users should ideally only take the car for trips longer than x minutes, since short trips are very prone to accidents

    • Trip duration (long): drivers will get tired on long-trips, so take a rest of at least e.g. 10 minutes or more after every 2 hours of driving

    • Eco-score: calculate your own eco-score, based on Eco-sensors (see above, the mix of transport-types and/or based on the type of car/fuel the user defined in your app

We also make score suggestions for scores 0-100, that are based on data analyses of our current user base. We geared the scores to see a Gaussian distribution of aggregated scores (total score per user) with a mean somewhere around 80 or 90. After all, we want to positively encourage people to improve their driving style (which is hard if we gave them bad scores for decent driving style). Please note that this is a global generic scoring distribution. However, age, culture and other factors may influence the driving style. So you may want to adjust your scoring mechanism to suit your user base.

Note that scores may not be there in case they cannot be calculated; e.g. DFD scores are not calculated in case Android phones do not grant the PHONE permission; DBE scores are not calculated in case the phone is used or is located at an unstable place in the car.

"scores": {
    "DFD":100,
    "SPEED":63,
    "ACCELERATION":85,
    "BRAKING":73,
    "CORNERING":95,
    "ECO_SENSOR": 67
}

/v20/timeline/{startTs}

GET https://sdk.dolph.in/v20/timeline/{startTs}

Fetch a single timeline item

Path Parameters

NameTypeDescription

startTs*

Number

start timestamp of the request timeline item

Query Parameters

NameTypeDescription

projectId*

integer

userId*

string

Headers

NameTypeDescription

Authorization*

string

Basic <ProjectId>:<API-Key>

{
  "productId": 0,
  "contractId": "string",
  "startTs": "2021-06-24T07:40:25.455Z",
  "endTs": "2021-06-24T07:40:25.455Z",
  "type": "UNKNOWN",
  "features": {
    "startLocation": {
      "lat": 0,
      "lon": 0
    },
    "endLocation": {
      "lat": 0,
      "lon": 0
    },
    "source": {
      "id": "string",
      "name": "string"
    },
    "scores": {
      "additionalProp1": 0,
      "additionalProp2": 0,
      "additionalProp3": 0
    },
    "drivingBehaviorEvents": {
      "events": [
        {
          "time": 0,
          "type": "CORNERING",
          "strength": 0,
          "lat": 0,
          "lon": 0,
          "rawAccel": 0
        }
      ],
      "validTime": 0
    },
    "phoneDistractions": {
      "distractions": [
        {
          "type": "PHONE_CALL_HANDS_FREE",
          "start": "2021-06-24T07:40:25.456Z",
          "end": "2021-06-24T07:40:25.456Z"
        }
      ],
      "secondsPerType": {
        "PHONE_CALL": 0,
        "PHONE_CALL_HANDS_FREE": 0,
        "SWIPE_AND_TYPE": 0
      }
    },
    "gpsStats": {
      "distance": 0,
      "maxSpeed": 0,
      "averageSpeed": 0
    },
    "transport": {
      "type": "TRAIN",
      "probabilities": {
        "TRAIN": 0,
        "TRAM": 0,
        "BUS": 0,
        "CAR": 0,
        "METRO": 0,
        "FAKETRIP": 0
      }
    },
    "metas": {
      "additionalProp1": "string",
      "additionalProp2": "string",
      "additionalProp3": "string"
    }
  }
}

Delete timeline Item

DELETE https://sdk.dolph.in/v20/timeline/{startTs}

Delete a single timeline item. startTs is an unix timestamp.

Path Parameters

NameTypeDescription

startTs *

integer

Start time of timeline item

Query Parameters

NameTypeDescription

projectId *

integer

userId *

string

{
    // Response
}

/v20/timeline

GET https://sdk.dolph.in/v20/timeline

Fetch the latest timeline items in a given time range for a user

Query Parameters

NameTypeDescription

limit

integer

The maximum number of items to return. Default: 10, The maximum accepted value is 10 000.

to

integer

UNIX timestamp. NO items with a startTS later than this will be returned

from

integer

UNIX timestamp. NO items with an endTS earlier than this will be returned

projectId*

string

userId*

string

allowedItems

String

list of allowed types. If empty all types are allowed.

tag

String

List of tags to filter the results, search in metas field of timeline item. Checks key and value of the meta data.

Headers

NameTypeDescription

*

string

Basic <ProjectId>:<API-Key>

[
  {
    "productId": 0,
    "contractId": "string",
    "startTs": "2021-06-24T07:40:44.941Z",
    "endTs": "2021-06-24T07:40:44.941Z",
    "type": "UNKNOWN",
    "features": {
      "startLocation": {
        "lat": 0,
        "lon": 0
      },
      "endLocation": {
        "lat": 0,
        "lon": 0
      },
      "source": {
        "id": "string",
        "name": "string"
      },
      "scores": {
        "additionalProp1": 0,
        "additionalProp2": 0,
        "additionalProp3": 0
      },
      "drivingBehaviorEvents": {
        "events": [
          {
            "time": 0,
            "type": "CORNERING",
            "strength": 0,
            "lat": 0,
            "lon": 0,
            "rawAccel": 0
          }
        ],
        "validTime": 0
      },
      "phoneDistractions": {
        "distractions": [
          {
            "type": "PHONE_CALL_HANDS_FREE",
            "start": "2021-06-24T07:40:44.941Z",
            "end": "2021-06-24T07:40:44.941Z"
          }
        ],
        "secondsPerType": {
          "PHONE_CALL": 0,
          "PHONE_CALL_HANDS_FREE": 0,
          "SWIPE_AND_TYPE": 0
        }
      },
      "gpsStats": {
        "distance": 0,
        "maxSpeed": 0,
        "averageSpeed": 0
      },
      "transport": {
        "type": "TRAIN",
        "probabilities": {
          "TRAIN": 0,
          "TRAM": 0,
          "BUS": 0,
          "CAR": 0,
          "METRO": 0,
          "FAKETRIP": 0
        }
      },
      "metas": {
        "additionalProp1": "string",
        "additionalProp2": "string",
        "additionalProp3": "string"
      },
      "dangerous_areas": {
        "areas": [
          {
            "id": "d0bd9509-20b1-43d8-8b19-1c56b2db1fdb"
            "name": "name"
            "lat": 48.1770551
            "lon": 16.3752482
            "radius": 200
          }
        ]
      }
    }
  }
]

​/v20​/timeline​/batch

POST https://sdk.dolph.in ​/v20​/timeline​/batch

Fetch a batch of timeline items

Request Body

NameTypeDescription

startTs*

string

Start time of requested timeline items

projectId*

string

userId*

string

[
  {
    "productId": 0,
    "contractId": "string",
    "startTs": "2021-06-24T07:40:44.941Z",
    "endTs": "2021-06-24T07:40:44.941Z",
    "type": "UNKNOWN",
    "features": {
      "startLocation": {
        "lat": 0,
        "lon": 0
      },
      "endLocation": {
        "lat": 0,
        "lon": 0
      },
      "source": {
        "id": "string",
        "name": "string"
      },
      "scores": {
        "additionalProp1": 0,
        "additionalProp2": 0,
        "additionalProp3": 0
      },
      "drivingBehaviorEvents": {
        "events": [
          {
            "time": 0,
            "type": "CORNERING",
            "strength": 0,
            "lat": 0,
            "lon": 0,
            "rawAccel": 0
          }
        ],
        "validTime": 0
      },
      "phoneDistractions": {
        "distractions": [
          {
            "type": "PHONE_CALL_HANDS_FREE",
            "start": "2021-06-24T07:40:44.941Z",
            "end": "2021-06-24T07:40:44.941Z"
          }
        ],
        "secondsPerType": {
          "PHONE_CALL": 0,
          "PHONE_CALL_HANDS_FREE": 0,
          "SWIPE_AND_TYPE": 0
        }
      },
      "gpsStats": {
        "distance": 0,
        "maxSpeed": 0,
        "averageSpeed": 0
      },
      "transport": {
        "type": "TRAIN",
        "probabilities": {
          "TRAIN": 0,
          "TRAM": 0,
          "BUS": 0,
          "CAR": 0,
          "METRO": 0,
          "FAKETRIP": 0
        }
      },
      "metas": {
        "additionalProp1": "string",
        "additionalProp2": "string",
        "additionalProp3": "string"
      }
    }
  }
]

​/v20​/timeline​/{startTs}​/points

GET https://sdk.dolph.in ​/v20​/timeline​/{startTs}​/points

Fetch the GPS Coordinates for the given timeline item. Not all timeline items have GPS points, for example IDLE items have no points.

Path Parameters

NameTypeDescription

start_ts*

string

start time of requested timeline item

Query Parameters

NameTypeDescription

withWayPointInfo

string

Whether to fetch detailed info for waypoints

userId*

string

projectId*

string

[
    {
        "timestamp": "2020-10-16T13:17:34.691Z",
        "lat": 48.12356,
        "lon": 16.122556,
        "wayPointInfo": { // will not be given if withWayPointInfo is false
            "speed": 45 // the speed of the user at the given coordinate
            "speedLimit": 50, // the speed limit at the given way point
            "wayType": "city", // on what kind of road the gps point lies valid values are "city", "tunnel", "motorway" and "other"
            "roadLat": 48.124599, // latitude on the matched road point
            "roadLon": 16.125567 // longitude on the matched road point
            "dangerousAreaId": "01bd3c3a-930c-4449-a035-827d091d72ed" //UUID of Dangerous Area
        }
    },
    ...
]

Label trip

PATCH https://sdk.dolph.in/v20/timeline/admin/label/{ts}/{label}

Change type of a trip

Query Parameters

NameTypeDescription

ts*

string

start timestamp of timeline item

label*

String

Label of timeline item

projectId*

String

userId*

String

Headers

NameTypeDescription

Authorization*

String

BASIC <PRODUCTID>:<API_KEY>

{
  "productId": 0,
  "contractId": "string",
  "startTs": "2022-04-11T07:46:17.168Z",
  "endTs": "2022-04-11T07:46:17.168Z",
  "type": "UNKNOWN",
  "features": {
    "startLocation": {
      "lat": 0,
      "lon": 0,
      "name": null
    },
    "endLocation": {
      "lat": 0,
      "lon": 0,
      "name": null
    },
    "source": {
      "id": "string",
      "name": "string"
    },
    "scores": {
      "additionalProp1": 0,
      "additionalProp2": 0,
      "additionalProp3": 0
    },
    "drivingBehaviorEvents": {
      "events": [
        {
          "time": 0,
          "type": "CORNERING",
          "strength": 0,
          "lat": 0,
          "lon": 0,
          "rawAccel": 0
        }
      ],
      "validTime": 0
    },
    "phoneDistractions": {
      "distractions": [
        {
          "type": "PHONE_CALL_HANDS_FREE",
          "start": "2022-04-11T07:46:17.169Z",
          "end": "2022-04-11T07:46:17.169Z"
        }
      ],
      "secondsPerType": {
        "PHONE_CALL": 0,
        "PHONE_CALL_HANDS_FREE": 0,
        "SWIPE_AND_TYPE": 0
      }
    },
    "gpsStats": {
      "distance": 0,
      "maxSpeed": 0,
      "averageSpeed": 0
    },
    "transport": {
      "type": "TRAIN",
      "probabilities": {
        "TRAIN": 0,
        "TRAM": 0,
        "BUS": 0,
        "CAR": 0,
        "METRO": 0,
        "FAKETRIP": 0
      }
    },
    "metas": {
      "additionalProp1": "string",
      "additionalProp2": "string",
      "additionalProp3": "string"
    },
    "dangerousAreas": {
      "areas": [
        {
          "id": "string",
          "name": "string",
          "lat": 0,
          "lon": 0,
          "radius": 0
        }
      ]
    }
  }
}

The Open API Specification for the MOVE SDK API is available here.

Getting notified about new timeline items

Notification about new timeline items happens via webhooks that can be defined in the MOVE Dashboard > Configuration > Timeline Notifier.

The webhook will be called with a POST request that contains a JSON body with a list of new timeline items. Updates are sent every 1000 items or every 5 seconds (whatever comes first). The information transmitted to the webhook is limited, you may request additional information via timeline API, preferably batched.

Schema

{
    "items": [
        {
            "projectId": <yourProjectId>, // integer
            "userId": <userId>, // string
            "startTime": <startTime>, // time formatted according to RFC 3339, e.g. "2020-07-10T06:59:44.377Z"
            "endTime": <endTime>, // time formatted according to RFC 3339, e.g. "2020-07-10T06:59:44.377Z"
            "type": <type> // string, one of IDLE, CYCLING, WALKING, DRIVING
        }, ...
    ]
}

If the webhook receiver is not available or does not respond with an HTTP 200 Status the request will be retried, until an HTTP 200 Status is received.

Security

With every request, a signature is being sent in the "X-Dolphin-Signature" Header that may be used to verify that the request has acutally been made by the Move SDK. The signature is calculated as an HMAC with SHA-256 of the request body, with the secret (token) you gave us during the receiver setup. The format of the header is "sha256=<base64-encoded-hash>".

Last updated

Dolphin Technologies GmbH