Journey Service

Journey service detects vehicle trips using data collected from the device. Please note that the words “trip” and “journey” are used here interchangeably.

Prerequisites

Before you begin, make sure you have:

Get Journey by ID

Querying a trip using GraphQL

Given a journey with id 123456789123456789, the following query requests the journey’s information.

Get Journey By ID
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
"""
Variables:
 
{
    "id": 123456789123456789
}

"""
query trip($id: ID!) {
  trip(id: $id) {
    start_event {
      time
      lat
      lng
    }
    end_event {
      time
      lat
      lng
    }
    duration
    distance
    co_2_estimation
    fuel_estimation
    tow_away
    max_speed
    status
    safety_score
    eco_driving_score
  }
}
1
2
3
4
5
6
export TOKEN=<YOUR_MUNIC_CONNECT_TOKEN>

curl --request POST 'https://api.munic.io/services/ekko/v2/graphql' \
--header "Authorization: Bearer $TOKEN" \
--header "Content-Type: application/json" \
--data '{"query": "query trip($id: ID!) {\n  trip(id: $id) {\n    start_event {\n      time\n      lat\n      lng\n    }\n    end_event {\n      time\n      lat\n      lng\n    }\n    duration\n    distance\n    co_2_estimation\n    fuel_estimation\n    tow_away\n    max_speed\n    status\n    safety_score\n    eco_driving_score\n  }\n}", "variables":{    "id": 123456789123456789}}'
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import requests

token = <YOUR_MUNIC_CONNECT_TOKEN>
url = "https://api.munic.io/services/ekko/v2/graphql"

payload = """
{"query": "query trip($id: ID!) {
  trip(id: $id) {
    start_event {
      time
      lat
      lng
    }
    end_event {
      time
      lat
      lng
    }
    duration
    distance
    co_2_estimation
    fuel_estimation
    tow_away
    max_speed
    status
    safety_score
    eco_driving_score
  }
}", "variables":
{
    "id": 123456789123456789
}
}
"""

payload = payload.replace("\n", "")

headers = {
  'Content-Type': 'application/json',
  'Authorization': 'Bearer %s'%(token)
}

response = requests.request("POST", url, headers=headers, data=payload)

print(response.text)
See JSON Answer
{
  "data": {
    "trip": {
      "co_2_estimation": 1848,
      "distance": 10465,
      "duration": 600,
      "eco_driving_score": 76.70220976290179,
      "end_event": {
        "lat": 39.92175,
        "lng": -78.1389,
        "time": "2023-07-11T11:52:11.000Z"
      },
      "fuel_estimation": 793,
      "max_speed": 63.990303999999995,
      "safety_score": 92.09524967918529,
      "start_event": {
        "lat": 39.88565,
        "lng": -78.25195,
        "time": "2023-07-11T11:42:11.000Z"
      },
      "status": "CLOSED",
      "tow_away": false
    }
  }
}

For more details, please read the full trip documentation graphql reference.

OPEN vs CLOSED Trip

A trip has three possible statuses:

  • open Status: Only the device’s last trip can have an open status. If the last trip’s status is open, this indicates that we don’t consider the trip to be finished yet. This can be for several reasons, such as the corresponding vehicle is currently moving, or it might start moving soon. Once a trip is open, the id and start_event never change. However, other information like fuel estimation or end location may change later.

  • closed Status: A trip is considered closed when it refers to a finished trip with all information available at this moment. While additional details may be added later, the trip’s status will remain closed.

  • provisionally_closed Status: A trip is considered provisionally_closed if the journey service believes there’s a high probability the trip is complete but doesn’t have enough information to officially close the trip. A provisionally_closed trip is technically still an open trip and may be reopened if new data arrives. However, it’s most likely that it will eventually become closed.

If you set up a webhook to receive your device data in real time, you can receive journey pokes to get updates about your Journey

Get a trip’s information using Pokes

If you set up a webhook to receive your device data in real time, you can also receive Journey pokes to keep you updated on your trip’s progress.

You may receive a Poke in the following situations:

  • whenever your trip’s status changes: OPEN, PROVISIONALLY_CLOSED, CLOSED
  • when certain metadata for your trip is updated after it has been CLOSED
  • if your trip isn’t officially finished and its information gets updated (see PROVISIONALLY_CLOSED status)

When you receive a Poke notification, the id attribute in the payload is the unique identifier for the trip. This allows you to link all notifications back to a single, specific trip.

It’s important to note that you might receive multiple Poke notifications for the same trip even if it has the same PROVISIONALLY_CLOSED or CLOSED status (as defined above).

The first Poke notification that includes a CLOSED status for a specific trip will include the definitive time for trip completion.

To ensure the quickest notification for a trip’s completion, the process of cleaning up trip position data is performed asynchronously by a job that runs after the trip ends. This means if you try to retrieve the cleaned position data immediately after receiving a CLOSED Poke, it might not be available yet while the processing is still underway. If you need to be notified when cleaned positions are ready, your account manager can enable a specific Poke notification for this. Once activated, a Poke will be triggered, and its metadata will include a positions_available attribute set to true when the cleanup (or map-matching if enabled) process is complete.

Journey Poke Payloads
{
    "id": "123456789123456789",
    "state": "OPEN",
    "start_time": "2023-07-11T11:42:11.000Z",
    "end_time": "2023-07-11T11:42:11.000Z",
    "start_event_id": "1786911231093121976",
    "end_event_id": "1786911272504112056",
    "total_duration_in_seconds": 120,
    "driving_duration_in_seconds": 120,
    "idling_duration_in_seconds": 0,
    "driving_percentage": 100,
    "idling_percentage": 0,
    "driver_behavior_events":[],
    "metadata": {
        "average_speed": {
            "double_value": 35.6
        },
        "distance_in_m": {
            "integer_value": 2136
        },
        "co2_estimation_in_g": {
            "integer_value": 451
        },
        "fuel_estimation_in_ml": {
            "integer_value": 148.2
        },
        "eco_driving_score": {
            "double_value": 89.70220976290179
        },
        "nb_overspeed": {
            "integer_value": 1
        },
        "overspeed_distance_in_m": {
            "integer_value": 1010
        },
        "overspeed_duration_in_s": {
            "integer_value": 29
        },
        "max_speed": {
            "double_value": 61.17263111212221
        },
        "nb_harsh_acceleration": {
            "integer_value": 0
        },
        "nb_harsh_braking": {
            "integer_value": 0
        },
        "nb_harsh_cornering": {
            "integer_value": 0
        },
        "safety_score": {
            "double_value": 98.19524967918529
        },
        "tow_away": {
            "bool_value": false
        }
    },
    "start_position": {
        "latitude": 39.92175,
        "longitude": -78.1389
    },
    "end_position": {
        "latitude": 39.89124,
        "longitude": -78.1462
    }
}
{
    "id": "123456789123456789",
    "state": "CLOSED",
    "start_time": "2023-07-11T11:42:11.000Z",
    "end_time": "2023-07-11T11:52:11.000Z",
    "start_event_id": "1786911231093121976",
    "end_event_id": "1786911486506142056",
    "total_duration_in_seconds": 600,
    "driving_duration_in_seconds": 570,
    "idling_duration_in_seconds": 30,
    "driving_percentage": 95,
    "idling_percentage": 5,
    "driver_behavior_events":[],
    "metadata": {
        "average_speed": {
            "double_value": 27.771428571428572
        },
        "distance_in_m": {
            "integer_value": 10465
        },
        "co2_estimation_in_g": {
            "integer_value": 1848
        },
        "fuel_estimation_in_ml": {
            "integer_value": 793
        },
        "eco_driving_score": {
            "double_value": 76.70220976290179
        },
        "nb_overspeed": {
            "integer_value": 1
        },
        "overspeed_distance_in_m": {
            "integer_value": 1010
        },
        "overspeed_duration_in_s": {
            "integer_value": 29
        },
        "max_speed": {
            "double_value": 63.990303999999995
        },
        "nb_harsh_acceleration": {
            "integer_value": 1
        },
        "nb_harsh_braking": {
            "integer_value": 0
        },
        "nb_harsh_cornering": {
            "integer_value": 0
        },
        "safety_score": {
            "double_value": 92.09524967918529
        },
        "nb_postprocessed_overspeed": {
            "integer_value": 1
        },
        "tow_away": {
            "bool_value": false
        },
        "positions_available": {
            "bool_value": true
        }
    },
    "start_position": {
        "latitude": 39.92175,
        "longitude": -78.1389
    },
    "end_position": {
        "latitude": 39.88565,
        "longitude": -78.25195
    }
}
{
    "id": "123456789123456789",
    "state": "PROVISIONALLY_CLOSED",
    "start_time": "2023-07-11T11:42:11.000Z",
    "end_time": "2023-07-11T11:50:11.000Z",
    "start_event_id": "1786911231093121976",
    "end_event_id": "1786911486506141056",
    "total_duration_in_seconds": 480,
    "driving_duration_in_seconds": 460,
    "idling_duration_in_seconds": 20,
    "driving_percentage": 95.83,
    "idling_percentage": 4.17,
    "driver_behavior_events":[],
    "metadata": {
        "average_speed": {
            "double_value": 27.771428571428572
        },
        "distance_in_m": {
            "integer_value": 8465
        },
        "co2_estimation_in_g": {
            "integer_value": 1648
        },
        "fuel_estimation_in_ml": {
            "integer_value": 703
        },
        "eco_driving_score": {
            "double_value": 76.70220976290179
        },
        "nb_overspeed": {
            "integer_value": 1
        },
        "overspeed_distance_in_m": {
            "integer_value": 1010
        },
        "overspeed_duration_in_s": {
            "integer_value": 29
        },
        "max_speed": {
            "double_value": 63.990303999999995
        },
        "nb_harsh_acceleration": {
            "integer_value": 1
        },
        "nb_harsh_braking": {
            "integer_value": 0
        },
        "nb_harsh_cornering": {
            "integer_value": 0
        },
        "safety_score": {
            "double_value": 92.09524967918529
        },
        "tow_away": {
            "bool_value": false
        }
    },
    "start_position": {
        "latitude": 39.92175,
        "longitude": -78.1389
    },
    "end_position": {
        "latitude": 39.88565,
        "longitude": -78.25184
    }
}
Journey Pokes Consistency with GraphQL

An OPEN poke is sent once the journey is detected while a CLOSE poke is sent once the journey is closed.

Poke Metadata Reference

The metadata object in Journey pokes contains trip-level statistics. Below is a reference for each field:

FieldTypeDescription
average_speeddoubleAverage speed of the vehicle during the trip (km/h).
distance_in_mintegerTotal distance traveled during the trip (meters).
co2_estimation_in_gintegerEstimated CO2 emissions for the trip (grams).
fuel_estimation_in_mlintegerEstimated fuel consumption for the trip (milliliters).
max_speeddoubleMaximum speed recorded during the trip (km/h).
eco_driving_scoredoubleEco-driving score for the trip (0-100).
safety_scoredoubleSafety score for the trip (0-100).
nb_overspeedintegerNumber of MDI_OVERSPEED tracking fields reported by the device during the trip. An MDI_OVERSPEED is emitted when the vehicle speed exceeds a fixed threshold configured on the device.
overspeed_distance_in_mintegerTotal distance traveled while overspeeding (meters), based on device-reported overspeeds.
overspeed_duration_in_sintegerTotal duration of overspeeding (seconds), based on device-reported overspeeds.
nb_postprocessed_overspeedintegerNumber of overspeeds detected through post-processing using map data and available in driver_behavior_events. The system retrieves the legal speed limit of each road segment from cartographic data and compares it to the vehicle’s actual speed.
nb_harsh_accelerationintegerNumber of harsh acceleration events detected during the trip.
nb_harsh_brakingintegerNumber of harsh braking events detected during the trip.
nb_harsh_corneringintegerNumber of harsh cornering events detected during the trip.
tow_awaybooleanWhether the trip was detected as a tow-away (vehicle transported without engine running).
positions_availablebooleanWhether cleaned position data is available for the trip. Only present in CLOSED pokes after position processing is complete.

Trip pokes and additional driver behavior events

Trip may be enriched with different type of driver behavior events: idlings, overspeeds, etc. These events can be recorded by journey if enabled and may be also notified in journey pokes in the field driver_behavior_events. Currently only idling and overspeed are added in journey pokes. Harsh events will come soon!

Please notice that idling events maybe open or closed.

Overspeed events vs. device-reported overspeeds

The overspeed events listed in driver_behavior_events are the result of post-processing using cartographic data (map-matched speed limits). Their count corresponds to the nb_postprocessed_overspeed metadata field. These are distinct from nb_overspeed, which counts device-reported MDI_OVERSPEED tracking fields based on a fixed speed threshold configured on the embedded system.

Journey Driver Behavior Events
{
    "idling": {
        "start_time": "2025-10-15T09:31:45Z",
        "end_time": "2025-10-15T09:44:33Z",
        "start_position": {
            "latitude": 48.8566,
            "longitude": 2.3522
        },
        "end_position": {
            "latitude": 48.8566,
            "longitude": 2.3522
        },
        "open": false
    }
}
{
    "overspeed": {
        "id": "1112889013628534791",
        "start_time": "2025-10-03T08:14:38Z",
        "end_time": "2025-10-03T08:15:38Z",
        "start_position": {
            "latitude": 48.8566,
            "longitude": 2.3522
        },
        "end_position": {
            "latitude": 48.8566,
            "longitude": 2.3582
        },
        "distance": 1281.849725795233,
        "mean_speed": 73.7110351743787,
        "peak_speed": 81.23684759211996,
        "speed_threshold": 56
    }
}

Trip Refreshing

One may need to receive the state of the current journey as soon as possible. Journey service can noify each 2m30s the journey with all the current attributes. Please ask support team if you want to enable this feature. Once enabled, please recall that many open (depening on journey duration) pokes can be received rather than only one if this feature is disabled.

Future Development

Future development may change the information delivered in the pokes. Other pokes will also be added to keep the receiving webhook updated.

To configure overspeed thresholds and duration per device, see Overspeed configuration.