Skip to content

ExoSense GraphQL API

Info

The API is available at the Organization ExoSense Tier.

Overview

The ExoSense API uses GraphQL, a query language for supporting requests for data, modify objects, or to delete / create objects such as assets. GraphQL can be compared to REST and RPC (Remote Procedure Calls)

The ExoSense API is provided for software integrations such as but not limited to:

  • Retrieving and displaying Asset status and/or signal values in another business software tool.
  • Automating the creation of Assets during manufacturing of devices
  • Automating user invitations from a separate registration system or billing process

Info

Currently the API is not recommended for nor does it fully support building custom user applications.

API Endpoint

This API has a single endpoint specific to your ExoSense instance and does not change no matter what query is being made, which is a difference from RESTful APIs.

https://<$YOUR_EXOSENSE_INSTANCE_DOMAIN>/api/graphql

<$YOUR_EXOSENSE_INSTANCE_DOMAIN> is your ExoSense domain (e.g. example.apps.exosite.io)

Using the GraphQL API

All requests take the following HTTP format. Note that the call is an HTTP POST request with a JSON encoded body for the operations. This API supports two operations, being queries and mutations.

POST https://<$YOUR_EXOSENSE_INSTANCE_DOMAIN>/api/graphql
Authorization: Automation $TOKEN
Content-Type: application/json
Accept: */*

{
    "query": "....",
    "operationName": "....",
    "variables": {"myvar1":"value1","myvar2":"value2", ...}
}
  • The query field in the JSON object is required.
  • The value for query is a GraphQL query encoded as a string.
  • operationName and variables optional fields, operationName is only required if multiple operations are present in the query.

Hint

GraphQL queries are like a GET request in a RESTful API. GraphQL mutations operate like POST, PATCH, and DELETE.

Queries

The GraphQL query essentially asks for fields in an object. A very simple example shows how to ask for a list of assets with the asset id and name. The object being 'assets'. Note that this call as is assumes all assets at the root level of the API token permission access.

For more information of GraphQL queries, the graphql.org site covers these details in more depth

Learn about GraphQL Queries

# Example of a Query, for Assets and to include the id and name fields
{
  assets {
    id
    name
  }
}
// Response JSON object
{
  "data": {
    "assets": [
      {
        "id": "6fe9fdcd-926e-4b39-a224-f0fd8c00a99b",
        "name": "Customer A Child 1 Asset 1"
      },
      {
        "id": "c6b918c7-a61d-4be9-aed6-06582d243e87",
        "name": "Customer A Child 1 Asset 2"
      },
      {
        "id": "85290cc8-db5b-4506-9f66-e1944f0a7322",
        "name": "Customer A Child 1 Asset 3"
      },
      {
        "id": "da965bda-d9ea-48d0-b2bf-869773744d3c",
        "name": "Customer A Child 1 Asset 4"
      },
      {
        "id": "73b79e78-d159-4c3e-b40d-f8b55dd9e286",
        "name": "Customer A Child 1 Asset 5"
      },
      {
        "id": "3a5b452a-a1b3-4efb-90c2-128853424491",
        "name": "Customer A Child 1 Asset 6"
      },
      {
        "id": "21811b22-d01a-4867-b1b2-81a2b554b63c",
        "name": "Customer A Child 1 Asset 7"
      },
      {
        "id": "3582118a-d390-457e-9836-2e72331d2b70",
        "name": "Customer A Child 1 Asset 8"
      }
    ]
  }
}

Arguments

Arguments allow you to specify query details for the the object and all nested objects. An example is to include the pagination argument (which happens to be an object itself) in with the assets query to limit the number of returned items.

# Query with arguments
{
  assets(pagination: {limit:3,offset:0}) {
    id name
  }
}
// JSON response, noting that only 3 assets were returned
{
  "data": {
    "assets": [
      {
        "id": "6fe9fdcd-926e-4b39-a224-f0fd8c00a99b",
        "name": "Customer A Child 1 Asset 1"
      },
      {
        "id": "c6b918c7-a61d-4be9-aed6-06582d243e87",
        "name": "Customer A Child 1 Asset 2"
      },
      {
        "id": "85290cc8-db5b-4506-9f66-e1944f0a7322",
        "name": "Customer A Child 1 Asset 3"
      }
    ]
  }
}

Aliases

Aliases allow you to query the same field but different arguments, so the result can be differentiated.

# Example query using aliases
{
  asset1: asset(id: "6fe9fdcd-926e-4b39-a224-f0fd8c00a99b") {
    name
  }
  asset2: asset(id: "c6b918c7-a61d-4be9-aed6-06582d243e87") {
    name
  }
}
{
  "data": {
    "asset1": {
      "name": "Customer A Child 1 Asset 1"
    },
    "asset2": {
      "name": "Customer A Child 1 Asset 2"
    }
  }
}

Note that GraphQL Fragments can also be used for more complex queries that reuse many of the same variables/fields, check out the GraphQL documentation for more information.

Operation Name & Type

Operation names are recommended, though optional unless you have a multi-operational document in which they are required. They allow the code to be more user friendly, in addition to helping with debugging. It's like a function name in programming.

The operation type is either query mutation or subscriptiondescribing the type of operation. It's required unless using shorthand syntax (the simple examples above). Shorthand syntax assumes no name or variables are provided.

# Example that includes the Operation Type and Name
query GetAllAssets {
  assets(pagination:{limit:3}) {
    name id
  }
}
// Resulting JSON object response
{
  "data": {
    "assets": [
      {
        "id": "6fe9fdcd-926e-4b39-a224-f0fd8c00a99b",
        "name": "Customer A Child 1 Asset 1"
      },
      {
        "id": "c6b918c7-a61d-4be9-aed6-06582d243e87",
        "name": "Customer A Child 1 Asset 2"
      },
      {
        "id": "85290cc8-db5b-4506-9f66-e1944f0a7322",
        "name": "Customer A Child 1 Asset 3"
      }
    ]
  }
}

Mutations

Mutations allow server side operations such as creating, deleting, and modifying objects. Mutations are similar to queries in syntax and responses. The one distinction is that when there are multiple fields in a mutation, they run in series, rather than parallel.

# example mutation request to delete an asset
mutation DeleteAsset  {
  deleteAsset(id: "3582118a-d390-457e-9836-2e72331d2b70") {
    id
  }
}
//response
{
  "data": {
    "deleteAsset": {
      "id": "3582118a-d390-457e-9836-2e72331d2b70"
    }
  }
}

Variables

Variables allow the client application making API requests to use a static string for the query but support dynamic variables that could be changed based on user inputs, etc. Note that the variable has to be declared for the query including specifying the type (in this case the type is Pagination ). Note that types are defined by the API itself. The variable is then passed in with the HTTP requests JSON body. Variables are declared with a $ prefix.

# query that includes a variable
query GetAllAssets($UserPaginationSetting: Pagination ) {
  assets(pagination: $UserPaginationSetting)
  {
    name id
  }
}

# the variables dictionary to pass in with the static string
{
    "UserPaginationSetting": {
        "limit": 3
    }
}
POST /api/graphql HTTP/2
Host: example.apps.exosite.io
Content-Type: application/json
Authorization: Automation <TOKEN HERE>
Accept: */*
Content-Length: 195

{
  "query":"query GetAllAssets($UserPaginationSetting: Pagination ) { \n  assets(pagination: $UserPaginationSetting) \n  {\n    name id\n  }\n}\n",
  "variables":{"UserPaginationSetting":{"limit":3}}
}

Format Notes

The examples provided in this document generally have been formatted for ease of readability. The following examples work exactly the same but the resulting query string passed into the HTTP request will contain any additional characters such as new lines.

query GetAllAssets {
  assets(pagination:{limit:3}) {
    name id
  }
}
# Note: drops the Operation Name
query {
    assets(pagination:{limit:3}) {name id}
}
# Note: Shorthand syntax only supported for query type
# and not recommended for documents containing many queries

{ assets(pagination:{limit:3}) {name id}}
# Note: Query is all on one line, otherwise same

query GetAllAssets { assets(pagination:{limit:3}) {name id}}

The resulting HTTP request for this last one looks like:

POST /api/graphql HTTP/2
Host: example.apps.exosite.io
Content-Type: application/json
Authorization: Automation <TOKEN WOULD BE HERE>
Accept: */*
Content-Length: 74

{"query":"query GetAllAssets { assets(pagination:{limit:3}) {name id}}"}

Authentication

This API uses Authentication Tokens that can be created in the application. Each token has a specific permission. This token is used in the Authorization header in the HTTP request. $TOKEN below is replaced with the actual token.

Authorization: Automation $TOKEN

Creating an API Token

If you haven't already, navigate to Setup > API Tokens and create a new token. The role you choose for the token, similar to a user role, will limit the ExoSense™ access granted to the token.

Save the token in a safe and secure location. You won't be able to access it again, so don't lose it. If you do lose it, a new token must be generated.

Responses

GraphQL responses takes the same shape as the query, returned as a JSON object. The response JSON object contains two fields, data and errors. The errors field is only returned if there were errors, otherwise it is not present.

{
  "data": { ... },
  "extensions": { ... },
  "errors": [ ... ]
}

Resource Limitations

The ExoSense GraphQL API has limitations in place to protect against excessive or abusive calls to Exosite's servers and prevent service disruption. These resource usage limits are tracked per request, as a rate for the API Token, and as a rate for the specific ExoSense instance.

The following resources are used to validate requests to the ExoSense GraphQL API. These resources, unless noted, are used per request and over a time period (rate).

Resources
Unit Description
time ms The time resource is a measure of the execution time taken for the request.
query [Future / Not Used] Represents the function's baseline resource usage
cost [Future / Not Used] Calculated as a "cost" weighting of how much information is being requested.
requests requests Number of API requests per API token within Rate Limit window. (Note: Only used for Rate)

Per Request Limits

Each API request will be validated against the following resource limits.

Resource Limit Enforced
time 10000 Yes
cost TBD No (Future)

Request Limit Usage and Limits Details in the Request Response

{
  "data": {...},
  "extensions": {
    "cost": {
      "usage": {
        "cost": 0,
        "time": 182
      },
      "limits": {
        "cost": 1000000,
        "time": 10000
      }
    }
  }
}

Handling the time limit

If developers see their per request is nearing the time limit, they should split up their requests.

Rate Limits

The rate limits uses a rolling window to enforce resource limitations over time.

Rate limit window: 10 seconds (rolling)

The following rate limits are in place for each API Token within the rate limit window.

Resource Rate Limit Enforced
time 100000 Yes
requests 10 Yes
cost TBD No (Future)
query TBD No (Future)

Rate Limit Usage and Limits in Request Response

{
  "data": {...},
  "extensions": {
    "cost": {
      "rateUsage": {
        "time": 825,
        "cost": 0,
        "query": 28,
        "requests": 1
      },
      "rateLimits": {
        "cost": 10000000,
        "query": 500000,
        "time": 100000,
        "requests": 10
      }
    }
  }
}

Tracking Limits

Each API request within the limits will include an extensions object in addition to the returned data. The information in extensions should be used to adjust API usage before exceeding limits. If a limit has been exceeded, only the errors object will be returned.

Summary of Response Objects

data:           # Requested data if request is within limits
extensions:     # Object Returned with response in addition to the requested `data`
  cost:   
    rateUsage:   # Object containing the current state of the rate limit usage after the request
    rateLimits:  # Object containing the rate limits in place for the API token
    usage:       # Object containing the specific request's usage of resources
    limits:      # Object containing the per request resource limits in place
    time:        # The time the request took
    overUsageLimit: # Deprecated, boolean flag, no longer used
    overRateLimit:  # Deprecated, boolean flag, no longer used
errors:  # Array of error messages if request exceeded any limits

An example response that is under Request Limits

{
    "data": {...},
    "extensions": {
        "cost": {
            "overRateLimit": false,
            "rateUsage": {
                "query": 0,
                "time": 0,
                "requests": 1,
                "cost": 0
            },
            "rateLimits": {
                "query": 1000000,
                "time": 100000,
                "requests": 10,
                "cost": 1000000
            },
            "limits": {
                "query": 1000000,
                "time": 10000,
                "cost": 1000000
            },
            "time": 115,
            "overUsageLimit": false,
            "usage": {
                "query": 0,
                "time": 115,
                "cost": 0
            }
        }
    }
}

An example response that is over a Request Limit

{
    "errors": [
        {
            "message": "ExceededRateLimitRequests"
        }
    ]
}

Limit Errors

There are a number of possible limit related errors that can be encountered when requesting data through the API. They fall into two categories - Usage and Rate. Usage errors are encountered when a single query exceeds any of the usage limits defined above. Rate errors are encountered when a single field in the usage limits is reached over a period of time.

Error
Cause Action
ExceededMaximumTime This error is available, but will not be encountered on a per request basis, only on rate limiting. A timeout will occur before it is reached Not a reachable error
ExceededRateLimitTime The sum of time taken for requests in the window has exceeded the per request time * rate scale Try again after the rate period` and reduce the frequency of requests
ExceededRateLimitRequests The number of requests has exceeded the requests rate limit for the given window_size for the token Reduce the request frequency and try again when the window rolls over from the first request

Error Handling

  • If usage errors are encountered, consider using pagination and requesting smaller chunks of data to prevent asking for too much data in one particular request.
  • If rate limits are encountered, consider reducing the frequency that requests are made.

Schema

GraphQL is powerful in that with a request, you can perform an introspection of the API realtime. (i.e. it is self documented) . Details about GraphQL Introspection

To perform this introspection, you can query the __schema and __type fields, although there are several tools that support GraphQL natively and provide an automated built-in way to view the documentation.

Tools

There are a number of tools that natively support GraphQL to test and run queries and introspect of the API, including Insomnia and Postman which are commonly used tools to test HTTP requests, REST APIs, etc.

https://insomnia.rest/graphql/

https://www.getpostman.com/product/api-client

Libraries

There are a number of libraries, tools, and services built for GraphQL, which can be found on the GraphQL organization site.

https://graphql.org/code/

Getting Started

Exosite provides an example JSON file that can be imported into Insomnia and provides a number of example queries and mutation requests. Please submit a support request to get a copy of this file.


Last update: October 11, 2022