REST API Overview
Introduction
This document provides a high level overview of the REST interface defined in the RIoT Secure OASIS product.
The REST API provides access to data in the OASIS Cloud on the RIoT Secure Platform. The API is based on restful HTTP requests. Using this API, it is possible to manage virtually all aspects of the platform. Applications include monitoring of devices, automated device and firmware management, batch updates of application firmwares, and more.
The REST API can be called from any programming language that can communicate over TCP/IP. Many programming languages provide high-level support and have libraries for making HTTP requests. This simplifies using the REST API.
The REST API can be accessed both from web-based client code and from server-side code. The programming guides use PHP and the example language, but languages like C/C++, Golang, Python and JavasScript can also be used.

The RIoT Secure Management Console is a web-based application that uses client-side JavaScript and XHR to access the OASIS Cloud and perform management of users, devices, and firmwares.
The IoT-Server is a RIoT Secure system component that calls the REST API to update device status.
Custom applications can use the REST API to implement a wide variety of management tools and dashboards across different industries.
See the Architecture section for further details regarding the role of the REST API in relation to the RIoT Secure Platform.
A reference that contains all API routes is available in the REST API Routes documentation.
REST Protocol
Representational State Transfer (REST) is a style of software architecture implemented over HTTP to provide a uniform, stateless infrastructure for client-server communication. The platform exclusively uses the HTTPS scheme with SSL for communication to ensure the channel is cryptographically secure.
URI Interface
The REST interface utilizes the Uniform Resource Identifier (URI) which has the following structure:
scheme://host:port/service_root/resource_path?query_options
Request Methods
The REST interface defined is restricted to HTTP methods, as detailed below:
| Method | Description |
|---|---|
| POST | used to create a resource |
| GET | used to retrieve a resource |
| PUT | used to update or upsert a resource |
| DELETE | used to delete a resource |
| OPTIONS | used to retrieve permitted HTTP methods and schema of a resource |
Response Codes
The following HTTP return codes and their meaning are defined (coupled to method type):
| Return Code | Result | Method |
|---|---|---|
| 2xx Success | ||
| 200 | OK | GET, OPTIONS |
| 201 | Created | POST |
| 202 | Accepted | PUT |
| 204 | No Content | GET, DELETE, OPTIONS |
| 3xx Redirection | ||
| 304 | Not Modified | GET, DELETE |
| 4xx Client Errors | ||
| 400 | Bad Request | POST, PUT |
| 401 | Unauthorized | POST, GET, PUT, DELETE, OPTIONS |
| 403 | Forbidden | POST, GET, PUT, DELETE, OPTIONS |
| 404 | Not Found | GET, PUT, DELETE |
| 405 | Method Not Allowed | POST, GET, PUT, DELETE, OPTIONS |
| 406 | Not Acceptable | POST, PUT |
| 409 | Conflict | POST |
| 410 | Gone | GET, PUT, DELETE |
| 415 | Unsupported Media Type | POST |
| 421 | Misdirected Request | POST, GET, PUT, DELETE, OPTIONS |
| 423 | Locked | DELETE |
| 5xx Server Errors | ||
| 500 | Internal Server Error | POST, GET, PUT, DELETE, OPTIONS |
The REST interface follows the W3C standard for HTTP response codes as described as part of RFC2616 (Hypertext Transfer Protocol HTTP/1.1).
http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
Query Options
The following query options are supported:
| Option | Description |
|---|---|
| expand | The response will be expanded to include all fields and sub fields of the object. This option is supported for routes that return an object or an array of objects. |
For example, the following request will return a JSON array of device objects for the tenant with id 2:
GET /tenant/2/devices?expand
Without expand the same request will return a JSON array with ids of device objects:
GET /tenant/2/devices
Server Implementation
Implementation notes to understand the server side work-flow with the handling of REST API requests.
REST API Entry Point

The above flow-chart represents the handling of the base REST API entry point; which deals with user authentication, checking that the required method exists, if there are any dependencies and if the user actually has permission to perform the request. At each level, when a condition isn’t satisfied, an error code is returned to the client.
POST

POST is used to create an entity in the platform. A JSON schema must exist for each entity, the data provided is checked and then validated to ensure data integrity. Under some circumstances, there may be a dependency which causes conflict (e.g. admin email address) but creation occurs when data validation is successful.
GET

GET is used to obtain an entity in the platform. If the resource exists and is not orphaned, the data is returned.
PUT

PUT is used to update an entity, or to upsert properties of an entity in the platform. A JSON schema must exist for each entity, the data provided is checked and then validated to ensure data integrity. If the resource exists and is not orphaned the data is updated.
DELETE

DELETE is used to remove an entity from the platform. If the resource exists and is not orphaned, the appropriate dependency clean up is performed and then the entity is marked as orphaned - which retains a shadow of the entity.
OPTIONS

OPTIONS is used to identify permissible REST methods but also access the underlying JSON schema of the entities of the platform. The JSON schema contains all the relevant data types of each entity coupled with a number of UX (user experience) hints to assist in the development of a client user interface.
Cache Validation
The REST interface adopts a checksum technique utilizing the entity tag field defined as part of the HTTP header to provide the ability to cache resource objects being requested. When a resource is requested; the ETag attribute in the HTTP header will be defined, which is effectively an MD5 hash of the returned content:
>> RESPONSE:
ETag: "4D9443EF1957D572720ED681A68E7019"
>> REQUEST:
If-None-Match: "4D9443EF1957D572720ED681A68E7019"
Subsequent requests for the same resource can include the ETag value in the HTTP request header within the If-None-Match attribute; when there is no change, a HTTP 304 Not Modified return code will be returned otherwise the content returned with a HTTP 200 OK return code where the object being referenced by the requesting entity should be replaced.
More information about the ETag attribute:
http://en.wikipedia.org/wiki/HTTP_ETag
User Authentication
The REST interface utilizes a proprietary authentication handler that encapsulates hashing techniques to ensure that hashes are single use and that user credentials are never transmitted and the implementation can be stateless, not depending on cookies or sessions.
A non-authorized request will look and provide a response as follows:
>> REQUEST:
GET /auth HTTP/1.1
Host: riotsecure.se
>> RESPONSE:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: oasis realm="riotsecure"
Authentication requires the requester to create an authority hash from a number of properties that can be verified independently; including, but not limited to hashed user credentials, the URI path and a randomly generated client nonce value.
The authority hash can be generated as follows:
hash1 = md5("username:realm:password");
nonce = EPOCH.toHex() + random_string(24);
hash2 = md5("method:uri");
authority = md5(hash1 + ":" + nonce + ":" + hash2);
An example using real values, in preparation for a request (our example forces uppercase on md5 values):
hash1 = md5("user@host.com:riotsecure:qwerty1234"); // "FF4FF42FB2F5817279588A8D2372BD06"
nonce = EPOCH.toHex() + random_string(24); // "5EE5E445KAHT2OSOVDA4CDU9JUBXO2VV"
hash2 = md5("GET:/auth"); // "F8DD4D2C31DDE73FC60874F08AF54D0E"
authority = md5("FF4FF42FB2F5817279588A8D2372BD06: \
5EE5E445KAHT2OSOVDA4CDU9JUBXO2VV: \
F8DD4D2C31DDE73FC60874F08AF54D0E"); // "<b>02139D7FD9915D75A155111F84C3160B</b>"
An authorized request would then look and respond as follows:
>> REQUEST:
GET /auth HTTP/1.1
Host: riotsecure.se
Authorization: oasis username ="user@host.com",
nonce ="5EE5E445KAHT2OSOVDA4CDU9JUBXO2VV",
authority="02139D7FD9915D75A155111F84C3160B"
>> RESPONSE:
HTTP/1.1 200 OK
Content-Type: application/json
{
"id_tenant": 1,
"id_user": 1,
"permissions":
{
...
}
}
An important design aspect of the authentication method is that the user credentials are never transmitted (only the hash is transmitted) and that the nonce value constantly changes between requests. Any nonce values that have previously been used from a specific user will be rejected; to ensure that no clashes occur, it is required to prefix the nonce value with hexadecimal representation of an EPOCH timestamp. The EPOCH timestamp on any request is valid if it is within 60 seconds of the servers timestamp.
On creation, or changing user credentials; the information transmitted should be hashed as defined. The authentication system is dependent on this for verifying the identity of a user using the REST interface.
URI Prefixes
A number of URI (Universal Resource Identifier) prefixes have been defined to logically group different types of resources together.
| Prefix | Description |
|---|---|
| /auth | used to retrieve user permissions and authentication details |
| /global | used for requests related to system level data |
| /modem | used for requests related to modems |
| /microcontroller | used for requests related to microcontrollers |
| /tenant | used for requests related to tenants |
| /tenant/{id}/user | used for requests related to users within a tenant |
| /tenant/{id}/device | used for requests related to devices within a tenant |
| /tenant/{id}/firmware_appl | used for requests related to application firmwares within a tenant |
| /tenant/{id}/packet | used for requests related to packets within a tenant |
| /tenant/{id}/interface | used for requests related to hardware interfaces within a tenant |
| /firmware_core | used for requests related to core firmwares |
| /image | used for requests related images |
JSON Schema
JSON Schema is a specification for a JSON-based format for defining the structure of the data to be communicated. It provides a contract for what data is required for a given application and how it can be modified. It is intended to be self-descriptive yet also provide validation, documentation, and interaction control mechanisms.
Example object schema:
{
"$id": "schema.example",
"type": "object",
"properties":
{
"id":
{
"type": "integer"
},
"keys":
{
"type": "object",
"properties":
{
"name":
{
"type": "string"
},
"price":
{
"type": "string"
}
},
"additionalProperties": false
},
"meta":
{
"type": "object",
"readOnly": true,
"properties":
{
"created":
{
"$comment": "oasis-timestamp",
"type": "integer"
},
"modified":
{
"$comment": "oasis-timestamp",
"type":
[
"integer",
"null"
]
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}
Example data object:
{
"id": 1,
"keys":
{
"name": "product name",
"price": "$100.00"
},
"meta":
{
"created": 1592101813,
"modified": null,
}
}
The meta data defined for each entity can be obtained by using the OPTIONS method on specific routes defined within the REST interface.
The JSON schema is used to validate the data communicated to filter incorrect or badly formed requests, effectively verifying:
- complete and well-formatted data is provided to the HTTP request handler
- complete and well-formatted data is returned by the HTTP response handler
- correct properties and property values have been provided to the HTTP request handler
- correct properties and property values have been provided by the HTTP response handler
There are many free, online resources that exist to validate JSON data against JSON schema.
References:
https://www.jsonschemavalidator.net/
https://json-schema.org/draft/2019-09/json-schema-validation.html
UX Hints
A number of UX (User Experience) hints are defined which provide suggestions, for how the user interface should present or collect data from the end user to maximize the user experience.
| UX hint | Description |
|---|---|
| oasis-password | use an extra "validation" field to verify value |
| oasis-image | use an image, reference to item in asset store |
| oasis-timestamp | use a formatted DateTime to present an EPOCH timestamp |
| oasis-select | use a select pull-down list (single |
| oasis-checkbox | use a checkbox (multiple) or radio button group (single) |
| oasis-textarea | use a text area (multi-line field) for entering information |
| oasis-index | use a memory based index to ensure order of objects |
| oasis-multiobject | use a selector to determine which attribute definition to use |
| oasis-crossreference | when inputs are linked together; on change they also update |
| oasis-permission | use a series of checkbox inputs to define CRUDO permissions |
| oasis-array | when an attribute is an array of objects (multiple items) |
| oasis-contextual | when an attribute requires a specific context to be used |
The UX (User Experience) hint is defined within the "$comment" attribute of the object in the JSON schema.
oasis-password
The oasis-password UX Hint is specifically for providing the password of a user. The client should ideally present it as a password input field (shows an asterix when typing), but at the same time provide the ability to verify the password to avoid setting a random password.
oasis-image
The oasis-image UX Hint is specifically designed for the presentation and/or selection of images associated to an entity. The client should allow the user to select from a list of image assets available on the platform.
{
"image":
{
"$comment": "oasis-image",
"type": "string",
"enum-ref":
{
"$ref": "schema.image#name"
},
"enum":
[
"mkr-gsm-1400",
"mkr-nb-1500",
"mkr-wifi-1010",
"mkr1000"
],
"required": true
}
}

oasis-timestamp
The oasis-timestamp UX Hint is specifically designed to present an EPOCH timestamp as a human readable (formatted DateTime); such as Wed, 13 Aug 2020 12:34:56 GMT; preferably, utilizing the users time zone.
oasis-select
The oasis-select UX Hint is specifically designed for a single value selection from an enumeration of values. It is typically shown as a pull down menu input UI component that can only allow the selection of one value.
oasis-checkbox
The oasis-checkbox UX Hint is specifically designed to display all values for selection from an enumeration of values but allow for either one (singleton) or multiple (array of values) to be selected. The client should detect the data type (singleton, or array) and use the appropriate UI component to suit the purpose.
oasis-textarea
The oasis-textarea UX Hint is specifically designed to display a multi-line textual area for showing the value.
oasis-index
The oasis-index UX Hint is specifically designed to indicate that the input is a index that identifies the order in which the object is ordered/positioned within the parent object. The client should manage the values, allowing the user to modify them via “move-up” and “move-down” UI elements, ensure that no duplicates and values are continuous in nature.
oasis-multiobject
The oasis-multiobject UX Hint is specifically designed to display a number of completely different objects on the display based on a value that is obtained from another input (such as oasis-select).
The selector has a pre-defined unique identifier that when combined with the value of the input item identifies which of the objects in the linked attribute should be evaluated.
For example, given this selector object schema:
"selector":
{
"$comment": "oasis-select",
"type": "string",
"enum":
[
"value1",
"value2",
"value3",
"value4"
]
}
The schema for the multiobject is used to retrieve the type for the object, which is selected from the in the “type” field:
"value":
{
"$comment": "oasis-multiobject",
<b>"$id": "#component-@selector",</b> // value defined by "selector"
"type":
[
{
<b>"$id": "#component-value1",</b> // when "selector" is "value1"
"type": ...
},
{
<b>"$id": "#component-value2",</b> // when "selector" is "value2"
"type": ....
},
...
]
}
The unique identifier in the UX Hints is defined within the "$id" attribute of the object in the JSON schema.
Additional details and examples of multiobjects can be found in the REST API Data Model documentation.
oasis-crossreference
The oasis-crossreference UX Hint, similar to the oasis-multiobject UX Hint is specifically for linking two or more attributes together, however specifically at primary data types. When the value of the selector is changed (such as an oasis-select) the value of the linked attribute of the same data type is also changed.
For example, given this selector object schema:
“selector”: { “$comment”: “oasis-select”, “type”: “string”, “enum”: [ “value1”, “value2”, “value3”, “value4” ] }
The schema for the crossreference is used to retrieve the corresponding value for the object, which is selected from the in the “enum” field:
“value”: { “$comment”: “oasis-crossreference”, "$id": “@selector”, // value defined by “selector” “type”: “string”, “enum”: [ “data1”, // when “selector” is “value1” “data2”, // when “selector” is “value2” “data3”, // when “selector” is “value3” “data4” // when “selector” is “value4” ] }
The unique identifier in the UX Hints is defined within the "$id" attribute of the object in the JSON schema.
Additional details and examples of crossreferences can be found in the REST API Data Model documentation.
oasis-permission
The oasis-permission UX Hint is specifically for user permissions to allow the client to be developed with a consistent look and feel while the user views and changes permissions. A schema of a key that uses this attribute would look like:
{
"$comment": "oasis-permission",
"type": "array",
"items":
{
"type": "string",
"enum":
[
"C", <-- available
"R", <-- available
"u", <-- NOT available
"d", <-- NOT available
"O" <-- available
]
}
}
A user permission entry contains characters from the acronym “CRUDO” - create, read, update, delete and options that directly correlate to the REST API access methods POST, GET, PUT, DELETE and OPTIONS respectively. When the character is uppercase, it is available for selection - when lowercase, the access method does not exist and hence it should not be selectable.
oasis-array
The oasis-array UX Hint is specifically for defining a collection of objects (multiple items) - as a collection; they should be displayed in a form of a table where the user can select individual items to show each object. The UX Hint oasis-permission is an example of a collection of one or more strings but has a custom implementation.
oasis-contextual
The oasis-contextual UX Hint is specifically designed to be used when the display of the attribute depends on the context in which the JSON schema is being used. A specific use-case exists within the OASIS platform when an attribute provided in an oasis-multiobject is being used to generate a schema of possible values.
User Defined Components
A number of User Defined Components to represent sensors and actuators (hardware components) used in the platform. They exist to help define the attributes within the sensor and assist to define the communication data packet format and the associated compacting and extraction of values over the platform.
User defined components are created and accessed using the /tenant/{id}/packet family of REST API routes.
The following packet component types are available:
| Component | Description |
|---|---|
| component-bitfield | use for flags, where multiple bits can be set |
| component-enum | use to select a single value from a range of values |
| component-range | use a range (min..max) to map into a set of bits |
| component-scalar | use a scale and offset to map a set of bits to raw data |
| component-integer | a signed or unsigned whole number (integer) |
| component-string | a NULL terminated string of characters |
The User Defined Component feature utilizes oasis-multiobject UX (User Experience) hint together with the packet and component REST API defined in this document. The bit numbering is based on LSB 0-bit-numbering, meaning the LSB (least significant bit) is bit 0 and the MSB (most significant bit) is bit number n-1.
component-bitfield
The component-bitfield object allows the reservation of a specific number of bits to represent on/off values (boolean) with multiple conditions to be defined at the same time. Each entry utilizes a bit in the data packet, to maximize the usage the number entries should be the bitdepth defined.
Example:
{
"name": "Car Status Flags",
"format": "bitfield",
"value":
{
"bitdepth": 7,
"items":
[
{ "bit": 6, "meaning": "Driver Seated" },
{ "bit": 5, "meaning": "Head Lights On" },
{ "bit": 4, "meaning": "Parking Lights On" },
{ "bit": 3, "meaning": "Boot Door Open" },
{ "bit": 2, "meaning": "Right Front Door Open" },
{ "bit": 1, "meaning": "Left Front Door Open" },
{ "bit": 0, "meaning": "Fuel Cap On" }
]
},
"order": 0
}

In the above case; a value of 0 would have no bits set - so, all the conditions would be treated as FALSE. The bitfield has 127 possible values, when it has a value of 97 (64+32+1), 1100001 in binary, it would indicate that the driver is seated (64), the head lights are on (32) and the fuel cap is on (1).
component-enum
The component-enum object allows the selection of a single element from a list of values. The index acts as a lookup into a table of values, the number of possible values is 2n-1, where n represents the bitdepth defined which means not all possible values may be utilized.
Example:
{
"name": "Gear Position",
"format": "enum",
"value":
{
"bitdepth": 3,
"items":
[
{ "index": 0, "meaning": "P" },
{ "index": 1, "meaning": "R" },
{ "index": 2, "meaning": "N" },
{ "index": 3, "meaning": "D" },
{ "index": 4, "meaning": "1" },
{ "index": 5, "meaning": "2" },
]
},
"order": 0
}

In the above case; the bitdepth defines that up to seven items can be defined (23-1 = 7). When a value of 3 is provided, it will refer to the “Drive” position of the gear stick and no other values are distinct and not used.
component-range
The component-range object allows the reservation of a specific number of bits to store a range of values. The entire bit range is utilized to produce a value between 0 and 2n-1, where n represents the bitdepth defined. The provided value is then scaled across the range (maximum - minimum) of potential values over the number of possible values, and offset by the minimum value.
Example:
{
"name": "Temperature",
"format": "range",
"value":
{
"bitdepth": 8,
"minimum": -40,
"maximum": 100,
"units": "degrees C"
},
"order": 0
}

value = (data * (maximum - minimum)/(2<sup>bitdepth</sup>-1)) + minimum;
In the above case; the bitdepth defines that up 255 possible values can be defined (28-1 = 255). The value of 0 represents the minimum value of -40 degrees C, and a value of 255 represents the maximum value of 100 degrees C. Any value in between, is mapped on a linear scale, using the provided formula. A value of 127 would represent just under halfway between minimum and maximum, which is 29.725 degrees C.
The more bits reserved by the data type, the more refined and accurate the eventual calculated value.
component-scalar
The component-scalar object, similar to the component-range object allows the reservation of a specific number of bits to store a range of values. The entire bit range is utilized to produce a value between 0 and 2n-1, where n represents the bitdepth defined. The provided value is then scaled using a multiplier and then offset by a specific value. Instead of mapping to a range, the resolution of accuracy is defined by the multiplier.
Example:
{
"name": "Latitude",
"format": "scalar",
"value":
{
"bitdepth": 32,
"signed": true,
"multiplier": 0.0000001,
"offset": 0,
"units": "degrees"
},
"order": 0
}

In the above case; the bitdepth, reserving one-bit for sign of 32 defines a possible value between -2,147,483,648 and 2,147,483,647. When applying the multiplier provided, the provided value is then converted to a value between -214.7483648 and 214.7483647, more than sufficient to store a GPS latitude position. An advantage is that the value is effectively decimal point accurate and as such is identifiable from the provided value easily. Using component-range would improve accuracy when using the same bitdepth.
The more bits reserved by the data type, the more refined and accurate the eventual calculated value.
component-integer
The component-integer object allows the representation of a signed or unsigned whole number (integer).
Example:
{
"name": "fix",
"format": "integer",
"value":
{
"bitdepth": 1,
"signed": false,
"units": ""
},
"order": 0
}
In the above case; the bitdepth defines that a single bit is used and that the possible value is an unsigned integer can have a value of 0 or 1.
component-string
The component-string object allows the representation of a NULL terminated string.
Example:
{
"name": "message",
"format": "string",
"order": 0
}
A string does not have the “value” field.

Entity Cross References
Complex data modelling requires that entities be linked together not only hierarchically; but also independently. The REST API provides named cross-referencing to reduce client side network requests and avoid compromising of data integrity; linking of entities is evaluated and managed server side.
Example of schema with a reference to another schema:
{
"id_modem":
{
"type": "integer",
"readOnly": true
},
...
"modem":
{
"$comment": "oasis-select",
"type": "string",
"enum-ref":
{
"$ref": "schema.modem#keys/name"
},
"readOnly": true,
"required": true
},
}
Referenced schema:
{
"modem":
{
"$comment": "oasis-select",
"type": "string",
"enum-ref":
{
"$ref": "schema.modem#keys/name"
},
<b>"enum":
[
"MKR 1000",
"MKR WiFi 1010",
"MKR GSM 1400",
"MKR NB 1500"
],</b>
"readOnly": true,
"required": true
},
}
In the above example; an entity is linked to the modem entity; when the schema is generated for the client - a name attribute from all modem entities is injected for presentation to the user where a link can be defined. When the resulting value is submitted to the REST API, the server does internal cross referencing to establish and maintain the link between the entities.