Motiontag API

This is a brief tutorial on how to set up your backend/scripts to work with the APIs MOTIONTAG has to offer.

1. Prerequisites

In order to create an account in any of our products you need to contact the MOTIONTAG staff. They will provide you with a dedicated domain, which also serves as the base URL for the APIs. When signed in you can see your custom tenant key below.

API base URL
Only accessible when signed in. You need a custom domain setup by MOTIONTAG (e.g. my-tenant-key.motion-tag.de) and an account to sign in. Contact MOTIONTAG Support to request a custom domain setup and an account. If you already have both, visit your custom domain and sign in to see a customized version of this documentation.

Furthermore, you'll be able to sign in into our dashboard to access information about your setup and your users. When logged in with the right privileges you'll be able to download or upload certain data manually as well.

Part of your base URL contains your tenant key, which you need to create correct iss claims. When signed in you can see your custom tenant key below.

Your tenant key
Only accessible when signed in. You need a custom domain setup by MOTIONTAG (e.g. my-tenant-key.motion-tag.de) and an account to sign in. Contact MOTIONTAG Support to request a custom domain setup and an account. If you already have both, visit your custom domain and sign in to see a customized version of this documentation.

2. Authentication

Every request must be authenticated with a JWT. The JWT must be signed with the shared secret which is available below when signed in and encoded with HS256.

Shared secret
Only accessible when signed in. You need a custom domain setup by MOTIONTAG (e.g. my-tenant-key.motion-tag.de) and an account to sign in. Contact MOTIONTAG Support to request a custom domain setup and an account. If you already have both, visit your custom domain and sign in to see a customized version of this documentation.
Attention
Don't publish the shared secret to your apps! You should create the JWTs for your apps on your backend to minimize the chance of a data breach.

Depending on the endpoint, the JWT must include specific claims such as sub and exp, which are documented in each endpoint's parameter list. Depending on the intended use, a JWT can either be scoped to a single end user, or to all users. Here an example of the claims for a single user.

{
  "iss": "tenant-key",
  "sub": "aaaaaaaa-1111-2222-3333-bbbbbbbbbbbb",
  "exp": 1709020393
}

Unless noted otherwise, the JSON Web Token must be sent as HTTP authorization header with Bearer, like this:

{ "Authorization": "Bearer abc123.def456.gehz7890" }

Code example

Here's a small Ruby and Python code example on how to create the JWT for a single user. Which includes the iss as tenant key, the sub as User ID (UUID) and exp as an expiration timestamp (seconds since epoch) as well as assuming the shared secret is available in the environment variable SHARED_SECRET.

In Ruby using the jwt gem

require 'jwt'

claims = {
  iss: 'tenant-key',
  sub: 'aaaaaaaa-1111-2222-3333-bbbbbbbbbbbb',
  exp: Time.now.to_i + 60,
}
JWT.encode(claims, ENV['SHARED_SECRET'], 'HS256')
# => abc123.def456.gehz7890

In Python using PyJWT package

import os
import time
import jwt

claims = {
  "iss": "tenant-key",
  "sub": "aaaaaaaa-1111-2222-3333-bbbbbbbbbbbb",
  "exp": int(time.time()) + 60
}
jwt.encode(claims, os.getenv("SHARED_SECRET"), algorithm="HS256")
# => abc123.def456.gehz7890

3. User management

3.1 Create user

In order to create a user on our backend the SDK (on the phone) must be configured with a user token at runtime. This enables the SDK to send us events we need to create tracks and stays.

Tokens can be generated on your backend, or manually with the form below (only accessible when signed in). They are signed JWTs. Users are identified by distinct UUIDs – the creation and management of the user UUIDs is up to you. MOTIONTAG creates a user entry in its database when data from the SDK for a new user UUID arrives for the first time. This user UUID is used to identify data transferred back to you.

To generate the JWTs on your backend, encode and sign a payload like the example below with the shared secret, which can be found above in Authentication section.

Claims for SDK token

Claim Type Description Required
iss String Your tenant key
sub String User UUID
exp Integer Expiration time in seconds since epoch

When using the HS256 algorithm the following example claim payload

{
  "iss": "tenant-key",
  "sub": "aaaaaaaa-1111-2222-3333-bbbbbbbbbbbb",
  "exp": 1709020393
}

can be encoded as a valid user token (JWT). A code example on how to do this can be found here. This token can be used to initialize the MOTIONTAG SDK within your iOS or Android app. Now you have to find a way to deliver this token to your own apps. We need this JWT to create an authorization header ourselves in order to send events (e.g. location data) from inside our SDK to our backend.

Attention
Don't publish the shared secret to your apps! You should create the JWTs for your apps on your backend to minimize the chance of a data breach.

Generate valid tokens

In this section you can generate one or multiple valid JWTs to test with your app. Additionally you can compare those generated JWTs with the ones you created and check if you did everything correctly.

Generate JWTs from user UUIDs
Only accessible when signed in. You need a custom domain setup by MOTIONTAG (e.g. my-tenant-key.motion-tag.de) and an account to sign in. Contact MOTIONTAG Support to request a custom domain setup and an account. If you already have both, visit your custom domain and sign in to see a customized version of this documentation.

3.2 Delete user

The following endpoint enqueues a job, which deletes related user data in the background, the complete process can take several hours. The delete user endpoint does not require any request parameters, instead it needs a user specific JWT with the following claims.

Endpoint

DELETE
/json_api/user

Response body

{
  "data": {
    "type": "Delete user",
    "id": "84342a1f-2963-4d78-a8ae-4651b81cb891"
  }
}

On success, you receive a JSON response with a type and id property, id contains the user id. The response HTTP status is 202 accepted.

Claims for authorization header

See the authentication section on how to create an authorization header from the following claims.

Claim Type Description Required
iss String Your tenant key
sub String User UUID
exp Integer Expiration time in seconds since epoch

4. Data management

There are two ways you can retrieve tracks and stays into your backend from MOTIONTAG. Both ways allow you to get multiple items of multiple users with only one authorization token (JWT).

The Synchronization API is like an endless stream of tracks and stays which are chronologically ordered and grouped into pages of reasonable size. New entries are appended continuously. This endpoint is deterministic, which means you can jump back and forth between pages any time and will always retrieve the same data.

The Dump Interface can be accessed programmatically, but allows you only to download data. This is basically the same endpoint you can access when downloading dumps from the dashboard. In order to download dumps they need to be created manually first. Luckily dumps can be scheduled by MOTIONTAG before execution, what allows e.g. for weekly dump creations for the next six months.

4.1 Synchronization API

This is an endpoint for full synchronization of the complete storyline of all users. Meant to be implemented by customers that run their own backend service and want to maintain a full copy of all their user's storyline on their backend service.

An initial request without the special page parameter will return the first available page, and include a link to the next page. When the end is reached, an empty page without a next link is returned. In this case the consumer should pause for a short time and then retry to load the same page again.

In case you are wondering, possible values for modes and purpose depend on the server configuration and can be looked up in the admin dashboard here.

The geometry attribute is optional. It depends on your tenant configuration whether your are able to receive it from the Synchronization API.

Advantages
  • Retrieve tracks and stays for multiple users and days at once
  • You only need one JWT to authenticate
  • It is deterministic, so every time you send the same request you get the same response
  • This also means you can go back and forth in time
  • You handle data edits directly on your side, between your app and your backend
Disadvantages
  • You can't retrieve any edits on tracks and stays
  • There is only tracks and stays data available via this API

Endpoint

GET
/sync/storyline

Claims for authorization header

See the authentication section on how to create an authorization header from the following claims.

Claim Type Description Required
sub String Fixed value for this endpoint: sync
exp Integer Expiration time in seconds since epoch, recommended to set to near future

Query parameters

Name Type Description
page[after] String Paging position, used by the server to determine next page

Initial request example

Do not include page parameter on the initial request.

GET
/sync/storyline
{
  "data": [
    {
      "type" : "Track",
      "id" : "8861e1e2-abd0-41cc-9b3d-f8c2d2613cf4",
      "attributes" : {
        "user_id" : "d28907c7-0846-461f-950e-9d40f2ca418c",
        "created_at" : "2018-01-01T16:00:00Z",
        "started_at" : "2018-01-01T14:00:00Z",
        "started_at_timezone" : "Europe/Zurich",
        "finished_at" : "2018-01-01T15:00:00Z",
        "finished_at_timezone" : "Europe/Zurich",
        "geometry" : {
           "type" : "LineString",
           "coordinates" : [
              [
                 1,
                 1.5
              ],
              [
                 1.5,
                 2
              ]
           ]
        },
        "track_mode" : "bicycle",
        "track_length_in_meters" : 5000
      }
    },
    {
       "type" : "Stay",
       "id" : "c862b09b-2786-4be3-b0d6-90997783a343",
       "attributes" : {
         "user_id" : "437b0a94-d495-4e4e-96fb-fe1783b5c95e",
         "created_at" : "2018-01-01T16:00:00Z",
         "started_at" : "2018-01-01T14:00:00Z",
         "started_at_timezone" : "Europe/Zurich",
         "finished_at" : "2018-01-01T15:00:00Z",
         "finished_at_timezone" : "Europe/Zurich",
         "geometry" : {
            "type" : "Point",
            "coordinates" : [
               1,
               1.5
            ]
         },
         "stay_purpose" : "study"
       }
    }
  ],
  "links": {
    "next" : "https://api.motion-tag.de/sync/storyline?page[after]=abc123"
  }
}


The consumer is expected to store the received entries and immediately afterwards request the next page given in links.next. The value of page[after] has no meaning beyond being used by the server to determine the next page.

Subsequent request example

In this example, no data is returned. In the case of no data, the consumer should pause for a short time and then request the same URL again.

GET
/sync/storyline?page[after]=abc123
{
  "data": []
}

4.2 Dump interface

Data dumps available on the admin dashboard interface under "Dumps" can be downloaded from this endpoint by using their filename. The filename is chosen automatically on creation and consists of the dump type, its user and date criteria, and the format. Note that the API currently only provides the ability to download dumps, not to create or delete them.

Advantages
  • Retrieve tracks and stays for multiple users and days at once
  • There is also other data, e.g. user statistics or trips and journeys available
  • It is even possible to get a custom dump design
  • You only need one JWT to authenticate
  • You can retrieve edits on tracks and stays
Disadvantages
  • It is not deterministic, so results might change over time regarding tracks and stays
  • Dumps can't be created programmatically and need to be scheduled ahead of time

Endpoint

GET
/dumps/{filename}

Claims for authorization header

See the authentication section on how to create an authorization header from the following claims.

Claim Type Description Required
sub String Fixed value for this endpoint: dumps
exp Integer Expiration time in seconds since epoch, recommended to set to near future

Path parameters

Name Type Description
filename String Filename of the dump

Query parameters

As an alternative authentication method you can use the JWT as URL query parameter instead of the HTTP request header.

Name Type Description Required
jwt String The generated JWT

Example request

GET
/dumps/ExampleDump.SomeProject.2020-01-01--2020-01-31.csv.gz

Example with alternative authorization

GET
/dumps/ExampleDump.SomeProject.2020-01-01--2020-01-31.csv.gz?jwt=abc123.def456.gehz7890

4.3 Stream API

This is an endpoint for full synchronization of the complete storyline of all users, including user edits. Meant to be implemented by customers that run their own backend service and want to maintain a full copy of all their users' storyline on their backend service.

An initial request without the special time parameter will return the results of the first time slice, and include a link to the next time slice. A time slice contains all objects that were created or updated within that time slice. Individual time slices can contain no results, but will still provide a link to the next time slice. The consumer should ensure that the requests are paused for a while when the next link reaches the current time and no results are returned.

In case you are wondering, possible values for modes and purposes depend on the server configuration and can be looked up in the admin dashboard here.

Advantages
  • Retrieve tracks and stays for multiple users and days at once
  • The consumer is always up to date with the latest user data and edits
  • You only need one JWT to authenticate
Disadvantages
  • The consumer needs to ensure that data known already on the consumer side is updated appropriately based on the object id

Endpoint

GET
/stream/storyline

Claims for authorization header

See the authentication section on how to create an authorization header from the following claims.

Claim Type Description Required
sub String Fixed value for this endpoint: stream
exp Integer Expiration time in seconds since epoch, recommended to set to near future

Query parameters

Name Type Description
time[after] Timestamp without time zone In ISO8601 format. Fractions of seconds are ignored. Starting position from which on user data and edits are fetched

Initial request example

Do not include the time parameter on the initial request.

GET
/stream/storyline
{
  "data": [
    {
      "id": "8b6d1f66-276f-45ff-9c25-622298043887",
      "user_id": "1263c31f-e0de-4154-b938-0740506c4076",
      "updated_at": "2024-08-22T12:01:00Z",
      "started_at": "2024-08-22T11:00:00Z",
      "started_at_timezone": "Europe/Vienna",
      "finished_at": "2024-08-22T11:10:00Z",
      "finished_at_timezone": "Europe/Berlin",
      "misdetected_completely": false,
      "geometry": {
        "type": "LineString",
        "coordinates": [
          [1.0, 1.5],
          [1.5, 2.0]
        ]
      },
      "created_at": "2024-08-22T11:20:00Z",
      "type": "Track",
      "length": 5000,
      "mode": "train",
      "detected_mode": "regional_train",
      "merged_into_id": null,
      "merge_count": 0,
      "public_transport_information": {
        "detected_departure_station_id": "16",
        "detected_departure_station_name": "Wien HBF",
        "detected_departure_station_location": {
          "type": "Point",
          "coordinates": [7.0, 8.0]
        },
        "detected_arrival_station_id": "17",
        "detected_arrival_station_name": "Berlin HBF",
        "detected_arrival_station_location": {
          "type": "Point",
          "coordinates": [7.0, 9.0]
        },
        "detected_line_number": "REX123",
        "detected_operator": "OEBB",
        "detected_route_id": "123",
        "detected_resource": "gtfs"
      }
    },
    {
      "id": "5fa64219-cb4b-478e-80a8-0122cc4e9890",
      "user_id": "56192c4a-682e-4701-ba2a-2ba30c72e696",
      "updated_at": "2024-08-22T12:01:10Z",
      "started_at": "2024-08-22T11:11:00Z",
      "started_at_timezone": "Europe/Berlin",
      "finished_at": "2024-08-22T11:21:00Z",
      "finished_at_timezone": "Europe/Berlin",
      "misdetected_completely": false,
      "geometry": {
        "type": "Point",
        "coordinates": [1.0, 1.5]
      },
      "created_at": "2024-08-22T11:30:00Z",
      "type": "Stay",
      "purpose": "study",
      "detected_purpose": "leisure",
      "point_of_interest_information": {
        "detected_poi_name": "Library"
      }
    }
  ],
  "links": {
    "next": "http://www.example.com/stream/storyline?time%5Bafter%5D=2024-08-01+12%3A15%3A17+UTC"
  }
}

Attributes

Merges can happen by the user between subsequent tracks. The later track B is always merged into the earlier track A. Such an operation increases the merge_count of track A by 1, and track B references track A in its merged_into_id attribute. Track A does not back-reference track B. Track A will have the combined geometry and timestamps of both tracks A and B. This means that items that have set a merged_into_id contain duplicated spatio-temporal information. Such and misdetected_completely items are not part anymore of the current state of the storyline, so it is likely that in an analysis, such items should be filtered out and only the attributes not prefixed with detected_ should be used. detected_* attributes never change. This means that if a e.g. a track was detected as public transport, but was corrected to car, it will still have the public transport attributes that were originally detected.

  • merge_count: How many tracks have been merged into the track.
  • merged_into_id: The id of the track that this track has been merged with.
  • misdetected_completely: Whether the user deleted an item because it was wrongly detected.

Paid attributes

Depending on which paid features are enabled on your tenant, some of the attributes may or may not be present in the response. Note that even if a feature is enabled, an item might not contain the attribute if no attribute was found. These are:

  • Geometries
  • Public transport information
  • Point of interest information

The consumer is expected to store the received entries and immediately afterwards request the next page given in links.next. The value of time[after] has refers to the start of the next time slice for which data can be fetched.

Syncing objects
In the example above, both items have user edits. That means that both objects might have appeared in the stream at an earlier time. An object can appear as often in the stream as there are updates to it. This means that the consumer needs to always replace the object on their side based on the object id, in database language, perform an UPSERT.

Subsequent request example

In this example, the consumer reached the end of the stream and no data is returned. Assuming the current time is 2024-08-22T12:00:00Z, and the consumer sends a request at that time, the response will be empty and contain a link pointing to a timestamp that is close to the request time. In this case, the request should be paused for a short time and then the same URL should be requested again. Note that the returned timestamp in the link is URL-encoded and needs to be decoded before being used.

GET
/stream/storyline?time[after]=2024-08-22T12:00:00Z
{
  "data": [],
  "links":{
    "next":"http://www.example.com/stream/storyline?time%5Bafter%5D=2024-08-01+12%3A00%3A01+UTC"
  }
}

5. Interactive API

If you plan to integrate our SDK and directly connect your app with our backend you are in the correct place here. The Interactive API allows the user to directly fetch (GET) and edit (PATCH) tracks and stays. This means your backend is only needed to create the JWTs for your users in order to directly identify the user on our backend side. You still need to maintain the user UUID and the JWT, but every interaction is completely decoupled from your backend. In order to download tracks or stays with their respective edits to your backend you would need to use the Dump Interface.

Advantages
  • Your users can directly edit tracks and stays
  • Your app talks directly to our backend and handles track and stay data accordingly
  • Your backend doesn't need to save any tracks and stays
  • Your backend only has to deal with user management, but avoids all the internet traffic and data persistence around tracks and stays
Disadvantages
  • You need for each user a separate JWT to authenticate and fetch data, which you anyways have to create in order to create a user for the SDK
  • There is only tracks and stays data available via this API

5.1 Storyline GET

This endpoint allows to retrieve a part of the storyline of a single user. It is meant for interactive use, such as displaying a list of tracks and stays on a map in a mobile app.

Endpoint

GET
/storyline/{date}

Claims for authorization header

See the authentication section on how to create an authorization header from the following claims.

Claim Type Description Required
sub String UUID of a user
aud String Fixed value for this endpoint: read
exp Integer Expiration time in seconds since epoch

Path parameters

Name Type Description Required
date Date Single date, e.g. "2020-01-01"

Query parameters

Name Type Description Required
fields[Track] String Comma separated list of fields to include in Track
fields[Stay] String Comma separated list of fields to include in Stay

The fields parameters allow you to limit the list of fields being returned to a sparse fieldset. In particular the geometry field of Track can become quite large and it makes sense to exclude it when not needed.

Tracks and stays are filtered to have started on the given date, in the local timezone they occurred in.

Response body

A list of storyline items. The list is ordered chronologically.

  • type
    Can be Track or Stay. Track has mode attributes, Stay has purpose attributes.
  • started_at
    Is in the local timezone it occured in and includes the UTC offset.
  • finished_at
    Is in the local timezone it occured in and includes the UTC offset.
  • length
    Is only present for Storyline items of type Track and is in meters.
  • mode_key
    Is the untranslated key of the mode, only present if the type is Track. This attribute can potentially be edited by the user.
  • mode_name
    Is the translated name of the mode, only present if the type is Track. See below for more information about the languages.
  • purpose_key
    Is the untranslated key of the purpose, only present if the type is Stay. This attribute can potentially be edited by the user.
  • purpose_name
    Is the translated name of the purpose, only present if the type is Stay. See below for more information about the languages.
  • detected_mode_key
    Is the untranslated key of the detected mode, only present if the type is Track.
  • detected_mode_name
    Is the translated name of the detected mode, only present if the type is Track. See below for more information about the languages.
  • detected_purpose_key
    Is the untranslated key of the detected purpose, only present if the type is Stay.
  • detected_purpose_name
    Is the translated name of the detected purpose, only present if the type is Stay. See below for more information about the languages.
  • trip_id
    In case trip generation is enabled for your tenant, this attribute will present the uuid of trip object. Can be empty.
  • trip_purpose_key
    In case trip generation is enabled for your tenant, this attribute will present the purpose of the trailing stay of the trip and is the untranslated key of the purpose. Can be empty.
  • misdetected_completely
    Is a flag if a storyline item was misdetected completely and should be therefore not being displayed once set to true. The default is false.
  • geometry
    geometry of a Track is a GeoJSON feature of type LineString or MultiLineString. geometry of a Stay is a GeoJSON feature of type Point.

The possible values for modes and purpose depend on the server configuration and can be looked up in the admin dashboard here.

The language of translated fields (currently mode_name and purpose_name) is determined by the given Accept-Language header, or the server's default. The available languages depend on the settings of the server.

Example request

GET
/storyline/2020-01-01
{
  "data": [
    {
      "id": "4db59ee6-92ae-4e96-b5ec-eb192ee175cc",
      "type": "Track",
      "attributes": {
        "started_at": "2020-11-01T11:00:00+01:00",
        "finished_at": "2020-11-01T13:00:00-05:00",
        "length": 5000,
        "mode_key": "light_rail",
        "mode_name": "Rapid transit railway",
        "detected_mode_key": "train",
        "detected_mode_name": "Train",
        "misdetected_completely": false,
        "geometry": {
          "type": "LineString",
          "coordinates": [[2.0, 3.0], [3.0, 4.0]]
        }
      }
    },
    {
      "id": "26966953-6a54-4f0f-886c-e2e0074b3005",
      "type": "Stay",
      "attributes": {
        "started_at": "2020-11-01T13:00:00-05:00",
        "finished_at": "2020-11-02T01:00:00-05:00",
        "purpose_key": "home",
        "purpose_name": "At home",
        "detected_purpose_key": "home",
        "detected_purpose_name": "At home",
        "misdetected_completely": true,
        "geometry": {
          "type": "Point",
          "coordinates": [3.0, 4.0]
        }
      }
    }
  ]
}

This example response contains two storyline items, one Track and one Stay.

Example request with limited fields

GET
/storyline/2020-01-01?fields[Track]=started_at,finished_at,mode_name&fields[Stay]=started_at,finished_at,purpose_name
{
  "data": [
    {
      "id": "4db59ee6-92ae-4e96-b5ec-eb192ee175cc",
      "type": "Track",
      "attributes": {
        "started_at": "2020-11-01T11:00:00+01:00",
        "finished_at": "2020-11-01T13:00:00-05:00",
        "mode_name": "Rapid transit railway"
      }
    },
    {
      "id": "26966953-6a54-4f0f-886c-e2e0074b3005",
      "type": "Stay",
      "attributes": {
        "started_at": "2020-11-01T13:00:00-05:00",
        "finished_at": "2020-11-02T01:00:00-05:00",
        "purpose_name": "At home"
      }
    }
  ]
}

5.2 Storyline PATCH

This endpoint allows to update a certain attribute of the storyline of a single user. For a storyline item of type Track you can edit the attribute mode. For a storyline item of type Stay you can edit the attribute purpose. For both types Track and Stay you can edit the attribute misdetected_completely, which indicates that this item should no longer be displayed. Note that you can only edit a single attribute per request. Currently the only supported operation in the meta object of the JSON body is edit.

Endpoint

PATCH
/storyline/{id}

Claims for authorization header

See the authentication section on how to create an authorization header from the following claims.

Claim Type Description Required
sub String UUID of a user
aud String Fixed value for this endpoint: read
exp Integer Expiration time in seconds since epoch

Path parameters

Name Type Description Required
id UUID ID of a storyline item, e.g. 26966953-6a54-4f0f-886c-e2e0074b3005

Editable attributes in body

Name Type Description
mode String Mode key of a storyline item of type Track, e.g. "car"
purpose String Purpose key of a storyline item of type Stay, e.g. "work"
misdetected_completely Boolean Whether the storyline item should be displayed, e.g. true

Example request

PATCH
/storyline/26966953-6a54-4f0f-886c-e2e0074b3005
with a JSON body like this, to update the purpose of a stay

{
  "data": {
    "id": "26966953-6a54-4f0f-886c-e2e0074b3005",
    "type": "Stay",
    "attributes": {
      "purpose": "sport"
    }
  },
  "meta": {
    "operation": "edit"
  }
}

Returns 204 No content if the operation was successful.

5.3 Calendar

This endpoint allows you to retrieve one element for each day with a count of storyline items since the activation of the user for the requested year.

GET
/calendar/{year}

Claims for authorization header

See the authentication section on how to create an authorization header from the following claims.

Claim Type Description Required
sub String UUID of a user
aud String Fixed value for this endpoint: read
exp Integer Expiration time in seconds since epoch

Path parameters

Name Type Description Required
year Integer Single year, e.g. "2020"

Response body

A chronologically sorted list of dates the user was active in, with storyline counts.

Example request

GET
/calendar/2020
{
  "data": {
    "id": "7015915d-cb3c-417e-a855-6091bf3f9d14/2020",
    "type": "Calendar",
    "attributes": {
      "days": [
        {
          "date": "2020-12-29",
          "storyline_count": 0
        },
        {
          "date": "2020-12-30",
          "storyline_count": 2
        },
        {
          "date": "2020-12-31",
          "storyline_count": 1
        }
      ]
    }
  }
}

This example response contains always just one entry. It shows all days of the year with a count of storyline items per day since the activation of the user (in this example case the user was activated on '2020-12-29').

5.4 Modes

This endpoint allows you to retrieve all enabled modes of your tenant.

Endpoint

GET
/modes

Claims for authorization header

See the authentication section on how to create an authorization header from the following claims.

Claim Type Description Required
sub String UUID of a user
aud String Fixed value for this endpoint: read
exp Integer Expiration time in seconds since epoch

Response body

An alphabetically sorted list of modes. The id property contains the provided user ID of the JWT token sub claim.

Example request

GET
/modes
{
  "data": {
    "id": "7015915d-cb3c-417e-a855-6091bf3f9d14/2020",
    "type": "Modes",
    "attributes": [
      {
        "id": "Mode::Airplane",
        "key": "airplane",
        "name": "Airplane",
        "color": "#e9b100",
        "darker_color": "#ba8e00",
        "icon": "/icons/modes/airplane.png",
        "icon_white_3x": "/icons/modes/airplane_white_3x.png",
        "icon_svg": "/icons/modes/airplane.svg",
        "carbon_emission_per_kilometer": 196,
        "public_transport": false
      },
      {..},
    ]
  }
}

On success, you receive a JSON response with a type and id property, id contains the user id. The response HTTP status is 200 OK.

5.5 Purposes

This endpoint allows you to retrieve all enabled purposes of your tenant.

Endpoint

GET
/purposes

Claims for authorization header

See the authentication section on how to create an authorization header from the following claims.

Claim Type Description Required
sub String UUID of a user
aud String Fixed value for this endpoint: read
exp Integer Expiration time in seconds since epoch

Response body

An alphabetically sorted list of purpose. The id property contains the provided user ID of the JWT token sub claim.

Example request

GET
/purposes
{
  "data": {
    "id": "7015915d-cb3c-417e-a855-6091bf3f9d14/2020",
    "type": "Purposes",
    "attributes": [
      {
        "id": "home",
        "key": "home",
        "name": "At home",
        "color": "#4f4f4f",
        "darker_color": "#3f3f3f",
        "icon_white_3x": "/icons/purposes/home_white_3x.png",
        "icon_svg": "/icons/purposes/home.svg",
        "icon_marker_png": "/icons/purposes/home_marker.png"
      },
      {..},
    ]
  }
}

On success, you receive a JSON response with a type and id property, id contains the user id. The response HTTP status is 200 OK.

5.6 Statistics

The response contains statistics for count, length and duration within a given date range for the current user. Additionally name translations, the days count and the carbon_emission_per_kilometer are included.

Endpoint

GET
/statistics/{date_range}

Claims for authorization header

See the authentication section on how to create an authorization header from the following claims.

Claim Type Description Required
sub String UUID of a user
aud String Fixed value for this endpoint: read
exp Integer Expiration time in seconds since epoch

Path parameters

Name Type Description Required
date_range Date Range e.g. "2020-01-01--2020-01-31"

Response body

A list of different statistics for the given date range.

  • count
    number of tracks, grouped by mode key
  • length
    travelled distance in meters, grouped by mode key
  • duration
    duration as number of minutes, grouped by mode key
  • days
    total number of days with tracks
  • name
    translation of the mode key to the user's language
  • carbon_emission_per_kilometer
    carbon emission value in grams per kilometer and mode key

Example request

GET
/statistics/2024-01-01--2024-01-31
{
  "data": [
    {
      "id": "2024-01-01--2024-01-31/user",
      "type": "statistics",
      "attributes": {
        "count": {
          "bicycle": 22,
          "car": 3,
          "...<count for each mode>..."
        },
        "length": {
          "bicycle": 53.25,
          "car": 32.44,
          "..."
        },
        "duration": {
          "bicycle": 421,
          "car": 222,
          "..."
        },
        "name": {
          "bicycle": "Fahrrad",
          "car": "Auto",
          "..."
        },
        "carbon_emission_per_kilometer": {
          "bicycle": 0,
          "car": 139,
          "..."
        },
        "days": 12
      }
    }
  ]
}
Info
  • Both dates of the range are required
  • To get statistics for a single day, request a date range that starts and ends on the same day
  • The part after the slash in the id, e.g. in 2016-01-01--2016-01-31/user, contains the scope for a user.