REST API Conventions

IDs and UIDs

IDs
Many GroveStreams resources can identified externally by an ID. IDs can be assigned by users and by GroveStreams API calls and are optional. They can be any string with a limit of 512 characters. Examples of resource IDs are a device's serial number, a device's MAC address, or a device data stream's identifier such as "temperature".

UIDs
Each GroveStreams resource is identified internally by a Universally Unique Identifier (UID). UIDs are assigned by GroveStreams and are mandatory. A GroveStreams UID is a version 5 SHA-1 hashed universally unique identifier (UUID).  No two GroveStreams objects have the same uid. Developers should NEVER create their own UIDs for their new GroveStreams entities. Always let GroveStreams create a new entity for you with a unique uid by appending "/new" to your GET URIs or leave the uid attribute blank during the PUT and GroveStreams will create one for you and return it with the entity that was inserted into the store.

A UID represented as a STRING data type takes 36 characters to represent. Here's an example of a UID: 3F2504E0-4F89-11D3-9A0C-0305E82C3301
A UID represented as a 16-byte (128 bit) number only takes 16 bytes.

The downside of UUIDs is that they are not easy for humans to work with because of the large number of characters and in the STRING format they consume a lot of bytes. So why do we use UUIDs for our internal entity identifiers versus say a unique LONG number?
  • The global uniqueness of a UUID will allow GroveStream entities to be passed around without the worry of "id collisions".
  • UUID generation scales. There is not a single uid generator where a bottleneck can occur and we'll never run out of UUIDs.
  • The SHA-1 hash provides enough randomness to prevent a malicious user from guessing a uid.
  • GroveStreams stores uids as 16 byte representations so they're not consuming much disk space and index look-ups on 16 bytes isn't much slower than say an 8 byte LONG key - at least not enough to outweigh the benefits of UUIDs.
  • In a high performance large distributed store, such as GroveStreams, entities need to be evenly partitioned across hundreds of servers. UUIDs give our entity key indexes the randomness needed for an even distribution across all servers. You don't need to know exactly what this means - just know that it allows you to insert and extract a lot of data very quickly.

Security

Tokens
A GroveStreams API token is an security identifier that is passed with an API call as a query string parameter or as a cookie. When passed as both a parameter and a cookie, GroveStreams will use the parameter token.

Tokens:
  • org
    • This is the uid of the organization the API call is for. All organization calls need this token only if the session token is used. api_key calls do not require the org token as the org is inferred based on the organization the api_key resides within.
  • session
    • The session token is created during the POST login or POST login_guest resource calls. The user credentials passed to the login resource will be associated with the session uid to determine access rights for each API call. Session tokens will expire during long time periods between API calls.
  • api_key
    • The api_key token is used for cases where a user email and password should not be hard-coded or stored in a file. Api_keys should be used inside devices or external web pages that make API calls to GroveStreams. Api keys can have very granular API rights assigned to them and can be restricted from devices with specific IP addresses or from specified domains. The api_key token is not the api_key resource UID, it is the api key's secret key. API keys are created and managed by a GroveStreams organization owner. The org token is not required when the api_key token is being used.
  • "Authorization: Bearer **AccessToken**" HTML Header (OAuth 2.0)
    • OAuth is used for cases where a user email and password should not be hard-coded or stored in a file or stored on a device. OAuth should also be used when each device should have its own unique security token that expires and needs to be refreshed frequently. GroveStreams is an OAuth 2.0 authorization server and can be used by other APIs such as Amazon's Alexa for authentication. More information can be found on the GroveStreams OAuth Page.


GroveStreams will use the api_key token for API calls passed with both an api_key and a session token.

SSL
GroveStreams supports SSL. Change http to https in the Resource URL to secure your calls to GroveStreams:
https://grovestreams.com/api

SSL encryption and decryption come with a performance cost. You will get better performance not using SSL if your data is not sensitive data and allows you to do so.

Date and Times

We refer to a date and time with millisecond precision as datetime throughout our API documentation. The GroveStreams API passes around datetimes as single LONG (8 bytes) data type values representing milliseconds since epoch January 1,1970 UTC. This allows for datetimes roughly +- 290 million years to millisecond precision from the epoch. Throughout the API documentation you'll see this format referred to as "epoch millis". Whenever a datetime is discussed in the documentation as a LONG data type it is safe to assume it is in "epoch millis" UTC format.

Handling datetimes as "epoch millis" is common practice in the programming world as it simplifies datetime management and improves performance. Most of the major programming languages have a date or time library that handles the conversion to and from human readable datetimes and "epoch millis". GroveStreams is written in Java 6.0 and adheres to the JDK standards for managing datetimes.

GroveStreams has support for time zones, leap years and daylight savings time during datetime calculations. GroveStreams does not support leap seconds as most applications don't have a need for this, most computer clocks are not accurate enough to be able to reflect it and supporting it would impact performance.

Request and Response Formats

The GroveStreams API uses JSON for almost all of its request and response body formats. Why does GroveStreams rely mainly on JSON?
  • GroveStreams is all about performance and JSON is faster and more scalable than XML
  • JSON, when formatted, is a standard and is human readable
  • JSON is supported by all major programming languages, all major browsers and is easy to work with (see www.json.org)

Compression

The GroveStreams API supports compression for request/response bodies. We highly encourage using compression as it will make your product and GroveStreams more scalable.

Add a request header "Accept-Encoding : gzip, deflate" to your request and GroveStreams will return your request compressed.

Add a request header "Content-Encoding : gzip" to your request when the body is compressed as gzip.


Sever-side Caching

The GroveStreams web servers will cache resource response requests for 10 seconds. This allows our servers to handle viral requests. The GroveStreams user interface frequently appends an argument called "_dc" with a unique number onto its API calls to avoid server caching for users with "Edit" rights.

HTTP Request Errors

GroveStreams has two types of error codes. HTTP status codes and internal error codes. HTTP status codes come back as HTTP status code errors. GroveStreams internal error codes will be part of the response body along with a description of the actual error.

GroveStreams Internal Error Codes
Grove Streams will map its internal error code with the following HTTP status codes:

Internal Error Code
HTTP Status Code
Description
UNKNOWN_EXCEPTION 500
Internal server error
ENTITY_MISSING 404
Resource not found
SESSION_EXPIRED 500
Session token expired due to inactivity. Watch for SESSION_EXPIRED in the response body to determine if a user should be re-authenticated.
RATE_LIMIT_EXCEEDED
403
Call rate limit has been exceeded.
ACCESS_DENIED 403
session or api_key tokens do not have rights for the action (PUT, POST, GET, DELETE) or resource (i.e. component, cycle, unit, ...).
ENTITY_VALIDATION_FAILURE 422
Resource failed validation during a PUT or SAVE. This error will also return a list of all validation warnings and errors.
CAPTCHA_VALIDATION_FAILURE 422
Captcha challenge failed.
REQUEST_VALIDATION_FAILURE 400
Slightly different than an entity validation error. Usually caused by malformed uid keys or other types of parsing errors.
EXPRESSION_PARSING 500
Stream derivation expression parsing error.
EXPRESSION_EVALUATION 500
Stream derivation expression evaluation error.
EXPRESSION_UNKNOWN 500
Stream derivation unknown expression error.
OAUTH_TOKEN_EXPIRED 400
OAuth2 access token expired.
OAUTH_INVALID_CLIENT 401
OAuth2 invalid token.
OAUTH_INVALID_GRANT 400
OAuth2 invalid grant.

List of HTTP status codes

Sample Request Error:
This is an example of the response body when a session has expired.
HTTP status code was: 401 Unauthorized
Response Body was:
{
  "message": "Session expired.",
  "errCode": "SESSION_EXPIRED",
  "success": false
}


Sample Request Error:
This is an example of a "POST component" where the component definition had warnings and errors.
HTTP status code was: 422 Unprocessable Entity
Response Body was:
{
  "message": "ERROR: '': Invalid name size. Range is 1 to 500 characters.<br>WARNING: '/Percent of Memory Used - bytes/Variable/var1': Derived stream expression does not contain dependent variable name 'var1'.<br>ERROR: '/Percent of Memory Used (WOP) - bytes/Variable/free': Functions cannot be performed on a stream base cycle.<br>",
  "errCode": "ENTITY_VALIDATION_FAILURE",
  "validationResult": {
    "validation": [
      {
        "message": "Invalid name size. Range is 1 to 500 characters.",
        "pathName": "",
        "itemType": "component",
        "valMsgType": "ERROR",
        "itemUid": "59d9d98d-bd17-4b7c-a99a-77b49fbad29b"
      },
      {
        "message": "Derived stream expression does not contain dependent variable name 'var1'.",
        "pathName": "/Percent of Memory Used - bytes/Variable/var1",
        "itemType": "stream",
        "valMsgType": "WARNING",
        "itemUid": "c8886c4c-0b21-4722-b7ae-599c24374a1f"
      },
      {
        "message": "Functions cannot be performed on a stream base cycle.",
        "pathName": "/Percent of Memory Used (WOP) - bytes/Variable/free",
        "itemType": "stream",
        "valMsgType": "ERROR",
        "itemUid": "1999ddb7-25c0-4d5a-8835-b1c947049633"
      }
    ]
  },
  "success": false
}