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.
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
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
orStay
.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 isfalse
. - geometry
-
geometry
of aTrack
is a GeoJSON feature oftype
LineString
orMultiLineString
.geometry
of aStay
is a GeoJSON feature oftype
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
{ "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