API Reference

Overview

The Mission Control LINK API has multiple key elements which enable users to easily and quickly connect_handler their robot to the web through a seamless ROS interface and begin logging/viewing data while controlling it securely and remotely.

Key elements of the API:

API Mentality

Table of Contents

Security

The system uses revocable tokens for security control with account, device and action level permissions.

SEE TOKEN DETAILS HERE

Each query must be:

Secrets are only returned when a new token is posted and can not be retrieved afterwards so please store them in a safe place.

If the secret is incorrect or the token does not have rights to access the query, or the specified account / device, it will return a 401 Unauthorized or 403 Forbidden.

API Base URL

To access the API, please use this base URL:

https://api.freedomrobotics.ai/

You will need to add mc_secret and mc_token to the headers (preferred) or as url params.

This means your calls will look something like this if you are using headers:

https://api.freedomrobotics.ai/accounts/MY_ACCOUNT/devices/MY_DEVICE
    headers: 
      mc_token=MY_TOKEN
      mc_secret=MY_SECRET

or

https://api.freedomrobotics.ai/accounts/MY_ACCOUNT/devices/MY_DEVICE?mc_token=MY_TOKEN&mc_secret=MY_SECRET

If you are using url params.

HTTP Status Codes

Code Error Name Description
200 OK Everything works as expected.
202 Created Instance created successfully.
400 Malformatted Request is malformatted, often due to missing parameter.
401 Unauthorized Token does not have rights to the query.
403 Forbidden Token has access to the query but not the specified account.
404 Not Found Requested instance does not exist.
409 Conflict Instance already exists.
500 Internal Server Error Please contact us immediately if you encounter this.
Standard Error Body
{
  "stackTrace": [ "Stack trace to help in debugging"],
  "errorType": "ERROR-NAME",
  "errorMessage": "[ERROR-NAME] MESSAGE: ERROR-MESSAGE HERE"
}

API Constraints

Ordering and Pagination

Ordering and Pagination are intended especially for the endpoints that return a large amount of items, and so we introduced it for some of the list GET endpoints that return application/json content:

Ordering is available via two query params:

Example:

{
    "order_keys": ["created", "name"],
    "order_directions": ["desc", "asc"]
}

Notes: If just one key is used for ordering, instead of sending a list with just one key for order_keys and just one sort direction for order_directions, you can send just the key and the direction, without the need of a list.

Pagination is available via two query params:

Example:

{
    "page_size": 10,
    "page": 3
}

Notes:

API Queries

Below are a full list of all available queries.

/ GET

You can use this to verify that you have connectivity with the API.

Returns JSON body:

{
  "message": "Please see the docs for how to use the API."
}

/accounts GET

Returns the list of all accounts and their attributes. This is for support and not used during standard usage by accounts. The endpoint supports ordering and pagination

See more info in the Account Specification

/accounts POST

Creates a new account and defines the primary email for the account. It will automatically create the 'account' and 'created' attributes which are read-only. You may create any other keys (Max 1k size) and values (Max 300k size) of data for your account. This is not meant as a data store, but instead to keep attributes about the account.

Example payload:

of attributes for the new account must include a minimum of the email for the primary access to the account.

{
  "email": "frankie@freedom.com",
  "description": "Our internal dev account for robot team 2."
  ...
}

Returns JSON body with the new account ID in it like this (Defaults may be different):

{
  "status": "success",
  "action": "create new account",
  "account": "AXXXXXXXXXXXXXXXXXXXXXXXXXXX",
}

Resulting initial attributes for account:

{
  "account": "AXXXXXXXXXXXXXXXXXXXXXXXXXXX",
  "created": "2017-12-12 23:55:13",
  "mc.account_plan": {
      "max_devices": 5,
      "max_device_data_retention_days": 5,
      "max_device_data_bandwidth_kbps": 5000      
  },
  ... // Below added from body of POST request
  "email": "frankie@freedom.com",
  "description": "Our internal dev account for robot team 2."
}

Account IDs are long and sparse keys which are unique within each account and start with "A".

DEPRECATED: You can still use /accounts/{account} to force a name, but this is deprecated and will be removed in the future.

See more info in the Account Specification

/accounts/{account} PUT

Adds or updates attributes in a JSON form as key / value pairs of a dictionary for the specified account. You may create any keys (max 1k size) and values (max 300k size) of data for your account. This is not meant as a data store, but instead to keep attributes about the account.

Example payload:
{
  "company": "SpaceX",
  "location": {
    "country": "United States",
    "state": "CA",
    "city": "Hawthorne"
  },
  "other att": null       // Deletes "other att"
}
Details:

See more info in the Account Specification

/accounts/{account} GET

Returns the JSON dictionary of keys and values for the account including 'account' and 'created' plus any specified ones.

See more info in the Account Specification

/accounts/{account} DELETE

Deletes the account and any belonging devices if they exist.

See more info in the Account Specification

Warning: This deletes all data which has been recorded and can not be undone.

/accounts/{account}/alerts?utc_start=-1d&utc_end=now&type=alert_type&name=alert_name&level=alert_level GET

Returns alerts that meet the specified criteria for any of the account's devices. This criteria may include a range for the creation and cleared timestamps, and a time range during which the alerts have been active.

The endpoint supports ordering and pagination

Query strings:

Note: Time filters (utc_start, utc_end, utc_active_start, utc_active_end, utc_clear_start, utc_clear_end) can be expressed as epoch timestamps or as a descriptive strings, ex: -7d, -24h, -10m etc.

/accounts/{account}/alerts/csv?utc_start=-1d&utc_end=now&type=alert_type&name=alert_name&level=alert_level GET

Same as previous endpoint, with the difference that the result will be a redirect link towards a csv file downloadable for 5 minutes, and the extra device data requested via settings query will be reported in the csv, as extra columns.

Default csv columns are: time, zones, device_id, device_name, alert_name, alert_level, alert_replay_url, alert_description, elapsed_time.

/accounts/{account}/alerts/summary?group_by=attribute_name1&count_on=attribute_name2... GET

This is yet another version of the alerts get endpoint, that will return only the summary of the alerts that match the specified filters. It supports the same filters and search options, and has an extra mandatory query param, group_by, which is used to specify the attribute name by which to group, and an optional one count_on, which is used to specify the attribute name on which to count the number of alerts for each group.

Summary will contain details about how many alerts where encountered per level, for each value based on which alerts were grouped the group. Example (grouped by name, count on level):

{
    "total": {
        "info": 14,
        "warning": 4,
        "error": 3
    },
    "Obstacle in the way": {
        "info": 12,
        "warning": 2,
        "error": 0
    },
    "Battery Low": {
        "info": 2,
        "warning": 2,
        "error": 1
    },
    "Device Disconnected": {
        "info": 0,
        "warning": 0,
        "error": 2
    }
}

Suggested attributes for group_by and count_on:

If not specified, count_on will be by default level

Beside the above attributes, group_by support special words that will return the summary for active alerts:

/accounts/{account}/alerts/{alert}?attributes=... GET

This endpoint will return details of a specific alert. By default, all fields except topics_snapshot are returned. You can use attributes query param to specify which attributes to be returned. If you need device data from the alert creation time to be included, you have to specify the topics_snapshot in the attributes list (extra permissions are required in this case - device data get).

/accounts/{account}/automation-projects GET

This endpoint returns the list of all created automation projects for the specified account.

The endpoint supports ordering and pagination

Note: These automation projects are part of the OFL (Open Fleet Language) project.

/accounts/{account}/automation-projects/{automation_project} GET

This endpoint returns the requested automation project by id

Response Example:

{
    "id": "AUP01B94497CD75AA53D4EF2C18",
    "name": "Line L3 - Intersection I2",
    "description": "This Automation project will handle all automation rules for intersection I2 from assembly line L3",
    "enabled": true,
    "context_variables": [
        {
            "id": "idgreenx2n4ndy5k4g",
            "name": "Green Time",
            "value": 200
        },
        {
            "id": "idminwaitd534jd65j68d",
            "name": "Minimal wait",
            "value": 30
        }
    ],
    "account_id": "A2D322BD4DF1635500332",
    "created": 1635500332,
    "updated": 1635500332
}

Note: These automations are part of the OFL (Open Fleet Language) project.

/accounts/{account}/automation-projects POST

Creates a new automation project for the specified account.

Mandatory fields: * name - The name of the automation project * context_variables - An array of dictionaries describing the variables (can be an empty array), each having next 3 attributes defined: id, name and value. The defined variables will be available to all automations from this project.

Optional fields: * description - A detailed description of the automation project * enabled - a boolean flag, by default true; Setting it flag to false, will make all automations from inside the project to be considered as disabled, regardless of their own enabled flag.

Read-only fields (generates/updates by the API): * id - The unique identifier for the automation. * account_id - The account id (taken from the url path). * created - The timestamp when the automation was created. * update - The timestamp when the automation was last updated.

Example payload:

{
    "name": "Line L3 - Intersection I2",
    "description": "This Automation project will handle all automation rules for intersection I2 from assembly line L3",
    "context_variables": [
        {
            "id": "idgreenx2n4ndy5k4g",
            "name": "Green Time",
            "value": 200
        }
    ]
}

Note: These automation projects are part of the OFL (Open Fleet Language) project.

/accounts/{account}/automation-projects/{automation_project} PUT

Modify attributes of the specified automation project on the url. You can update any attribute of the automation project except the read-only ones.

Payload example:

{
    "context_variables": [
        {
            "id": "idgreenx2n4ndy5k4g",
            "name": "Green Time",
            "value": 200
        },
        {
            "id": "idminwaitd534jd65j68d",
            "name": "Minimal wait",
            "value": 30
        }
    ]
}

Note: These automations are part of the OFL (Open Fleet Language) project.

/accounts/{account}/automation-projects/{automation_project} DELETE

Deletes the specified automation project on the url and all its associated automations.

Note: These automations are part of the OFL (Open Fleet Language) project.

/accounts/{account}/automations GET

This endpoint returns the list of all the created automations for the specified account.

The endpoint supports ordering and pagination

Note: These automations are part of the OFL (Open Fleet Language) project.

/accounts/{account}/automation-projects/automations GET

This endpoint is similar to the previous one and returns the list of automations for the specified account that are in the specified automation project.

Note: These automations are part of the OFL (Open Fleet Language) project.

/accounts/{account}/automations/{automation} GET

This endpoint returns the requested automation by id

Fields added by the API:

Fields from POST:

All the logic behind lookups, conditions, trigger_logic and task is handled by the Automation Engine. The API only supports the creation and listing of the automations.

Note: These automations are part of the OFL (Open Fleet Language) project.

/accounts/{account}/automations POST

Creates an automation for an account. Check at the Automations GET for the needed attributes

Example payload

{
 "name": "demotest",
 "description": "Test Automation to show as example on API docs",
 "enabled": true,
 "project_id": "AUP01B94497CD75AA53D4EF2C18",
 "lookup_bindings": [
   {
     "id": "f23_is_at_named_location",
     "name": "is in location",
     "device_id": "D0654B0A81E5DB23DDA7F50DE26",
     "fleet_name": "fetch",
     "robot_name": "fetch26",
     "lookup_id": "is_at_named_location"
   },
   {
     "id": "f23_battery_lower_than",
     "name": "battery lower than",
     "device_id": "D0654B0A81E5DB23DDA7F50DE26",
     "fleet_name": "fetch",
     "robot_name": "fetch26",
     "lookup_id": "battery_is_lower_than"
   }
 ],
 "conditions": [
   {
     "id": "f26_in_middle",
     "name": "Fetch26 is in the middle",
     "expression": [
       "f23_battery_lower_than",
       "ff_middle"
     ]
   },
   {
     "id": "f23_low_battery",
     "name": "f23 has low battery",
     "expression": [
       "f23_battery_lower_than",
       2
     ]
   }
 ],
 "trigger_logic": [
   "$",
   "f23_low_battery"
 ],
 "tasks": [
   {
     "id": "TT100001",
     "name": "fetch23 move task name",
     "device_id": "D3C18A79A5DCC55E40DB6C76795",
     "fleet_name": "fetch",
     "robot_name": "fetch23",
     "task_type": "OFL",
     "ofl_task_type": 1,
     "OFL_DETAILS": {
       "x": -19.5,
       "y": 21.8,
       "yaw": 0
     }
   }
 ]
}

Note: These automations are part of the OFL (Open Fleet Language) project.

/accounts/{account}/automations/{automation} PUT

Modifies attributes of the specified automation on the url. The available attributes are the same as the POST ones.

Note: These automations are part of the OFL (Open Fleet Language) project.

/accounts/{account}/automations/{automation} DELETE

Deletes the specified automation on the url.

Note: These automations are part of the OFL (Open Fleet Language) project.

/accounts/{account}/trails?utc_start=-1h&utc_end=now&... GET

This endpoint will return the list of trails created when updating resources that belong to or relates to the specified account.

The endpoint supports ordering and pagination

Example response for a trail created when a user updates its first_name:

[
    {
        "account_id": "A1615205284020316ACCOUNT", 
        "created": 1615219684.0, 
        "id": 42231, 
        "item_model": "User", 
        "item_operation": "update", 
        "item_pk": "user2_603@freedomrobotics.ai", 
        "payload": {
            "previous": {
                "first_name": "Jhonny"
            }, 
            "updated": {
                "first_name": "Jhon"
            }
        }, 
        "rel_item1_model": null, 
        "rel_item1_pk": null, 
        "rel_item2_model": null, 
        "rel_item2_pk": null, 
        "request_id": "427cc5bf-aa08-45a6-bb7f-69afc6935fef", 
        "request_ip": "1.2.3.4,5.6.7.8", 
        "request_method": "PUT", 
        "request_path": "/users/user2_603@freedomrobotics.ai", 
        "request_qp": null, 
        "request_utc_time": 1615219684.2102, 
        "token": "TF4E1961BE08EF5F3B1B720A7", 
        "token_owner": "user2_603@freedomrobotics.ai"
    }
]

Explaining the trail attributes:

Notes: * User creation and deletion is not currently visible at the account level at the time, as when user is created it is not associated to and account yet, and when it is deleted it is already removed from all accounts. The account has visibility only on the UsersGroups/UsersPolicies relation in these scenarios.

Models

Each time an Account/Device/Zone/Policy/ResourceSet/ActionSet/User/Token is created/updated/saved/deleted, or when their relation with other objects change, a trail will be created to record the event.

Here is the complete list of internal models tracked via trails:

and their relational models (also tracked):

Filtering

These trails are intended for tracking who changed an entity, and as their number can grow large, we have multiple ways to filter the trails returned by this endpoint, in order to narrow the number of the results to the relevant ones.

All next filters are expected part of the request query params (?filter1=value1&filter2=value2&...):

Notes:

Examples
Getting activity related to a group:

Request: GET /account/A1615274194872844ACCOUNT/trails?model=group&pk=24 Response:

[
    {
        "account_id": "A1615274194872844ACCOUNT", 
        "created": 1615288999.0, 
        "id": 322, 
        "item_model": "UsersGroups", 
        "item_operation": "delete", 
        "item_pk": "9", 
        "payload": {
            "deleted": {
                "group_id": 24, 
                "id": 9, 
                "user_id": "user603+frtest603@freedomrobotics.ai"
            }
        }, 
        "rel_item1_model": "User", 
        "rel_item1_pk": "user603+frtest603@freedomrobotics.ai", 
        "rel_item2_model": "Group", 
        "rel_item2_pk": "24", 
        "request_id": "4489ad8b-75e2-428e-8c0f-c914a5baed84", 
        "request_method": "DELETE", 
        "request_path": "/users/user603+frtest603@freedomrobotics.ai", 
        "request_utc_time": 1615288595.14557, 
        "token": "T5F1F8DDD0614A32D1107BEBD", 
        "token_owner": "user603+frtest603@freedomrobotics.ai"
    }, 
    {
        "account_id": "A1615274194872844ACCOUNT", 
        "created": 1615288594.0, 
        "id": 318, 
        "item_model": "UsersGroups", 
        "item_operation": "create", 
        "item_pk": "9", 
        "payload": {
            "created": {
                "group_id": 24, 
                "id": 9, 
                "user_id": "user603+frtest603@freedomrobotics.ai"
            }
        }, 
        "rel_item1_model": "User", 
        "rel_item1_pk": "user603+frtest603@freedomrobotics.ai", 
        "rel_item2_model": "Group", 
        "rel_item2_pk": "24", 
        "request_id": "66f12833-dfca-4cea-9d3e-1f331dd2e297", 
        "request_method": "POST", 
        "request_path": "/users/user603+frtest603@freedomrobotics.ai", 
        "request_utc_time": 1615288594.95664, 
        "token": "TD1D8F028BC59C869507516D9", 
        "token_owner": "account_admin@freedomrobotics.ai"
    }
]

/accounts/{account}/uploads?file=filename GET

This endpoint will return an URL (under location attribute) that can be used for a limited amount of time, only to download the specified file. It can not be used for the actual file download, use the returned URL instead. Example response:

{
  "location": "https://data.freedomrobotics.ai/uploaded_data/a/A12345/filename?..."
}

/accounts/{account}/uploads?file=filename PUT

This endpoint is similar to the previous one, but the returned URL can be used only to upload a file.

/accounts/{account}/usage GET

Returns the JSON dictionary describing the account usage in current month or previous month in case it is called in the 1st of the month.

It supports query string detailed=true to include daily details in the response (by default they won't be included).

In case the report for a specific period is needed can use start_date and/or end_date query strings, which are expected in the %Y-%m-%d format.

Current date usage is not included as the day is not finished. For partial current day usage, call this API

Only super admins can use this API.

See more info in the Account Usage report Format section

/accounts/{account}/usage/{year}/{month} GET

Same as account usage API, with the remark that it will return the usage for the specified year and month

/accounts/{account}/usage/{year}/{month}/{day} GET

Same as accounts usage API, with the remark that it will return the usage for the specified year, month and day.

This api can return up to date partial results for current day, but in this case it might perform a bit slower.

/accounts/{account}/zones?[device_meta_data=true] GET

Returns the zones tree hierarchy of an account as a list of dicts.

Any administrator, user or operator of the account can get the zone hierarchy tree.

Example response dict:

[
    {
        "parent_zone_id": null, 
        "description": "Zone 2", 
        "has_subzones": true, 
        "account_id": "A1594285852709364ACCOUNT", 
        "subzones": [
            {
                "parent_zone_id": "Z2", 
                "description": "Zone 2 1", 
                "has_subzones": true, 
                "account_id": "A1594285852709364ACCOUNT", 
                "subzones": [
                    {
                        "parent_zone_id": "Z3", 
                        "description": "Zone 2 1 1", 
                        "has_subzones": false, 
                        "account_id": "A1594285852709364ACCOUNT", 
                        "has_devices": true,
                        "devices": ["D15a97e7331f71b65d07e78"],
                        "id": "Z4", 
                        "name": "Z2-1-1"
                    }
                ], 
                "has_devices": false,
                "devices": ["D15a97e7331f71b65d07e78"],
                "id": "Z3", 
                "name": "Z2-1"
            },
            {
                "parent_zone_id": "Z2",
                "description": "Zone 2 2",
                "has_subzones": false,
                "account_id": "A1594285852709364ACCOUNT",
                "has_devices": true,
                "devices": ["D34b35a7351e22f67e0ae20"],
                "id": "Z5",
                "name": "Z2-2"
            }
        ],
        "has_devices": false,
        "devices": ["D15a97e7331f71b65d07e78", "D34b35a7351e22f67e0ae20"],
        "id": "Z2", 
        "name": "Z2"
    }, 
    {
        "parent_zone_id": null, 
        "description": "Zone 1", 
        "has_subzones": false, 
        "account_id": "A1594285852709364ACCOUNT", 
        "has_devices": false,
        "devices": [],
        "id": "Z1", 
        "name": "Z1"
    }
]

When query param device_meta_data is used and set to true, instead of a list on device ids, as value for devices attribute of each zone, a list of dictionaries will be returned, like the one bellow:

{
    "device": "D34b35a7351e22f67e0ae20",
    "name": "SlimRobot"
}

For more details please check Zone Specification

/accounts/{account}/zones POST

Will create a new zone using the provided details.

Only account administrators are allowed to use this API endpoint.

Attributes used from the sent payload: * name (mandatory) - Name of the zone * description (optional) - Text describing the zone * parentzoneid (optional) - parent zone id

Returns the new zones tree hierarchy of the account (same as here)

/accounts/{account}/zones/{zone} PUT

This allows the update of an existing zone.

Only account administrators are allowed to use this API endpoint.

All attributes used at creation time are updatable: name, description and parent_zone_id.

Important to know: * A zone can not have assigned both child zones (subzones) and devices at the same time.

Returns the new zones tree hierarchy of the account (same as here)

/accounts/{account}/zones/{zone} DELETE

Deletes an existing zone.

Only account administrators are allowed to use this API endpoint.

It supports next query strings: * movedevicestoparent - when "true", it changes the zone of any device assigned to the zone being deleted,
with the parent zone (of the deleted zone). Parent zone must not have other child zones (subzones) beside the one being deleted, or an error will be returned. * move
devicesnozone - when "true", will leave any device assigned to the zone being deleted with no zone assigned. * movesubzonestoparent - when "true", it changes the parent zone of any child zone (subzone) of the zone being deleted, with the parent zone (of the deleted zone). * movesubzonesnozone - when "true", it removes the parent zone field for any subzone assigned to the zone being deleted, making the subzones to move at the root of the zones hierarchy tree.

Returns the new zones tree hierarchy of the account (same as here)

/accounts/{account}/zones/{zone}/alerts?utc_start=-7d&utc_end=now&type=alert_type&name=alert_name&level=alert_level GET

Will return all alerts created, active or cleared for the device in specified time interval. This endpoint is just an extension of account alerts endpoint, filtering the alerts for the devices in the specified zone, and so, same query strings are supported (please check it for details). Using the devices query string, is possible, but only the ones that are in the specified zone will be considered.

/accounts/{account}/zones/{zone}/alerts/csv?utc_start=-7d&utc_end=now&type=alert_type&name=alert_name&level=alert_level GET

Will return a redirect link towards a csv file with all alerts that were created, active or cleared for any device in zone in the specified time interval. This endpoint is just an extension of account alerts csv endpoint, filtering the alerts for the devices in the specified zone, and so, same query strings are supported (please check it for details). Using the devices query string, is possible, but only the ones that are in the specified zone will be considered.

/accounts/{account}/zones/{zone}/alerts/summary?group_by=attribute_name&... GET

Will return a summary of the alerts from devices that are in the specified zone for the specified time interval. This endpoint is just an extension of account alerts summary endpoint, filtering the alerts for the specified device, and so, same query strings are supported/mandatory, please check it for details.

/invitations GET

List all existing invitations.

Returns a JSON array of dictionaries of the attributes for each invitation.

Requesting token must include invitations GET.

See Invitation Specification for more details.

Errors:

/invitations POST

Creates a new invitation.

One could make a call to this endpoint without specifying any details for the new invitation, case in which the invitation will have a random generated code, capacity set to 1, expiration set to 1 month in advance, trial_length set to 2592000 (equivalent of 30 days in seconds) and a default account_plan.

If you want to customize the invitation, you can do it by sending attributes details in the payload; example:

{
    "code": "Limited-200-2019",
    "capacity": 200,
    "description": "Allowing 200 new accounts until end of 2019",
    "expiration": 1577829600,
    "trial_length": 1296000,
    "account_plan": {
        "type":"single_user",
        "max_devices": 5,
        "max_users": 1,
        "max_device_data_retention_days": 30,
        "max_device_data_bandwidth_kbps": 1000,
        "features": ["sharing", "commands", "logging", "remote_ssh", "remote_deploy", "remote_scripts", "remote_launch", "api"]
    }
}

Notes:

Returns a JSON dictionary with a few attributes, describing the last action result.

Requesting token must include invitations POST.

See Invitation Specification for more details.

Errors:

/invitations/{invitation_code} GET

Get existing invitation details.

Returns a JSON dictionary of the invitation attributes (of the invitation with the specified code), plus an extra attribute accounts containing a list of dictionaries representing the accounts created via the specified invitation code (just part of the account attributes are present in the dictionaries).

Example response:

{
    "code": "TEST_PROMO_100_PERCENT_OFF",
    "capacity": 0,
    "description": "Just for those who run tests",
    "created": 1561473187,
    "account_plan": {
        "max_device_data_bandwidth_kbps": 100,
        "features": ["api"],
        "max_device_data_retention_days": 1,
        "max_users": 1,
        "max_devices": 5,
        "type": "single_user"
    },
    "accounts":[
        {
            "account": "A283A890D230130D52B0BB780",
            "created": "2019-06-25 14:33:07",
            "company": "Freedom Robotics",
            "email": "api-unittest+1561462387710767@freedomrobotics.ai",
            "name": "Unittest Account 1561462387710767"
        },
        {
            "account": "AE12F396D52E0D8F73D3800F4",
            "created": "2019-06-25 14:33:07",
            "company": "Freedom Robotics",
            "email": "api-unittest+1561462387869999@freedomrobotics.ai",
            "name": "Unittest Account 1561462387869999"
        }
    ],
    "expiration": 1561559587,
    "trial_length": 1296000
}

Note: As in order to compose the accounts list, the accounts table is scanned, the response will take some extra time.

Requesting token must include invitation GET.

See Invitation Specification for more details.

Errors:

/invitations/{invitation_code} PUT

Updates the invitation with the specified code.

Only capacity, description, expiration, trial_length and account_plan attributes can be updated, providing one or more in the payload.

Example payload:

{
    "capacity": 0
}

Returns a JSON dictionary with a few attributes, describing the last action result.

Requesting token must include invitation PUT.

See Invitation Specification for more details.

Errors:

/invitations/{invitation_code} DELETE

Deletes the invitation with the specified code from the database.

Returns a JSON dictionary with a few attributes, describing the last action result.

In case of an invalid code (for which there is no invitation), it will return as it was successful.

Requesting token must include invitation DELETE.

See Invitation Specification for more details.

Errors:

/invitations/{invitation_code}/accounts POST

Creates a new account and a new user, based on the provided invitation code if it is valid.

Example payload:

{
    "email": "guru@robots.com",
    "first_name": "Filip",
    "last_name": "x0fbc25ff",
    "company": "Robots COM",
    "name": "Guru Demo Account",
    "address", "Santa Maria, CA",
    "description": "The account used by our guru to demo",
    "domain": "robots.com",
    "location": {
        "city": "Chicago",
        "country": "United States",
        "state": "Illinois"
    }
}

Only email, first_name and last_name attributes are mandatory in the payload, the rest are optional. Unsupported attributes will be ignored, without failing the request.

The endpoint will return status code 409 in case there already exist an user with the specified email address.

If the domain of the specified email address is not a known free email provider domain, the endpoint will also check that there is no account already created for that domain, failing with status code 409 if found. In order to disable this domain check, query param domain_check should be provided with value false (?domain_check=false).

If account and user were created successfully, the endpoint will also send the "Welcome" email to the user.

For this call there is no need of a token, anyone can use this endpoint as long as a valid invitation code is provided.

Returns a JSON array of dictionaries of the attributes for each invitation.

Errors:

/accounts/{account}/users GET

List all users associated with the account (that have any access permission on the specified account).

Returns a JSON array of dictionaries of the attributes for each user who is visible to this user's current token.

Each user has a minimum set of attributes:

[
    {
        "accounts": ["A3223423423423234"], 
        "user": "frankie@freedomrobotics.ai"
    },
    {
        "accounts": ["A3223423423423234"], 
        "user": "mickie@freedomrobotics.ai"
    },
    ...
]

Endpoint supports next query strings: * attributes - a list of attributes to be returned by the API for each user, will get all if not specified. * search_keys - a list of attribute names on which to search each corresponding word from search_words * only next attributes are supported: user, first_name and last_name. * search_words - a list of same length as search_keys list, containing a word to be searched for each of keys mentioned. Notes: * word #x will be searched only in key #x * we will consider an alert to match if any of the search word #x in key #x matches. * if needed same search key can be mentioned multiple times, with different corresponding search words

The endpoint supports ordering and pagination, so you can use also order_keys, order_directions, page_size and page query strings.

Requesting token must have users - get permissions.

See User Specification for more details.

Errors:

/accounts/{account}/user-groups GET

List all user groups associated with the account.

Returns a JSON array of dictionaries of the attributes for each group.

Errors: * Unauthorized (401) - If the token does not have users GET access.

/accounts/{account}/user-groups/{user_group} GET

Returns the user group with the specified id, if exists for account.

Returns a JSON dictionary describing the group details.

Errors: * Unauthorized (401) - If the token does not have users GET access.

/accounts/{account}/user-groups/{user_group}/users POST

Adds the specified user to the specified user group.

The endpoint expects the user to be specified in the payload.

Example payload:

{
    "user": "existing.user@freedomrobotics.ai"
}

Errors: * Unauthorized (401) - If the token does not have groups-users POST access.

/accounts/{account}/user-groups/{user_group}/users/{user} DELETE

Removes the specified user from the specified user group.

Errors: * Unauthorized (401) - If the token does not have groups-users DELETE access.

/accounts/{account}/user-groups/{user_group}/users?action=<action> PUT

Adds or removes a list of users to/from the specified user group.

The endpoint expects as payload a list of user emails, and query param action having value add or remove, to indicate the type of the desired operation.

Example payload:

[
    "existing.user1@freedomrobotics.ai",
    "existing.user2@freedomrobotics.ai"
]

Errors: * Unauthorized (401) - If the token does not have groups-users PUT access.

/accounts/{account}/user-groups/{user_group}/users GET

This endpoint is similar to /accounts/{account}/users GET endpoint, just that it returns only the users that are in the specified group.

Errors: * Unauthorized (401) - If the token does not have users GET or groups-users GET access.

/users GET

List all users which are visible to this user, based on their own user token's privileges.

Returns a JSON array of dictionaries of the attributes for each user who is visible to this user's current token.

Each user has a minimum set of attributes:

[
    {
        "created": "2018-08-13 14:38:02", 
        "devices": "*", 
        "token": "T************************",
        "accounts": ["A3223423423423234"], 
        "user": "frankie@freedomrobotics.ai"
    },
    {
        "created": "2018-05-13 3:28:12", 
        "devices": "*", 
        "token": "T************************", 
        "accounts": ["A3223423423423234"], 
        "user": "mickie@freedomrobotics.ai"
    },
    ...
]

Requesting token must include users.

See User Specification for more details.

Errors:

Notes: * This endpoint is deprecated, start using account-users-get instead

/users/{user} GET

List the attributes for the specified user if the current user's token has rights to view it.

{
    "created": "2018-08-13 14:38:02", 
    "devices": "*", 
    "token": "T************************", 
    "accounts": ["A3223423423423234"], 
    "user": "frankie@freedomrobotics.ai", 
    "custom_attribute1": "value1",
    "custom_attribute2": "value2",
    "custom_attribute3": "value3"
}

Requesting token must include user GET to access self and user_management to access others.

See User Specification for more details.

Errors:

/users/{user} PUT

Updated the attributes for this user. Body must be a JSON dictionary. To delete an attribute, make the value null.

{
    "attribute": "new value"
}

Note: You can not change the created time, user name or token with this request.

Requesting token must include user PUT to access self and user_management to access others.

See User Specification for more details.

Errors:

/users/{user} POST

Creates a new user with this email address if no user already exists or returns a HTTP Conflict error if one does.

Create from a role (Normal)

The easiest way to create a user is to define a role for the user and a list of accounts. role should be in the list of ["user", "administrator", "super_administrator"] (super_administrator is not normal).

{
    "role": "administrator",
    "accounts": ["MY ACCOUNT"],
    "att":"val",
    ...
}

The system will auto-generate a token with the correct permissions automatically.

Create from token (Advanced)

The other way is to define the specific token and secret for the user if you want to give non-normal privileges.

To do so, you must include a valid token and secret in the JSON dictionary body. This will be used as the exact permissions for the user.

{
    "token":"T************************",
    "secret":"S************************",
    "att":"val",
    ...
}

Token Specification:

Requesting token must include users POST and user_management.

See User Specification for more details.

Errors:

/users/{user} DELETE

Deletes the specified user if the calling token has rights to do so and at least equivalent privileges to the specified user.

Note: This does not delete an account, token or devices.

Requesting token must include user DELETE to delete self and additionally user_management DELETE to delete others.

See User Specification for more details.

Errors:

/users/{user}/login PUT

Returns a new token for use in the browser with a lifetime of 2 weeks for this user as part of the headers. The token has identical credentials to their base user token.

{
  "password": "{VALUE}"
}

Note: New users receive an email with their credentials, validating that they

No token is necessary to call this method.

See User Specification for more details.

Errors:

/users/{user}/logout PUT

Remove the header token for the user and delete the previous user login token from the system.

Requesting token must include user PUT.

See User Specification for more details.

Errors:

/users/{user}/password PUT

You may PUT here to update the password for the specified user given that your token has at least as many rights as

{
   "old_password":"{VALUE1}",
   "new_password":"{VALUE2}"
}

Requesting token must include user PUT to update self and user_management PUT to update others.

Errors:

/users/{user}/reset_password PUT

Sends an email to the user's email address with a link to set the user's password. This will log them in and redirect them to the Change Password user page in the portal.

No token is necessary to reset a password.

Errors:

/accounts/{account}/tokens?type=token_type&user=user_email&shared_by=user_email&device=device_id&... GET

Lists all access tokens this query's token has rights to see. If the query token has rights to see "\*" then all tokens are returned. If the query token has rights to a subset of accounts, actions or devices then only tokens which are fully accessible by this token are returned. If a token has rights to any account, action or device that the requester's token does not have a right to, that token will not be shown.

The results from this endpoint can be filtered using a few query params:

The endpoint supports ordering and pagination

See more info in the Access Token Specification

/tokens POST

Returns a new access token with the permissions specified in the body of the request. The maximum permissions allowed are those of the current token being used.

Example payload:
{
  "token": "T************************"        // Your Token
  "allowed_actions": {              // Dictionary of queries and actions
    "install_script": ["get"],
    "download": ["get"]
    "device": ["get","put"],
    "device_data": ["put"],
    "device_commands": ["get"]
  }
  "accounts": ["A0000000000000000000"],
  "devices": ["ID1"]   // or "*" to access all devices in account
}
Example Response:
{
  'status': 'success', 
  'action': 'create new token', 
  'token': 'T************************', 
  'secret': '************************************************'
}

NOTE: You must add create_secret=true as a URL parameter to create a token with a secret for the next while, allowing the legacy tokens to still work. As a best practice, please use create_secret=true for all new tokens.

The secret is only available when you create a token, so please record it somewhere for future use. You will need to send it into the query as a mc_token parameter.

See more info in the Access Token Specification

/tokens/{token} DELETE

Deletes the access token specified if it exists.

Warning: This can not be undone.

See more info in the Access Token Specification

/accounts/{account}/devices?attributes=[]&zones=[] GET

Returns the list of all devices and their attributes for a specific account in a JSON array. The endpoint supports ordering and pagination

Supported arguments:

See more info in the Device Specification

/accounts/{account}/devices POST

Creates a new device for this account, returning a unique name. The body must specify a dictionary with zero or more key / value pairs as attributes for the new device in JSON form.

Well-known attributes are:

Example payload:
{
  "type": "turtlebot 1",
  "location": "san francisco",
  "platform": "ros"
}
Example Response:
{
  "status": "success",
  "action": "create device",
  "device": "DEVICE-ID",
  "account": "ACCOUNT-ID"
}
Details:

DEPRECATED: /accounts/{account}/devices/{device} POST still works but is deprecated and will be removed.

See more info in the Device Specification

/accounts/{account}/devices/{device} PUT

Adds or edits the key/value pairs of attributes of the device specified in a JSON dictionary form. Can not change the account, device or created attributes. It will only update the subset of attributes which are in the query so you do not have to PUT back all attributes.

Example payload updates these attributes:
{
  "type": "turtlebot 3",
  "hardware version": "0.01.32"
}
Details:

/accounts/{account}/devices/{device} GET

Returns the JSON formatted dictionary of attributes for the device including account, device, created and any other account-defined ones.

See more info in the Device Specification

/accounts/{account}/devices/{device} DELETE

Deletes the device for the specified account if it exists.

See more info in the Device Specification

Warning: This deletes all data which has been recorded and can not be undone.

/accounts/{account}/devices/{device}/installscript GET

Returns an install script specific to this account and device giving it an access token, verifying prerequisites for installation such as OS, CPU architecture, ROS version, etc to allow a single line install and setup of a new device.

Usage:

On the robot, run this line of code, replacing the URL with the full URL for the install script, including your token. Remember to add quotes around the URL so the parameters are seen within the URL.

curl -sSf "https://api.freedomrobotics.ai/...script_url/installscript?mc_token={TOKEN}" | python

Optional Url Parameters:

Actions:

/accounts/{account}/devices/{device}/credentials GET

Returns the content to be saved in credentials file, generating a new token specific to this account and device.

Usage: Download the content and save it on the robot as file ~/.config/freedomrobotics/credentials or, on the robot, run next line of code, replacing in the URL your account and device IDs and your user token and secret.

curl -sSf "https://api.freedomrobotics.ai/accounts/{account}/devices/{device}/credentials?mc_token={TOKEN}&mc_secret={SECRET}" > ~/.config/freedomrobotics/credentials

This endpoint supports a part of the install script endpoint optional Url Parameters:

/accounts/{account}/devices/{device}/commands PUT

Adds one or more commands in a JSON array of dictionaries for the device of the specified account. When the device requests commands the next time, they will be sent from the queue once and then be deleted.

Each command must include these attributes:

Optional Attributes:

Example payload:
  [
    {
      "message": {
        "msg": "Hi there, this is my name.",
        "level": 4,
      },
      "platform": "ros",
      "topic": "/rosout_agg",
      "type": "rosgraph_msgs/Log",
      "expiration_secs": 100,
      "repeat": {
        "rate": 4,
        "length": 5,
        "cancel_on_next_message": true
      }
    }
  ]
Details:

NOTE: See more info in the Device Specification

/accounts/{account}/devices/{device}/commands?platforms=["ros"] GET

Single use queue which returns all commands once in a JSON array of dictionaries in the order they were received if their lifetime is still valid.

Supports a platforms=["ros"] query where you can specify a single type of command platform to return for. This allows multiple listeners to take different commands from the robot. ros is the current publicly supported platform and other private ones are used within mission_control to direct messages correctly. If you do not specify a platform, it will return all commands, which is not the best practice. Other platforms include ['ros', 'mission_control_link', 'mission_control_controller'].

Details:

WARNING: This must only be called by the device or commands will be lost

Note: We Should make it mandatory to specify at least one platform rather than dumping everything.

See more info in the Device Specification

/accounts/{account}/devices/{device}/data?utc_start=-1d&utc_end=now GET

Returns the subset of data specified in the time window. You can use UTC seconds since 1970 or relative times. For UTC seconds please only use the value without appending a suffix. Example: /data?utc_start=1511187927.16

For relative times, please see more information below on which different suffixes are available.

Without utc_start parameter in the URL the call returns the last minute as default. This is normally called by a viewer in real-time or to retrieve log data from the past.

See more info in the Device Specification

Example Return:
[
    {
        "platform": "ros",
        "utc_time": 154323443.23423,
        "topic": "/rosout_agg",
        "type": "rosgraph_msgs/Log"
        "data": [
          {"firstData": "Some data here..."},
          {"secondData": "Some data here..."}
        ]
    },
    {
        "platform": "ros",
        "utc_time": 154323445.55552,
        "topic": "/rosout_agg",
        "type": "rosgraph_msgs/Log"
        "data": [
          {"firstData": "Some data here..."},
          {"secondData": "Some data here..."}
        ]
    },
    ...
]
Relative times:
Additional Parameters:
Pagination Formatting:

Below is an example of a payload where 100 seconds of data have been requested, and roughly 10 seconds were returned because that was the maximum allowed for the payload. The requester can use the returned_utc_end as the next starting point to request more data.

{
    "requested_utc_end": 1571249549.25836,
    "requested_utc_start": 1571249429.258337,
    "num_returned_messages": 1093,
    "is_paginated": true,
    "returned_utc_start": 1571249429.272869,
    "returned_utc_end": 1571249438.9818637,
    "messages": [ ... ],
    "num_total_messages": 9614,
    "pagination_direction": "forward"
}
Details:

/accounts/{account}/devices/{device}/data PUT

WARNING: This is normally only called by the device

Adds one or more elements of data for the device, such as logs, debugging, commands, etc in JSON form as a JSON array of dictionaries. It is automatically used by the ROS Mission Control package to upload correctly formatted data. You can also add more data here if you would like to extend your data collection.

Example Payload:
[
  {
    "platform": "ros",
    "utc_time": 154323443.23423,
    "topic": "/rosout_agg",
    "type": "rosgraph_msgs/Log"
    "data": [
      {"firstData": "Some data here..."},
      {"secondData": "Some data here..."}
    ]
  }
]
Details:

See more info in the Device Specification

/accounts/{account}/devices/{device}/data?utc_start=-1d&utc_end=now DELETE

Deletes the subset of data specified in the time window. You can use UTC seconds since 1970 or relative times. For UTC seconds please only use the value without appending a suffix. Example: /data?utc_start=1511187927.16

For relative times, please see more information below on which different suffixes are available.

Relative times:

Warning: This deletes all data which has been recorded and can not be undone.

Details:

See more info in the Device Specification

/accounts/{account}/devices/{device}/alerts?utc_start=-7d&utc_end=now&type=alert_type&name=alert_name&level=alert_level GET

Will return all alerts that were created, active or cleared for the device in specified time interval. This endpoint is just an extension of account alerts endpoint, filtering the alerts for the specified device, and so, same query strings are supported (please check it for details). Of course, it does not make sens to use devices query string, as results are already filtered by the device specified in the query params.

/accounts/{account}/devices/{device}/alerts/csv?utc_start=-7d&utc_end=now&type=alert_type&name=alert_name&level=alert_level GET

Will return a redirect link towards a csv file with all alerts that were created, active or cleared for the device in specified time interval. This endpoint is just an extension of account alerts csv endpoint, filtering the alerts for the specified device, and so, same query strings are supported (please check it for details). Of course, it does not make sens to use devices query string, as results are already filtered by the device specified in the query params.

/accounts/{account}/devices/{device}/alerts/summary?group_by=attribute_name&... GET

Will return a summary for the device alerts in the specified time interval. This endpoint is just an extension of account alerts summary endpoint, filtering the alerts for the specified device, and so, same query strings are supported/mandatory, please check it for details. Of course, it does not make sens to use devices query string, as results are already filtered by the device specified in the query params.

/accounts/{account}/devices/{device}/alerts PUT

Emit an alert which has been triggered for this device if the alert has not already been broadcast or resolves (removes) an alert which is no longer in existance.

Please see the Device Alerts Specification for full details on how to enable alerts, triggers and emitters for an account and specific devices.

Request Body Elements:
Example Alert Body:
[
    {
        "name": "DILITHIUM CRYSTAL FAILURE",
        "action": "trigger",
        "level": "error",
        "type": "mc.data_outside_bounds"
        "description": "The dilithium crystal's resonant frequency is too low. Please add more fuel.",
        "attributes": {
            "remaining_mass": "12.212",
            "frequency": 31211233212321.21
        },
    },
]
Actions this does:
  1. Checks the mc.alerts.enabled attribute to validate if this device should be reporting alerts. If it is not enabled, the alert is disregarded.
  2. Checks if the alert already exists for the device and disregards if there is no change, or updates if the level is different.
  3. Updates the read only device attribute mc.alerts.active_alerts either adding a new alert, clearing a resolved one or resolving and recreating the alert with the new severity level.
  4. Records the alert to the mission_control_controller data endpoint for logging and replay.
  5. Broadcasts the alert to the enabled account endpoints based on mc.alerts.emitters for the account.

NOTE: If an emitter raises an error while broadcasting an event, the enabled attribute for it will be set to false, an error attribute with a human readable string will be created and the main email address for the account will be emailed, notifying of the issue.

/accounts/{account}/devices/{device}/pilot PUT

Adds or edits the fr.pilot.last_user attribute and its value specified in a JSON dictionary form for the specified device. Can not change the any other device attribute.

Example payload updates these attributes:
{
  "fr.pilot.last_user": {
    "name":"User X",
    "uid":"0b4pn5",
    "utc_time":1589960504072,
    "user":"userX@example.com"
  }
}
Details:

See more info in the Device Specification

/accounts/{account}/devices/{device}/statistics PUT

Adds custom statistics for a device for each hour. These statistics could be anything the user needs to define of almost any type. Bear in mind that all the statistics are stored by name and time rounded by hour, so any new statistic will overwrite whatever is already in the same hour.

Example Payload:
[
  {
    "statistic": "agent_connected",
    "utc_time": 154320000.0,
    "value": 60.0,
    "denominator": 60.0,
  },
  {
    "statistic": "device_active",
    "utc_time": 154320000.0,
    "value": 55.0,
    "denominator": 60.0,
  }
]
Details:

/accounts/{account}/devices/{device}/statistics?utc_start=-1d&utc_end=now GET

Returns all the statistics for a device within the specified range of params utc_start and utc_end. If those are not present will return the data for today from the hour 0 until the current hour.

Example Return:
{
  15432000: {
    "agent_connected": {
      "value": 60.0,
      "denominator": 60.0,
      "other_statistic_key": "other_statistic_value"
    },
    "device_active": {
      "value": 55.0,
      "denominator": 60.0,
      "other_statistic_key": "other_statistic_value"
    }
  },
  15433000: {
    "agent_connected": {
      "value": 60.0,
      "denominator": 60.0,
      "other_statistic_key": "other_statistic_value"
    },
    "device_active": {
      "value": 60.0,
      "other_statistic_key": "other_statistic_value"
    }
  }
}
Relative times:
Parameters:
Using filters examples:

Get all statistics from last hour:

?utc_start=-1h&utc_end=-1h

Get only active statistics from today until the past hour:

?utc_end=-1h&statistics=["active"]

Get only active and connected statistics from 3 hours ago until now:

?utc_start=-3h&statistics=["active", "connected"]

Get all the statistics from the past month:

?utc_start=-1M&utc_end=-1M

Get all the statistics for specific hour (utcstart and utcend should be the same):

?utc_start=154330000&utc_end=154330000

/accounts/{account}/settings GET

Endpoint for getting static configuration that is used in different applications (APP, AutomationEngine, etc) in order to share the same data across such as available automations task types, OFL definitions. The global settings are configurable only by a developer (not associated with a specific account), while for the account specific ones one could use the other endpoints to do CRUD operation on them. If there are global and account specific settings of same type, the endpoint it will return the account one, global settings are returned only when there is no account specific setting of that type.

Table details
Example Response:
{
  "automations_task_types": [
    {
      "type": "ofl",
      "name": "OFL",
      "params": [
        {"id": "ofl_task_type", "name": "OFL task type", "type": "integer"},
        {"id": "device_id", "name": "Device id", "type": "string"},
        {"id": "ofl_details", "name": "OFL details", "type": "dict"}
      ]
    },
    {
      "type": "alert",
      "name": "Alert",
      "params": [
        {"id": "level", "name": "Level", "type": "select",
          "options": ["DEBUG", "INFO", "WARN", "ERROR", "FATAL"]},
        {"id": "device_id", "name": "Device id", "type": "string"}
      ]
    },
    {
      "type": "kepware",
      "name": "Kepware",
      "params": [
        {"id": "tag", "name": "Tag", "type": "string"},
        {"id": "value", "name": "Value", "type": "string"}
      ]
    }
  ],
  "ofl_types": [
    {
      "name" : "LinearActuator",
      "lookups" : [
        {
          "id": "velocity_equals_to",
          "name" : "Velocity equals to",
          "params" : [
            {
              "id" : "value",
              "name" : "Value",
              "type" : "float"
            }
          ],
          "automation_engine_logic" : {
            "expression" : [
              "defun",
              "velocity_equals_to",
              "value",
              "==",
              [
                "$",
                "velocity"
              ],
              [
                "$",
                "value"
              ]
            ],
            "sources" : {
              "velocity" : {
                "path" : [
                    "velocity"
                ]
              }
            }
          }
        }
      ]
    }
  ]
}

/accounts/{account}/settings POST

Endpoint used to create account specific setting. When creating a new one, type and value have to be provided in the payload, and any other attributes are ignored.

/accounts/{account}/settings/{setting_type} DELETE

Used to delete the setting of the type specified.

Note: Only account specific settings can be deleted.

/accounts/{account}/settings/{setting_type} GET

Returns only the value content for the specified type in the url. The value could be of various types, depending on each setting (dict, list, int, bool, etc.).

Example request

GET /accounts/account/settings/automations_task_types

Example response
[
  {
    "type": "ofl",
    "name": "OFL",
    "params": [
      {"id": "ofl_task_type", "name": "OFL task type", "type": "integer"},
      {"id": "device_id", "name": "Device id", "type": "string"},
      {"id": "ofl_details", "name": "OFL details", "type": "dict"}
    ]
  },
  {
    "type": "alert",
    "name": "Alert",
    "params": [
      {"id": "level", "name": "Level", "type": "select",
        "options": ["DEBUG", "INFO", "WARN", "ERROR", "FATAL"]},
      {"id": "device_id", "name": "Device id", "type": "string"}
    ]
  },
  {
    "type": "kepware",
    "name": "Kepware",
    "params": [
      {"id": "tag", "name": "Tag", "type": "string"},
      {"id": "value", "name": "Value", "type": "string"}
    ]
  }
]

/accounts/{account}/settings/{setting_type} PUT

Used to update and existing setting. Payload needs to provide type and/or value attributes, any other attribute being ignored.

Note: Only account specific settings can be updated.

/accounts/{account}/statistics/csv?utc_start=-1d&utc_end=now&normalized=false GET

This endpoint is an extension of previous API, the main differences are: - it collects data from all devices of the account - it returns data in csv format

Parameters:

If utc_start is not provided, by default it will consider it to be 7 days ago.

Values for the statistics are presented each in its own column, alphabetically ordered and having statistic. as a prefix to their name (as column name).

The rows of the resulting csv will be ordered first by utc_time and then by device.id.

CSV response will include some extra columns like: device.id and device.name, a human readable time and zones tree path (we use -> between a prent zone name and a child zone name), here is an example:

utc_time,device.id,time,device.name,zones,statistic.connected
1598832000,D1598864373023075DEVICE,2020-08-31 00:00:00,Test Device,z1->z11,0.046332443495926134
1598832000,DTESTGLOBAL1598896774,2020-08-31 00:00:00,Global Device for multiple tests,,0.6447856600982477
1598835600,D1598864373023075DEVICE,2020-08-31 01:00:00,Test Device,z1->z11,0.3395155855228522
1598835600,DTESTGLOBAL1598896774,2020-08-31 01:00:00,Global Device for multiple tests,,0.1461878695880422
1598839200,D1598864373023075DEVICE,2020-08-31 02:00:00,Test Device,z1->z11,0.3518129680999388
1598839200,DTESTGLOBAL1598896774,2020-08-31 02:00:00,Global Device for multiple tests,,0.6896407089747354

/accounts/{account}/statistics/summary?utc_start=-1d&utc_end=now&grouping=hour GET

This endpoint returns an aggregated summary of statistics for all devices and zones in the account, in the format:

{
    by_device: {
        device_id: {
            group<truncated utc timestamp/device id>: {
                statname: {avg, sum, count, min, max, percent, denominator}
                ...
            }
            ...
        }
        ...
    },
    by_zone: {
        zone_id: {
            group<truncated utc timestamp/device id>: {
                statname: {avg, sum, count, min, max, percent, denominator}
                ...
            }
            ...
        }
        ...
    }
}
Parameters:

/accounts/{account}/zones/{zone}/statistics/summary?utc_start=-1d&utc_end=now&grouping=hour GET

Same as the previous endpoint but only for a single zone

/accounts/{account}/devices/{device}/statistics/summary?utc_start=-1d&utc_end=now&grouping=hour GET

Same as the previous endpoint but only for a single device

/accounts/{account}/devices/{device}/uploads?file=filename GET

This endpoint will return an URL (under location attribute) that can be used for a limited amount of time, only to download the specified file. It can not be used for the actual file download, use the returned URL instead. Example response:

{
  "location": "https://data.freedomrobotics.ai/uploaded_data/a/A12345/d/D12345/filename?..."
}

/accounts/{account}/devices/{device}/uploads?file=filename PUT

This endpoint is similar to the previous one, but the returned URL can be used only to upload a file.

/accounts/{account}/devices/{device}/usage GET

Same as accounts usage API, with the remark that it will include in the response only usage for the specified device.

/accounts/{account}/devices/{device}/usage GET

Same as accounts month usage API, with the remark that it will include in the response only usage for the specified device.

/accounts/{account}/devices/{device}/usage/{year}/{month}/{day} GET

Same as accounts day usage API, with the remark that it will include in the response only usage for the specified device.

/accounts/{account}/devices/{device}/videos GET

Returns the list of topics which have uploaded a frame of video in the last 10 seconds. You use this to identify what to request on the /accounts/{account}/devices/{device}/videos/{video} endpoint.

If a topic stops uploading data, the name will be removed from the list. Names have been sanitized by removing the / and other bad characters and are replaced with -.

Retrieving Historical Topic Names:

When turned on for a device, the system caches frames from each topic at a sub-sampled rate for future retrieval.

/accounts/{account}/devices/{device}/videos?pre_signed=true GET Pre-signed urls version

Videos list endpoint is updating. In this new version the endpoint returns a list of tuples, each tuple containing: - the name of the topic (as in the previously returned list) - the pre-signed url where the actual frame can be downloaded from directly

To enable this form of response you have to use the pre_signed query param with value true.

Beside the pre_signed, the endpoint now supports new query_params (only for this version):

Notes:

Retrieving Historical Frames:

When turned on on for a device, the system caches frames from each topic at a sub-sampled rate for future retrieval.

/accounts/{account}/devices/{device}/videos/{video} GET

This is deprecated, please use pre-signed urls instead, without hitting the API.

Returns a encoded base64 JPEG video frame of the active video topic. You can see a full lists of active topics by running a GET on /videos as described above.

If no active video with the topic name is found it will return a 404.

Retrieving Historical Frames:

When turned on on for a device, the system caches frames from each topic at a sub-sampled rate for future retrieval.

/download/{area} GET

Returns the list of files for the area which can be downloaded. ros is a well-known area which you can list to identify .deb packages to install.

/download/{area}/{file} GET

Returns a signed redirect to the file location specified if the file exists or a 404 if it does not. This is used to download install files with secure login for the system. The current area is ros.

/issues PUT

A general purpose error reporter to notify the system when there is an issue. Based on the component, the error will be appropriately routed to the correct location.

You must provide a JSON body with:

/ofl GET

Returns the OFL definition including its types, the lookups for each type and the automation engine logics for each type. For more information check the OFL wiki

Example response:

{
  "ofl_types": [
    {
      "id": "mobile_robot",
      "name": "MobileRobot",
      "lookups": [
        {
          "name": "is at named location",
          "params": [
            {"name": "location_name", "type": "string"}
          ],
          "automation_engine_logic": {
            "expression": ["==", ["$", "current_location"], ["$", "location_name"]],
            "sources": {
              "current_location": {
                "path": ["header", "location", "location_name"],
                "topic": "/fleets/$FLEET_NAME/devices/$DEVICE_NAME/state",
                "msg_type": "MobileRobotState"
              }
            }
          }
        }
      ]    
    },
    ....
  ]
}

/utc_now GET

This a public endpoint that returns the current UTC date time and timestamp (seconds since Epoch).

Example response:

{
    "date_time": "2020-05-28T08:23:54.549464Z",
    "timestamp": 1590654234.549464
}

Account Specification

Accounts are device owners and can be managed by multiple tokens. The account will also give you access to the web portal where it is possible to see live feeds from your devices and update settings on-the-fly.

Please remember that the devices are belonging to your account, so if you delete it, all belonging devices and data will be gone as well.

Example account attributes:
  {
    "account": READ-ONLY,
    "created": READ-ONLY,
    "name": "Project Development",
    "company": "SpaceX",
    "location": {
      "country": "United States",
      "state": "CA",
      "city": "Hawthorne"
    },
    "email": "email@example.com",
    "description": "an optional longer description of this account if you have many accounts to differentiate between",
    "other att": null       // Deletes "other att"
  }
Details:

Account Plan Specification

This is a JSON dictionary representation for the account limits (features, users, devices, etc.)

Default Account plan:

{
    "type":"single_user",
    "max_devices": 5,
    "max_users": 1,
    "max_device_data_retention_days": 30,
    "max_device_data_bandwidth_kbps": 1000,
    "features": ["sharing", "commands", "logging", "remote_ssh", "remote_deploy", "remote_scripts", "remote_launch", "api"]
}

Account Usage report Format

This is a JSON dictionary representing the usage summary report for a specific period of time (decided at api level).

It includes 3 main attributes:

In the case a detailed report is requested, there will be an additional attribute for each day included in the report, containing details for each device, similar to devices attribute.

Example:

{
    "total": {
        "connection_devices": 3, 
        "activity_minutes": 1, 
        "activity_hours": 1, 
        "activity_devices": 1, 
        "connection_minutes": 3468, 
        "connection_hours": 60
    },
    "devices": {
        "D4D982085DB5855ABDE3F4CF8E3": {
            "connection_minutes": 786, 
            "activity_minutes": 1, 
            "activity_hours": 1, 
            "connection_hours": 14, 
            "connection_days": 1, 
            "activity_days": 1
        },
        "D64A8BD202863775F9AC571A387": {
            "connection_minutes": 1440, 
            "activity_minutes": 0, 
            "activity_hours": 0, 
            "connection_hours": 24, 
            "connection_days": 1, 
            "activity_days": 0
        },
        "DA2A589FB1F23F5BB81EF0F27AC": {
            "connection_minutes": 1242, 
            "activity_minutes": 0, 
            "activity_hours": 0, 
            "connection_hours": 22, 
            "connection_days": 1, 
            "activity_days": 0
        }
    },
    "meta": {
        "account": "A48900E3671CA877948A96FE8", 
        "end_date": "2019-09-13", 
        "devices": "*", 
        "final": true, 
        "generated": "2019-10-18 15:03", 
        "start_date": "2019-09-13"
    }, 
    "2019-09-13": {
        "total": {
            "connection_devices": 3, 
            "activity_minutes": 1, 
            "activity_hours": 1, 
            "activity_devices": 1, 
            "connection_minutes": 3468, 
            "connection_hours": 60
        }, 
        "devices": {
            "D4D982085DB5855ABDE3F4CF8E3": {
                "connection_minutes": 786, 
                "activity_minutes": 1, 
                "connection_hours": 14, 
                "activity_hours": 1
            }, 
            "D64A8BD202863775F9AC571A387": {
                "connection_minutes": 1440, 
                "connection_hours": 24
            }, 
            "DA2A589FB1F23F5BB81EF0F27AC": {
                "connection_minutes": 1242, 
                "connection_hours": 22
            }
        }
    }
}

User Specification

Users are a human-usable wrapper around tokens which enable browser sessions, password login, email retrieval of forgotten credentials and specification of the user's privileges on the platform through a base token.

Example user attributes:
{
    'user': 'john.doe@example.com',
    'locked': False,
    'last_seen': 1585547224.87392,
    'created': '2019-05-15 06:54:01',
    'first_name': 'John',
    'last_name': 'Doe',
    'role': 'administrator'
}
Details:

User Creation

When a new user is created, a valid email address, a token and it's secret must be specified. The user will then receive an email to activate their account and set their own password.

User Session Management

When they log in to the web portal, a new token is created with identical privileges to their core token, with the exception that it only has a 2 week life-span.

When a user actively logs out of their session, the user_session token they were using is destroyed.

For security reasons, a user's password and the token's secret is never stored, so if a password is forgotten, the user must have access to their email to receive a new password.

User Management

All users have access to update their own attributes, change their token, set a password and delete their own accounts.

If a user also has user_management access, they will be able to access other user's accounts who are within their scope of privileges.

User Access Control

A user with user_management can only see other users who have equal or fewer privileges based on their tokens. This guarantees that users only see other users in their own accounts and within the security scope of their tokens.

Examples:

This enables a simple token-based authentication system with a guarantee of specific access for each user.

Invitation Specification

Invitations are used to facilitate onboarding of new companies to use this product. If someone is provided with a valid invitation code, it should be able to sign up creating an account and user using it.

Example invitation attributes:
{
    "code": "Demo_219-RxF",
    "created": 1559340000,
    "capacity": 9,
    "expiration": 1577829600,
    "trial_length": 1296000,
    "description": "Invitation to be used during demo 219",
    "account_plan": {
        "type":"single_user",
        "max_devices": 5,
        "max_users": 2,
        "max_device_data_retention_days": 3,
        "max_device_data_bandwidth_kbps": 100,
        "features": ["commands", "remote_deploy", "api"]
    },
    "other att": null
}
Details:

Attribute code can be specified once at invitation creation time, created can't be specified at creation time (it automatically capture the invitation creation time).

Both code and created can not be updated after the invitation creation (readonly); only capacity, description, expiration, trial_length and account_plan attributes can be updated.

More details on account_plan see Account Plan Specification

Other attributes are not supported and will be ignored if provided without failing the request.

Access Token Specification

Below are extensive documentation on how to create new tokens and how to set up its appropriate access and abilities.

Different uses of the system require only a subset of access to an account. You should always give out the minimum privileges for each access token. As an example, a device should never have rights to delete the entire account, or even create other devices. On the other hand, a normal user should definitely have the ability to create other devices, but not delete the overall account unless they are an administrator.

When you create a new access token, you can only give it access to actions, accounts and devices that the token creating it has access to, so no one can ever give more privileges than they currently have.

Full List of Supported Action Types

Optional Token Parameters

There are multiple other parameters which are optional and can be defined for a token.

Example Token Contents

Below are examples of the correct action permissions to give each use case to create a secure environment.

Device Token

This gives a single device the ability to install Mission Control, update its attributes, upload data and get commands.

{
  "allowed_actions": {                
    "install_script": ["get"],
    "download": ["get"]
    "device": ["get","put"],
    "device_data": ["put"],
    "device_commands": ["get"]
  }
  "accounts": ["A0000000000000000000"],
  "devices": ["D0000000000000000000"]   
}
Account Administration Token

This query would give access to everything in these two accounts.

{
  "allowed_actions": {
      "*": "*"
  },
  "accounts": ["A0000000000000000000", "A1111111111111111111"],       
  "devices": "*"   
}
Account User Token

A usual set of permissions for a user would be:

{
"allowed_actions": {
    "device_videos": ["get"],
    "device": "*",
    "devices": "*",
    "device_commands": "*",
    "token": ["get", "post"],
    "tokens": ["get"],
    "device_data": "*",
    "accounts": ["get"],
    "account": ["get"],
    "install_script": ["get"],
    "user": "*",
    "download": ["get"]
},
  "accounts": ["A0000000000000000000"],
  "devices": "*"
}
Sharing Viewer Token

A usual sharing link to show the viewer for a period of 300 seconds to be used in the next week would look something like this:

{
  "allowed_actions": {
    "device": ["get"],
    "device_data": ["get"],
    "device_videos": ["get"],
    "token", ["get"]
  },
  "accounts": ["A0000000000000000000"],
  "devices": ["D000000000000000000"],
  "utc_start": 1523443243.12,
  "utc_end": 1523443543.43,
  "expiration_utc_secs": 1523483243.56
}

Zone Specification

A zone represents a logical entity that allows device grouping. A zone can have child zones associated (subzones), allowing forward organization of devices based on their associated zone path (in the hierarchy zones tree).

Important to know: * A zone can have either subzones or devices, but not both at the same time (Only leaf zones can have devices associated) * Only account administrators are able to create, delete or modify zones, allowing them to define the zones hierarchy structure.

Zone Attributes

A zone has a set of default attributes, but beside these it could support any other attribute names, just sent your extra needed attributes in the payload (when creating or updating a zone), and then you can find them in the response of the next get call.

Here are the default attributes defined for a zone:

id [READ ONLY]

This is an internal id that uniquely identifies the zone

name

A human readable string specifying the name of the zone.

parent_zone_id

This is a reference to an other existing zone id. It is used to build the zone hierarchy.

description [OPTIONAL]

A human readable longer description of the zone which can contain notes and other details. It should be a string.

account_id [READ ONLY]

The account identifier string.

has_subzones [READ ONLY]

A flag that indicates if there are other zones beneath it in the hierarchy.

subzones [READ ONLY]

The list of zones that are beneath it in the hierachy. This attribute is present in the response only when has_subzones is True.

has_devices [READ ONLY]

A flag that indicates if there are devices associated with the zone.

Device Specification

A device represents an individual robot in the system. You can interact with it in 3 ways:

  1. Change it's attributes which control the system
  2. Specify what topics of data to upload from the device to the cloud
  3. Send commands to the device on specific topics

Device Attributes

There are multiple device attributes which are well known and necessary for the account to function properly.

account [READ ONLY]

Specifies which account this device is part of. It is read-only and created when the device is created.

device [READ ONLY]

Specifies the unique name for this device within the account. It is read-only and created when the device is created. It is usually a long key starting with D such as D23423AWERF5563.

created [READ ONLY]

The string specifying the time the device object was created such as 2017-11-13 22:35:17.

name

A human readable string specifying the device name, such as Development Team's turtlebot used to display device names in the portal and differentiate between devices.

type

A "well known" string naming the type of robot to allow for automatic setup of accounts and understanding of data. Examples are:

You may use any name you want. It should be repeated across the account to specify devices of the same type.

description [OPTIONAL]

A human readable longer description of the robot which can contain notes and other details. It should be a string.

zone_id [OPTIONAL]

The internal id of an already existing Zone. It should be a string. By setting this attribute, the device will be associated with the specified zone.

mc.ros.topics.uploadable

This attribute specifies the types of topics which are uploadable from the device either with specific names (ex: /cmd_vel) or for topics which start with a string (ex: /mobile_base/*).

Parameter Specification:

NOTE: In case of conflict, include has precedence over default and exclude has precedence over include.

You can send the device endpoint a dictionary containing a minimum of the attribute and its values.

{
  "mc.ros.topics.uploadable": {
    "default": "none",
    "include": {
      "/rosout_agg": "all"
    },
    "exclude": []
  }
}

A normal version might be something like the below which would upload the /cmd_vel topic if data is published on it, upload all topics starting with /battery/ at a maximum rate of 10 hz (0.1 second minimum interval), upload all topics starting with /camera except one specifically named /camera/depth/raw and upload all data on /rosout_agg so that all log files are uploaded, no matter how fast they come in.

{
  "mc.ros.topics.uploadable": {
    "default": "none",
    "include": {
      "/cmd_vel": 0.1,
      "/battery/*": 0.1,
      "/camera*":0.25,
      "/rosout_agg": "all"
    },
    "exclude": ["/camera/depth/raw"]
  }
}
mc.ros.topics.commandable

This attribute specifies the types of topics which can be commanded by people. For security or stability reasons, many topics should not be externally commanded, such as ones that report a device's current position or state.

Parameter specification:

NOTE: In case of conflict, include has precedence over default and exclude has precedence over include.

You can send the device endpoint a dictionary containing a minimum of the attribute and its values.

{
  "mc.ros.topics.commandable": {
    "default": "none",
    "include": [],
    "exclude": []
  }
}

A normal version might be something like the below which would only allow you to send data to topics starting with /cmd_vel or the /dock or /undock topics. Any other commanded topics will be rejected.

{
  "mc.ros.topics.commandable": {
    "default": "none",
    "include": ["/cmd_vel*", "/dock", "/undock"],
    "exclude": []
  }
}
connection_status

This is a read only attribute that is calculated by the API when returning the device attributes. It could be one of active, connected or offline. For more details on how this field is calculated see Device Status Specification

Device Commands

Basic Command Data Structure

You upload a list of one or more commands to the endpoint. Each command must be a dictionary contiaining a minimum of platform and expiration_secs. For each platform type, additional elements, which are specified below, are necessary.

[
  {
    "platform": "ros",
    "expiration_secs": 10,
    ...
  }
]

ROS Commands (platform = ros)

When you send a command to the ros platform endpoint on a device, it is intepreted into a ros topic message on the specified topic and published. Additionally, you can specify a rate and duration to re-publish the message locally on the topic. This is useful because there is significant lag and timing instability for communication so if you need to repeat a command such as /cmd_vel at 10 hz, it is not feasible to reliably do so remotely. You can also set a lifetime for the command so that if the device does not receive it for multiple days, all legacy commands will not be replayed innappropriately.

You can upload one or more commands at a time in an array.

The below command would tell the robot to publish a message on ROS to the /speak topic with type std_msgs/String if the robot asks for messages within 10 seconds.

Send a String Message
[
  {
    "message": {
      "data": "This is a simple message"
    },
    "platform": "ros",
    "topic": "/speak",
    "type": "std_msgs/String",
    "expiration_secs": 10
  }
]

The below message would repeat the ros message on the /speak topic at 10hz for 5 seconds unless it receives another message to broadcast on the same topic.

Send a Repeating String Message
[
  {
    "message": {
      "data": "This is a simple message"
    },
    "platform": "ros",
    "topic": "/speak",
    "type": "std_msgs/String",
    "expiration_secs": 10,
    "repeat": {
      "rate": 10,
      "length": 5,
      "cancel_on_next_message": true
    }
  }
]

The below message would send a message to drive the robot forward slowly.

Send a repeating "Drive Forward" Message
[
  {
    "message": {
      "linear": {
        "x":0.1,
        "y":0,
        "z":0
      },
      "angular": {
        "x":0,
        "y":0,
        "z":0.5
      }
    },
    "platform": "ros",
    "topic": "/cmd_vel_mux/input/mission_control",
    "type": "geometry_msgs/Twist",
    "expiration_secs":10,
    "repeat": {
      "rate":50,
      "length":2,
      "cancel_on_next_message":true
    }
  }
]

Mission Control Link Commands (platform = missioncontrollink)

These commands are specifically meant to tell the link when to do things remotely so it keeps in sync with the cloud.

update_topics

This command is sent to the robot each time the mc.ros.topics.commandable and mc.ros.topics.uploadable attributes are specified.

Command:

[
    {
        "command": "update_topics",
        "platform": "mission_control_link",
        "expiration_secs": 100,
        "data" { }
    }
]

Mission Control Controller Commands (platform = missioncontrolcontroller)

The Controller allows you to directly activate commands safely on your robot. Each command is written to be robust, secure and is improved each time an edge case is found.

To send commands to the controller, you set the "platform": "mission_control_controller" parameter in the command when it is sent in.

remote_ssh

Command:

[
    {
        "command": "enable_remote_ssh",
        "platform": "mission_control_controller",
        "expiration_secs": 100,
        "data" {
            "enable": true/false,
        "uid": "AEFF321AEF56ECD"
        },
    }
]

Creates a URL + Port which is publicly available from anywhere in the world where you can SSH into the device if you have the right key / password. The URL will be available as long as something is connected to it and will disappear 30 minutes after the last connection is removed. The mc.instrumentation.remote_ssh attribute is added to the device during this time and removed when the port is invalidated.

Device Attributes:

{
    ...,
    "mc.instrumentation.remote_ssh": {
        "user": "root",
        "command": "ssh root@tunnel.freedomrobotics.ai -p 18474",
        "port": 18474,
        "address": "tunnel.freedomrobotics.ai",
        "user_password_enabled": true
    },
    ...
}

If the command is triggered repeatedly, it will not create a new instance.

Once you have triggered the install of SSH, if you want to enable password login (especially for ROOT users), you can use the script below on the local machine to add it. You can then log in over SSH with a password rather than a key.

cat /etc/ssh/ssh_config > /etc/ssh/ssh_config.old && sed -i "/.*PermitRootLogin.*/d" /etc/ssh/ssh_config && sed -i "/.*PasswordIdentification.*/d" /etc/ssh/ssh_config && echo "PermitRootLogin ' + enable_str + '" >> /etc/ssh/ssh_config && echo "PasswordIdentification ' + enable_str + '" >> /etc/ssh/ssh_config

NOTE: Passwords are less secure than keys, so please make sure you understand what you are doing.

enable_remote_desktop

NOT IMPLEMENTED YET!

ros_launch

Launches the specified launch_file in the ros_package if it exists. It does not shut down any previously running nodes or ros master instances so you can trigger multiple files if you want.

Command:

[
    {
        "command": "ros_launch",
        "platform": "mission_control_controller",
        "expiration_secs": 100,
        "data" {
            "ros_package": "freedom_robot",
            "launch_file": "robot.launch",
            "environment_variables": {
              "var": "value"
            }
        }, 
        "uid": "AEFF321AEF56ECD"
    }
]

Prerequisites:

ros_local_script

Launches the specified script_path in the ros_package if it exists.

Command:

[
    {
        "command": "controller_restart",
        "platform": "mission_control_controller",
        "expiration_secs": 100,
        "data" {
            "ros_package": "freedom_robot",
            "script_path": "/scripts/diagnostics.sh"
        }, 
        "uid": "AEFF321AEF56ECD"
    }
]

Prerequisites:

ros_shutdown

This cleanly buy fully shuts down ROS as quickly as possible. It first triggers rosnode kill <node> on all running nodes to have them shut themselves down. If they have not fully shut down in 20 seconds, the system kills the rosmaster process using its process id which will result in all other nodes shutting down.

Command:

[
    {
        "command": "ros_shutdown",
        "platform": "mission_control_controller",
        "expiration_secs": 100,
        "data" { }, 
        "uid": "AEFF321AEF56ECD"
    }
]
ros_catkin_make

Calls catkin_make in the directory specified by mc.ros.catkin_ws. This attribute should specify a complete path which has been expanded such as /home/user/catkin_ws and does not use ~/ as that can create errors in execution based on the user permissions.

If "clean": true is specified, the system will first delete the /devel and /build directories to remove any corrupted elements and then trigger catkin_make.

Command:

[
    {
        "command": "ros_catkin_make",
        "platform": "mission_control_controller",
        "expiration_secs": 100,
        "data" {
            "clean":false
        }, 
        "uid": "AEFF321AEF56ECD"
    }
]

Prerequisites:

controller_restart

Restarts this part of the system if it has encountered a problem. Note: If there is a problem, it isn't guaranteed that this will actually work.

Command:

[
    {
        "command": "controller_restart",
        "platform": "mission_control_controller",
        "expiration_secs": 100,
        "data" { }, 
        "uid": "AEFF321AEF56ECD"
    }
]
git_update

NOT IMPLEMENTED YET!

Command:

[
    {
        "command": "git_update",
        "platform": "mission_control_controller",
        "expiration_secs": 100,
        "data" {
            "ros_package":"freedom_robot"
        }, 
        "uid": "AEFF321AEF56ECD"
    }
]
git_branch

NOT IMPLEMENTED YET!

Command:

[
    {
        "command": "git_branch",
        "platform": "mission_control_controller",
        "expiration_secs": 100,
        "data" {
            "ros_package":"freedom_robot",
            "git_branch": "feature_branch"
        }, 
        "uid": "AEFF321AEF56ECD"
    }
]
system_restart

Restarts the robots operative system.

Command:

[
    {
        "command": "system_restart",
        "platform": "mission_control_controller",
        "expiration_secs": 100,
        "data" { }, 
        "uid": "AEFF321AEF56ECD"
    }
]
system_shutdown

Shutdown the robots operative system.

Command:

[
    {
        "command": "system_shutdown",
        "platform": "mission_control_controller",
        "expiration_secs": 100,
        "data" { }, 
        "uid": "AEFF321AEF56ECD"
    }
]
agent_update

Requests the agent to updates itself with the specified arguments: * data.code_branch: The branch name used for updating the agent. release by default. * data.force_restart: Force to restart the agent after updating it or not. False by default.

Command:

[
    {
        "command": "agent_update",
        "platform": "mission_control_controller",
        "expiration_secs": 100,
        "data" {
            "code_branch": "qa",
            "force_restart": True
        }, 
        "uid": "AEFF321AEF56ECD"
    }
]
agent_restart

Restarts the agent.

Command:

[
    {
        "command": "agent_restart",
        "platform": "mission_control_controller",
        "expiration_secs": 100,
        "data" {}, 
        "uid": "AEFF321AEF56ECD"
    }
]
profile_enable

Starts yappi profiler on the agent (if platform=mission_control_controller) or on link (if platform=mission_control_link. If yappi is not installed or the profiler was already enabled it won't do anything.

Command:

[
    {
        "command": "profile_enable",
        "platform": "mission_control_controller",
        "expiration_secs": 100,
        "data" { }, 
        "uid": "AEFF321AEF56ECD"
    }
]
profile_disable

Stops yappi profiler, write results into ~/.config/freedomrobotics/logs/profiler and send them into controller logs. If yappi is not installed or the profiler wasn't running, it won't do anything.

Command:

[
    {
        "command": "profile_disable",
        "platform": "mission_control_controller",
        "expiration_secs": 100,
        "data" { }, 
        "uid": "AEFF321AEF56ECD"
    }
]
profile_enable_and_disable

With this command we can start the profiler and tell the system to stop profiling after N seconds. This way the agent will automatically stop the profiler. Notice that for doing this, we need to use next_commands argument.

Command:

[
    {
        "command": "profile_enable",
        "platform": "mission_control_controller",
        "expiration_secs": 100,
        "data" { }, 
        "uid": "AEFF321AEF56ECD",
        "next_commands": [
            {
                "exipration_secs": 30,
                "command": {
                    "command": "profile_disable",
                    "platform": "mission_control_controller",
                    "expiration_secs": 100,
                    "data" { }, 
                    "uid": "AEFF321AEF56ECD"
                }
            }
        ]
    }
]

Device Data

Device data are individual messages on a topic for a specific device in an account.

Overall Data Format

The data format is a general format allowing for ROS, MQTT, * MQ and other pub/sub message formats to be generically and clearly represented.

{
   "account": "{ ACCOUNT_ID }",
   "data": {

        // Message contents go here

   },
   "topic": "{ TOPIC_NAME }",
   "platform": "{ PLATFORM }",
   "device": "{ DEVICE_ID }",
   "type": "{ TOPIC_TYPE }",
   "utc_time": 1512169455.57
}

Specification of where the data came from:

Specification of the contents of the data:

The actual data for the message is stored in the data dictionary and is fully specified by the combination of the platform and type elements. It can include any combination of data representable by JSON.

ROS Specific Details

The ros formats differentiate numbers into many different low-level formats (int8, int16, uint8, uint16, float32, float64, ...). When it is converted to JSON, that information is removed and all elements are represented as a number.

Detailed Examples

Below is a sensor_msgs/Imu message from a Turtlebot. It is one of the more complex common messages. It contains a significant amount of data.

{
  "account": "A0000000000000000000",
  "data": {
    "orientation_covariance": [
      1000000,
      0,
      0,
      0,
      1000000,
      0,
      0,
      0,
      0.05
    ],
    "orientation": {
      "y": -0.0037715228945,
      "x": 0.00132820520157,
      "z": 0.326002750474,
      "w": 0.945360364184
    },
    "angular_velocity_covariance": [
      1000000,
      0,
      0,
      0,
      1000000,
      0,
      0,
      0,
      0.05
    ],
    "linear_acceleration_covariance": [
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0
    ],
    "header": {
      "stamp": {
        "secs": 16892,
        "nsecs": 650000000.0
      },
      "frame_id": "base_link",
      "seq": 1654217
    },
    "linear_acceleration": {
      "y": 0.0803365332539,
      "x": -0.029762356525,
      "z": 9.67450430845
    },
    "angular_velocity": {
      "y": -0.000157186214558,
      "x": -0.000215108706182,
      "z": -0.000292216502647
    }
  },
   "topic": "/mobile_base/sensors/imu_data",
   "platform": "ros",
   "device": "turtlebotsim",
   "type": "sensor_msgs/Imu",
   "utc_time": 1512169455.57
}

Below is a geometry_msgs/Twist message. It is usually used in a topic called /cmd_vel and tells a robot to drive forward/backwards and turn.

This tells the robot to move forward at 0.2 meters per second while turning at 0.5 radians per second for 2 seconds, repeating the message locally at 50hz and cancelling if another message updating the topic appears on mission control during that time.

{
  "message": {
    "linear": {
      "x": 0.2,
      "y": 0,
      "z": 0
    },
    "angular": {
      "x": 0,
      "y": 0,
      "z": 0.5
    }
  },
  "platform": "ros",
  "topic": "/cmd_vel_mux/input/teleop",
  "type": "geometry_msgs/Twist",
  "expiration_secs": 10,
  "repeat": {
    "rate": 50,
    "length": 2,
    "cancel_on_next_message": true
  }
}

Below would send an "Empty" message on the /dock topic to tell an iRobot Create to go find its dock.

{
  "message": {},
  "platform": "ros",
  "topic": "/dock",
  "type": "std_msgs/Empty",
  "expiration_secs": 10
}

Device Data Platforms Specification

There are multiple patforms where you can send device data depending on its type:

Device Statuses Specification

Device Smart Alerts Specification

Alerts are events specifying an issue with the robot such as:

Which are "emitted" to external services such as:

Alert System Elements:
Enabling Alerts

To use Alerts, you will need to set up the device and account appropriately:

In the account attributes:
In the device attributes:

Alert Definition

An alert is a triggering or clearing of an event which represents an issue for the device.

Alert attributes:
Example Alert:

The body must be a list of dictionaries where each dictionary represents a single alert to be triggered or cleared.

[
    {
        "name": "DILITHIUM CRYSTAL FAILURE",
        "level": "error",
        "action": "trigger",
        "type": "mc.data_outside_bounds"
        "description": "The dilithium crystal's resonant frequency is too low. Please add more fuel.",
        "attributes": {
            "remaining_mass": "12.212",
            "frequency": 31211233212321.21
        },
        "escalations":{
            "fatal": {
                  "delay": 300
            }
        }
    },
    ...
]

Supported Alert Triggers

Triggers are the specifications of what should be tracked on a device and what will create Alert events.

Currently supported Alert Emitters are:

To define alerts, you will need to create a mc.alerts.triggers attribute for each device with the list of alert types which you want to be triggered.

Details:
Example Triggers definition for a device:

Below is an example set of triggers that would activate an alert when not connected, not active or when the battery voltage is less than 14 volts. These are saved as an attribute for the device.

{
    ...,
    "mc.alerts.triggers": [
        {
            "name": "NOT_CONNECTED",
            "description": "Robot has been disconnected from the network or shutdown",
            "level": "error",
            "type": "mc.device.not_connected",
            "attributes": {
              "secs": 60
            }
        },
        {
            "name": "NOT_ACTIVE",
            "description": "Robot is connected but has not been running for at least 5 minutes",
            "level": "error",
            "type": "mc.device.not_active",
            "attributes": {
              "secs": 300
            }
        },
        {
            "name": "LOW_BATTERY",
            "description": "Battery is very low. Please charge.",
            "level": "warn",
            "type": "mc.device.data_outside_bounds",
            "attributes": {
              "topic": "/battery",
              "element": "["data"]["voltage"],
              "value": 14,
              "comparison": "<"
            },
            "escalations":{
                "error": {
                      "delay": 300
                }
            }
        }
    ],
...
}
Device not Connected Trigger

Triggered when the device has been disconnected for more than N seconds.

Type: mc.device.not_connected

Attributes:

Device not Active Trigger

Triggered if the device is either connected or not connected, but has not uploaded data on a topic on the last N seconds.

Type: mc.device.not_active

Attributes:

Data Outside Bounds Trigger

Triggered when the value of a specific topic's message element is not correct.

Type: 'mc.device.data_outside_bounds'

Attributes:

Diagnostic Error Trigger

Triggers when a diagnostic status message is received with a level greater than or equal to the min_level. If a value of ok (0) or stale (3) exists for a diagnostic, then the alert is cleared.

Type: mc.device.data_topic_diagnostics

Triggers on messages from any topic with either of these types:

Note: You do not have be running a ROS robot to use this. Just match the topic type and message format in JSON and it will work.

Attributes:

System Resources Trigger

Triggered if the is running low on system resources - cpu, ram, disk, etc.

Type: mc.device.system_resources

Attributes:

Topic Bandwidth Trigger

Triggered if a topic or the overall bandwidth is not within normal range. This is based on the previous 30 second window of time. The hz numbers are the number of actual messages in the system before they are filtered for uploading.cor

Type: mc.device.topic_bandwidth

Attributes:

Supported Alert Emitters

Emitters are external endpoints where alert notifications are sent if the alert level is high enough.

Supported Alert Emitters are:

Necessary Attributes:

Optional Filtering Attributes: * devices - a list of device IDs. The emitter will be active only for devices in this list (or in the zones provided if any). * zones - a list of zone IDs. Will extend the devices list with the devices from the specified zones. * alerts - a list of alerts names. Only the alerts with the name in this list will be considered.

If neither devices and zones attributes are specified, there won't be any device filtering.

To broadcast events to slack, email or external systems, you will need to set a 'mc.alerts.emitters' attribute for the account which owns this device, specifying at least one emitter endpoint to broadcast to.

The details of the event and a replay URL of the device at that time will be included in the data for the event.

Example Emitters attribute for an account:
{
    ...
    "mc.alerts.emitters": [
        {
            "type": "email",
            "email": "dev@work.com",
            "min_broadcast_level": "error",
            "name": "Email Dev Team",
            "devices": ["D1", "D2"],
            "zones": ["Z1", "Z2"],
            "alerts": ["Alert1", "Alert2"]
        }, 
        {
            "type": "email",
            "email": "support@work.com",
            "min_broadcast_level": "fatal",
            "name": "Email Support on Fatal Issues",
            "enabled": false,
            "error": "Email address does not exist so can not deliver"
        },
        {
            "url": ""https://hooks.slack.com/services/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",
            "type": "webhook.slack",
            "min_broadcast_level": "info",
            "name": "Post to Slack Dev Channel"
        },
        {
            "integration_key": "ekljhaewrkhj423r3kjlh3434kljh3t4",
            "type": "webhook.pagerduty",
            "min_broadcast_level": "info",
            "name": "Pager Duty Integration"
        },
        {
            "ifttt_key": "lkaserl23ra3ra3rr",
            "ifttt_trigger": "freedom_smart_alert",
            "type": "webhook.pagerduty",
            "min_broadcast_level": "info",
            "name": "IFTTT Trigger"
        },
        {
            "url": "https://myserver.company.com/webhook",
            "request": "put",
            "type": "webhook.custom",
            "min_broadcast_level": "error",
            "name": "Lava Lamp Endpoint for Errors"
        }
    ],
    ...
}

NOTE: If an emitter endpoint encounters an error when sending an alert, it will be automatically disabled and a "enabled": false value plus a "error": "reason" may appear explaining what happened.

Custom Webhook Emitter (webhook.custom)

Sends a JSON payload to the specified URL.

Requires:

Example Payload which is sent to the HTTP endpoint:

{
    "account": "A0000000000000000000",
    "device": "D0000000000000000000",
    "alert_id": "ALT0000000000000000",
    "name": "LOW_BATTERY",
    "type": "mc.device.data_outside_bounds",
    "action": "clear",
    "level": "warn",
    "attributes": {             // Specified by the trigger
        "voltage": 14.3,
        "current": -3.12,
        "state": "NOT_CHARGING"
    },
    "utc_time": 144032234.23,
    "elapsed_secs": 1213.43    // Only exists on "action": "clear"
}
Email Emitter (email)

Sends an email to the specified email address.

Requires:

Payload:

A human readable email with the details of the request.

IFTTT Webhook Emitter (webhook.ifttt)

Sends a HTTP put request to the specified IFTTT webhook endpoint.

Requires:

Payload:

IFTTT allows you to use specific values in its description language.

How to set up an IFTTT web hook:

A. Create a Webhook Applet

Click New Applet then click on the + this and search for webhooks. Select 'Receive a web request' and name it something like Freedom Smart Alerts. Next, select the service you want to trigger. If you are triggering a text output, you can specify it like this:

What: {{EventName}}<br>
When: {{OccurredAt}}<br>
<br>
ALERT: {{Value1}}<br>
LEVEL: {{Value2}}<br>
REPLAY URL: {{Value3}}

This is the payload which is sent to IFTTT for your event.

{
    "value1": "`BATTERY ISSUE` alert for `Hans Macbook Pro`",
    "value2": "ERROR TRIGGERED", 
    "value3": "https://app.freedomrobotics.ai/#/device/DF6ADA7559152E845A8168EEC8E/data?utc_time=1538523330.79"
}

B. Get your IFTTT Webhook URL

Go to https://ifttt.com/maker_webhooks and click Settings. Copy the URL, which will look something like https://maker.ifttt.com/use/glXKWifXrghs4EBfut5DfasdWpUz7YN-fBCMIU8lgH5oDu_

The trigger is the name of your event such as "freedomsmartalert" and the key is the long string after the .../use/ such as glXKWifXrghs4EBfut5DfasdWpUz7YN-fBCMIU8lgH5oDu_.

Pager Duty Emitter (webhook.pagerduty)

Creates or resolves an incident on the pager duty platform.

Requires:

How to set up:

In the pager duty interface, click Add a Service then select User our API directly and specify Events API v2. You can specify the other details appropriately for your team. You will then receive an Integration Key like 69b2235097144c03a297a05a3eca7d3b which you should use as the integration_key for setting up the emitter.

Slack Emitter (webhook.slack)

Sends a message to the specified slack channel with the details of the alert and a url to replay what happened.

Requires:

How to set up:

On the Slack platform, set up a new Incoming Webhook using their tutorial. You will need to save the URL for this webhook to use in the creating the emitter.

Microsoft Teams Emitter (webhook.microsoft_teams)

Sends a message to the specified Microsoft Teams channel with the details of the alert and a url to replay what happened.

Requires:

How to set up:

On the Teams platform, set up a new Incoming Webhook using their tutorial. You will need to save the URL for this webhook to use when the creating the emitter.

Debugging and Bug Reporting

In case of error, please reach out to us at support@freedomrobotsoft.com and include the following: