Motiontag API

Overview

The MOTIONTAG backend API provides endpoints meant to be consumed by other backends, for example to download analysis results in bulk. In order to access it, you need to be given access by MOTIONTAG staff. As part of that you will be provided with a dedicated domain, which also serves as the base URL for the API.

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.

The API tries to follow the JSON API standard where applicable.

As part of evolving the API endpoints described here, additional attributes may be added later. Clients should take this into account and not break when new attributes are added.

Authentication with JWT

Every request must be authenticated with a JWT (see JSON Web Token for general information about JWTs). The JWT must be signed with the shared secret which is available when signed in. 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.

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

Authorization: Bearer abc123.def456.gehz7890
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.

Example code

In Ruby using the jwt gem, assuming the shared secret is available in the environment variable SHARED_SECRET, and sub is foo:

require "jwt"
JWT.encode({ "sub" => "foo", "exp" => Time.now.to_i + 60 }, ENV["SHARED_SECRET"], "HS256")
# => abc123.def456.gehz7890

Delete user

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. Also see the SDK user authentication below.

To delete an existing user:

DELETE
/json_api/user

JWT parameters

Name Type In Description
iss String JWT The tenant key that you have received from MOTIONTAG
sub String JWT the user UUID
exp Integer JWT Optional. Expiration time in seconds since epoch

Example request

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.

SDK user authentication

The SDK must be configured at runtime with a user-specific token.

Tokens can be generated on your backend. They are signed JWTs (see jwt.io). 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.

To generate the JWTs on your backend, encode and sign a payload like the example below with the shared secret, which is accessible in the admin dashboard under "Authentication tokens".

Example payload:

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

Interactive API

Storyline GET API

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.

GET
/storyline/{date}

Request parameters

Name Type In Description
sub String JWT UUID of a user
aud String JWT Fixed value for this endpoint: read
exp Integer JWT Optional. Expiration time in seconds since epoch
date Date Path Single date, e.g. "2020-01-01"
fields[Track] String, comma separated Query Optional. List of fields to include in Track
fields[Stay] String, comma separated Query Optional. 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 occured 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.
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 mode and purpose depend on the server configuration and can be looked up in the admin dashboard.

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"
      }
    }
  ]
}

Storyline PATCH API

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.

PATCH
/storyline/{id}

Request parameters

Name Type In Description
sub String JWT UUID of a user
aud String JWT Fixed value for this endpoint: read
exp Integer JWT Optional. Expiration time in seconds since epoch
id UUID Path ID of a storyline item, e.g. "26966953-6a54-4f0f-886c-e2e0074b3005"
mode String Body Mode key of a storyline item of type Track, e.g. "car"
purpose String Body Purpose key of a storyline item of type Stay, e.g. "work"
misdetected_completely Boolean Body 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.

Calendar API

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}

Request parameters

Name Type In Description
sub String JWT UUID of a user
aud String JWT Fixed value for this endpoint: read
exp Integer JWT Optional. Expiration time in seconds since epoch
year Integer Path 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').

Modes API

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

GET
/modes

Request parameters

Name Type In Description
sub String JWT UUID of a user
aud String JWT Fixed value for this endpoint: read
exp Integer JWT Optional. 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.

Purposes API

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

GET
/purposes

Request parameters

Name Type In Description
sub String JWT UUID of a user
aud String JWT Fixed value for this endpoint: read
exp Integer JWT Optional. 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.

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.

The storyline is an endless stream of chronologically sorted entries. New entries are appended continuously by the Modedetection. This endpoint implements a mechanism to retrieve the complete stream, grouped into pages of reasonable size, by adding a link to the next page of data to a response that contains entries. 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.

GET
/sync/storyline

Request parameters

Name Type In Description
sub String JWT Fixed value for this endpoint: sync
exp Integer JWT Mandatory. Expiration time in seconds since epoch, recommended to set to near future
page[after] String Query Paging position, used by the server to determine next page

Example initial request

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.

Example subsequent request

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": []
}

Dump downloads

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.

GET
/dumps/{filename}

Request parameters

Name Type In Description
sub String JWT Fixed value for this endpoint: dumps
exp Integer JWT Mandatory. Expiration time, recommended to set to near future
filename String Path Filename of the dump

Example request

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

Alternative authorization

Alternatively to using an HTTP header, the dump downloads can also be authenticated with a URL parameter.

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