EVRYTHNG Developer Hub

Welcome! Here's where you'll find what you need to start working with EVRYTHNG as quickly as possible. There are comprehensive guides, documentation, and support if you get stuck. We encourage you to dive in and explore.

Create Free Account Read Documentation
Suggest Edits

Standard API Introduction

 

The Standard API is a REST API between client apps and the EVRYTHNG Platform. It is used to create and manage projects, applications, products, thngs, actions, and more platform resources.

This API Reference section of the Developer Hub contains sections for each module of the API. The overview of each section goes through conceptual material about how the module works.

For more detail on the more high-level aspects of the EVRYTHNG API as well as walkthroughs and SDKs, check out the Documentation section.


Requests

The API root domain is shown below, and must be used for all API requests. URLs shown with no domain use the API root as their domain unless otherwise specified:

https://api.evrythng.com


Examples

Example requests are shown for all endpoints for at least the main Create, Read, Update, and Destroy (CRUD) types for each resource. Most example requests shown here are written first in HTTP specification format, then in cURL, evrythng.js, and evrythng-java-sdk if appropriate.

Sub-pages of some sections contain interactive examples of the most common uses of that module's endpoints. You can use the output from the creation (POST) examples to try out updating (PUT) and deleting (DELETE) those resources entirely within the browser, without navigating away. Reading (GET) request examples will return pre-set data from an example account.

More examples of other usage examples can be found in each section's overview page.

Notes

  • All timestamps are Unix time in milliseconds (i.e, number of milliseconds since Unix epoch Jan 1 1970). Time zones are currently not supported, therefore client applications must handle the conversion from UNIX timestamps to appropriate time zone.
  • Strings are always in UTF-8 format.
 

Multiple Operators can be associated with an EVRYTHNG Platform account. Through use of the Roles API each can be given the appropriate permissions and levels of scope necessary for their role in the application.

From each Operator's perspective, they each have a unique access to each account they are associated with, and are granted an Operator API Key for each.


API Status
Stable:
/accounts
/accounts/:accountId
/accounts/:accountId/shortDomains


AccountDocument Data Model

.name (string)
    Friendly name of this resource.

.id (string, read-only)
    The ID of this resource.

.createdAt (integer, read-only)
    Timestamp when the resource was created.

.updatedAt (integer, read-only)
    Timestamp when the resource was updated.

.customFields (object)
    Object of case-sensititve key-value pairs of custom fields 
    associated with the resource.

.imageUrl (string)
    The account Dashboard logo.

.tfaRequired (boolean, read-only)
    Whether two-factor authentication is required for this 
    account.
{
  "type": "object",
  "description": "An object representing a Platform account.",
  "properties": {
    "name": {
      "type": "string",
      "description": "Friendly name of this resource."
    },
    "id": {
      "type": "string",
      "description": "The ID of this resource.",
      "readOnly": true
    },
    "createdAt": {
      "type": "integer",
      "description": "Timestamp when the resource was created.",
      "readOnly": true
    },
    "updatedAt": {
      "type": "integer",
      "description": "Timestamp when the resource was updated.",
      "readOnly": true
    },
    "customFields": {
      "type": "object",
      "description": "Object of case-sensititve key-value pairs of custom fields associated with the resource."
    },
    "imageUrl": {
      "type": "string",
      "description": "The account Dashboard logo."
    },
    "tfaRequired": {
      "type": "boolean",
      "description": "Whether two-factor authentication is required for this account.",
      "readOnly": true
    }
  }
}
{
  "id": "UhpHrg39QCy9dsSddN8xhwnb",
  "createdAt": 1471862431010,
  "customFields": {
    "region": "en-gb"
  },
  "updatedAt": 1510593794364,
  "name": "Example Account",
  "imageUrl": "https://avatars1.githubusercontent.com/u/5732010?v=4&s=460",
  "tfaRequired": true
}

AccessDocument Data Model

The AccessDocument contains role and authentication information for an account.

.account (string, read-only)
    The account ID.

.apiKey (string, read-only)
    The account's Operator API key.

.id (string, read-only)
    The ID of this resource.

.operator (string, read-only)
    The account's user ID.

.role (string)
    The ID of the currently assigned role.
{
  "type": "object",
  "description": "An object representing an account's access record.",
  "properties": {
    "account": {
      "type": "string",
      "description": "The account ID.",
      "readOnly": true
    },
    "apiKey": {
      "type": "string",
      "description": "The account's Operator API key.",
      "readOnly": true,
      "minLength": 80,
      "maxLength": 80
    },
    "id": {
      "type": "string",
      "description": "The ID of this resource.",
      "readOnly": true
    },
    "operator": {
      "type": "string",
      "description": "The account's user ID.",
      "readOnly": true
    },
    "role": {
      "type": "string",
      "description": "The ID of the currently assigned role.",
      "minLength": 4,
      "maxLength": 24
    }
  }
}
{
  "id": "57bad69fc18104025b292da8",
  "account": "UhpHrg39QCy9dsSddN8xhwnb",
  "operator": "UEp4rDGsnpCAF6xABbys5Amc",
  "apiKey": "AGiWrH5OteA4aHiMFiwnwF08p3PQvPIr9GJX...",
  "role": "admin"
}

Read All Accounts

Read an array of all accounts visible to an Operator with their API key.

GET /accounts
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X GET 'https://api.evrythng.com/accounts'
const operatorApiKey = '$OPERATOR_API_KEY';

EVT.api({
  url: '/accounts',
  authorization: operatorApiKey
}).then(console.log);
HTTP/1.1 200 OK
Content-Type: application/json

[
  {
    "id": "UhpHrg39QCy38dht93kiuwnb",
    "createdAt": 1471862430889,
    "customFields": {
      "owner": "UEp4rDGsnpjdu37d76yg5Amc"
    },
    "updatedAt": 1471862431010,
    "name": "Main Account",
    "tfaRequired": false
  }
]

Read an Account

Read an account by its ID by sending a GET request to the /accounts/:accountId endpoint.

GET /accounts/:accountId
Content-Type: application/json
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X GET 'https://api.evrythng.com/accounts/UhpHrg39QCy9dsSddN8xhwnb'
const accountId = 'UhpHrg39QCy9dsSddN8xhwnb';
const operatorApiKey = '$OPERATOR_API_KEY';

EVT.api({
  url: `/accounts/${accountId}`,
  authorization: operatorApiKey
}).then(console.log);
HTTP/1.1 200 Ok
Content-Type: application/json

{
  "id": "UhpHrg39QCy9dsSddN8xhwnb",
  "createdAt": 1471862430889,
  "customFields": {
    "owner": "UEp4rDGsnpjdu37d76yg5Amc"
  },
  "updatedAt": 1471862431010,
  "name": "Main Account",
  "tfaRequired": false
}

Update an Account

Update an account with new user information or custom fields by sending an AccountDocument with only the fields to be updated. For example, the imageUrl.

PUT /accounts/:accountId
Content-Type: application/json
Authorization: $OPERATOR_API_KEY

AccountDocument (subset)
curl -i -H "Content-Type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X PUT 'https://api.evrythng.com/accounts/UhpHrg39QCy38dht93kiuwnb'
  -d '{ 
    "imageUrl": "https://example.com/image.png" 
  }'
const accountId = 'UhpHrg39QCy38dht93kiuwnb';
const operatorApiKey = '$OPERATOR_API_KEY';

const update = { 
  imageUrl: 'https://example.com/image.png'
};

EVT.api({
  url: `/accounts/${accountId}`,
  method: 'PUT',
  authorization: operatorApiKey,
  data: update
}).then(console.log);
HTTP/1.1 200 OK
Content-Type: application/json

{
  "id": "UhpHrg39QCy38dht93kiuwnb",
  "createdAt": 1471862430889,
  "customFields": {
    "owner": "UEp4rDGsnpjdu37d76yg5Amc"
  },
  "updatedAt": 1471862431010,
  "name": "Updated Account Name",
  "imageUrl": "https://example.com/image.png",
  "tfaRequired": false
}

Read all Short Domains

Read all short domains available to an account.

GET /accounts/:accountId/shortDomains
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X GET 'https://api.evrythng.com/accounts/UhpHrg39QCy9dsSddN8xhwnb/shortDomains'
const operatorApiKey = '$OPERATOR_API_KEY';
const accountId = 'UGYYcfmsCy9nQtRwwYcgnfKb';

EVT.api({
  url: `/accounts/${accountId}/shortDomains`,  
  authorization: operatorApiKey
}).then(console.log);
HTTP/1.1 200 OK
Content-Type: application/json

[
  "tn.gg"
]
 

A fundamental concept in the EVRYTHNG Platform are actions. An action simply represents a discrete event or real-world actions performed by an app, user, or device within an application, and usually created on a product or a Thng to establish an association corresponding to the real world event. For example, an action created on a Thng could represent a 'switched on' event when the Thng's device was switched on.

Actions are usually created by an Application User within an application and a Thng, product, or collection ID must be specified as the target. In addition to specifying a resource in an action, it is also possible to create an action through the resource itself using aliased actions.


Action Types

The basic actions types supported within the Platform are scans, implicitScans, checkins and shares. It is also possible to create your own action types. Custom action types must always start with an underscore (_).

See the Action Types section for more information on using custom action types.


Action Context

When reading actions, the ?context=true query parameter can be used to include the context field in the response. This includes useful context information about the creation of the action. See below for an example.


API Status
Stable:
/actions/:type
/actions/:type/:actionId
/products/:productId/actions/:actionType
/thngs/:thngId/actions/:actionType
/collections/:collectionId/actions/:actionType

ActionDocument Data Model

.type (string, read-only)
    The action type.

.id (string, read-only)
    The ID of this resource.

.user (string, read-only)
    The EVRYTHNG ID of the user who has performed this action.

.createdAt (integer, read-only)
    Timestamp when the resource was created.

.createdByProject (string, read-only)
    The EVRYTHNG ID of the project who has performed this 
    action.

.thng (string, read-only)
    The EVRYTHNG ID of the Thng this action was carried out on.

.product (string, read-only)
    The EVRYTHNG product ID associated with the Thng this action 
    was carried out on.

.collection (string, read-only)
    The EVRYTHNG ID of the collection this action was carried 
    out on. Custom action types only.

.timestamp (integer, read-only)
    The timestamp when the action took place. Filled 
    automatically if unspecified.

.identifiers (object)
    Various identifiers (EPC, ISBN, etc.) as a JSON object with 
    one or more key-value pairs.

.location (LocationDocument)

.locationSource (string, read-only, one of 'sensor', 'geoIp', 'unknown')
    The method used to find the location where the action took 
    place. Requires `location` to be set.

.context (object, read-only)
    A placeholder for any contextual information to be stored.

.customFields (object)
    Object of case-sensititve key-value pairs of custom fields 
    associated with the resource.

.scopes (ScopesDocument)

.tags (array of string)
    Array of string tags associated with this resource.

.reactions (array of ReactionDocument)
    An array of Redirector reactions that occured from this 
    action.
{
  "type": "object",
  "description": "An object representing a Platform action.",
  "properties": {
    "type": {
      "type": "string",
      "description": "The action type.",
      "readOnly": true
    },
    "id": {
      "type": "string",
      "description": "The ID of this resource.",
      "readOnly": true
    },
    "user": {
      "type": "string",
      "description": "The EVRYTHNG ID of the user who has performed this action.",
      "readOnly": true
    },
    "createdAt": {
      "type": "integer",
      "description": "Timestamp when the resource was created.",
      "readOnly": true
    },
    "createdByProject": {
      "type": "string",
      "description": "The EVRYTHNG ID of the project who has performed this action.",
      "readOnly": true
    },
    "thng": {
      "type": "string",
      "description": "The EVRYTHNG ID of the Thng this action was carried out on.",
      "readOnly": true
    },
    "product": {
      "type": "string",
      "description": "The EVRYTHNG product ID associated with the Thng this action was carried out on.",
      "readOnly": true
    },
    "collection": {
      "type": "string",
      "description": "The EVRYTHNG ID of the collection this action was carried out on. Custom action types only.",
      "readOnly": true
    },
    "timestamp": {
      "type": "integer",
      "description": "The timestamp when the action took place. Filled automatically if unspecified.",
      "readOnly": true
    },
    "identifiers": {
      "type": "object",
      "description": "Various identifiers (EPC, ISBN, etc.) as a JSON object with one or more key-value pairs."
    },
    "location": { "$ref": "LocationDocument" },
    "locationSource": {
      "type": "string",
      "description": "The method used to find the location where the action took place. Requires `location` to be set.",
      "enum": [ "sensor", "geoIp", "unknown" ],
      "readOnly": true
    },
    "context": {
      "type": "object",
      "description": "A placeholder for any contextual information to be stored.",
      "readOnly": true
    },
    "customFields": {
      "type": "object",
      "description": "Object of case-sensititve key-value pairs of custom fields associated with the resource."
    },
    "scopes": { "$ref": "ScopesDocument" },
    "tags": {
      "type": "array",
      "description": "Array of string tags associated with this resource.",
      "items": {
        "type": "string",
        "maxLength": 60
      }
    },
    "reactions": {
      "type": "array",
      "description": "An array of Redirector reactions that occured from this action.",
      "items": { "$ref": "ReactionDocument" }
    }
  }
}
{
  "id": "U4aPKKSEWy9nQQRaaghRasaa",
  "createdAt": 1510914839892,
  "customFields": {
    "region_code": "en_gb"
  },
  "tags": [
    "example",
    "actions"
  ],
  "timestamp": 1510914839892,
  "type": "scans",
  "user": "UnRPKGnUMQtVEPaaageq5ehk",
  "location": {
    "latitude": 51.4333,
    "longitude": 0.1833,
    "position": {
      "type": "Point",
      "coordinates": [
        0.1833,
        51.4333
      ]
    }
  },
  "locationSource": "geoIp",
  "context": {
    "ipAddress": "141.0.154.202",
    "city": "Crayford",
    "region": "England",
    "countryCode": "GB",
    "userAgentName": "Unknown",
    "operatingSystemName": "Unknown",
    "timeZone": "Europe/London"
  },
  "reactions": [
    {
      "type": "redirection",
      "redirectUrl": "https://google.com",
      "redirectionContext": {
        "constants": {
          "constant_key": "constant_value"
        }
      }
    }
  ],
  "createdByProject": "UmxHK6K8BXsa9KawRh4bTbqc",
  "createdByApp": "U3pxRQh2eD8RtKwaRgerfQgc",
  "identifiers": {
    "ean_13": "786432786349"
  },
  "thng": "U4wpchcBqm8hhqwwag8kgnqc"
}

See also: LocationDocument, ScopesDocument, ReactionDocument


ReactionDocument Data Model

.type (string, one of 'redirection')
    The reaction type.

.redirectUrl (string)
    The redirection URL.

.redirectionContext (object)
    Object of redirection context value.
{
  "type": "object",
  "description": "A single reaction from Redirector.",
  "properties": {
    "type": {
      "type": "string",
      "description": "The reaction type.",
      "enum": [ "redirection" ]
    },
    "redirectUrl": {
      "type": "string",
      "description": "The redirection URL."
    },
    "redirectionContext": {
      "type": "object",
      "description": "Object of redirection context value."
    }
  }
}

Create an Action

Create a new action of the specified type.

Notes

  • all can be used as type. In this case, the action type must be supplied in the payload.

  • Actions with a custom action type do not require a target thng, product, or collection.

POST /actions/:type
Content-Type: application/json
Authorization: $APP_USER_API_KEY

ActionDocument
curl -i -H "Content-Type: application/json" \
  -H "Authorization: $APP_USER_API_KEY" \
  -X POST "https://api.evrythng.com/actions/scans" \
  -d '{
    "product": "UmAkSdqe69QVhswwRYNdbxna",
    "type": "scans",
    "identifiers": {
      "sapId": "KDL-46EX402AEP",
      "event_Id": "027242784925"
    },
    "customFields": {
      "color": "blue"
    }
  }'
const actionType = 'scans';
const productId = 'UmAkSdqe69QVhswwRYNdbxna';
    
const action = {
  product: productId,
  identifiers: {
    sapId: "KDL-46EX402AEP",
    event_Id: "027242784925"
  },
};

app.action(actionType).create(action).then(console.log);

// with the nested resources
app.product(productId).action(actionType).create().then(console.log);

// or on the entity itself
app.product(productId).read().then((product) => {
  product.action(actionType).create().then(console.log);
});
String productId = "UmAkSdqe69QVhswwRYNdbxna";

ScanAction action = new ScanAction();
action.setProduct(productId);  // Could also be a product or shortId
apiManager.actionService().actionCreator(action).execute();
HTTP/1.1 201 Created
Content-Type: application/json
Location: https://api.evrythng.com/actions/scans/UGByEXMEq9QBE8aRaYNeYnkb

{
  "id": "UGByEXMEq9QBE8aRaYNeYnkb",
  "createdAt": 1497528007035,
  "timestamp": 1497528007035,
  "type": "scans",
  "user": "UmAFxMDnqQ9VEsRwaErAxfrr",
  "location": {
    "latitude": 48.86,
    "longitude": 2.34,
    "position": {
      "type": "Point",
      "coordinates": [
        2.34,
        48.86
      ]
    }
  },
  "locationSource": "unknown",
  "createdByProject": "UGdkbcyhqQtVEsaaRhrASatf",
  "createdByApp": "UmAFxcdSMt9VE8awRE7dba9n",
  "product": "UmAkSdqe69QVhswwRYNdbxna"
}

Read an Action

Application Users and Operators can read an action by its ID. Both the Application User and Operator API keys can be used. You can use all as the action type to access the actions of unknown type.

GET /actions/:actionType/:actionId
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X GET 'https://api.evrythng.com/actions/scans/Uh7HKqtFryhmBaShGHVySmeg'
const productId = 'UhrHD9BnngymSrapaakKrq9g';
const actionId = 'Uh7HKqtFryhmBaShGHVySmeg';
const actionType = 'scans';

// Read a user's specific 'scan' action
user.action(actionType, actionId).read().then(console.log);

// Read user product's specific scan action
user.product(productId).action(actionType, actionId).read().then(console.log);

// Read a user's specific actions (regardless of type)
user.action('all').read().then(console.log);
String actionId = "Uh7HKqtFryhmBaShGHVySmeg";

// The class parameter should match the class of the action being read
// For example, ScanAction.class for the 'scan' action type
ScanAction action = apiManager.actionService().actionReader(ScanAction.class, actionId).execute();
HTTP/1.1 200 OK
Content-Type: application/json

{
  "id": "Uh7HKqtFryhmBaShGHVySmeg",
  "createdAt": 1473953016966,
  "timestamp": 1473953016966,
  "type": "scans",
  "location": {
    "latitude": 51.5142,
    "longitude": -0.0931,
    "position": {
      "type": "Point",
      "coordinates": [
        -0.0931,
        51.5142
      ]
    }
  },
  "locationSource": "geoIp",
  "thng": "Uh74neTHnDcmbNwpaRmUhEht",
  "product": "UhrHD9BnngymSrapaakKrq9g"
}

Read all Actions of a Type

Read all actions of a given type. For example: GET /actions/scans for all the scans actions. The result may be paginated if there are more than 30 items.

A special type all can be used to get all actions regardless of their type.

See Action Types to see how to read and manage action types.

GET /actions/:type
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X GET 'https://api.evrythng.com/actions/scans'
const actionType = 'scans';
const productId = 'UGTdRHP4BDsa95waRhqfYhTh';

// Read user's product's most recent scans:
user.action(actionType).read({
  params: {
    filter: {
      product: productId
    }
  }
}).then(console.log);

// Above, simplified
user.product(productId).action(actionType).read().then(console.log);

// Read user's most recent scans:
user.action(actionType).read().then(console.log);

// Read all recent actions in this app:
user.action('all').read().then(console.log);
Iterator<PVector<Action>> actions = apiManager.actionService().iterator().perPage(10).filter("type=scans").execute();
while(actions.hasNext()) {
    PVector<Action> page = actions.next();
    for(Action action : page) {
        System.out.println("action: " + action.toString());
    }
}
HTTP/1.1 200 OK
Content-Type: application/json

[
  {
    "id": "UmBSBbDBBg8atKaRwXFBDwct",
    "createdAt": 1497448804712,
    "timestamp": 1497448804712,
    "type": "scans",
    "user": "UGBxVbDeVXsRt5aRwhQTngep",
    "location": {
      "latitude": 39.02,
      "longitude": 125.75,
      "position": {
        "type": "Point",
        "coordinates": [
          125.75,
          39.02
        ]
      }
    },
    "locationSource": "unknown",
    "createdByProject": "UmSqCDt5BD8atKRRagdqUnAa",
    "createdByApp": "UGxqWERBBgsatKwRwgG9Kfcb",
    "product": "UGTdRHP4BDsa95waRhqfYhTh"
  }
]

Read an Action with Context

The ?context=true query parameter can be used to include the context field in the action read.

GET /actions/:actionType/:actionId?context=true
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X GET 'https://api.evrythng.com/actions/scans/UkqgkMW5egsatKawRYbywqeb?context=true'
const actionType = 'scans';
const actionId = 'UkqgkMW5egsatKawRYbywqeb';

user.action(actionType, actionId).read({
  params: {
    context: true
  }
}).then(console.log);
HTTP/1.1 200 OK
Content-Type: application/json

{
  "id": "UkqgkMW5egsatKawRYbywqeb",
  "createdAt": 1489407066926,
  "customFields": {
    "ExternalScan": true
  },
  "timestamp": 1489407066926,
  "type": "scans",
  "user": "Ukqg2MbseDPwtKwwaYE6QfRs",
  "location": {
    "latitude": 57.75,
    "longitude": -0.9133,
    "position": {
      "type": "Point",
      "coordinates": [
        -0.9133,
        57.75
      ]
    }
  },
  "locationSource": "geoIp",
  "context": {
    "ipAddress": "218.46.189.90",
    "city": "Thame",
    "region": "England",
    "countryCode": "GB",
    "userAgentName": "Chrome",
    "operatingSystemName": "macOS 10.12 Sierra",
    "timeZone": "Europe/London"
  },
  "createdByProject": "UF5hQHxMBg8a9pwwRYxqXkqp",
  "createdByApp": "UkMgYtdEVDsaQKaRwgdAWsdn",
  "thng": "Uk6XkAPbBXPwtKwawDAepMmm",
  "product": "UFKE9nHBBg8atKaawgVrKKbs"
}

Delete an Action

Delete a single action by ID.

DELETE /actions/:type/:actionId
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X DELETE 'https://api.evrythng.com/actions/scans/UE9Akx9WaMQwgWdpMYAGUr9p'
const actionType = 'scans';
const actionId = 'UE9Akx9WaMQwgWdpMYAGUr9p';

operator.action(actionType, actionId).delete();
String actionId = "UE9Akx9WaMQwgWdpMYAGUr9p";

// The class should match the type of action to be deleted
// For example, ScanAction.class for the 'scan' action type
apiManager.actionService().actionDeleter(ScanAction.class, actionId).execute();
HTTP/1.1 200 OK

Create Multiple Actions

It is possible to create multiple actions at the same time using the /actions/all endpoint. Each action in the payload array must state the action type. Different types can be used at the same time for different actions.

POST /actions/all
Authorization: $OPERATOR_API_KEY
Content-Type: application/json

[ ActionDocument, ... ]
curl -H "Content-Type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X POST 'https://api.evrythng.com/actions/all' \
  -d '[
    {
      "type": "scans",
      "thng": "Um2MEbqHMm8Eh6aaaDBSQkHm"
    },
    {
      "type": "checkins",
      "thng": "Um2MEbqHMm8Eh6aaaDBSQkHm"
    }
  ]'
const actions = {
  type: 'scans',
  thng: 'UmCygXyDeXsw9KwwRhnnGg2a'
}, {
  type: 'checkins',
  thng: 'UmCygXyDeXsw9KwwRhnnGg2a'
};

operator.action('all').create(actions).then(console.log);
HTTP/1.1 201 Created
Content-Type: application/json

[
  {
    "id": "UmpbCgWRMGsEEqRRwgYEbtEs",
    "createdAt": 1504794192742,
    "timestamp": 1504794192742,
    "type": "scans",
    "location": {
      "latitude": 51.45,
      "longitude": 0.2167,
      "position": {
        "type": "Point",
        "coordinates": [
          0.2167,
          51.45
        ]
      }
    },
    "locationSource": "geoIp",
    "context": {
      "ipAddress": "141.0.154.202",
      "city": "Dartford",
      "region": "England",
      "countryCode": "GB",
      "userAgentName": "Unknown",
      "operatingSystemName": "Unknown",
      "timeZone": "Europe/London"
    },
    "thng": "Um2MEbqHMm8Eh6aaaDBSQkHm",
    "product": "U3EtU2k3BD8wQpwwR6EMXgKb"
  },
  {
    "id": "U3KbCgfRMmPhh6wwagYYbtka",
    "createdAt": 1504794192756,
    "timestamp": 1504794192756,
    "type": "checkins",
    "location": {
      "latitude": 51.45,
      "longitude": 0.2167,
      "position": {
        "type": "Point",
        "coordinates": [
          0.2167,
          51.45
        ]
      }
    },
    "locationSource": "geoIp",
    "context": {
      "ipAddress": "141.0.154.202",
      "city": "Dartford",
      "region": "England",
      "countryCode": "GB",
      "userAgentName": "Unknown",
      "operatingSystemName": "Unknown",
      "timeZone": "Europe/London"
    },
    "thng": "Um2MEbqHMm8Eh6aaaDBSQkHm",
    "product": "U3EtU2k3BD8wQpwwR6EMXgKb"
  }
]

Aliased Actions

It is also possible to use alias endpoints to create or read actions performed on a specific Thng, product or collection using its ID. The available endpoints are:

  • /products/:productId/actions/:actionType - Actions on a specific product
  • /thngs/:thngId/actions/:actionType - Actions on a specific Thng
  • /collections/:collectionId/actions/:actionType - Actions on a specific collection

Use the same payload as for /actions/:actionType endpoint. The action's product, thng, or collection property must match the ID that is specified in the URL, or in this special case may be omitted from the payload. The responses are of the same format as the Read all actions of a type operation.

Note

Only custom action types can be used for aliased actions.

Create an Aliased Action

POST /products/:productId/actions/:actionType
Content-Type: application/json
Authorization: $OPERATOR_API_KEY

ActionDocument
POST /thngs/:thngId/actions/:actionType
Content-Type: application/json
Authorization: $OPERATOR_API_KEY

ActionDocument
POST /collections/:collectionId/actions/:actionType
Content-Type: application/json
Authorization: $OPERATOR_API_KEY

ActionDocument

Read all Aliased Actions on a Resource

GET /products/:productId/actions/:actionType
Authorization: $OPERATOR_API_KEY
GET /thngs/:thngId/actions/:actionType
Authorization: $OPERATOR_API_KEY
GET /collections/:collectionId/actions/:actionType
Authorization: $OPERATOR_API_KEY

Read an Aliased Action

GET /products/:productId/actions/:actionType/:actionId
Authorization: $OPERATOR_API_KEY
GET /thngs/:thngId/actions/:actionType/:actionId
Authorization: $OPERATOR_API_KEY
GET /collections/:collectionId/actions/:actionType/:actionId
Authorization: $OPERATOR_API_KEY
Suggest Edits

Action Types

 

An Operator can define new custom action types for the account. When defining these action types, it's possible to add specific custom fields and tags to aid in sorting. An action type can be regarded as a class identifier of an individual action, much in the same way that a product should be used to model classes of Thngs.

Action types that are created in a project scope can later be shared with other projects using the 'Add to another project' button in the Dashboard, or through the REST API. See Scoping for more information on sharing resources between projects.

Note

Custom action types must always start with an underscore (_).


API Status
Stable:
/actions
/actions/:type


ActionTypeDocument Data Model

.name (string, read-only)
    The name of the action type. Custom action types must begin 
    with an underscore.

.id (string, read-only)
    The ID of this resource.

.createdAt (integer, read-only)
    Timestamp when the resource was created.

.updatedAt (integer, read-only)
    Timestamp when the resource was updated.

.tags (array of string)
    Array of string tags associated with this resource.

.customFields (object)
    Object of case-sensititve key-value pairs of custom fields 
    associated with the resource.

.scopes (ScopesDocument)
{
  "type": "object",
  "description": "An object describing a custom action type.",
  "properties": {
    "name": {
      "type": "string",
      "description": "The name of the action type. Custom action types must begin with an underscore.",
      "readOnly": true
    },
    "id": {
      "type": "string",
      "description": "The ID of this resource.",
      "readOnly": true
    },
    "createdAt": {
      "type": "integer",
      "description": "Timestamp when the resource was created.",
      "readOnly": true
    },
    "updatedAt": {
      "type": "integer",
      "description": "Timestamp when the resource was updated.",
      "readOnly": true
    },
    "tags": {
      "type": "array",
      "description": "Array of string tags associated with this resource.",
      "items": {
        "type": "string",
        "maxLength": 60
      }
    },
    "customFields": {
      "type": "object",
      "description": "Object of case-sensititve key-value pairs of custom fields associated with the resource."
    },
    "scopes": { "$ref": "ScopesDocument" }
  }
}
{
  "id": "UHwsppkh69tVhPRaaDEpdCbg",
  "createdAt": 1510914951694,
  "customFields": {
    "region_code": "en_sc"
  },
  "tags": [
    "example",
    "actionType"
  ],
  "updatedAt": 1510914951694,
  "name": "_AlertFired"
}

See also: ScopesDocument


Create an Action Type

Submit a valid ActionTypeDocument to the /actions endpoint to create a custom action type.

POST /actions
Content-Type: application/json
Authorization: $OPERATOR_API_KEY

ActionTypeDocument
curl -i -H "Content-Type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X POST 'https://api.evrythng.com/thngs' \
  -d '{
    "name": "_Arrived"
  }'
const actionType = {
  name: '_Arrived'
};

operator.actionType().create(actionType).then(console.log);
ActionType actionType = new ActionType();
actionType.setName("_Arrived");	// Must start with '_'
apiManager.actionService().actionTypeCreator(actionType).execute();
HTTP/1.1 201 Created
Content-Type: application/json
Location: https://api.evrythng.com/actions/Uh8cBH7dMnXd8NbmyxB7tdqk

{
  "id": "Uh8cBH7dMnXd8NbmyxB7tdqk",
  "createdAt": 1474448083763,
  "updatedAt": 1474448083763,
  "name": "_Arrived"
}

Read all Action Types

Action types can be retrieved by a GET on the /actions endpoint. The action types are returned with the built-in types coming first, followed by the custom types sorted by descending creation date. The result may be paginated if there are more than 30 items.

GET /actions
Authorization: $APPLICATION_USER_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X GET 'https://api.evrythng.com/actions'
user.actionType().read().then(console.log);
List<ActionType> types = apiManager.actionService().actionTypesReader().execute();
for(ActionType type : types) {
    System.out.println(type.getName());
}
HTTP/1.1 200 OK
Content-Type: application/json

[
  {
    "name": "checkins"
  },
  {
    "name": "implicitScans"
  },
  {
    "name": "scans"
  },
  {
    "name": "shares"
  },
  {
    "id": "UGFqk8eCVXPRQKwwwgGcHmGh",
    "createdAt": 1502383685794,
    "tags": [
      "custom"
    ],
    "updatedAt": 1505491159408,
    "name": "_Arrived"
  }
]

Update an Action Type

Update an action type by making a PUT request.

PUT /actions/:name
Content-Type: application/json
Authorization: $APPLICATION_USER_API_KEY

ActionTypeDocument (subset)
curl -i -H "Content-Type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X PUT 'https://api.evrythng.com/actions/_Arrived' \
  -d '{
    "tags": [ "example" ]
  }'
const type = '_Arrived';
const update = {
  tags: [ "example" ]
};

user.actionType(type).update(update).then(console.log);
HTTP/1.1 200 OK
Content-Type: application/json

{
  "id": "Uk7WdgFAMQtehsRawgdwEkCn",
  "createdAt": 1490368131932,
  "tags": [
    "example"
  ],
  "updatedAt": 1497450736023,
  "name": "_Arrived"
}

Delete an Action Type

You can delete an action type in a similar manner to creating one using the actionType.

Note

The default action types cannot be deleted.

DELETE /actions/:actionType
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X DELETE 'https://api.evrythng.com/actions/_Arrived'
const actionType = '_Arrived';

operator.actionType(actionType).delete().then(console.log);
String actionType = "_Arrived";

apiManager.actionService().actionTypeDeleter(actionType).execute();
HTTP/1.1 200 OK
Suggest Edits

Applications

 

The EVRYTHNG Platform allows developers to create applications inside their projects. An application resource will usually correspond to an actual web or native applications (remote HTTP clients) that can interact with the EVRYTHNG API.

In this way all resources that are associated with the real-world counterpart app can be correctly scoped and grouped accordingly, for the purpose of an application as well as its Application Users. Multiple applications can exist inside a project resource, with each granted its own Application and Trusted Application API Keys that can be used to manipulate only those resources in the application's project scope.


API Status
Stable:
/applications/me
/projects/:projectId/applications
/projects/:projectId/applications/:applicationId
/projects/:projectId/applications/:applicationId/secretKey


ApplicationDocument Data Model

.name (string)
    Friendly name of this resource.

.socialNetworks (object)
    An array of social networks that this application will 
    support.

.id (string, read-only)
    The ID of this resource.

.createdAt (integer, read-only)
    Timestamp when the resource was created.

.updatedAt (integer, read-only)
    Timestamp when the resource was updated.

.tags (array of string)
    Array of string tags associated with this resource.

.customFields (object)
    Object of case-sensititve key-value pairs of custom fields 
    associated with the resource.

.description (string)
    Friendly description of this resource.

.project (string, read-only)
    ID of the platform project this application belongs to.

.defaultUrl (string)
    The URL where the default client app for this application is 
    deployed.

.appApiKey (string, read-only)
    The API Key available to the application.

.defaultRole (string)
    The default Application User role new Application Users 
    created in this application will be given.

.scopes (ScopesDocument)
{
  "type": "object",
  "description": "An object describing a platform application.",
  "properties": {
    "name": {
      "type": "string",
      "description": "Friendly name of this resource."
    },
    "socialNetworks": {
      "type": "object",
      "description": "An array of social networks that this application will support.",
      "default": {}
    },
    "id": {
      "type": "string",
      "description": "The ID of this resource.",
      "readOnly": true
    },
    "createdAt": {
      "type": "integer",
      "description": "Timestamp when the resource was created.",
      "readOnly": true
    },
    "updatedAt": {
      "type": "integer",
      "description": "Timestamp when the resource was updated.",
      "readOnly": true
    },
    "tags": {
      "type": "array",
      "description": "Array of string tags associated with this resource.",
      "items": {
        "type": "string",
        "maxLength": 60
      }
    },
    "customFields": {
      "type": "object",
      "description": "Object of case-sensititve key-value pairs of custom fields associated with the resource."
    },
    "description": {
      "type": "string",
      "description": "Friendly description of this resource."
    },
    "project": {
      "type": "string",
      "description": "ID of the platform project this application belongs to.",
      "readOnly": true
    },
    "defaultUrl": {
      "type": "string",
      "description": "The URL where the default client app for this application is deployed."
    },
    "appApiKey": {
      "type": "string",
      "description": "The API Key available to the application.",
      "readOnly": true,
      "minLength": 80,
      "maxLength": 80
    },
    "defaultRole": {
      "type": "string",
      "description": "The default Application User role new Application Users created in this application will be given.",
      "minLength": 13,
      "maxLength": 24
    },
    "scopes": { "$ref": "ScopesDocument" }
  }
}
{
  "id": "U4axGkeeeq8r97aaRgBB9fTr",
  "createdAt": 1510053956736,
  "customFields": {
    "key": "value"
  },
  "tags": [
    "example",
    "app"
  ],
  "updatedAt": 1510915098055,
  "name": "Test Application",
  "description": "Example description",
  "project": "UmxHK6K8BXsa9KawRh4bTbqc",
  "socialNetworks": {},
  "defaultUrl": "https://google.com",
  "defaultRole": "base_app_user",
  "appApiKey": "a4AjjHgehcKlGajImlgmrtInvSnYLG80yZ7sDEl..."
}

See also: ScopesDocument


Create an Application

Creates an application within the project specified with projectId.

The object returned contains the appApiKey parameter, which is an Application API key that must be used within an external application to issue calls to the API. However, because this API key can be hard coded within external applications (e.g. mobile/JS) and is visible to anyone, it can be used only for a very limited set of operations (create users, read a product, send actions etc.).

POST /projects/:projectId/applications
Content-Type: application/json
Authorization: $OPERATOR_API_KEY

ApplicationDocument
curl -i -H "Content-Type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X POST 'https://api.evrythng.com/projects/URG5FDahhBePgNfHVkHTkama/applications' \
  -d '{ 
    "name": "Mobile App", 
    "socialNetworks": {} 
  }'
const projectId = 'URG5FDahhBePgNfHVkHTkama';

const application = {
  name: 'Mobile App',
  socialNetworks: {}
};

operator.project(projectId).application().create(application).then(console.log);
String projectId = "URG5FDahhBePgNfHVkHTkama";
HashMap<String, SocialNetwork> networks = new HashMap<String, SocialNetwork>();

Application application = new Application();
application.setName("Mobile App");
application.setSocialNetworks(networks);
apiManager.applicationService().applicationCreator(projectId, application).execute();
HTTP/1.1 201 Created
Content-Type: application/json
Location: https://api.evrythng.com/projects/URG5FDahhBePgNfHVkHTkama/applications/UmAFxcdSMt9VE8awRE7dba9n

{
  "id": "UmAFxcdSMt9VE8awRE7dba9n",
  "name": "Mobile App",
  "project": "URG5FDahhBePgNfHVkHTkama",
  "appApiKey": "cYFWnAiRTaNeOnj8ejc0qQg9FgwyPDV4UXDPQ...",
  "createdAt": 1372243297475,
  "updatedAt": 1372243297475,
  "socialNetworks": {},
  "defaultRole": "app_user_admin"
}

Read Applications in a Project

Read a list of applications within the project specified with projectId. The result may be paginated if there are more than 30 items.

GET /projects/:projectId/applications
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X GET 'https://api.evrythng.com/projects/U2meqbNWegsaQKRRaDUmpssr/applications'
const projectId = 'U2meqbNWegsaQKRRaDUmpssr';

operator.project(projectId).application().read().then(console.log);
String projectId = "U2meqbNWegsaQKRRaDUmpssr";

List<Application> apps = apiManager.applicationService().applicationsReader().project(projectId).list().getResult();
for(Application app : apps) {
    System.out.println(app.toString());
}
HTTP/1.1 200 OK
Content-Type: application/json

[
  {
    "id": "Umbn56MmBDsatpaawg6XgKfk",
    "name": "Mobile App",
    "project": "U2meqbNWegsaQKRRaDUmpssr",
    "appApiKey": "cYFWnAiRTaNeOnj8ejc04qQg9Fgwy3PDV4UXDP...",
    "createdAt": 1372243297475,
    "updatedAt": 1372243297475,
    "socialNetworks": {},
    "defaultRole": "app_user_admin"
  }
]

Update an Application

Update a single application by ID.

PUT /projects/:projectId/applications/:applicationId
Authorization: $OPERATOR_API_KEY
Content-Type: application/json

ApplicationDocument (subset)
curl -i -H "Content-Type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X PUT 'https://api.evrythng.com/projects/URG5FDahhBePgNfHVkHTkama/applications/UF3Vqb7D6G8EhMwaRYgQ2pFc' \
  -d '{ 
    "name": "Updated App Name" 
  }'
const projectId = 'URG5FDahhBePgNfHVkHTkama';
const applicationId = 'UF3Vqb7D6G8EhMwaRYgQ2pFc';

const update = {
  name: 'Updated App Name'
};

operator.project(projectId).application(applicationId).update(update)
  .then(console.log);
String projectId = "URG5FDahhBePgNfHVkHTkama";
String applicationId = "UF3Vqb7D6G8EhMwaRYgQ2pFc";

// Read an application
Application application = apiManager.applicationService().applicationReader(applicationId).project(projectId).execute();

// Update it
application.setDescription("Updated application description");
apiManager.applicationService().applicationUpdater(projectId, applicationId, application).execute();
HTTP/1.1 200 OK
Content-Type: application/json

{
  "id": "UF3Vqb7D6G8EhMwaRYgQ2pFc",
  "name": "Updated App Name",
  "project": "URG5FDahhBePgNfHVkHTkama",
  "appApiKey": "cYFWnAiRTaNeOnj8ejc0qQg9FgwyPDV4UXDPQ...",
  "createdAt": 1372243297475,
  "updatedAt": 1372243297475,
  "socialNetworks": {},
  "defaultRole": "app_user_admin"
}

Delete an Application

Delete an application by ID. This action cannot be undone.

DELETE /projects/:projectId/applications/:applicationId
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X DELETE 'https://api.evrythng.com/projectId/UmACfDggBDswQpawRk4pAqDq/applications/U3dCCgDYBDswtpawRYBMcnHb'
const projectId = 'UmACfDggBDswQpawRk4pAqDq';
const applicationId = 'U3dCCgDYBDswtpawRYBMcnHb';

operator.project(projectId).application(applicationId).delete();
String projectId = "UmACfDggBDswQpawRk4pAqDq";
String applicationId = "U3dCCgDYBDswtpawRYBMcnHb";

apiManager.applicationService().applicationDeleter(applicationId).project(projectId).execute();
HTTP/1.1 200 OK

Read an Application

Returns a single application matching the applicationId within the project specified by projectId.

GET /projects/:projectId/applications/:applicationId
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X GET 'https://api.evrythng.com/projects/URG5FDahhBePgNfHVkHTkama/applications/UF3Vqb7D6G8EhMwaRYgQ2pFc'
const projectId = 'URG5FDahhBePgNfHVkHTkama';
const applicationId = 'UF3Vqb7D6G8EhMwaRYgQ2pFc';

operator.project(projectId).application(applicationId).read().then(console.log);
String projectId = "URG5FDahhBePgNfHVkHTkama";
String applicationId = "UF3Vqb7D6G8EhMwaRYgQ2pFc";

Application app = apiManager.applicationService().applicationReader(projectId, applicationId).execute();
HTTP/1.1 200 OK
Content-Type: application/json

{
  "id": "UF3Vqb7D6G8EhMwaRYgQ2pFc",
  "name": "Mobile App",
  "project": "URG5FDahhBePgNfHVkHTkama",
  "appApiKey": "cYFWnAiRTaNeOnj8ejc0qQg9FgwyPDV4UXDPQsBjCg6...",
  "createdAt": 1372243297475,
  "updatedAt": 1372243297475,
  "socialNetworks": {},
  "defaultRole": "base_app_user"
}

Read the Trusted Application API Key

Read the Trusted Application API Key for the application with applicationId. This is a secret key that has access to more endpoints than the standard Application API key.

Note

Operator users with read-only permissions (i.e.: global_read and project_read) will not be able to see this endpoint, or read the Trusted Application API Key.

GET /projects/:projectId/applications/:applicationId/secretKey
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X GET 'https://api.evrythng.com/projects/URG5FDahhBePgNfHVkHTkama/applications/UF3Vqb7D6G8EhMwaRYgQ2pFc/secretKey'
const operatorApiKey = '$OPERATOR_API_KEY';
const projectId = 'UG4WExTKBqPr9NwRa3twYDnk';
const applicationId = 'UG4NWfQ7BMPN97wawGQRrxtm';

EVT.api({
  url: `/projects/${projectId}/applications/${applicationId}/secretKey`,
  authorization: operatorApiKey
}).then(console.log);
HTTP/1.1 200 OK
Content-Type: application/json

{
  "secretApiKey": "cYFWnAiRTaNeOnj8ejc0qQg94FgwyPDV1PQsBjCg..."
}

Read Self Application

An application can read its own metadata without knowledge of which project it is in using the /applications/me endpoint with its Application API key.

GET /applications/me
Authorization: $APPLICATION_API_KEY
curl -H "Authorization: $APPLICATION_API_KEY" \
  -X GET 'https://api.evrythng.com/applications/me'
const trustedAppApiKey = '$TRUSTED_APPLICATION_API_KEY';

EVT.api({
  url: '/applications/me',
  authorization: trustedAppApiKey
}).then(console.log);
HTTP/1.1 200 OK
Content-Type: application/json

{
  "id": "UmSMbD9ACTtn99aaa2gfnKgp",
  "createdAt": 1495013107545,
  "customFields": {},
  "updatedAt": 1495013620339,
  "name": "Mobile App",
  "project": "UGSMSDs5fc9n9QaRwkXC4pDg",
  "socialNetworks": {},
  "appApiKey": "gxQiaD7gwHiuyCIexB4NMSpxu56565BxZemIqud..."
}

Update Self Application

The /applications/me endpoint can also be used to allow an application to update its own meta data using its Trusted Application API Key.

PUT /applications/me
Content-Type: application/json
Authorization: $TRUSTED_APPLICATION_API_KEY

ApplicationDocument (subset)
curl -i -H "Content-Type: application/json" \
  -H "Authorization: $TRUSTED_APPLICATION_API_KEY" \
  -X PUT 'https://api.evrythng.com/applications/me' \
  -d '{ 
		"customFields": {
      "somekey": "somevalue"
    }
  }'
HTTP/1.1 200 OK
Content-Type: application/json

{
  "id": "UGf5HamGBDPa95waah7Can5c",
  "createdAt": 1499177113975,
  "customFields": {
    "somekey": "somevalue"
  },
  "updatedAt": 1510565887701,
  "name": "Test Application",
  "project": "UmxHK6K8BXsa9KawRh4bTbqc",
  "socialNetworks": {},
  "defaultRole": "base_app_user",
  "appApiKey": "gXHaJVTBUrgYS3gDqVoy06WqXNM4zeGU..."
}
Suggest Edits

Application Users

 

Each EVRYTHNG application can create individual Application Users that are scoped to just that application. Each of these have their own personal details and login credentials, and are designed to serve as an access to only those resources that the application is concerned with. Each Application User is granted an Application User API Key when they are created, as well as each subsequent log in. This key is revoked if and when an Application User logs out.

For example, a commercial traceability application might create an Application User for each of the staff members that interact with the traceability solution, and can then use their own API key to create actions or access other resources to carry out their job.

In addition to named Application Users, an application may also create anonymous counterparts, which are not intended to be remembered or uniquely identified, but exist purely to allow a user-level access to resources or to perform a once-only action, and do not need to persist for any given length of time. For example, a single scan of an on-pack QR code would use an anonymous user to allow a more detailed action record to be created.


API Status
Stable:
/auth/evrythng
/auth/evrythng/facebook
/auth/evrythng/users
/users
/users/:evrythngUser


ApplicationUserDocument Data Model

.id (string, read-only)
    The ID of this resource.

.email (string, read-only)
    E-mail address of the user. Unique within the app.

.firstName (string, read-only)
    User's first name.

.lastName (string, read-only)
    User's last name.

.password (string, read-only)
    The user's password.

.birthday (BirthdayDocument)

.gender (string, one of 'male', 'female')
    User's gender.

.timezone (string)
    The current timezone for the user. For example: 
    `Pacific/Honolulu` or `Europe/Uzhgorod`).

.locale (string)
    The default language tag for the user, complied with RFC 
    5646.

.photo (string)
    URL of the user's photo.

.customFields (object)
    Object of case-sensititve key-value pairs of custom fields 
    associated with the resource.

.tags (array of string)
    Array of string tags associated with this resource.

.project (string, read-only)
    The project ID of the application this user belongs to.

.app (string, read-only)
    The ID of the application this user belongs to.

.createdAt (integer, read-only)
    Timestamp when the resource was created.

.updatedAt (integer, read-only)
    Timestamp when the resource was updated.

.access (ApplicationUserAccessDocument)

.scopes (ScopesDocument)
{
  "type": "object",
  "description": "An object describing an Application User.",
  "properties": {
    "id": {
      "type": "string",
      "description": "The ID of this resource.",
      "readOnly": true
    },
    "email": {
      "type": "string",
      "description": "E-mail address of the user. Unique within the app.",
      "readOnly": true
    },
    "firstName": {
      "type": "string",
      "description": "User's first name.",
      "readOnly": true
    },
    "lastName": {
      "type": "string",
      "description": "User's last name.",
      "readOnly": true
    },
    "password": {
      "type": "string",
      "description": "The user's password.",
      "minLength": 8,
      "maxLength": 30,
      "readOnly": true
    },
    "birthday": { "$ref": "BirthdayDocument" },
    "gender": {
      "type": "string",
      "description": "User's gender.",
      "enum": [ "male", "female" ]
    },
    "timezone": {
      "type": "string",
      "description": "The current timezone for the user. For example: `Pacific/Honolulu` or `Europe/Uzhgorod`)."
    },
    "locale": {
      "type": "string",
      "description": "The default language tag for the user, complied with RFC 5646."
    },
    "photo": {
      "type": "string",
      "description": "URL of the user's photo."
    },
    "customFields": {
      "type": "object",
      "description": "Object of case-sensititve key-value pairs of custom fields associated with the resource."
    },
    "tags": {
      "type": "array",
      "description": "Array of string tags associated with this resource.",
      "items": {
        "type": "string",
        "maxLength": 60
      }
    },
    "project": {
      "type": "string",
      "description": "The project ID of the application this user belongs to.",
      "readOnly": true
    },
    "app": {
      "type": "string",
      "description": "The ID of the application this user belongs to.",
      "readOnly": true
    },
    "createdAt": {
      "type": "integer",
      "description": "Timestamp when the resource was created.",
      "readOnly": true
    },
    "updatedAt": {
      "type": "integer",
      "description": "Timestamp when the resource was updated.",
      "readOnly": true
    },
    "access": { "$ref": "ApplicationUserAccessDocument" },
    "scopes": { "$ref": "ScopesDocument" }
  }
}
{
  "id": "UnaP5scsfcQ4tQwaRXEyheBd",
  "createdAt": 1510915630522,
  "customFields": {
    "age": 25
  },
  "tags": [
    "example",
    "user"
  ],
  "updatedAt": 1510915657700,
  "email": "example@evrythng.com",
  "firstName": "Example",
  "lastName": "User",
  "timezone": "Pacific/Honolulu",
  "locale": "en-gb",
  "photo": "https://upload.wikimedia.org/wikipedia/commons/8/84/Example.svg",
  "birthday": {
    "day": 1,
    "month": 12,
    "year": 1992
  },
  "gender": "male",
  "project": "UmxHK6K8BXsa9KawRh4bTbqc",
  "app": "U3pxRQh2eD8RtKwaRgerfQgc"
}

See also: BirthdayDocument, ScopesDocument, ApplicationUserAccessDocument


BirthdayDocument Data Model

.day (integer)

.month (integer)

.year (integer)
{
  "type": "object",
  "description": "An Application User's birthday.",
  "properties": {
    "day": { "type": "integer" },
    "month": { "type": "integer" },
    "year": { "type": "integer" }
  }
}

ApplicationUserAccessDocument Data Model

.id (string)
    The access ID.

.project (string)
    The ID of the project the user belongs to.

.app (string)
    The ID of the application the user belongs to.

.user (string)
    This user's ID.

.actor (string)
    The accessing actor's ID.

.role (string)
    The ID of the user's role.

.apiKey (string)
    The actor's Application User API Key.

.customFields (object)
    Object of case-sensititve key-value pairs of custom fields 
    associated with the resource.
{
  "type": "object",
  "description": "Object containing data relevant to an Application User' platform access.",
  "properties": {
    "id": {
      "type": "string",
      "description": "The access ID."
    },
    "project": {
      "type": "string",
      "description": "The ID of the project the user belongs to."
    },
    "app": {
      "type": "string",
      "description": "The ID of the application the user belongs to."
    },
    "user": {
      "type": "string",
      "description": "This user's ID."
    },
    "actor": {
      "type": "string",
      "description": "The accessing actor's ID."
    },
    "role": {
      "type": "string",
      "description": "The ID of the user's role."
    },
    "apiKey": {
      "type": "string",
      "description": "The actor's Application User API Key."
    },
    "customFields": {
      "type": "object",
      "description": "Object of case-sensititve key-value pairs of custom fields associated with the resource."
    }
  }
}

Create an Application User

Note

This endpoint creates an unactivated user. Use the /auth/evrythng/users/:evrythngUser/validate endpoint to finish the creation process once the user has confirmed their email address etc.

See the Activate an Application User page for more information.

Create a new unactivated Application User, using the Application API Key. The email must be a unique identifier within the app, therefore your application will not be able to create more than one user with the same email address.

If the user doesn't exist yet, the platform will create a new user and return an activationCode in the response. Before the user gets activated and can access data in the API, the application will need to send back the activation code to our API for that user as shown below. This additional step allows the client application to ensure that the email provided has been validated to be real and accessible by the user.

POST /auth/evrythng/users
Content-Type: application/json
Authorization: $APP_API_KEY

ApplicationUserDocument
curl -i -H "Content-type: application/json" \
  -H "Authorization: $APP_API_KEY" \
  -X POST 'https://api.evrythng.com/auth/evrythng/users' \
  -d '{ 
    "firstName": "Example First Name", 
    "lastName": "Example Last Name", 
    "email": "example@test.com", 
    "password": "password" 
  }'
const appUser = {
  email: 'example@test.com',
  password: 'password',
  firstName: 'Example First Name',
  lastName: 'Example Last Name'
};

app.appUser().create(appUser).then(console.log);
ApiManager appApiManager = new ApiManager(APP_API_KEY);

User user = new User();
user.setEmail("example@test.com");
user.setPassword("password");
user.setFirstName("Example First Name");
user.setLastName("Example Last Name");

Credentials creds = apiManager.authService().evrythngUserCreator(user).execute();
System.out.println("evrythngUser=" + creds.getEvrythngUser());
System.out.println("activationCode=" + creds.getActivationCode());
HTTP/1.1 201 CREATED
Content-Type: application/json

{
  "evrythngUser": "UmVxeKVQeg8wQ5waaXsrda2t",
  "activationCode": "3RwaN5LQ",
  "status": "inactive",
  "email": "example@test.com"
}

Activate an Application User

Once a user has been created with the /auth/evrythng/users endpoint, it needs to be activated (by the client application) by sending a POST request including the activationCode.

If the activationCode is correct, the user will be activated and authenticated, and the Application User will be assigned an Application User API Key.

If you forget the activationCode between creating the Application User and activation, you can retrieve it using a GET request to the /users/:evrythngUser/status endpoint.

POST /auth/evrythng/users/:evrythngUser/validate
Content-Type: application/json
Authorization: $APP_API_KEY

{
  "activationCode": String
}
curl -i -H "Content-Type: application/json" \
  -H "Authorization: $APP_API_KEY" \
  -X POST 'https://api.evrythng.com/auth/evrythng/users/:evrythngUser/validate' \
  -d '{ 
    "activationCode": "348s83j" 
  }'
const appUser = {
  email: 'some.one@anyone.com',
  password: 'password', 
  firstName: 'Some',
  lastName: 'One'
};

// Create and validate in one step!
app.appUser().create(appUser).then((newUser) => {
  return newUser.validate();
}).then(() => console.log('Validated!'));

// OR
// Validate a previously created user
otherUser.validate().then((validated) => {
  console.log(`Validated app user: ${validated}`);

  // User can now login successfully using app.login(...) or,
  // be used immediately by creating a User scope:
  const userScope = new EVT.User({
    id: validated.evrythngUser, 
    apiKey: validated.evrythngApiKey
  }, app);
});
// Credentials from a previous invokation of evrythngUserCreator()
creds = apiManager.authService().evrythngUserValidator(creds.getEvrythngUser(), creds.getActivationCode()).execute();
System.out.println("status=" + creds.getStatus());
HTTP/1.1 201 CREATED
Content-Type: application/json

{
  "status": "active",
  "evrythngUser": "UmVxeKVQeg8wQ5waaXsrda2t",
  "evrythngApiKey": "AGCSBhdt07tnLxtObxqObpOI91234S4KBfot9T5oi..."
}

Read all Application Users

Read all available Application Users using the Trusted Application API Key or the Operator API Key. The result may be paginated if there are more than 30 items.

GET /users
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X GET 'https://api.evrythng.com/users'
operator.user().read().then(console.log);
HTTP/1.1 200 OK
Content-Type: application/json

[
  {
    "id": "UmVxeKVQeg8wQ5waaXsrda2t",
    "createdAt": 1497451343302,
    "updatedAt": 1497451406584,
    "email": "some@email.com",
    "firstName": "Test",
    "lastName": "User",
    "project": "UmxHK6K8BXsa9KawRh4bTbqc",
    "app": "Umbn56MmBDsatpaawg6XgKfk"
  }
]

Update an Application User

Authorised Application Users can modify their own data (including the password) using their Application User API key. An Operator is also allowed to update data of users registered within their accounts.

PUT /users/:evrythngUser
Authorization: $OPERATOR_API_KEY
Content-Type: application/json

ApplicationUserDocument (subset)
curl -i -H "Content-type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X PUT 'https://api.evrythng.com/users/UYtUFbWKRq9wDCd5qEdmccyp' \
  -d '{ 
    "firstName": "New First Name" 
  }'
var update = {
  firstName: 'New First Name'
};

// User scope can update itself
user.update(update).then(console.log);

// Operator can also update a user on its behalf
operator.user(userId).update(update).then(console.log);
var email = "test@example.com";
var password = "s3cr3T@";

// Read data by authenticating
Credentials creds = new Credentials(email, password);
creds = appApiManager.authService().evrythngUserAuthenticator(creds).execute();
System.out.println("User ID: " + creds.getEvrythngUser());
System.out.println("User API key: " + creds.getEvrythngApiKey());
HTTP/1.1 200 OK
Content-Type: application/json

{
  "id": "UYtUFbWKRq9wDCd5qEdmccyp",
  "createdAt": 1494924625617,
  "updatedAt": 1497451913694,
  "email": "test@example.com",
  "firstName": "New First Name",
  "lastName": "user",
  "project": "UmxHK6K8BXsa9KawRh4bTbqc",
  "app": "Umbn56MmBDsatpaawg6XgKfk"
}

Update an Application User's Password.

An Application User can update the password they use to log into an application using a PUT request. However, they must also provide their current password as a form of verification.

PUT /users/:evrythngUser
Content-Type: application/json
Authorization: $APPLICATION_USER_API_KEY

{
  "password": "new password",
  "oldPassword": "old password"
}
curl -H "Content-Type: application/json" \
  -H "Authorization: $APPLICATION_API_KEY" \
  -X PUT 'https://api.evrythng.com/users/UmQ6dEBKMG8YEMaRah3FVf3f' \
  -d '{
    "oldPassword": "old password",
    "password": "new password"
  }'
HTTP/1.1 200 OK
Content-Type: application/json

{
  "id": "UmQ6dEBKMG8YEMaRah3FVf3f",
  "createdAt": 1509701451010,
  "updatedAt": 1509701489615,
  "email": "example-user@example.net",
  "firstName": "Example",
  "lastName": "User",
  "project": "UmQMUhSFqm8hYMaawhmkBChr",
  "app": "U3QMUhTn6GPEhMwawE32Bfkr"
}

Delete an Application User

Delete an Application User by ID. This action cannot be undone.

DELETE /users/:evrythngUser
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X DELETE 'https://api.evrythng.com/users/UYtUFbWKRq9wDCd5qEdmccyp'
const userId = 'UYtUFbWKRq9wDCd5qEdmccyp';

operator.user(userId).delete().then(console.log);
HTTP/1.1 200 OK

Create an Anonymous User

Create an anonymous user that does not need to be activated by including the ?anonymous=true parameter in the query parameters and body of the request.

This kind of user is useful for tracking devices not pre-associated with a user.

POST /auth/evrythng/users?anonymous=true
Content-Type: application/json
Authorization: $APPLICATION_API_KEY

{
  "anonymous": true
}
curl -i -H "Content-Type: application/json" \
  -H "Authorization: $APPLICATION_API_KEY" \
  -X POST 'https://api.evrythng.com/auth/evrythng/users?anonymous=true' \
  -d '{ 
    "anonymous": true 
  }'
// Create the user and store the returned credentials
app.appUser().create({
  anonymous: true
}).then((anonymousUser) => {
  // Returns a ready to use User Scope that doesn't need validation
  console.log(`Created anonymous user: ${anonymousUser}`);
  
  // Store anonymous user details locally
  if(window.localStorage) {
    localStorage.userId = anonymousUser.id;
    localStorage,apiKey = anonymousUser.apiKey;
  }
});

// Load the user from localstorage
const anonymousUser = new EVT.User({
  id: localStorage.userId,
  apiKey: localStorage.apiKey
}, app);
User user = new User();
Credentials creds = apiManager.authService().evrythngUserCreator(user).queryParam("anonymous", "true").execute();
System.out.println("Anonymous ID: " + creds.getEvrythngUser());
System.out.println("Anonymous API key: " + creds.getEvrythngApiKey());
HTTP/1.1 201 CREATED
Content-Type: application/json

{
  "evrythngUser": "U3BSenB6Bg8w9pRawk5ycwdt",
  "status": "anonymous",
  "email": "anon-12d855f0-510f-11e7-bc07-0bd000cf37af.app-umbn56mmbdsatpaawg55gkfk@anon.evrythng.com",
  "evrythngApiKey": "Tw9Tw6M4s0ip5k12Pi2xd4VpcliT0H41tW912343X07Z...",
  "socialNetwork": "evrythng"
}

Log a User In

Authenticate (log in) an existing user with their email and password used at registration time. Apps can authenticate existing users using the pair email + password and receiving an Application User API key. The request is made with an ApplicationUserLoginDocument.

If the user is active and if the credentials are correct, the platform will then return a new Application User API key which can be used until the user is logged out.

POST /auth/evrythng
Content-Type: application/json
Authorization: $APPLICATION_API_KEY

ApplicationUserLoginDocument
curl -i -H "Content-type: application/json" \
  -H "Authorization: $APPLICATION_API_KEY" \
  -X POST 'https://api.evrythng.com/auth/evrythng' \
  -d '{ 
    "email": "someone@somewhere.com", 
    "password": "s0mepassw0rd" 
  }'
const credentials = {
  email: 'someone@somewhere.com',
  password: 's0mepassw0rd'
};

// Login user (with EVRYTHNG Auth) and create user scope
// app.login('evrythng', {...}); behaves the same way
app.login(credentials).then((response) => {
  // evrythng.js automatically creates a User scope.
  const userScope = response.user;
  
  // Now every call using user will use their userApiKey
  userScope.thng().read().then(console.log);
});
Credentials creds = new Credentials(email, password);
creds = appApiManager.authService().evrythngUserAuthenticator(creds).execute();
System.out.println("User ID: " + creds.getEvrythngUser());
System.out.println("User API key: " + creds.getEvrythngApiKey());
HTTP/1.1 201 CREATED
Content-Type: application/json

{
  "socialNetwork": "evrythng",
  "evrythngUser": "U3BSenB6Bg8w9pRawk5ycwdt",
  "evrythngApiKey": "cYFWnAiRTaNeOnj8ejc0qQg9Fg4wyPDV4U31sB2j...",
  "email": "someone@somewhere.com"
}

Log a User In with Access Info

Similar to the standard login process, an Application API Key can log a user in and receive access information about that user at the same time. If the actor and user have the same value, the user is authenticating themselves. If these values differ, the actor is logging in on behalf of the user.

In this case, the email property in the request payload can be replaced with the evrythngUser ID, if known.

POST /users/login
Content-Type: application/json
Authorization: $APPLICATION_API_KEY

UserLoginDocument
curl -H "Content-Type: application/json" \
  -H "Authorization: $APPLICATION_API_KEY" \
  -X POST 'https://api.evrythng.com/users/login' \
  -d '{
    "email": "someone@somewhere.com",
    "password": "s0omepassw0rd"
  }'
const credentials = {
  email: 'someone@somewhere.com',
  password: 's0mepassw0rd'
};

app.login(credentials).then(console.log);
HTTP/1.1 201 Created
Content-Type: application/json

{
  "id": "UmW8bewpVD8RQKRaR2qpDtyh",
  "createdAt": 1499337739424,
  "updatedAt": 1499337754551,
  "email": "someone@somewhere.com",
  "firstName": "Some",
  "lastName": "One",
  "project": "UGBNTUbf6tQVhswwagmfVrcm",
  "app": "U3fdqbMpqt9VhsaRwED3xmtn",
  "access": {
    "id": "595e141ab4a46a2c00b7bdf1",
    "project": "UGBNTUbf6tQVhswwagmfVrcm",
    "app": "U3fdqbMpqt9VhsaRwED3xmtn",
    "user": "UmW8bewpVD8RQKRaR2qpDtyh",
    "actor": "UmW8bewpVD8RQKRaR2qpDtyh",
    "role": "base_app_user",
    "apiKey": "Lz5RtLxwDP3kS6VawbKip2DrQJkKAiTu01lGMjfS..."
  }
}

ApplicationUserLoginDocument Data Model

.email (string)
    The email address assigned to the Application User.

.password (string)
    The Application User's password used at account creation 
    time.
{
  "type": "object",
  "description": "An object representing an Application User login request.",
  "properties": {
    "email": {
      "type": "string",
      "description": "The email address assigned to the Application User.",
      "readOnly": true
    },
    "password": {
      "type": "string",
      "description": "The Application User's password used at account creation time.",
      "readOnly": true
    }
  }
}

Create/Authenticate a User via External Profiles

EVRYTHNG applications can also sign up and login users using their accounts on external services.

In practice, apps must log in users via Facebook and then create a user account on EVRYTHNG that is bound to his Facebook account. This is done by posting the user's Facebook access token on the /auth/facebook endpoint.

Note that the authentication uses the Application API Key (returned in the appApiKey parameter of the object of your application, as seen in Applications. When creating a new user, the platform will send the Access token to Facebook to validate the user has been created by the correct Facebook application (the access token corresponds to the appId of the Facebook object in the corresponding application).

Important

The EVRYTHNG Platform uses the provided Facebook token to retrieve information about the user. But some of this user data is only visible if the corresponding permissions have been requested upon authenticating the user through the Facebook API.

In order to allow the user to be created properly you must ask for at least the email and user_birthday permissions.

POST /auth/facebook
Content-Type: application/json
Authorization: $APPLICATION_API_KEY

{
  "access": {
    "expires": Timestamp,
    "token"": String       // Facebook Token
  }
}
HTTP/1.1 201 CREATED
Content-Type: application/json

{
  "socialNetwork": "facebook",
  "access": {
    "expires": Timestamp,
    "token": String          // Facebook-Token
  },
  "socialNetworkId": String, // Facebook User Id,
  "appId": String,           // Facebook App Id,
  "evrythngUser": Ref,
  "evrythngApiKey": UserApiKey
}

Authenticate a User via Facebook with Evrythng.js

Note

evrythng.js asynchronously loads the Facebook Javascript SDK for you. You do not need to authenticate with Facebook another way.

The following login calls open a generic Facebook login popup. Once authenticated with Facebook you're also authenticated with EVRYTHNG.

  1. Using Facebook default permissions (email):
app.login('facebook').then((response) => {
  const fbStatus = response.status;
  const user = response.user;

  // Now every call using user will use their Application User API Key
});
  1. Using custom permissions (be sure to have them enabled in your Facebook Application):
app.login('facebook', { 
  scope: 'email,user_birthday' 
}).then((response) => {
  const fbStatus = response.status;
  const user = response.user;

  // Now every call using user will use their UserApiKey
});

Read more about Facebook login permissions and connection status in the Facebook API reference.

Return Parameters

  • socialNetwork - String
    Indicates the external social network type. At the moment, only "facebook" is supported.

  • access - Object
    The access token for this user on the social network, and includes the user token and the expiration time. Example:

    {
      "token": "XXX",
      "expires": Timestamp
    }
    
  • socialNetworkId - String
    The user ID of the user on that social network (e.g. the Facebook user ID.).

  • appId - String
    The application ID on the social network the user has signed in (e.g. the Facebook app ID).

  • evrythngUser - String
    The EVRYTHNG user ID corresponding for the that user.

  • evrythngApiKey - String
    This is the unique EVRYTHNG USER API Key that uniquely identifies this user within this application. This API Key must be used for all subsequent activities by the user within the application for all calls to the EVRYTHNG platform (read Thngs, post actions, etc.).


Log the User Out

User access token can be invalidated by using it to call to /auth/all/logout regardless of the method the user authenticated with (EVRYTHNG or 3rd party). The user's API key will become invalid after the call.

POST /auth/all/logout
Content-Type: application/json
Authorization: $APPLICATION_USER_API_KEY
curl -i -H "Content-type: application/json" \
  -H "Authorization: $APPLICATION_USER_API_KEY" \
  -X POST 'https://api.evrythng.com/auth/all/logout'
user.logout();
// creds obtained from a previous invokation of evrythngUserAuthenticator()
apiManager.authService().authLogouter().apiKey(creds.getEvrythngApiKey()).execute();

The platform acknowledges the logout operation in the response body.

HTTP/1.1 201 CREATED
Content-Type: application/json

{
  "logout": "ok"
}
Suggest Edits

Collections

 

A Collection resources allows you to group a set of Thngs that make it easier for Application Users to manage all the Thngs they created while using an application. This works by assigning a reference to the collection to each Thng.

Collections have a similar data model to other resources in the Platform (a name and description, tags and properties) and are created in a similar manner. Once a collection has been created, users can add or remove manually Thngs to that collection, using the /thngs sub-resource of each collection.

In addition to specifying a collection in an action, it is also possible to create an action through the collection itself using aliased actions.


API Status
Stable:
/collections
/collections/:collectionId
/collections/:collectionId/thngs
/collections/:collectionId/collections
/collections/:collectionId/collections/:childCollectionId


CollectionDocument Data Model

.name (string)
    Friendly name of this resource.

.id (string, read-only)
    The ID of this resource.

.createdAt (integer, read-only)
    Timestamp when the resource was created.

.updatedAt (integer, read-only)
    Timestamp when the resource was updated.

.description (string)
    Friendly description of this resource.

.customFields (object)
    Object of case-sensititve key-value pairs of custom fields 
    associated with the resource.

.tags (array of string)
    Array of string tags associated with this resource.

.identifiers (object)
    Various identifiers (EPC, ISBN, etc.) as a JSON object with 
    one or more key-value pairs.

.collections (array of string)
    An array of collection IDs of any collections this 
    collection belongs to.

.scopes (ScopesDocument)
{
  "type": "object",
  "description": "An object representing a Platform collection, which can contain groups of Thngs.",
  "properties": {
    "name": {
      "type": "string",
      "description": "Friendly name of this resource."
    },
    "id": {
      "type": "string",
      "description": "The ID of this resource.",
      "readOnly": true
    },
    "createdAt": {
      "type": "integer",
      "description": "Timestamp when the resource was created.",
      "readOnly": true
    },
    "updatedAt": {
      "type": "integer",
      "description": "Timestamp when the resource was updated.",
      "readOnly": true
    },
    "description": {
      "type": "string",
      "description": "Friendly description of this resource."
    },
    "customFields": {
      "type": "object",
      "description": "Object of case-sensititve key-value pairs of custom fields associated with the resource."
    },
    "tags": {
      "type": "array",
      "description": "Array of string tags associated with this resource.",
      "items": {
        "type": "string",
        "maxLength": 60
      }
    },
    "identifiers": {
      "type": "object",
      "description": "Various identifiers (EPC, ISBN, etc.) as a JSON object with one or more key-value pairs."
    },
    "collections": {
      "description": "An array of collection IDs of any collections this collection belongs to.",
      "type": "array",
      "items": { "type": "string" }
    },
    "scopes": { "$ref": "ScopesDocument" }
  }
}
{
  "id": "UHa8qAMtVgPRQKaawEf5hfQm",
  "createdAt": 1510917071603,
  "customFields": {
    "host": "8974237894"
  },
  "tags": [
    "example",
    "collection"
  ],
  "updatedAt": 1510917071603,
  "name": "Example Collection",
  "description": "An example collection resource.",
  "identifiers": {
    "ean_8": "32833232"
  }
}

See also: ScopesDocument
__

Create a Collection

Create a new collection.

POST /collections
Content-Type: application/json
Authorization: $OPERATOR_API_KEY

CollectionDocument
curl -i -H "Content-Type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X POST 'https://api.evrythng.com/collections' \
  -d '{
    "name": "Office-322",
    "description": "The collection of all thngs in office 322",
    "customFields": {
      "insured": "true"
    },
    "tags": [ "Office", "Zurich", "322", "EVRYTHNG" ],
    "identifiers": {
      "collectionCode": "OZ322",
      "collectionReferenceNumber": "12345"
    }
  }'
const collection = {
  name: 'Office-322',
  description: 'The collection of all thngs in office 322',
  customFields: {
    insured: 'true'
  },
  tags: [ 'Office', 'Zurich', '322', 'EVRYTHNG' ],
  identifiers: {
    collectionCode: 'OZ322',
    collectionReferenceNumber: '12345'
  }
};

user.collection().create(collection).then(console.log);
Collection collection = new Collection();
collection.setName("Office-322");
apiManager.collectionService().collectionCreator(collection).execute();
HTTP/1.1 201 Created
Location: https://api.evrythng.com/collections/UmSyPf5RBg8RtpRaag9F9s3b
Content-Type: application/json

{
  "id": "UmSyPf5RBg8RtpRaag9F9s3b",
  "createdAt": 1346865441719,
  "updatedAt": 1346865441719,
  "tags": [ "Office","Zurich","322","EVRYTHNG" ],
  "name": "Office-322",
  "description": "The collection of all Thngs in office 322"
}

Read All Collections

Read all available collections. The result may be paginated if there are more than 30 items.

GET /collections
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X GET 'https://api.evrythng.com/collections'
operator.collection().read().then(console.log);
List<Collection> collections = apiManager.collectionService().collectionsReader().execute();
for(Collection collection : collections) {
    System.out.println(collection.getId());
}
HTTP/1.1 200 OK
Content-Type: application/json

[
  {
    "id": "UmSc8WnPeXswtKRwwgkqBqHh",
    "createdAt": 1494410670561,
    "updatedAt": 1494836856606,
    "name": "My Collection",
    "collections": [
      "UmSyPf5RBg8RtpRaag9F9s3b"
    ]
  }
]

Update a Collection

Update a collection's fields with new data. Nested fields such as tags are replaced entirely with the value in the payload. The response payload is the complete updated collection document.

Note

There is a limit of 10,000 Thngs updated per API call on collections. If your collection contains more than 10,000 elements you should consider an iterative solution.

PUT /collections/:collectionId
Authorization: $OPERATOR_API_KEY

CollectionDocument (subset)
curl -i -H "Content-Type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X PUT 'https://api.evrythng.com/collections/U2a4AsGtqerbcWD6qhYGfb5d' \
  -d '{
    "name": "New Collection Name",
    "description": "New Collection Description",
  }'
const collectionId = 'UhQMMnGQbxnteaSDp7G8Fynn';

const collectionUpdate = {
  name: 'New Collection Name',
  description: 'New Collection Description'
};

// in a single request
user.collection(collectionId).update().then(console.log);

// or on the entity itself
user.collection(collectionId).read().then((collection) => {
  collection.update(collectionUpdate).then(console.log);
});
String collectionId = "U2a4AsGtqerbcWD6qhYGfb5d";

Collection collection = new Collection();
collection.setName("New Collection Name");
collection.setDescription("Updated collection description");
collection = apiManager.collectionService().collectionUpdater(collectionId, collection).execute();
HTTP/1.1 200 OK
Content-Type: application/json

{
  "id": "U2a4AsGtqerbcWD6qhYGfb5d",
  "createdAt": 1474984969259,
  "updatedAt": 1474984989915,
  "name": "New Collection Name",
  "description": "New Collection Description"
}

Delete a Collection

Delete a single collection by ID.

DELETE /collections/:collectionId
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X DELETE 'https://api.evrythng.com/collections/U2a4AsGtqerbcWD6qhYGfb5d'
const collectionId = 'U2a4AsGtqerbcWD6qhYGfb5d';

// in a single request
operator.collection(collectionId).delete();

// or on the entity itself
operator.collection(collectionId).read().then((collection) => {
  collection.delete();
});
String collectionId = "U2a4AsGtqerbcWD6qhYGfb5d";

apiManager.collectionService().collectionDeleter(collectionId).execute();
HTTP/1.1 200 OK

Read All Thngs in a Collection

Get the list of all the thngs in a collection can be retrieved via a GET on the /thngs resource of a collection. The result may be paginated if there are more than 30 items.

GET /collections/:collectionId/thngs
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X GET 'https://api.evrythng.com/collections/U2wGPSspbrqFcKxdVtM8yhgt/thngs'
const collectionID = 'U2wGPSspbrqFcKxdVtM8yhgt';

// In a single request
user.collection(collectionId).thng().read().then(console.log);

// Or on the resource itself
user.collection(collectionId).read().then((collection) => {
  collection.thng().read().then(console.log);
});
String collectionId = "U2wGPSspbrqFcKxdVtM8yhgt";

List<Thng> thngs = apiManager.collectionService().thngsReader(collectionId).execute();
for (Thng t : thngs) {
    System.out.println(t.toString());
}
HTTP/1.1 200 OK
Content-Type: application/json

[
  {
    "id": "U2wGPSspbrqFcKxdVtM8yhgt",
    "createdAt": 1492606257456,
    "updatedAt": 1496752159725,
    "name": "Rubiks Cube - Recommended",
    "description": "Updated Thng description.",
    "product": "UFKE9nHBBg8atKaawgVrKKbs",
    "collections": [
      "U2wGPSspbrqFcKxdVtM8yhgt"
    ]
  }
]

Read a Collection

Read a single collection by its ID.

GET /collections/:collectionId
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X GET 'https://api.evrythng.com/collections/U2wGPSspbrqFcKxdVtM8yhgt'
const collectionId = 'U2wGPSspbrqFcKxdVtM8yhgt';

operator.collection(collectionId).read().then(console.log);
String collectionId = "U2wGPSspbrqFcKxdVtM8yhgt";

Collection collection = apiManager.collectionService().collectionReader(collectionId).execute();
HTTP/1.1 200 OK
Content-Type: application/json

{
  "id":"U2wGPSspbrqFcKxdVtM8yhgt",
  "createdAt":1346865128086,
  "updatedAt":1347024162647,
  "name": "Office-322",
  "description": "The collection of all thngs in office 322"
  "customFields": {
    "insured": "true"
  },
  "tags": [ "Office", "Zurich", "322", "EVRYTHNG" ]
}

Add Thngs to a Collection

Thngs can be added to a collection by PUTing an array with their respective thngIds to the /thngs.

Note

The maximum number of Thngs that can be added to a collection is 10,000 per request.

PUT /collections/:collectionId/thngs
Content-Type: application/json
Authorization: $OPERATOR_API_KEY

[ ThngID, ... ]
curl -i -H "Content-Type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X PUT 'https://api.evrythng.com/collections/UhQMMnGQbxnteaSDp7G8Fynn/thngs' \
  -d '[
    "UBMpmseRseKRBaDYKg5ychya", 
    "UemSgmdP8BpwT4N9sHbAUd5q"
  ]'
const collectionId = 'UhQMMnGQbxnteaSDp7G8Fynn';

const thngs = [
  'UBMpmseRseKRBaDYKg5ychya',
  'UemSgmdP8BpwT4N9sHbAUd5q'
];

// in a single request
appUser.collection(collectionId).thng().update(thngs).then(console.log);

// or on the resource itself
appUser.collection(collectionId).read().then((collection) => {
  collection.thng().update(thngs).then(console.log);
});
String collectionId = "UhQMMnGQbxnteaSDp7G8Fynn";

apiManager.collectionService().thngsAdder(collectionId, thngIdList).execute();
HTTP/1.1 200 OK

Remove a Thng from a Collection

Thngs can be removed from within a collection, by sending a DELETE to their ID within the :collectionID/thngs sub-resource of a collection.

Note

There is a limit of 500 Thngs deleted per API call on collections. If your collection contains more than 500 elements you should consider an iterative solution.

DELETE /collections/:collectionId/thngs/:thngId
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X DELETE 'https://api.evrythng.com/collections/UhQMMnGQbxnteaSDp7G8Fynn/thngs/UF6qwDbaVDPwQKRwagmnqyqs'
const collectionId = 'UhQMMnGQbxnteaSDp7G8Fynn';
const thngId = 'UF6qwDbaVDPwQKRwagmnqyqs';

// in a single request
operator.collection(collectionId).thng(thngId).delete();

// or on the resource itself
operator.collection(collectionId).read().then((collection) => {
  collection.thng(thngId).delete();
});
String collectionId = "UhQMMnGQbxnteaSDp7G8Fynn";
String thngId = "UF6qwDbaVDPwQKRwagmnqyqs";

apiManager.collectionService().thngRemover(collectionId, thngId).execute();
HTTP/1.1 200 OK

Remove All Thngs from a Collection

All Thngs within a collection can be removed by sending a DELETE to the /thngs resource of a collection.

DELETE /collections/:collectionId/thngs
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X DELETE 'https://api.evrythng.com/collections/U2a4AsGtqerbcWD6qhYGfb5d/thngs'
const collectionId = 'U2a4AsGtqerbcWD6qhYGfb5d';

// in a single request
operator.collection(collectionId).thng().delete();

// or on the resource itself
operator.collection(collectionId).read().then((collection) => {
  collection.thng().delete();
});
String collectionId = "U2a4AsGtqerbcWD6qhYGfb5d";

apiManager.collectionService().thngsRemover(collectionId).execute();
HTTP/1.1 200 OK

Read All Collections in a Collection

The list of all the collections in a collection can be retrieved via a GET on the /collections sub-resource of a collection. The result may be paginated if there are more than 30 items.

GET /collections/:collectionId/collections
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X GET 'https://api.evrythng.com/collections/U2a4AsGtqerbcWD6qhYGfb5d/collections'
const collectionId = 'U2a4AsGtqerbcWD6qhYGfb5d';

operator.collection(collectionId).collection().read().then(console.log);
String collectionId = "U2a4AsGtqerbcWD6qhYGfb5d";

List<Collection> collections = apiManager.collectionService().childrenCollectionsReader(collectionId).execute();
for(Collection child : collections) {
    System.out.println(child.toString());
}
HTTP/1.1 200 OK
Content-Type: application/json

[
  {
    "id": "UmSyPf5RBg8RtpRaag9F9s3b",
    "createdAt": 1494410672569,
    "updatedAt": 1494836852529,
    "name": "New collection name",
    "description": "New collection description",
    "collections": [
      "U2a4AsGtqerbcWD6qhYGfb5d"
    ]
  }
]

Add Collections to a Collection

Child collections can be added to a collection by POSTing an array with their respective IDs to the /collections endpoint.

POST /collections/:collectionId/collections
Content-Type: application/json
Authorization: $OPERATOR_API_KEY

[ CollectionID, ... ]
curl -i -H "Content-Type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X POST 'https://api.evrythng.com/collections/U2amsb7M7a2bD4xD9mEVDdWf/collections' \
  -d '[
    "UFwG8xsAb7qkcKbdeQMscDer", 
    "U2a4AsGtqerbcWD6qhYGfb5d"
  ]'
const collectionId = 'U2amsb7M7a2bD4xD9mEVDdWf';

// The child collections to add to collection
const childCollectionIds = [ 
  'UFwG8xsAb7qkcKbdeQMscDer', 
  'U2a4AsGtqerbcWD6qhYGfb5d' 
];

operator.collection(collectionId).read().then((collection) => {
  collection.collection().create(childCollectionIds).then(console.log);
});
String collectionId = "U2amsb7M7a2bD4xD9mEVDdWf";

HashSet<String> childCollectionSet = new HashSet<String>();
childCollectionSet.add("UFwG8xsAb7qkcKbdeQMscDer");
childCollectionSet.add("U2a4AsGtqerbcWD6qhYGfb5d");

apiManager.collectionService().childrenCollectionsAdder(collection1Id, childCollectionSet).execute();
HTTP/1.1 200 OK

Remove a Collection from a Collection

Collections can be removed from within a collection, by sending a DELETE to their ID within the :collectionId/collections sub-resource of a collection.

DELETE /collections/:collectionId/collections/:childCollectionId
Authorization: $OPERATOR_API_KEY
curl -i -H "Content-Type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X DELETE 'https://api.evrythng.com/collections/UhQMMnGQbxnteaSDp7G8Fynn/collections/U2amsb7M7a2bD4xD9mEVDdWf'
const parentCollectionId = 'UhQMMnGQbxnteaSDp7G8Fynn';
const childCollectionId = 'U2amsb7M7a2bD4xD9mEVDdWf';

operator.collection(childCollectionId).read().then((collection) => {
  collection.collection(parentCollectionId).delete();
});
String parentCollectionId = "UhQMMnGQbxnteaSDp7G8Fynn";
String childCollectionId = "U2amsb7M7a2bD4xD9mEVDdWf";

apiManager.collectionService().childCollectionRemover(parentCollectionId, childCollectionId).execute();
HTTP/1.1 200 OK

Remove all Collections from a Collection

All collections within a collection can be removed by sending a DELETE to the /collections resource of a collection.

DELETE /collections/:collectionId/collections
Authorization: $OPERATOR_API_KEY
curl -i -H "Content-Type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X DELETE 'https://api.evrythng.com/collections/UhQMMnGQbxnteaSDp7G8Fynn/collections'
const parentCollectionId = 'UhQMMnGQbxnteaSDp7G8Fynn';

operator.collection(parentCollectionId).collection().delete();
String parentCollectionId = "UhQMMnGQbxnteaSDp7G8Fynn";

apiManager.collectionService().childrenCollectionsRemover(parentCollectionId).execute();
HTTP/1.1 200 OK
Suggest Edits

Identifier Recognition

 

EVRYTHNG offers an Identifier Recognition service through the REST API. Developers use this resource to identify barcodes in images, and any linked products or Thngs linked to those codes.

Any Application and Operator API key can POST an image, and if a product/Thng redirection or barcode matches the image, the service returns information about the matches made. This includes any redirection information set up for the resource that matched the image recognition query.

Walkthrough

For a complete walkthrough of the process of utilizing product recognition, read Scan and Track Products.


API Status
Stable: /scan/identifications
Preview: /scan/identifications when method=ocr


Enabling Recognition

To enable recognition of a specific type of barcode, include the equivalent key name as an item in the identifiers field in the Thng or product to be recognised at scan time. For the ocr method, add text to this field.

This can be done through the REST API, or via the Dashboard. The value of the key should be the numerical value encoded within the barcode itself. For example:

{
  "name": "Thng with barcode",
  "description": "An example Thng with a barcode identifier to aid in barcode recognition",
  "tags": [ "object", "scan-friendly", "barcode" ],
  "identifiers: {
    "ean_8": "96385074"
  }
}

Alternatively, if the scanned image is a Thng or product redirection QR code, you can also use the encoded URL as the value field, where applicable. QR codes generated when adding a redirection for a given Thng or product will also be matched independently of the identifiers listed.


Performing a Scan or Identification

Scanning an image is performed by making a POST request to /scan/identifications through the API, or using the scan() method in the evrythng.js SDK. The filter query parameter specified dictates which types of codes are searched for or recognition methods are attempted with the supplied image payload.

Similarly, a GET request to the same resource will attempt to find any products or Thngs visible to the authenticating Application or Operator API Key with a code containing the value query parameter value. For example, a QR code with tn.gg redirection to a product would match a value=https://tn.gg/yyZZ1nAh query parameter value.

See Identify from an Image or Read an Identifier's Product or Thng for example requests.


Filter Format

The filter query parameter can contain the following items:

  • method - String
    The scanning method to use.

  • type - String
    The type of identifier to identify.

You can optionally also include the following parameters in the URL:

  • perPage - Integer
    Specify how many items to return per page of results.

  • debug - Boolean
    Set to true to view extra information used to measure performance.

Important

The filter text must be URL encoded, similar to other Platform filters.

The different types of methods and identifier types are shown in the table below:

Methods
Types

ocr - Optical character recognition

text - Text recognition.

ir - Image recognition

image - Recognition based on pre-submitted images.

1d - One dimensional barcodes

codabar - Codabar
code_11 - Code 11
code_39 - Code 39
code_93 - Code 93
code_128 - Code 128
ean_8 - EAN-8
ean_13 - EAN-13
industr_25 - Code 25 Industrial
itf - ITF-14
rss_14 - GS1 DataBar (RSS-14)
rss_expanded - DataBar Expanded (RSS Expanded)
rss_limited - DataBar Limited (RSS Limited)
upc_a - UPC-A
upc_e - UPC-E

2d - Two dimensional barcodes

dm - DataMatrix
qr_code - QR Code

Using Multiple Types of Recognition

Both the method and type fields of the filter parameter may be specified as lists containing multiple items from their respective available values. For example, to use both OCR text recognition and QR code recognition:

POST /scan/identifications?filter=method=<method1>,<method2>&type=<type1>,<type2>
Content-Type: application/json
Authorization: $APPLICATION_API_KEY
curl -H "Content-Type: application/json" \
  -H "Authorization: $APPLICATION_API_KEY" \
  -X POST 'https://api.evrythng.com/scan/identifications?filter=method%3D1d%2C2d%26type%3Dqr_code%2Cdm' \
  -d '{
    "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAA..."
  }'
// As an object
app.scan({
  filter: {
    method: ['2d', 'ocr'],
    type: ['qr_code', 'text']
  }
}).then(console.log);
        
// As a query parameter string
app.scan({
  filter: 'method=2d,ocr&type=qr_code,text'
}).then(console.log);

In the case that multiple types are specified, the method may be omitted:

POST /scan/identifications?filter=type=<type1>,<type2>
Content-Type: application/json
Authorization: $APPLICATION_API_KEY
curl -H "Content-Type: application/json" \
  -H "Authorization: $APPLICATION_API_KEY" \
  -X POST 'https://api.evrythng.com/scan/identifications?filter=type%3Dqr_code%2Cdm' \
  -d '{
    "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAA..."
  }'
// As an object
app.scan({
  filter: {
    type: ['text', 'qr_code']
  }
}).then(console.log);
           
// As a query parameter string
app.scan({
  filter: 'type=qr_code,text'
}).then(console.log);

Lastly, it is possible to attempt to identify any and all codes in the image by omitting the filter query parameter entirely.


Matching and Response

When a code type matching the filter specified is found in the image, the Identifier Recognition API looks for a Thng/product that is visible to the API key that performed the call (i.e.: in the same scope).

The response is an array containing metadata and a set of matching results, if any were found. If there were no results, or an incompatible combination of method and type were specified, an empty array will be returned. If the scan was performed on behalf of a user, their object will also be included, allowing extra functionality such as Scan actions. An example is shown below:

HTTP/1.1 200 OK
Content-Type: application/json

[
  {
    "results": [
      {
        "redirections": [
          "https://tn.gg/Q9Wqcg4w"
        ],
        "product": {
          "id": "UYKNDMGcswyFBdf6wr7M5Erm",
          "properties": {},
          "fn": "Test Product",
          "name": "Test Product",
          "identifiers": {}
        }
      }
    ],
    "meta": {
      "method": "2d",
      "score": 100,
      "value": "https://tn.gg/Q9Wqcg4w",
      "type": "qr_code"
    }
  }
]

Data Fields

  • results - Object array
    Array of identification results, containing the matching Thng/product and any redirections associated with it.

  • meta - Object
    Object containing metadata about the identification operation. Not intended for business logic use.

    • meta.method - String
      The method used to identify the product/Thng.

    • meta.score - Number
      Confidence score of the results for the ir method, or 100 for one or more matches for the other method values, else 0.

    • meta.value - String
      The actual value encoded within the identifier. Can be used with a GET request to retrieve the associated Thng/product.

    • meta.type - String
      The type used to identify the Thng/product.


Payload Sources

The /scan/identifications endpoint accepts JPG and PNG images as a file upload or Base64-encoded image data as a string.

File Upload

You can upload a file either from a file browser or a direct camera capture on mobile devices. The evrythng-scan.js library includes this functionality as part of its API. When the scan() method is invoked, the device-specific native dialog for choosing a file or capturing from a camera is shown to the user. See below for examples.

Note

On desktops or laptops, we suggest using the Say Cheese library to capture from the user's webcam, if available.

Base64 Data

As an alternative to uploading a file through evrythng.js, Base64 image data may also be submitted in the request body. The image must be encoded in a string containing the MIME type header before the actual image data (i.e.: "data:{content-type},...{Base64 data}...") and be nested inside a JSON object with the image key. A truncated example is shown below:

{
  "image": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAASABIAAD/4QCARXhpZgAAKg..."
}

Example File Uploads

File Upload

const EVT = require('evrythng');
const Scan = require('evrythng-scan');

EVT.use(Scan);

const app = new EVT.App(APPLICATION_API_KEY);

// Launch image capture or file upload for image to scan
// The 'filter' chosen here will look for a 2D QR code
app.scan({
  filter: 'method=2d&type=qr_code'
}).then((response) => {
  // Do something on success
}).catch((error) => {
  // Do something on error
});

Base64 Data

POST /scan/identifications?filter=method%3D<method>%26type%3D<type>
Content-Type: application/json
Authorization: $APPLICATION_API_KEY

{
  "image": Base64 Data
}
curl -i -H "Content-Type: application/json" \
  -H "Authorization: $APPLICATION_API_KEY" \
  -X POST 'https://api.evrythng.com/scan/identifications?filter=method%3D<method>%26type%3D<type>' \
  -d '{
    "image": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAASABIAAD/4QCARAAKg..."
  }'
const EVT = require('evrythng');
const Scan = require('evrythng-scan');

EVT.use(Scan);

const app = new EVT.App(APPLICATION_API_KEY);
const data = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA...';
const options = { filter: 'method=2d&type=qr_code' };

app.scan(data, options).then((response) => {
  // Do something with the result, such as a scan action
}).catch((error) => {
  // Do something on error
});

Identify from an Image

Important

The filter text must be URL encoded, similar to other Platform filters.

When a request is made to the /scan/identifications endpoint with the POST method, the image is analysed according to the filter query string provided, and one or more matches to products/Thngs are produced if they list the matching identifiers values.

POST /scan/identifications?filter=method=<method>&type=<type>
Content-Type: application/json
Authorization: $APPLICATION_API_KEY

{
  "image": Base64 Data
}
# The `image` value below has been truncated for brevity.

curl -H "Content-Type: application/json" \
  -H "Authorization: $APPLICATION_API_KEY" \
  -X POST 'https://api.evrythng.com/scan/identifications?filter=method%3D2d%26type%3Dqr_code' \
  -d '{
    "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAA..."
  }'
app.scan({
  filter: 'method=2d&type=qr_code'
}).then(console.log).catch((error) => {
  // Do something on error
});

Read an Identifier's Product or Thng

If a value has already been extracted from an image via the POST /scan/identifications endpoint, you can read the associated product or Thng with a GET request containing a filter specifying the type of the identifier.

Note

The type parameter must be specified with the submitted value for the identification to succeed.

GET /scan/identifications?filter=type=<type>&value=<value>
Authorization: $APPLICATION_API_KEY
curl -H "Authorization: $APPLICATION_API_KEY" \
  -X GET 'https://api.evrythng.com/scan/identifications?filter=type%3Dqr_code%26value%3Dhttps://tn.gg/yyZZ1nAh'
// Get identification info for pre-obtained value 'https://tn.gg/Q9Wqcg4w'
app.identify({ 
  filter: 'type=qr_code&value=https://tn.gg/Q9Wqcg4w' 
}).then(console.log).catch((error) => {
  // Do something on error
});
HTTP/1.1 200 OK
Content-Type: application/json

[
  {
    "results": [
      {
        "redirections": [
          "https://tn.gg/yyZZ1nAh"
        ],
        "product": {
          "id": "UYKNDMGcswyFBdf6wr7M5Erm",
          "properties": {},
          "fn": "Test Product",
          "name": "Test Product",
          "identifiers": {}
        }
      }
    ],
    "meta": {
      "method": "2d",
      "score": 100,
      "value": "https://tn.gg/yyZZ1nAh",
      "type": "qr_code"
    }
  }
]

Scan Actions

A Scan action created upon a successful recognition generates valuable contextual data which can be used for business rules/decision making and data insights analysis. For example: geolocation; browser and device type; anonymous user ID.

Creating a Scan action can done using the result returned by scan() with the evrythng.js SDK. An example is shown below:

Note

Since an action cannot be created with an Application API Key, the createAnonymousUser: true option is required in order to be able to create a Scan action as an Application User. Alternatively, a regular Application User API Key can also be used.

app.scan({
  filter: 'score>=80&method=ocr',
  createAnonymousUser: true
}).then((response) => {
  // Create a Scan action as the anonymous user for the first result
  const result = response[0].results[0];  
  return result.thng.action('scans').create();
}).catch((error) => {
  // Do something on error
});

With this option enabled, you will receive a lot more useful data items, shown in the example below. Most notable are the addition of the location, and createdBy... items, which can be used for analysis and decision making when aggregated across a large number of scans.

[
  {
    "id": "UkAsQqTWN2y2tfwVkyBKykws",
    "createdAt": 1480522789344,
    "timestamp": 1480522789344,
    "type": "scans",
    "user": "UFAsQqTRtEf5FpRekYC5CgKg",
    "location": {
      "latitude": 51.5142,
      "longitude": -0.0931,
      "position": {
        "type": "Point",
        "coordinates": [ -0.0882956, 51.5303183 ]
      }
    },
    "locationSource": "geoIp",
    "createdByProject": "U2As9WsMthW5F5aeFECpCeDs",
    "createdByApp": "UFUstWQVQEC5FpRe2EWpWBhf",
    "product": "UFd8t2aaN2y2tCwBFTepTgEs"
  }
]

Redirections

Once an identifier has been recognized, the results can be used to redirect the user to a pre-set destination according to any redirections attached to the product/Thng associated with the result. For example:

// Perform a scan for identifiers
app.scan({
  filter: 'method=2d&type=qr_code',
  createAnonymousUser: true
}).then((response) => {
  // Redirect to the first redirection set on the resource
  const result = response[0].results[0];  
  app.redirect(result.redirections[0]);
}).catch((error) => {
  // Do something on error
});

Error Codes

The table below details the error codes that may be returned by the Identifier Recognition API:

Error Code
Descriptions

404

Image not recognized
Image does not contain a valid identifier

403

Access denied: Image recognition not permitted with this API key

400

Alpha transparency not supported
Duplicate images are not allowed

503

Service unavailable


Known Issues

  • Denying access to location in Firefox, IE11, and Edge browsers can cause issues with evrythng-scan.js. Make sure to guide users to always accept the request for location data.

  • Using a web app or page in Safari Incognito mode can cause issues with evrythng-scan.js. Apps should display a message instructing users to not use Safari Incognito mode.

A place resource maps geographical coordinates to a known real-world location (a shop, an office, a park, etc.) where a product or Thng may be interacted within. It can be thought of as a defined fixed location, such as a manufacturing warehouse that every product will be manufactured at, as opposed to raw location that a Thng may have. As well as physical coordinates, places can contain conventional street address information, but these are not dynamically linked (coordinates bear no relation to the strings assigned to the address fields).

Places can be used to specify a Thng or action location instead of a set of physical coordinates. See the examples below to learn more.


API Status
Stable:
/places
/places/:placeId


PlaceDocument Data Model

.name (string)
    Friendly name of this resource.

.id (string, read-only)
    The ID of this resource.

.description (string)
    Friendly description of this resource.

.customFields (object)
    Object of case-sensititve key-value pairs of custom fields 
    associated with the resource.

.tags (array of string)
    Array of string tags associated with this resource.

.identifiers (object)
    Various identifiers (EPC, ISBN, etc.) as a JSON object with 
    one or more key-value pairs.

.createdAt (integer, read-only)
    Timestamp when the resource was created.

.updatedAt (integer, read-only)
    Timestamp when the resource was updated.

.position (object)
    A GeoJSON Point object. The coordinate order is longitude, 
    then latitude.

.icon (string)
    URL to an image representing the place.

.address (AddressDocument)

.longitude (number)
    The longitude.

.latitude (number)
    The latitude.

.scopes (ScopesDocument)
{
  "type": "object",
  "description": "An object describing a real physical place with an address.",
  "properties": {
    "name": {
      "type": "string",
      "description": "Friendly name of this resource."
    },
    "id": {
      "type": "string",
      "description": "The ID of this resource.",
      "readOnly": true
    },
    "description": {
      "type": "string",
      "description": "Friendly description of this resource."
    },
    "customFields": {
      "type": "object",
      "description": "Object of case-sensititve key-value pairs of custom fields associated with the resource."
    },
    "tags": {
      "type": "array",
      "description": "Array of string tags associated with this resource.",
      "items": {
        "type": "string",
        "maxLength": 60
      }
    },
    "identifiers": {
      "type": "object",
      "description": "Various identifiers (EPC, ISBN, etc.) as a JSON object with one or more key-value pairs."
    },
    "createdAt": {
      "type": "integer",
      "description": "Timestamp when the resource was created.",
      "readOnly": true
    },
    "updatedAt": {
      "type": "integer",
      "description": "Timestamp when the resource was updated.",
      "readOnly": true
    },
    "position": {
      "type": "object",
      "description": "A GeoJSON Point object. The order is longitude, then latitude.",
      "properties": {
        "type": {
          "type": "string",
          "description": "The type of the point.",
          "enum": [ "Point" ]
        },
        "coordinates": {
          "type": "array",
          "description": "The point coordinates",
          "items": { "type": "number" }
        }
      }
    },
    "icon": {
      "type": "string",
      "description": "URL to an image representing the place."
    },
    "address": { "$ref": "AddressDocument" },
    "longitude": {
      "type": "number",
      "description": "The longitude."
    },
    "latitude": {
      "type": "number",
      "description": "The latitude."
    },
    "scopes": { "$ref": "ScopesDocument" }
  }
}
{
  "id": "U4w8qhpArDRSkRaRwFRUfnVe",
  "createdAt": 1510918067036,
  "customFields": {
    "address_book": "Known Addresses"
  },
  "tags": [
    "example",
    "place"
  ],
  "updatedAt": 1510918067036,
  "name": "Example place",
  "description": "An example place resource",
  "position": {
    "type": "Point",
    "coordinates": [
      5.625,
      21.94304553343818
    ]
  },
  "address": {
    "extension": "33",
    "street": "No Name",
    "postalCode": "CM875H",
    "city": "London",
    "county": "Essex",
    "state": "England",
    "country": "United Kingdom",
    "district": "Greater London",
    "buildingName": "The Moorings",
    "buildingFloor": "4",
    "buildingRoom": "Back Office",
    "buildingZone": "Light Industrial",
    "crossing1": "West and 5th",
    "crossing2": "Sixth and Memorial"
  },
  "identifiers": {
    "ean_13": "4389483927"
  },
  "latitude": 21.94304553343818,
  "longitude": 5.625
}

See also: ScopesDocument, AddressDocument


AddressDocument Data Model

.extension (string)
    The extension of the address.

.street (string)
    The street of the address.

.postalCode (string)
    The postal code of the address.

.city (string)
    The city of the address.

.county (string)
    The county of the address.

.state (string)
    The state of the address.

.country (string)
    The country of the address.

.countryCode (string)
    The country code of the address.

.district (string)
    The district of the address.

.buildingName (string)
    The building name of the address.

.buildingFloor (string)
    The building floor of the address.

.buildingRoom (string)
    The building room of the address.

.buildingZone (string)
    The building zone of the address.

.crossing1 (string)
    The first crossing of the address.

.crossing2 (string)
    The second crossing of the address.
{
  "type": "object",
  "description": "An object representing a street address.",
  "properties": {
    "extension": {
      "type": "string",
      "description": "The extension of the address."
    },
    "street": {
      "type": "string",
      "description": "The street of the address."
    },
    "postalCode": {
      "type": "string",
      "description": "The postal code of the address."
    },
    "city": {
      "type": "string",
      "description": "The city of the address."
    },
    "county": {
      "type": "string",
      "description": "The county of the address."
    },
    "state": {
      "type": "string",
      "description": "The state of the address."
    },
    "country": {
      "type": "string",
      "description": "The country of the address."
    },
    "countryCode": {
      "type": "string",
      "description": "The country code of the address."
    },
    "district": {
      "type": "string",
      "description": "The district of the address."
    },
    "buildingName": {
      "type": "string",
      "description": "The building name of the address."
    },
    "buildingFloor": {
      "type": "string",
      "description": "The building floor of the address."
    },
    "buildingRoom": {
      "type": "string",
      "description": "The building room of the address."
    },
    "buildingZone": {
      "type": "string",
      "description": "The building zone of the address."
    },
    "crossing1": {
      "type": "string",
      "description": "The first crossing of the address."
    },
    "crossing2": {
      "type": "string",
      "description": "The second crossing of the address."
    }
  }
}

Create a Place

Places are created using a POST request to the /places resource containing a JSON document.

POST /places
Content-Type: application/json
Authorization: $OPERATOR_API_KEY

PlaceDocument
curl -i -H "Content-Type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X POST "https://api.evrythng.com/places" \
  -d '{
    "name": "Evrythng London Office",
    "position": {
      "type": "Point",
      "coordinates": [ -0.0882956, 51.5303183 ]
    },
    "address": {
      "extension": "Unit 4, 122",
      "street": "East Road",
      "postalCode": "N1 6FB",
      "city": "London",
      "countryCode": "GB"
    },
    "description": "London HQ",
    "icon": "https://evrythng.com/wp-content/themes/evrythng_v2.4/img/logo.svg",
    "tags": [
      "Office", "London"
    ],
    "identifiers": {
      "Place code": "KDL-46EX402AEP",
      "Place number": "027242784925"
    },
    "customFields": {
      "Internet": "Wi-Fi"
    }
  }'
const place = {
  name: 'Evrythng London Office',
  position: {
    type: 'Point',
    coordinates: [ -0.0882956, 51.5303183 ]
  },
  address: {
    'extension':'Unit 4, 122',
    'street':'East Road',
    'postalCode':'N1 6FB',
    'city':'London',
    'countryCode':'GB'
  },
  description: 'London HQ',
  icon: 'https://evrythng.com/wp-content/themes/evrythng_v2.4/img/logo.svg',
  tags: [ 'Office', 'London' ],
  identifiers: {
    'Placecode: 'KDL-46EX402AEP',
    'Placenumber: '027242784925'
  },
  customFields: {
    'Internet':'Wi-Fi'
  }
};

operator.place().create(place).then(console.log);
Place place = new Place();
place.setName("Evrythng London Office");
place.setPosition(new GeoJsonPoint(51.5008, -0.1247));
place.setTags(Arrays.asList("Office", "London"));
place = apiManager.placeService().placeCreator(place).execute();
HTTP/1.1 201 Created
Content-Type: application/json
Location: https://api.evrythng.com/places/UmBy2FXc6QQVhPaRaYNVErAb

{
  "id": "UmBy2FXc6QQVhPaRaYNVErAb",
  "createdAt": 1497453272208,
  "customFields": {
    "Internet": "Wi-Fi"
  },
  "tags": [
    "Office",
    "London"
  ],
  "updatedAt": 1497453272208,
  "name": "Evrythng London Office",
  "description": "London HQ",
  "icon": "https://evrythng.com/wp-content/themes/evrythng_v2.4/img/logo.svg",
  "position": {
    "type": "Point",
    "coordinates": [
      -0.0882956,
      51.5303183
    ]
  },
  "address": {
    "extension": "Unit 4, 122",
    "street": "East Road",
    "postalCode": "N1 6FB",
    "city": "London",
    "countryCode": "GB"
  },
  "identifiers": {
    "Place code": "KDL-46EX402AEP",
    "Place number": "027242784925"
  },
  "latitude": 51.5303183,
  "longitude": -0.0882956
}

Read a Place

Perform a GET request on the /places resource including the place's ID to read it.

GET /places/:placeId
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X GET "https://api.evrythng.com/places/UFR3PBUe7a2xDnbXQGYVnrGh"
const placeId = 'UFR3PBUe7a2xDnbXQGYVnrGh';

app.place(placeId).read().then(console.log);
String placeId = "UFR3PBUe7a2xDnbXQGYVnrGh";

Place place = apiManager.placeService().placesReader(placeId).execute();
System.out.println(place.getName());
HTTP/1.1 200 OK
Content-Type: application/json

{
  "id": "UFR3PBUe7a2xDnbXQGYVnrGh",
  "createdAt": 1416944292231,
  "tags":[ "Office", "London" ],
  "updatedAt": 1416944292231,
  "name": "Evrythng London Office",
  "description": "London HQ",
  "icon": "https://evrythng.com/wp-content/themes/evrythng_v2.4/img/logo.svg",
  "address": {
    "extension": "Unit 4, 122",
    "street": "East Road",
    "postalCode": "N1 6FB",
    "city": "London",
    "countryCode": "GB"
  }
}

Read All Places

Read all places associated with an account. The result may be paginated if there are more than 30 items.

GET /places
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X GET "https://api.evrythng.com/places"
app.place().read().then(console.log);
List<Place> places = apiManager.placeService().placesReader().execute();
HTTP/1.1 200 OK
Content-Type: application/json

[ PlaceDocument, ... ]

Update a Place

You can update the data of a place using any subset of a valid document model.

Note

The old fields are entirely overwritten by the new values, so to add a tag for example you must submit the previous tag array including the new tags.

PUT /places/:placeId
Content-Type: application/json
Authorization: $OPERATOR_API_KEY

PlaceDocument (subset)
curl -i -H "Content-Type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X PUT 'https://api.evrythng.com/places/UFR3PBUe7a2xDnbXQGYVnrGh' \
  -d '{
    "name": "My Updated Place",
    "description": "Currently changing the name of the place."
  }'
const placeId = 'UFR3PBUe7a2xDnbXQGYVnrGh';

copnst update = {
  name: 'My Updated Place',
  description: 'Currently changing the name of the place.'
};

operator.place(placeId).update(update).then(console.log);
String placeId = "UFR3PBUe7a2xDnbXQGYVnrGh";

// Read a place
Place place = apiManager.placeService().placeReader(placeId).execute();

// Update it
place.setName("My Updated Place");
place.setDescription("Currently changing the name of the place.");
place = apiManager.placeService().placeUpdater(placeId, place).execute();
HTTP/1.1 200 Ok
Content-Type: application/json

{
  "id": "UFR3PBUe7a2xDnbXQGYVnrGh",
  "createdAt": 1433245674189,
  "updatedAt": 1433245917068,
  "name": "My Updated Place",
  "description": "Currently changing the name of the place."
}

Delete a Place

To delete a place, simply send a DELETE to the place URL including the placeId. This action cannot be undone.

DELETE /places/:placeId
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X DELETE 'https://api.evrythng.com/places/UFR3PBUe7a2xDnbXQGYVnrGh'
const placeId = 'UFR3PBUe7a2xDnbXQGYVnrGh';

operator.place(placeId).delete().then(console.log);
String placeId = "UFR3PBUe7a2xDnbXQGYVnrGh";

apiManager.placeService().placeDeleter(placeId).execute();
HTTP/1.1 200 OK

Get All Places Near Origin

The following request returns a (paginated) list of all places within a radius of a longitude and latitude location origin, sorted by distance. The query parameters are:

  • lon - Double
    Longitude of the search origin.

  • lat - Double
    Latitude of the search origin.

  • maxDist - Integer
    The radius from the origin in kilometres.

GET /places?lat=:lat&lon=:lon&maxDist=:maxDist
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X GET 'https://api.evrythng.com/places?lat=49.5&lon=127.4&maxDist=20'
app.place().read({
  params: {
    lat: 48.856,
    lon: 2.352,
    maxDist: 20
  }
}).then(console.log);
List<Place> places = apiManager.placeService().placesReader()
        .queryParam("lat", "48.856")
        .queryParam("lon", "2.352")
        .queryParam("maxDist", "20")
        .execute();
for(Place place : places) {
    System.out.println(place.getName());
}
HTTP/1.1 200 OK
Content-Type: application/json

[
  {
    "id": "UVEVNH4B8e5RgDUdmVr4Pxpc",
    "createdAt": 1416944292231,
    "tags": [ "Office","London" ],
    "updatedAt": 1416944292231,
    "name": "Evrythng London Office",
    "description": "London HQ",
    "icon": "https://evrythng.com/wp-content/themes/evrythng_v2.4/img/logo.svg",
    "address": {
      "street": "East Road",
      "city": "London",
      "countryCode": "GB"
    }
  }
]

Set a Thng's Location to a Place

It is possible to set a Thng's location to a place by adding a location history item with the place ID.

curl -H "Content-Type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X PUT 'https://api.evrythng.com/thngs/UmCxU5c7BgPatpRawmWG7g6m/location' \
  -d '[{
    "place": "UkR2KhB7cGeMVmyENHxEXf2f"
  }]'
const thngId = 'UG4NgF9nBM8rtraRam9RreHg';
const update = [{
  place: 'UkR2KhB7cGeMVmyENHxEXf2f'
}];

user.thng(thngId).location().update(update).then(console.log);
HTTP/1.1 200 OK
Content-Type: application/json

[
  {
    "scopes": {
      "users": [],
      "projects": []
    },
    "timestamp": 1501160092412,
    "place": "UkR2KhB7cGeMVmyENHxEXf2f"
  }
]

Set an Action's Location to a Place

Similar to settings a Thng's location to a place, it is also possible to set an action's location to a place by its ID within the location object, and setting locationSource to "place".

curl -H "Content-Type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X POST 'https://api.evrythng.com/actions/scans' \
  -d '{
    "type": "scans", 
    "thng": "UmCxU5c7BgPatpRawmWG7g6m", 
    "locationSource": "place", 
    "location": { 
      "place": "UkR2KhB7cGeMVmyENHxEXf2f"
    }
  }'
const action = {
  type: 'scans', 
  thng: 'UG4NgF9nBM8rtraRam9RreHg', 
  locationSource: 'place', 
  location: { 
    place: 'UmYhUGaFWy9ntQwRwYyXHgVg'
  }
};

user.action('scans').create(action).then(console.log);
HTTP/1.1 201 Created
Content-Type: application/json

{
  "id": "U3EG8cFyegPw9pwawEeAmpKd",
  "createdAt": 1501160066085,
  "timestamp": 1501160066085,
  "type": "scans",
  "location": {
    "place": "UkR2KhB7cGeMVmyENHxEXf2f",
    "latitude": 10.1,
    "longitude": 125.6,
    "position": {
      "type": "Point",
      "coordinates": [
        125.6,
        10.1
      ]
    }
  },
  "locationSource": "place",
  "context": {
    "ipAddress": "141.0.154.202",
    "city": "London",
    "countryCode": "GB",
    "userAgentName": "Unknown",
    "operatingSystemName": "Unknown"
  },
  "thng": "UmCxU5c7BgPatpRawmWG7g6m"
}
 

Product resources are used to model a class of objects, and as such should contain information common to all instances. For example, a product resource could be used to describe a particular model of a physical product with specific characteristics (perhaps to match a specific SKU).

For example, of a specific TV model that has various properties such as a model number, a description, a brand, a category, etc. Each Thng instance of this TV product would reference the product that contains the SKU data, and instead only contain instance-level data, such as realtime state.

Products are useful to store properties or other data items that are common to a set of Thngs (so you don't need to replicate the model name or weight for thousands of Thngs that are individual instances of the same product). Therefore a product can be used to define the basic common properties of many Thngs.

In addition to specifying a product in an action, it is also possible to create an action through the product itself using aliased actions.


API Status
Stable:
/products
/products/:productId
/products/:productId/properties


ProductDocument Data Model

The product's data model used in our platform has been designed to be compatible with the h-product microformat, therefore it can easily be integrated with the h-product data model and applications supporting that microformat.

.name (string)
    Friendly name of this resource.

.id (string, read-only)
    The ID of this resource.

.createdAt (integer, read-only)
    Timestamp when the resource was created.

.updatedAt (integer, read-only)
    Timestamp when the resource was updated.

.activatedAt (integer, read-only)
    Timestamp when the product was activated.

.description (string)
    Friendly description of this resource.

.categories (array of string)
    An array of product categories as strings.

.photos (array of string)
    An array of product photo URLs as strings.

.url (string)
    The URL linking to the product information.

.identifiers (object)
    Various identifiers (EPC, ISBN, etc.) as a JSON object with 
    one or more key-value pairs.

.properties (object)
    A JSON object with key-value pairs describing properties of 
    the product.

.tags (array of string)
    Array of string tags associated with this resource.

.customFields (object)
    Object of case-sensititve key-value pairs of custom fields 
    associated with the resource.

.fn (string)
    Friendly name of the product.

.brand (string)
    The product's brand name.

.scopes (ScopesDocument)
{
  "type": "object",
  "description": "An object representing a platform product.",
  "properties": {
    "name": {
      "type": "string",
      "description": "Friendly name of this resource."
    },
    "id": {
      "type": "string",
      "description": "The ID of this resource.",
      "readOnly": true
    },
    "createdAt": {
      "type": "integer",
      "description": "Timestamp when the resource was created.",
      "readOnly": true
    },
    "updatedAt": {
      "type": "integer",
      "description": "Timestamp when the resource was updated.",
      "readOnly": true
    },
    "activatedAt": {
      "type": "integer",
      "description": "Timestamp when the product was activated.",
      "readOnly": true
    },
    "description": {
      "type": "string",
      "description": "Friendly description of this resource."
    },
    "categories": {
      "description": "An array of product categories as strings.",
      "type": "array",
      "items": { "type": "string" }
    },
    "photos": {
      "description": "An array of product photo URLs as strings.",
      "type": "array",
      "items": { "type": "string" }
    },
    "url": {
      "type": "string",
      "description": "The URL linking to the product information."
    },
    "identifiers": {
      "type": "object",
      "description": "Various identifiers (EPC, ISBN, etc.) as a JSON object with one or more key-value pairs."
    },
    "properties": {
      "type": "object",
      "description": "A JSON object with key-value pairs describing properties of the product."
    },
    "tags": {
      "type": "array",
      "description": "Array of string tags associated with this resource.",
      "items": {
        "type": "string",
        "maxLength": 60
      }
    },
    "customFields": {
      "type": "object",
      "description": "Object of case-sensititve key-value pairs of custom fields associated with the resource."
    },
    "fn": {
      "type": "string",
      "description": "Friendly name of the product."
    },
    "brand": {
      "type": "string",
      "description": "The product's brand name."
    },
    "scopes": { "$ref": "ScopesDocument" }
  }
}
{
  "id": "UHR8MGsSCct4t9wwRgEKqrUr",
  "createdAt": 1510918625202,
  "customFields": {
    "region": "en_gb"
  },
  "tags": [
    "example",
    "product"
  ],
  "updatedAt": 1510918625202,
  "brand": "ACME",
  "categories": [
    "packaged",
    "consumer"
  ],
  "properties": {
    "in_use": false
  },
  "description": "An example product resource",
  "fn": "Example Product",
  "name": "Example Product",
  "photos": [
    "https://upload.wikimedia.org/wikipedia/commons/8/84/Example.svg"
  ],
  "url": "https://google.com",
  "identifiers": {
    "ean_13": "438348983329"
  }
}

See also: ScopesDocument


Create a Product

POST /products
Content-Type: application/json
Authorization: $OPERATOR_API_KEY

ProductDocument
curl -i -H "Content-Type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X POST 'https://api.evrythng.com/products' \
  -d '{
    "name": "KDL-46EX",
    "description": "Just a simple SONY TV",
    "photos": ["https://images.icecat.biz/img/norm/high/3965331-6265.jpg"],
    "url": "https://icecat.ch/p/sony/kdl-46ex402aep/lcd-tvs-kdl-46ex-3965331.html",
    "identifiers": {
      "UPC": "027242784925"
    },
    "customFields": {
      "FullHD": "true"
    },
  }'
const product = {
  name: 'KDL-46EX',
  description: 'Just a simple SONY TV',
  photos: [ 'https://images.icecat.biz/img/norm/high/3965331-6265.jpg' ],
  url: 'https://icecat.ch/p/sony/kdl-46ex402aep/lcd-tvs-kdl-46ex-3965331.html',
  identifiers: {
    UPC: '027242784925'
  },
  customFields: {
    FullHD: 'true'
  }
};

operator.product().create(product).then(console.log);
Product product = new Product();
product.setName("KDL-46EX");
product.setDescription("Just a simple SONY TV");
apiManager.productService().productCreator(product).execute();
HTTP/1.1 201 Created
Content-Type: application/json
Location: https://api.evrythng.com/products/UGeTG9HmVDPwtKwwwEW7ffWg

{
  "id": "UGeTG9HmVDPwtKwwwEW7ffWg",
  "createdAt": 1346856870595,
  "updatedAt": 1346856870595,
  "name": "CDL-4dsX",
  "description": "Just a simple TV",
  "photos": ["https://images.icecat.biz/img/norm/high/3965331-6265.jpg"]
  "url": "https://icecat.ch/p/sony/kdl-46ex402aep/lcd-tvs-kdl-46ex-3965331.html",
  "customFields": {
    "FullHD": "true"
  },
  "identifiers": {
    "UPC": "027242784925"
  }
}

Read a Product

When provided a productId, the Platform will return a single product document corresponding to that ID, if it exists.

GET /products/:productId
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X GET 'https://api.evrythng.com/products/UEqs3aV3NyYGVRxhmnphVGdh'
const productId = 'UEqs3aV3NyYGVRxhmnphVGdh';

app.product(productId).read().then(console.log);
String productId = "UEqs3aV3NyYGVRxhmnphVGdh";

Product product = apiManager.productService().productReader(productId).execute();
HTTP/1.1 200 OK
Content-Type: application/json

{
  "id": "UEqs3aV3NyYGVRxhmnphVGdh",
  "createdAt": 1473155145323,
  "customFields": {},
  "updatedAt": 1494836869302,
  "brand": "Own",
  "properties": {},
  "description": "Test updated description",
  "fn": "Test Product",
  "name": "Test Product",
  "url": "https://google.com",
  "identifiers": {}
}

Read all Products

Read all products in scope of the authenticating API key. The result may be paginated if there are more than 30 items.

GET /products
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X GET 'https://api.evrythng.com/products'
// List products in the App's Project context
app.product().read().then(console.log);

// List products managed/visible to the user
user.product.read().then(console.log);
Iterator<PVector<Product>> products = apiManager.productService().iterator().perPage(10).execute();

while(products.hasNext()) {
    PVector<Product> page = products.next();
    for(Product product : page) {
        System.out.println("Product: " + product.getName());
    }
}
HTTP/1.1 200 OK
Content-Type: application/json

[
  {
    "id": "UGTdRHP4BDsa95waRhqfYhTh",
    "createdAt": 1495468778553,
    "customFields": {},
    "updatedAt": 1496138109642,
    "properties": {},
    "description": "Use me!",
    "fn": "EngagementProduct",
    "name": "EngagementProduct",
    "identifiers": {}
  }
]

Update a Product

Update a single product by ID.

PUT /products/:productId
Content-Type: application/json
Authorization: $OPERATOR_API_KEY

ProductDocument (subset)
curl -i -H "Content-type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X PUT 'https://api.evrythng.com/products/UXGmadd2EwqQkXe3F5Gg4hbg' \
  -d '{ 
  	"name": "New Product Name",
    "description": "Updated product description"
  }'
const productId = 'UXGmadd2EwqQkXe3F5Gg4hbg';

user.product(productId).read().then((product) => {
  // Edit properties by property name
  product.name = 'New Product Name';
  product.description = 'New product description';
  
  // Then send the update and print returned updated product
  product.update().then(console.log);

  // Or send all changed properties in one go without printing response
  const update = {
    name: 'New Product Name',
    description: 'Updated product description'
  };
  product.update(update).then(console.log);
});
String productId = "UXGmadd2EwqQkXe3F5Gg4hbg";

Product product = apiManager.productService().productReader(productId).execute();
product.setName("New Product Name");
product.setDescription("Updated product description");
apiManager.productService().productUpdater(productId, product).execute();
HTTP/1.1 200 OK
Content-Type: application/json

{
  "id": "UXGmadd2EwqQkXe3F5Gg4hbg",
  "createdAt": 1452868408882,
  "customFields": {},
  "tags": [
    "level", "product", "audio", "tool"
  ],
  "updatedAt": 1453468149305,
  "description": "Updated product description",
  "fn": "Headphones",
  "name": "New Product Name",
  "photos": ["https://www.plantronics.com/images/catalog/product/large/audio995.png"],
  "url": "https://headphones.com",
  "identifiers": {}
}

Delete a Product

Danger!

Deleting a product resource will remove its product reference from all Thngs that point to it. This action cannot be reversed!

DELETE /products/:productId
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X DELETE 'https://api.evrythng.com/products/UEqs3aV3NyYGVRxhmnphVGdh'
const productId = 'UEqs3aV3NyYGVRxhmnphVGdh';

operator.product(productId).delete().then(() => console.log('Deleted!'));

// or on the resource itself
operator.product(productId).read((product) => product.delete());
String productId = "UEqs3aV3NyYGVRxhmnphVGdh";

apiManager.productService().productDeleter(productId).execute();
HTTP/1.1 200 OK

Update Multiple Products

Using a suitable filter, it is possible to apply (PUT) an updated ProductDocument to multiple products, providing they match the filter query parameter. For example, all products that begin with "Smart" to contain the 'smart' tag.

PUT /products?filter=name%3DSmart*
Content-Type: application/json
Authorization: $OPERATOR_API_KEY

{
  "tags": [ "smart" ]
}
curl -H "Content-Type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X PUT 'https://api.evrythng.com/products?filter=name%3DSmart*' \
  -d '{
    "tags": [ "smart" ]
  }'
const update = { 
  tags: [ "updated" ]
};

operator.product().update(update, { 
  params: { 
    filter: 'name=Test*' 
  } 
}).then(console.log);

The update will be applied asynchronously, and the response will be 204 'No Content'.

HTTP/1.1 204 No Content

Create a Product's Redirection

POST /products/:productId/redirector
Content-Type: application/json
Authorization: $OPERATOR_API_KEY

RedirectionDocument
curl -H "Content-Type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X POST 'https://api.evrythng.com/products/Uk6qwgSReXPat5awagfNpHsk/redirector' \
  -d '{
    "defaultRedirectUrl": "https://google.com/{productId}"
  }'
const operatorApiKey = '$OPERATOR_API_KEY';
const productId = 'Uk6qwgSReXPat5awagfNpHsk';

EVT.api({
  url: `/products/${productId}/redirector`,
  method: 'POST',
  authorization: operatorApiKey,
  data: { 
    defaultRedirectUrl: 'https://example.com/{productId}'
  }
}).then(console.log);
HTTP/1.1 201 Created
Content-Type: application/json

{
  "createdAt": 1496668317836,
  "updatedAt": 1496668348257,
  "defaultRedirectUrl": "https://google.com/Uk6qwgSReXPat5awagfNpHsk",
  "evrythngUrl": "https://api.evrythng.com/products/Uk6qwgSReXPat5awagfNpHsk",
  "shortDomain": "tn.gg",
  "shortId": "799yeEi4qp",
  "hits": 0
}

Read a Product's Redirection

GET /products/:productId/redirector
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X GET 'https://api.evrythng.com/products/Uk6qwgSReXPat5awagfNpHsk/redirector'
const operatorApiKey = '$OPERATOR_API_KEY';
const productId = 'Uk6qwgSReXPat5awagfNpHsk';

EVT.api({
  url: `/products/${productId}/redirector`,
  authorization: operatorApiKey
}).then(console.log);
HTTP/1.1 200 OK
Content-Type: application/json

{
  "createdAt": 1496664729327,
  "updatedAt": 1496664729327,
  "defaultRedirectUrl": "https://google.com",
  "evrythngUrl": "https://api.evrythng.com/products/Uk6qwgSReXPat5awagfNpHsk",
  "shortDomain": "tn.gg",
  "shortId": "39aZNy4g",
  "hits": 0
}

Update a Product's Redirection

PUT /products/:productId/redirector
Content-Type: application/json
Authorization: $OPERATOR_API_KEY

RedirectionDocument
curl -H "Content-Type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X PUT 'https://api.evrythng.com/products/Uk6qwgSReXPat5awagfNpHsk/redirector' \
  -d '{
    "defaultRedirectUrl": "https://google.com/{productId}"
  }'
const operatorApiKey = '$OPERATOR_API_KEY';
const productId = 'Uk6qwgSReXPat5awagfNpHsk';

EVT.api({
  url: `/products/${productId}/redirector`,
  method: 'PUT',
  authorization: operatorApiKey,
  data: { 
    defaultRedirectUrl: 'https://example.com/{productId}'
  }
}).then(console.log);
HTTP/1.1 201 Created
Content-Type: application/json

{
  "createdAt": 1496668317836,
  "updatedAt": 1496668348257,
  "defaultRedirectUrl": "https://google.com/Uk6qwgSReXPat5awagfNpHsk",
  "evrythngUrl": "https://api.evrythng.com/products/Uk6qwgSReXPat5awagfNpHsk",
  "shortDomain": "tn.gg",
  "shortId": "799yeEi4qp",
  "hits": 0
}

Delete a Product's Redirection

DELETE /products/:productId/redirector
Content-Type: application/json
Authorization: $OPERATOR_API_KEY
curl -H "Content-Type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X DELETE 'https://api.evrythng.com/products/Uk6qwgSReXPat5awagfNpHsk/redirector' 
const operatorApiKey = '$OPERATOR_API_KEY';
const productId = 'Uk6qwgSReXPat5awagfNpHsk';

EVT.api({
  url: `/products/${productId}/redirector`,
  authorization: operatorApiKey,
  method: 'DELETE'
}).then(() => console.log('Deleted!'));
HTTP/1.1 200 OK
Content-Type: application/json
 

Every account may contain one or more projects. A project is a container for applications and Application Users, and provides an easy way to share and work with a subset of the resources that exist in an account through Scoping.

Projects are their own scope; every resource may or may not be part of the project scope, which in turn limits visibility of those items to only that project. For more details about scoping, see API Scope and Permissions.


API Status
Stable:
/projects
/projects/:projectId


ProjectDocument Data Model

.name (string)
    Friendly name of this resource.

.id (string, read-only)
    The ID of this resource.

.createdAt (integer, read-only)
    Timestamp when the resource was created.

.updatedAt (integer, read-only)
    Timestamp when the resource was updated.

.tags (array of string)
    Array of string tags associated with this resource.

.description (string)
    Friendly description of this resource.

.startsAt (integer)
    Timestamp when this project starts.

.endsAt (integer)
    Timestamp when this project ends.

.imageUrl (string)
    URL link to the image to use in the project properties view.

.customFields (object)
    Object of case-sensititve key-value pairs of custom fields 
    associated with the resource.

.identifiers (object)
    Various identifiers (EPC, ISBN, etc.) as a JSON object with 
    one or more key-value pairs.

.shortDomains (array of string)
    An array of short domains assigned for the project, for 
    example: `tn.gg` by default.

.scopes (ScopesDocument)
{
  "type": "object",
  "description": "An object describing a platform project.",
  "properties": {
    "name": {
      "type": "string",
      "description": "Friendly name of this resource."
    },
    "id": {
      "type": "string",
      "description": "The ID of this resource.",
      "readOnly": true
    },
    "createdAt": {
      "type": "integer",
      "description": "Timestamp when the resource was created.",
      "readOnly": true
    },
    "updatedAt": {
      "type": "integer",
      "description": "Timestamp when the resource was updated.",
      "readOnly": true
    },
    "tags": {
      "type": "array",
      "description": "Array of string tags associated with this resource.",
      "items": {
        "type": "string",
        "maxLength": 60
      }
    },
    "description": {
      "type": "string",
      "description": "Friendly description of this resource."
    },
    "startsAt": {
      "type": "integer",
      "description": "Timestamp when this project starts."
    },
    "endsAt": {
      "type": "integer",
      "description": "Timestamp when this project ends."
    },
    "imageUrl": {
      "type": "string",
      "description": "URL link to the image to use in the project properties view."
    },
    "customFields": {
      "type": "object",
      "description": "Object of case-sensititve key-value pairs of custom fields associated with the resource."
    },
    "identifiers": {
      "type": "object",
      "description": "Various identifiers (EPC, ISBN, etc.) as a JSON object with one or more key-value pairs."
    },
    "shortDomains": {
      "type": "array",
      "description": "An array of short domains assigned for the project, for example: `tn.gg` by default.",
      "items": { "type": "string" }
    },
    "scopes": { "$ref": "ScopesDocument" }
  }
}
{
  "id": "U4aPM5gyBg8w9pwaaYKCepVg",
  "createdAt": 1510919010607,
  "customFields": {
    "international": true
  },
  "tags": [
    "example",
    "project"
  ],
  "updatedAt": 1510919010607,
  "name": "Example Project",
  "description": "An example project resource",
  "imageUrl": "https://upload.wikimedia.org/wikipedia/commons/8/84/Example.svg",
  "startsAt": 1510918931000,
  "endsAt": 1542454931000,
  "shortDomains": [
    "tn.gg"
  ],
  "identifiers": {
    "ean_13": "43884389392"
  }
}

See also: ScopesDocument


Create a Project

Create a new project within an account.

POST /projects
Content-Type: application/json
Authorization: $OPERATOR_API_KEY

ProjectDocument
curl -i -H "Content-type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X POST 'https://api.evrythng.com/projects' \
  -d '{ 
    "name": "My Project" 
  }'
const project = {
  name: 'My Project'
};

operator.project().create(project).then(console.log);
Project project = new Project();
project.setName("My Project");
apiManager.projectService().projectCreator(project).execute();
HTTP/1.1 201 Created
Content-Type: application/json
Location: https://api.evrythng.com/projects/UGeTG9HmVDPwtKwwwEW7ffWg

{
  "id": "UGeTG9HmVDPwtKwwwEW7ffWg",
  "createdAt": 1418814288042,
  "updatedAt": 1418814288042,
  "name": "My Project",
  "shortDomains": [ 
     "tn.gg" 
  ]
}

Read a Project

Gets a single project by its projectId.

GET /projects/:projectId
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X GET 'https://api.evrythng.com/projects/UmxHK6K8BXsa9KawRh4bTbqc'
const projectId = 'UmxHK6K8BXsa9KawRh4bTbqc';

operator.project(projectId).read().then(console.log);
String projectId = "UmxHK6K8BXsa9KawRh4bTbqc";

Project project = apiManager.projectService().projectReader(projectId).execute();
HTTP/1.1 200 OK
Content-Type: application/json

{
  "id": "UmxHK6K8BXsa9KawRh4bTbqc",
  "createdAt": 1418814288042,
  "updatedAt": 1418814288042,
  "name": "My Project",
  "shortDomains": [ "tn.gg" ]
}

Read all Projects

Read all projects in an account. The result may be paginated if there are more than 30 items.

GET /projects
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X GET 'https://api.evrythng.com/projects'
operator.project().read().then(console.log);
List<Project> projects = apiManager.projectService().projectsReader().list().getResult();
for(Project project : projects) {
    System.out.println(project.toString());
}
HTTP/1.1 200 OK
Content-Type: application/json

[
  {
    "id": "URG5FDahhBePgNfHVkHTkama",
    "createdAt": 1418814288042,
    "updatedAt": 1418814288042,
    "name": "My Project",
    "shortDomains": [ 
      "tn.gg" 
    ]
  }
]

Update a Project

Update the fields of a project by its id by POSTing a subset of the ProjectDocument containing the changed fields.

PUT /projects/:projectId
Content-Type: application/json
Authorization: $OPERATOR_API_KEY

ProjectDocument (subset)
curl -i -H "Content-type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X PUT 'https://api.evrythng.com/projects/UEqs3aV3NyYGVRxhmnphVGdh' \
  -d '{ 
    "name": "Updated Name" 
  }'
const projectId = 'UEqs3aV3NyYGVRxhmnphVGdh';

const update = {
  name: 'Updated Name'
};

operator.project(projectId).update(update).then(console.log);
String projectId = "UEqs3aV3NyYGVRxhmnphVGdh";

Project project = apiManager.projectService().projectReader(projectId).execute();
project.setName("Updated Name");

apiManager.projectService().projectUpdater(projectId, project).execute();
HTTP/1.1 200 OK
Content-Type: application/json

{
  "id": "UEqs3aV3NyYGVRxhmnphVGdh",
  "createdAt": 1418814288042,
  "updatedAt": 1418814288042,
  "name": "Updated Name",
  "shortDomains": [ "tn.gg" ]
}

Delete a Project

Delete a project by its id. This will also remove access to any contained applications, Application Users, etc.

Danger

This action cannot be reversed! You will lose access to ALL applications and Application Users within the deleted project.

DELETE /projects/:projectId
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X DELETE 'https://api.evrythng.com/projects/UEqs3aV3NyYGVRxhmnphVGdh'
const projectId = 'UEqs3aV3NyYGVRxhmnphVGdh';

operator.project(projectId).delete().then(() => console.log('Project deleted'));
String projectId = "UEqs3aV3NyYGVRxhmnphVGdh";

apiManager.projectService().projectDeleter(projectId).execute();
HTTP/1.1 200 OK
Suggest Edits

Properties

 

Thngs and products can have many properties, which are simple typed key/value pairs of data that are attached to a resource. Where it gets interesting is that each property update is timestamped and stored historically, which allows you to retrieve the set of all the values of a property during any specific time range and infer trends over time.

The most common use case of properties is to store dynamic information about a Thng, which is likely to change over time. This is particularly useful for devices, where properties can store the sensor readings of the device. For longer-lived data that does not require a history, customFields or identifiers should be used as appropriate.

For example, the current power consumption of a bulb should be stored in properties, the particular IP address is has connected to a customField, and the model number or SKU code in identifiers.

Limited Lifetime

With a free account, Thng and product property histories older than six months will be deleted (though the properties themselves will not).

Users that require longer property history longevity should contact us to enquire about an Enterprise account.


API Status
Stable:
/thngs/:thngId/properties
/products/:productId/properties


PropertiesDocument Data Model

Thng or product properties take the form of an array of PropertyDocument objects that always contain a key (case-insensitive, gets converted to lowercase automatically by the API), a value, and a timestamp.

.key (string)
    The property key-value key.

.value (string,integer,boolean)
    The property key-value value.

.timestamp (integer, read-only)
    The timestamp of the property update.

.createdAt (integer, read-only)
    Timestamp when the resource was created.
{
  "type": "object",
  "description": "An object representing a single property update. The `value` property may be a string or integer.",
  "properties": {
    "key": {
      "type": "string",
      "description": "The property key-value key."
    },
    "value": {
      "type": [ "string", "integer", "boolean" ],
      "description": "The property key-value value."
    },
    "timestamp": {
      "type": "integer",
      "description": "The timestamp of the property update.",
      "readOnly": true
    },
    "createdAt": {
      "type": "integer",
      "description": "Timestamp when the resource was created.",
      "readOnly": true
    }
  }
}
{
  "createdAt": 1507734727579,
  "timestamp": 1507734727579,
  "key": "example_property",
  "value": 10
}

Property Units

If the property type is a number, then you can visualize it as a graph in the dashboard. For this reason, we suggest you don't include the unit in value, but rather encode it in the property name. For example:

{
  "key": "temperature_celsius",
  "value": 21
}

If the optional timestamp is not provided when updating the property, it is automatically set by the API at the moment when the request has been processed.

If a device is not connected permanently (e.g. due to limited network availability or power saving), it can cache the sensor readings and upload them in batches by specifying timestamps to accompany each object.


Create/Update Properties

Create

When a resource is created, you can specify an initial set of properties and their values with a POST request.

POST /thngs/:thngId/properties
POST /products/:productId/properties

Update

To update those properties or to add new ones to the resource, use PUT on the following endpoints, with a PropertiesDocument JSON object containing the changed fields.

PUT /thngs/:thngId/properties
PUT /products/:productId/properties

If the properties being updated do not exist, they are created automatically by this request and no error/notification will be returned.

Note

Exceptionally, to cope with the limitations of mobile network providers you can use both POST and PUT to create/update multiple properties, which behave identically in this particular case.

However, only the PUT method may be used to update properties by name.

The returned payload is an array of the properties updated, not all of the Thng's the properties.

PUT /thngs/:thngId/properties
Content-Type: application/json
Authorization: $OPERATOR_API_KEY

[ PropertyDocument, ... ]
curl -i -H "Content-Type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X PUT 'https://api.evrythng.com/thngs/UEqs3aV3NyYGVRxhmnphVGdh/properties' \
  -d '[{
    "key": "temperature_celsius",
    "value": 33
  }, {
    "key": "humidity_percent",
    "value": 71
  }]'
const thngId = 'UEqs3aV3NyYGVRxhmnphVGdh';

const newProperties = [{
  key: 'temperature_celsius',
  value: 33
}, {
  key: 'humidity_percent',
  value: 71
}];

// in a single request
user.thng(thngId).property().update(newProperties).then(console.log);

// or on the resource itself
user.thng(thngId).read().then((thng) => {
  thng.property().update(newProperties).then(console.log);
});
String thngId = "UEqs3aV3NyYGVRxhmnphVGdh";

List<Property<?>> properties = new ArrayList<>();
properties.add(new NumberProperty("temperature_celsius", 33));
properties.add(new NumberProperty("humidity_percent", 71));

// Add to Thng
apiManager.thngService().propertiesCreator(thngId, properties).execute();

// Add to product
apiManager.productService().propertiesCreator(productId, properties).execute();
HTTP/1.1 200 OK
Content-Type: application/json

[
  {
    "timestamp": 1346860078782,
    "createdAt": 1346860078782,
    "key": "temperature_celsius",
    "value": 33
  },
  {
    "timestamp": 1346860078782,
    "createdAt": 1346860078782,
    "key": "humidity_percent",
    "value": 71
  }
]

Read All Properties

GET /thngs/:thngId/properties
GET /products/:productId/properties

Retrieve a list of all the properties of a Thng or product by its ID. The result may be paginated if there are more than 30 items.

GET /thngs/:thngId/properties
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X GET 'https://api.evrythng.com/thngs/UEqs3aV3NyYGVRxhmnphVGdh/properties'
const thngId = 'UEqs3aV3NyYGVRxhmnphVGdh';

// in a single request
user.thng(thngId).property().read().then(console.log);

// or on the resource itself
user.thng(thngId).read().then((thng) => {
  thng.property().read().then(console.log);
});
// thng from previous thngService reader invocation
Map<String, Object> properties = thng.getProperties();
for(Map.Entry<String, Object> entry : properties.entrySet()) {
    String key = entry.getKey();
    String value = entry.getValue().toString();
    System.out.println("key=" + key + " value=" + value);
}

The returned payload is the complete array of all the properties with the last value of each.

HTTP/1.1 200 OK
Content-Type: application/json

[
  {
    "key": "temperature_celsius",
    "value": 33,
    "timestamp": 1346856615704,
    "createdAt": 1346856615704
  }, 
  {
    "key": "humidity_percent",
    "value": 71,
    "timestamp": 1346856615704,
    "createdAt": 1346856615704
  }, 
  {
    "key": "light_sensor_cd",
    "value": 249,
    "timestamp": 1346856615704,
    "createdAt": 1346856615704
  }
]

Create/Update a Single Property

Properties can be updated individually using their dedicated endpoints. If the property does not exist, it will be created automatically by this request and no error/notification will be returned.

PUT /thngs/:thngId/properties/:key
PUT /products/:productId/properties/:key

It's also possible to upload cached values of a property in batches by specifying timestamps for each data item object.

PUT /thngs/:thngId/properties/:key
Content-Type: application/json
Authorization: $OPERATOR_API_KEY

PropertyDocument
curl -i -H "Content-Type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X PUT 'https://api.evrythng.com/thngs/UEqs3aV3NyYGVRxhmnphVGdh/properties/my_key' \
  -d '[{
    "value": 301
  }]'
const thngId = 'UEqs3aV3NyYGVRxhmnphVGdh';
const key = 'temperature_celsius';
const value = 23;

// in a single request
user.thng(thngId).property(key).update(value).then(console.log);

// or on the resource itself
user.thng(thngId).read().then((thng) => {
  thng.property(key).update(value).then(console.log);
});
String value = "UEqs3aV3NyYGVRxhmnphVGdh";
String key = "temperature_celsius";
String value = "23";

apiManager.thngService().propertyUpdater(thngId, key, value).execute();
HTTP/1.1 200 OK
Content-Type: application/json

[
  {
    "timestamp": 1347549410295,
    "createdAt": 1347549410295,
    "value": 301
  }
]

You can also provide data via three different payload types to the property() update method:

const thngId = 'UEqs3aV3NyYGVRxhmnphVGdh';
const key = 'temperature_celsius';

// 1. Just the value (simple values only - string, number, boolean. 
// Use 2. for arrays and objects)
user.thng(thngId).property(key).update(320);

// 2. An object (similar to the API), allowing you to specify the timestamp:
user.thng(thngId).property(key).update({
  value: 320,
  timestamp: 142963528000
});

// 3. In bulk, with multiple objects
user.thng(thngId).property(key).update([
  {
    value: 320,
    timestamp: 142963528000
  }, {
    value: 420,
    timestamp: 142963535600
	}
]);

Read a Property's History

To retrieve the history of values of a specific property, you must access the unique endpoint for each property. By default, all the updates of this property are returned using pagination in a reverse history fashion (the first element of the returned array is the latest update).

GET /thngs/:thngId/properties/:key
GET /products/:productId/properties/:key

If you attempt to fetch the history of values for a property that doesn't exist you will receive an HTTP 200 status code and an empty list as a result.

GET /thngs/:thngId/properties/:key[?from=Timestamp&to=Timestamp]
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X GET 'https://api.evrythng.com/thngs/UEqs3aV3NyYGVRxhmnphVGdh/properties/temperature_celsius'
const thngId = 'UEqs3aV3NyYGVRxhmnphVGdh';
const key = 'temperature_celsius';

// in a single request
user.thng(thngId).property(key).read().then(console.log);

// or on the resource itself
user.thng(thngId).read().then((thng) => {
  thng.property(key).read().then(console.log);
});
String key = "temperature_celsius";

// thng from previous thngService reader invocation
Map<String, Object> properties = thng.getProperties();
String value = properties.get(key).toString();

For accessing only a subset of all the updates to a single property, you can specify a time range using ?from=Timestamp and ?to=Timestamp to specify respectively the beginning and end of the time window. You will receive an array of all the property updates during this time.

HTTP/1.1 200 OK
Content-Type: application/json

[
  {
    "timestamp":1346860078782,
    "createdAt":1346860078782,
    "value":33
  },
  {
    "timestamp":1346856870595,
    "createdAt":1346856870595,
    "value":29
  },
  {
    "timestamp":1346856779570,
    "createdAt":1346856779570,
    "value":32
  }
]

Delete a Property

Property updates are not uniquely identified (do not have their own URL). Therefore, the only way to delete data points is to append the ?to=Timestamp URL query parameter to specify a point in time before which all the data points will be removed.

DELETE /thngs/:thngId/properties/:key
DELETE /products/:productId/properties/:key

Warning

If the ?to= parameter is not specified, ALL updates of this property will be deleted!

DELETE /thngs/:thngId/properties/:key[?to=Timestamp]
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X DELETE 'https://api.evrythng.com/thngs/UEqs3aV3NyYGVRxhmnphVGdh/properties/my_key'
const thngId = 'UEqs3aV3NyYGVRxhmnphVGdh';
const key = 'temperature_celsius';

// in a single request
operator.thng(thngId).property(key).delete();

// or on the resource itself
operator.thng(thngId).read().then((thng) => thng.property(key).delete());
String thngId = "UEqs3aV3NyYGVRxhmnphVGdh";
String key = "temperature_celsius";

apiManager.thngService().propertyDeleter(thngId, key).execute();
HTTP/1.1 200 OK

Delete all Properties

Delete all the properties of a Thng or product. Example below is for Thngs.

DELETE /thngs/:thngId/properties
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X DELETE 'https://api.evrythng.com/thngs/UEqs3aV3NyYGVRxhmnphVGdh/properties'
const thngId = 'UEqs3aV3NyYGVRxhmnphVGdh';

operator.thng(thngId).property().delete();
HTTP/1.1 200 OK

System Properties

In addition, the EVRYTHNG Platform automatically sets some system properties. They start with ~ (tilde) and are read-only. Apart from that they behave exactly like other properties.

The following lists the currently available system properties available on Thngs:

  • ~connected - Boolean
    Set to true if the Thng is updated with its own API key and if it is currently connected to the pub/sub broker through MQTT or WebSockets. The property is created on the Thng first connection and updated every time the Thng disconnects or connects again.
Suggest Edits

Redirections

 

Redirections allow fixed physical barcodes and QR codes to point to dynamically changing locations. Each redirection resource therefore contains the ID of the Thng or product it is associated with, the short domain and short URL it may encode, and the full URL it will redirect to.

Redirector

Using the Redirector service, rules including location, user data, action context, date/time, and more can be used to selectively redirect the user, enabling a more powerful experience. For example, a different web page to be returned for users in different countries, or during different days of the week.

See Resource Redirections for more conceptual information.


API Status
Stable: https://tn.gg/redirections


RedirectionDocument Data Model

.createdAt (integer, read-only)
    Timestamp when the resource was created.

.updatedAt (integer, read-only)
    Timestamp when the resource was updated.

.defaultRedirectUrl (string)
    The location to redirect to.

.redirectUrl (string)
    The redirect URL.

.evrythngUrl (string)
    The location of the EVRYTHNG resource.

.evrythngId (string)
    The EVRYTHNG ID of the associated resource.

.shortDomain (string)
    The associated short domain for the redirection.

.shortId (string)
    The short ID of the redirection

.type (string, one of 'thng', 'product', 'collection', 'place')
    The type of the associated EVRYTHNG resource.

.hits (integer)
    The number of times the redirection has been hit.
{
  "type": "object",
  "description": "A redirection on a resource.",
  "properties": {
    "createdAt": {
      "type": "integer",
      "description": "Timestamp when the resource was created.",
      "readOnly": true
    },
    "updatedAt": {
      "type": "integer",
      "description": "Timestamp when the resource was updated.",
      "readOnly": true
    },
    "defaultRedirectUrl": {
      "type": "string",
      "description": "The location to redirect to."
    },
    "redirectUrl": {
      "type": "string",
      "description": "The redirect URL"
    },
    "evrythngUrl": {
      "type": "string",
      "description": "The location of the EVRYTHNG resource."
    },
    "evrythngId": {
      "type": "string",
      "description": "The EVRYTHNG ID of the associated resource."
    },
    "shortDomain": {
      "type": "string",
      "description": "The associated short domain for the redirection."
    },
    "shortId": {
      "type": "string",
      "description": "The short ID of the redirection",
      "minLength": 10,
      "maxLength": 10
    },
    "type": {
      "type": "string",
      "description": "The type of the associated EVRYTHNG resource.",
      "enum": [ "thng", "product", "collection", "place" ]
    },
    "hits": {
      "type": "integer",
      "description": "The number of times the redirection has been hit."
    }
  }
}
{
  "createdAt": 1510919459418,
  "defaultRedirectUrl": "https://google.com?thng=UFqMRDbaqm8EEMRaRF5h7cEg",
  "evrythngId": "UFqMRDbaqm8EEMRaRF5h7cEg",
  "evrythngUrl": "https://api.evrythng.com/thngs/UFqMRDbaqm8EEMRaRF5h7cEg",
  "hits": 4,
  "redirectUrl": "https://google.com?thng=UFqMRDbaqm8EEMRaRF5h7cEg",
  "shortDomain": "tn.gg",
  "shortId": "GrqPQbvgkG",
  "type": "thng",
  "updatedAt": 1510919527219
}

Create a Redirection

Thng and Product Redirections

It is possible to create a Thng or products' redirection directly on its resource. Read the Thngs and Products pages for more information.

To create a redirection for a EVRYTHNG resource it is required to specify resource type and reference evrythngId. The required parameter defaultRedirectUrl is the target URL of the application that will be called for each access of the short URL.

Note

The default response is a QR code image representing the redirection, unless the accept request header is set to application/json.

POST https://tn.gg/redirections
Content-Type: application/json
Authorization: $OPERATOR_API_KEY

RedirectionDocument
curl -i -H "Content-Type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X POST 'https://tn.gg/redirections' \
  -d '{
    "defaultRedirectUrl":"https://museum.com/displayWorks/catalogue.php?catalogue_id={evrythngId}",
    "evrythngId":"UVpfh4EFsBpasUmnVDWdktKs",
    "type":"thng"
  }'
HTTP/1.1 201 Created
Content-type: application/json
Location: https://tn.gg/a3gZQXfF

{
  "createdAt":1434088921717,
  "updatedAt":1434088921717,
  "shortDomain":"tn.gg",
  "defaultRedirectUrl":"https://museum.com/displayWorks/catalogue.php?catalogue_id=UVpfh4EFsBpasUmnVDWdktKs",
  "redirectUrl":"https://museum.com/displayWorks/catalogue.php?catalogue_id=UVpfh4EFsBpasUmnVDWdktKs",
  "type":"thng",
  "evrythngUrl":"https://api.evrythng.com/thngs/UVpfh4EFsBpasUmnVDWdktKs",
  "shortId":"a3gZQXfF",
  "evrythngId":"UVpfh4EFsBpasUmnVDWdktKs",
  "hits":0
}

Read a Redirection

Read a redirection to obtain information about the Thng associated with it. You can also read a short domain URL, for example: GET https://tn.gg/:shortId.

Note

To be able to read the JSON metadata for the redirection, make sure to set the Accept header to application/json.

GET https://tn.gg/redirections/:shortId
Authorization: $OPERATOR_API_KEY
Accept: application/json
curl -H "Authorization: $OPERATOR_API_KEY" \
  -H "Accept: application/json"
  -X GET 'https://tn.gg/redirections/a3gZQXfF'
HTTP/1.1 200 OK
Content-type: application/json

{
  "createdAt": 1434088921717,
  "updatedAt": 1434091515411,
  "shortDomain": "tn.gg",
  "defaultRedirectUrl": "https://museum.com/displayWorks/catalogue.php?catalogue_id=UVpfh4EFsBpasUmnVDWdktKs",
  "redirectUrl": "https://museum.com/displayWorks/catalogue.php?catalogue_id=UVpfh4EFsBpasUmnVDWdktKs",
  "type": "thng",
  "evrythngUrl": "https://api.evrythng.com/thngs/UVpfh4EFsBpasUmnVDWdktKs",
  "shortId": "a3gZQXfF",
  "evrythngId": "UVpfh4EFsBpasUmnVDWdktKs",
  "hits": 347
}

Update a Redirection

Submit a new set of redirection information to dynamically change where the short ID will send the user.

PUT https://tn.gg/redirections/:shortId
Content-Type: application/json
Authorization: $OPERATOR_API_KEY

RedirectionDocument
curl -i -H "Content-Type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X PUT 'https://tn.gg/redirections/a3gZQXfF' \
  -d '{
    "defaultRedirectUrl": "https://museum.com/displayWorks/catalogue.php?catalogue_id=:evrythngId"
  }'
HTTP/1.1 200 OK
Content-type: application/json

{
  "createdAt": 1434088921717,
  "updatedAt": 1434091515411,
  "shortDomain": "tn.gg",
  "defaultRedirectUrl": "https://museum.com/displayWorks/catalogue.php?catalogue_id=UVpfh4EFsBpasUmnVDWdktKs",
  "redirectUrl": "https://museum.com/displayWorks/catalogue.php?catalogue_id=UVpfh4EFsBpasUmnVDWdktKs",
  "type": "thng",
  "evrythngUrl": "https://api.evrythng.com/thngs/UVpfh4EFsBpasUmnVDWdktKs",
  "shortId": "a3gZQXfF",
  "evrythngId": "UVpfh4EFsBpasUmnVDWdktKs",
  "hits": 481
}

Delete a Redirection

Delete a redirection. After this operation, the short link will no longer work.

DELETE https://tn.gg/redirections/:shortId
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X DELETE 'https://tn.gg/redirections/a3gZQXfF'
HTTP/1.1 200 OK

Read all Redirections on a Thng

List all redirections associated to EVRYTHNG resource. The main parameter is evrythngId, the ID of the resource possessing the redirection. Multiple redirections can be added to a single resource by making multiple redirection creation requests.

GET https://tn.gg/redirections?evrythngId=:evrythngId
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X GET 'https://tn.gg/redirections?evrythngId=UVpfh4EFsBpasUmnVDWdktKs'

The output is an array of redirections associated with the resource.

HTTP/1.1 200 OK
Content-type: application/json

[
  {
    "createdAt":1434088921717,
    "updatedAt":1434091515411,
    "shortDomain":"tn.gg",
    "defaultRedirectUrl":"https://museum.com/displayWorks/catalogue.php?catalogue_id=UVpfh4EFsBpasUmnVDWdktKs",
    "redirectUrl":"https://museum.com/displayWorks/catalogue.php?catalogue_id=UVpfh4EFsBpasUmnVDWdktKs",
    "type":"thng",
    "evrythngUrl":"https://api.evrythng.com/thngs/UVpfh4EFsBpasUmnVDWdktKs",
    "shortId":"a3gZQXfF",
    "evrythngId":"UVpfh4EFsBpasUmnVDWdktKs",
    "hits":0
  }
]

Generate a QR Code

Once a short URL has been created in the Platform, you can easily generate a customizable QR code that contains the short URL, which can then be attached to the physical object. The QR code can be scanned using any standard bar code / QR code scanning application, which will point to the redirector and will be dynamically redirected to the target application.

GET https://tn.gg/:shortId.qr
Accept: image/:format
curl -H "Accept: image/png" \
  -X GET 'https://tn.gg/EESmUazB.qr' \
  -o output.png

The accept header is used to choose the :format (the file format as a MIME type) of the generated QR code. Available formats:

  • image/png (default, PNG bitmap image)
  • image/svg+xml (SVG vector)
  • application/pdf (vector PDF document)

For the moment, only the height and width of the QR code can be specified, using respectively the h= and w= (default values 95 pixels, maximum 800 pixels). Because the short URL is fixed (23 characters, from which 10 are used for the shortId), the QR code generated is version 2 (25x25 modules) using the error correction level Q (25% of error correction capacity).

You can also use alias endpoints to generate QR code in the preferred format:

PNG

GET https://tn.gg/:shortId.png

SVG

GET https://tn.gg/:shortId.svg

PDF

GET https://tn.gg/:shortId.pdf
Suggest Edits

Redirector

 

The Redirector allows you to dynamically control where a short URL points at, based on a set of business rules. For example, a QR-code could point to different web pages depending on the country it was scanned in, or the age group of the user, etc.

To achieve this, each account and contained application contains a Redirector which processes all incoming scan and implicitScan actions. The rules contained in the Redirector are processed every time one of these actions is created, by the application the user belongs to. In the first instance, the account Redirector handles all that account's resource's redirections, but can delegate the final redirection output decision to an application if so configured.


API Status
Stable:
/redirector
/projects/:projectId/applications/:applicationId/redirector


Redirection Process

The Redirector adds redirection reaction data to scan and implicitScan type actions. In the latter case, because implicitScan actions are created by the server when a short URL is resolved, the resolved long URL will be read from the action's reaction and put in the HTTP redirect. The action will contain a reactions object:

[
  {
    "id": "UmeSC6ebeDPwtpwawFKkgFcc",
    "createdAt": 1497455681787,
    "timestamp": 1497455681787,
    "reactions": [
      {
        "type": "redirection",
        "redirectUrl": "https://google.com",
        "redirectionContext": {
          "constants": {
            "some": "value"
          },
          "thng.tags": [
            "Offer"
          ]
        }
      }
    ],
    "type": "implicitScans",
    "user": "10ff93f061029f0a20273005",
    "location": {
      "latitude": 51.45,
      "longitude": 0.2167,
      "position": {
        "type": "Point",
        "coordinates": [
          0.2167,
          51.45
        ]
      }
    },
    "locationSource": "geoIp",
    "createdByProject": "10ff93f061029f0a20273005",
    "createdByApp": "10ff93f061029f1a20273005",
    "thng": "UFwgcnE7mgSTpHhDcB4rmaEg",
    "product": "UFwDc4t9mgSyKHEXyBn7HdXk"
  }
]

There are cases where the user is not known, typically when a third-party scanner is used to scan a QR-code. In that case, the account Redirector will take care of the action created. The account Redirector can decide to either produce a redirection or to delegate the decision to one or more application Redirectors. Each delegate Redirector may produce a redirection but they don't have to.

In case of implicitScan, if no redirection is produced, the default redirect URL is used; if several redirections are produced, only one of them will be non-deterministically selected (this should be avoided).

Important

In some cases, If a Redirector rule refers to non-existent IDs or field names, the resolution process will exit early and produce no result.


Anatomy

A Redirector's configuration consists of a list of rules. The rules are processed one after the other in sequence. Each rule contains a boolean expression, which will match or not when processed. As soon as a rule matches, its redirect URL is produced. In the case of the account Redirector, the decision can also be delegated to applications listed in the rule.

Match Expression

The match expression uses the filter syntax. The available fields are listed in the Redirection Context section. The whole filter syntax is supported, except the Like (~) operator (see Filters). In addition, * wildcard can be used with Equal (=) operator to match any symbols. * should be the last character of the right-hand side value of the expression. An example is shown below.

product.identifiers.upc=12345&dayOfWeek=sun&thng.name=examp*

Redirect URL

The redirect URL in each rule is produced if the rule matches. It can reference fields from the context in the path, the query and the fragment parts of the URL. Slashes are not escaped in the path part and thus are used as a sub-path delimiter. In order to reference a field, you must put it inside braces. Braces are not allowed for any other usage. Everything including the braces must be properly URL encoded. For example, https://evrythng.com/{thng.id} becomes https://evrythng.com/%7bthng.id%7d.

See the [Context Data Fields](#section-context-data-fields] section below for a full list of available template fields.


Redirection Context

During the process of a redirection, there is aredirectionContext attached to the action, which contains information about various parts involved. If the redirection happened at an application level, the context is stored within the action. If an account level redirection occurred, the context is not stored.

  • action: the action that was created and initiated the process.
  • thng: on what Thng was the action done.
  • product: on what product was the action done.
  • user: who did the action. User-related context parameters are empty for account level Redirector rules.
  • place: the closest place. Must be in project scope when used in application level Redirector rules.
  • time: when it happened. Additional values for time elements are also available (see below).
  • constants: "Key : Value" pairs (user can introduce as many as required).

Note: Not all of these may be available in a given context.

Context Data Fields

The table below outlines the fields that a Redirector rule template may contain. For example:

https://example.com/action?name={thng.name}&sap_code={thng.identifiers.sap_code}&timestamp={timestamp}
Resource/Group
Description
Fields

thng

The action's target thng

id, name, description, identifiers.<key>, customFields.<key>, tags

product

The action's target product

id, name, description, identifiers.<key>, customFields.<key>, tags

user

The Application User that created the action

gender, age, customFields.<key>, locale

Time

The current time & date

time, timeOfDay, dayOfWeek, dayOfMonth, month, year, timezone

action

The action created

customFields.<key>, type

location

The action's location

lat, lon

place

The action's place

id, tags, customFields.<key>, distance

Constants

The Redirector rule constants

As specified in the rule

project

The application's project

id, name, description, customFields.<key>, identifiers.<key>, tags

application

The application

id, name, description, customFields.<key>, `tags

Country

Action's location country

country


Account Level Redirector

The account level Redirector handles all redirections that are not associated with an application. These can be delegated at this level to multiple apps within various projects.


AccountRedirectorRulesDocument Data Model

.rules (array of AccountRedirectorRuleDocument)
    The rules set up for a Redirector.
.id (string, read-only)
    The ID of this resource.
.createdAt (integer, read-only)
    Timestamp when the resource was created.
.updatedAt (integer, read-only)
    Timestamp when the resource was updated.
{
  "type": "object",
  "description": "An object representing the account's Redirector rules",
  "properties": {
    "rules": {
      "description": "The rules set up for a Redirector.",
      "type": "array",
      "items": { "$ref": "AccountRedirectorRuleDocument" }
    },
    "id": {
      "type": "string",
      "description": "The ID of this resource.",
      "readOnly": true
    },
    "createdAt": {
      "type": "integer",
      "description": "Timestamp when the resource was created.",
      "readOnly": true
    },
    "updatedAt": {
      "type": "integer",
      "description": "Timestamp when the resource was updated.",
      "readOnly": true
    }
  }
}

AccountRedirectorRuleDocument Data Model

.match (string)
    Filter used to evaluate this rule. Will always match if this field is not set.
.name (string)
    Friendly name of this resource.
.redirectUrl (string)
    URL to redirect to if the rule matches.
.delegates (array of DelegateDocument)
    Array of app and/or project delegates that will receive this redirection if necessary.
.constants (object)
    Key-value pairs (both must be strings). User can add as many as needed.
{
  "type": "object",
  "description": "An object containing a single Redirector rule set.",
  "properties": {
    "match": {
      "type": "string",
      "description": "Filter used to evaluate this rule. Will always match if this field is not set."
    },
    "name": {
      "type": "string",
      "description": "Friendly name of this resource."
    },
    "redirectUrl": {
      "type": "string",
      "description": "URL to redirect to if the rule matches."
    },
    "delegates": {
      "description": "Array of app and/or project delegates that will receive this redirection if necessary.",
      "type": "array",
      "items": { "$ref": "DelegateDocument" }
    },
    "constants": {
      "type": "object",
      "description": "Key-value pairs (both must be strings). User can add as many as needed."
    }
  }
}

DelegateDocument Data Model

.app (string)
    The delegate application ID.
.project (string)
    The delegate project ID.
{
  "type": "object",
  "description": "A single app/project delegate.",
  "properties": {
    "app": {
      "type": "string",
      "description": "The delegate application ID."
    },
    "project": {
      "type": "string",
      "description": "The delegate project ID."
    }
  }
}

Read the Account Redirector

Reads the redirector on an account.

GET /redirector
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X GET 'https://api.evrythng.com/redirector'
const operatorApiKey = '$OPERATOR_API_KEY';

EVT.api({
  url: '/redirector',
  authorization: operatorApiKey
}).then(console.log);
RedirectorRules redirectorRules = apiManager.redirectorService().redirectorRulesReader().execute();
List<RedirectorRule> rules = redirectorRules.getRules();
for(RedirectorRule rule : rules) {
    System.out.println(rule.getRedirectUrl());
}
HTTP/1.1 200 OK
Content-Type: application/json

{
  "id": "UEpPPNxqhMwwwhMnVRceysKt",
  "createdAt": 1472130332791,
  "updatedAt": 1497455601519,
  "rules": [
    {
      "match": "thng.tags=Offer",
      "name": "Example rule",
      "redirectUrl": "https://google.com",
      "delegates": [],
      "constants": {
        "some": "value"
      }
    }
  ]
}

Update the Account Redirector

Updates the Redirector (created if non-existent).

PUT /redirector
Content-Type: application/json
Authorization: $OPERATOR_API_KEY

AccountRedirectorRulesDocument (subset)
curl -i -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X PUT 'https://api.evrythng.com/redirector' \
  -d '{
    "rules": [{
      "match": "thng.name=test",
      "delegates": [{
        "app": "Ue9G3eMPNVMgesY6GVfGadBg",
        "project": "UB9mGBRy7e6XB8hM3BfGwded"
      }]
    }]
  }'
const operatorApiKey = '$OPERATOR_API_KEY';

const redirectorupdate = {
  rules: [{
    name: 'Example Rule',
    match: 'name=ExampleThng',
    redirectUrl: 'https://example.com',
    deletgates: [{
      app: 'Uh6GfhFnmgScpnxwMsqmbbBk',
      project: 'Uh5qRBqc4DcmbNbAANtMnrYm'
    }]
  }]
};

EVT.api({
  url: '/redirector',
  authorization: operatorApiKey,
  method: 'PUT',
  data: redirectorupdate
}).then(console.log);
// Get existing rules
RedirectorRules accountRules = apiManager.redirectorService().redirectorRulesReader().execute();
List<RedirectorRule> rules = accountRules.getRules();

// Create a new rule
RedirectorRule newRule = new RedirectorRule();
newRule.setName("New Java Rule");
newRule.setRedirectUrl("https://example.com/java");

// Add to list of rules
rules.add(newRule);
accountRules.setRules(rules);

// Update the Platform
apiManager.redirectorService().redirectorRulesUpdater(accountRules).execute();
HTTP/1.1 200 OK
Content-type: application/json

{
  "id":"UfUQg6saFEfpfpFqMFKKgg7c",
  "createdAt":1430220000963,
  "updatedAt":1430220324121,
  "rules": [
    {
      "match":"thng.name=test",
      "delegates": [
        {
          "app":"Ue9G3eMPNVMgesY6GVfGadBg",
          "project":"UB9mGBRy7e6XB8hM3BfGwded"
        }
      ]
    }
  ]
}

Application Level Redirector

Each application has its own Redirector that handles redirects associated with it, and those delegated from the account level Redirector.


ApplicationRedirectorRulesDocument Data Model

.rules (array of ApplicationRedirectorRuleDocument)
    The rules set up for a Redirector.
.id (string, read-only)
    The ID of this resource.
.createdAt (integer, read-only)
    Timestamp when the resource was created.
.updatedAt (integer, read-only)
    Timestamp when the resource was updated.
{
  "type": "object",
  "description": "An object representing an Application Redirector's rules.",
  "properties": {
    "rules": {
      "description": "The rules set up for a Redirector.",
      "type": "array",
      "items": { "$ref": "ApplicationRedirectorRuleDocument" }
    },
    "id": {
      "type": "string",
      "description": "The ID of this resource.",
      "readOnly": true
    },
    "createdAt": {
      "type": "integer",
      "description": "Timestamp when the resource was created.",
      "readOnly": true
    },
    "updatedAt": {
      "type": "integer",
      "description": "Timestamp when the resource was updated.",
      "readOnly": true
    }
  }
}

ApplicationRedirectorRuleDocument Data Model

.match (string)
    Filter used to evaluate this rule. Will always match if this field is not set.
.name (string)
    Friendly name of this resource.
.redirectUrl (string)
    URL to redirect to if the rule matches.
.constants (object)
    Key-value pairs (both must be strings). User can add as many as needed.
{
  "type": "object",
  "description": "An object containing a single Redirector rule set.",
  "properties": {
    "match": {
      "type": "string",
      "description": "Filter used to evaluate this rule. Will always match if this field is not set."
    },
    "name": {
      "type": "string",
      "description": "Friendly name of this resource."
    },
    "redirectUrl": {
      "type": "string",
      "description": "URL to redirect to if the rule matches."
    },
    "constants": {
      "type": "object",
      "description": "Key-value pairs (both must be strings). User can add as many as needed."
    }
  }
}

Read the Application Redirector

Reads the redirector on an application.

GET /projects/:projectId/applications/:applicationId/redirector
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X GET 'https://api.evrythng.com/projects/U2meqbNWegsaQKRRaDUmpssr/applications/UF3Vqb7D6G8EhMwaRYgQ2pFc/redirector'
const projectId = 'U2meqbNWegsaQKRRaDUmpssr';
const applicationId = 'UF3Vqb7D6G8EhMwaRYgQ2pFc';
const operatorApiKey = '$OPERATOR_API_KEY';

EVT.api({
  url: `/projects/${projectId}/applications/${applicationId}/redirector`,
  authorization: operatorApiKey
}).then(console.log);
String projectId = "U2meqbNWegsaQKRRaDUmpssr";
String applicationId = "UF3Vqb7D6G8EhMwaRYgQ2pFc";

RedirectorRules applicationRules = apiManager.redirectorService().redirectorRulesReader(projectId, applicationId).execute();
List<RedirectorRule> rules = applicationRules.getRules();

for(RedirectorRule rule : rules) {
    System.out.println(rule.getRedirectUrl());
}
HTTP/1.1 200 OK
Content-Type: application/json

{
  "id": "Umx4pq63BaNgVrcyRkeF8fRf",
  "createdAt": 1494924489840,
  "updatedAt": 1497456491278,
  "rules": [
    {
      "match": "thng.name=test",
      "name": "Test Rule",
      "redirectUrl": "https://google.com",
      "constants": {}
    }
  ]
}

Update the Application Redirector

Updates the Redirector (it will be created if it does not exist).

PUT /projects/:projectId/applications/:applicationId/redirector
Content-Type: application/json
Authorization: $OPERATOR_API_KEY

ApplicationRedirectorRulesDocument (subset)
curl -i -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X PUT 'https://api.evrythng.com/projects/U2meqbNWegsaQKRRaDUmpssr/applications/UF3Vqb7D6G8EhMwaRYgQ2pFc/redirector' \
  -d '{
    "rules": [{
      "match": "thng.name=test",
      "redirectUrl": "https://evrythng.com"
    }]
  }'
const projectId = 'U2meqbNWegsaQKRRaDUmpssr';
const applicationId = 'UF3Vqb7D6G8EhMwaRYgQ2pFc';
const operatorApiKey = '$OPERATOR_API_KEY';

const redirectorUpdate = {
  rules: [{
    name: 'Example Rule',
    match: 'name=ExampleThng',
    redirectUrl: 'https://example.com',
    deletgates: [{
      app: 'Uh6GfhFnmgScpnxwMsqmbbBk',
      project: 'Uh5qRBqc4DcmbNbAANtMnrYm'
    }]
  }]
};

EVT.api({
  url: `/projects/${projectId}/applications/${applicationId}/redirector`,
  authorization: operatorApiKey,
  method: 'PUT',
  data: redirectorUpdate
}).then(console.log);
String projectId = "U2meqbNWegsaQKRRaDUmpssr";
String applicationId = "UF3Vqb7D6G8EhMwaRYgQ2pFc";

// Read existing rules
RedirectorRules applicationRules = apiManager.redirectorService().redirectorRulesReader(projectId, applicationId).execute();
List<RedirectorRule> rules = applicationRules.getRules();

// Create a new rule
RedirectorRule newRule = new RedirectorRule();
newRule.setName("New Java Rule");
newRule.setRedirectUrl("https://example.com/java");

// Add to rule list
rules.add(newRule);
applicationRules.setRules(rules);

// Update the application in the Platform
apiManager.redirectorService().redirectorRulesUpdater(projectId, applicationId, applicationRules).execute();
HTTP/1.1 200 OK
Content-type: application/json

{
  "id": "UfUtDs8d2ECpfpF6qFKKXg7d",
  "createdAt": 1430220515122,
  "updatedAt": 1430220515122,
  "rules": [
    {
      "match": "thng.name=test",
      "redirectUrl": "https://evrythng.com"
    }
  ]
}

When an account is shared with other Operators, it is possible to define and assign roles to these Operators in order to control their access rights. Each Operator can only have a single and unique Operator role assigned to them at any one time.

In addition, the account owner Operator can define roles for Application Users, assign these roles to applications, and set permissions on individual resources and operations.

Read Roles and Permissions in the Documentation section for more conceptual information on roles and permissions.


API Status
Stable:
/roles
/roles/:roleId
/accounts/:accountId/accesses
/accounts/:accountId/accesses/:accessId


Operator Roles

Jump To ↓

OperatorRoleDocument Data Model
Create an Operator Role
Read an Operator Role
Update an Operator Role


OperatorRoleDocument Data Model

The OperatorRoleDocument contains the basic information about a role, as well as the allowed permissions if set.

.id (string, read-only)
    The ID of this resource.

.name (string)
    Friendly name of this resource.

.description (string)
    Friendly description of this resource.

.customFields (object)
    Object of case-sensititve key-value pairs of custom fields 
    associated with the resource.
{
  "type": "object",
  "description": "Object representing an Operator role.",
  "properties": {
    "id": {
      "type": "string",
      "description": "The ID of this resource.",
      "readOnly": true
    },
    "name": {
      "type": "string",
      "description": "Friendly name of this resource."
    },
    "description": {
      "type": "string",
      "description": "Friendly description of this resource."
    },
    "customFields": {
      "type": "object",
      "description": "Object of case-sensititve key-value pairs of custom fields associated with the resource."
    }
  }
}
{
  "id": "5a0ece3ec1527b00300cc9a5",
  "name": "Example Operator Role",
  "description": "An example Operator role resource",
  "customFields": {
    "admin": false
  }
}

A table of possible values for customFields.enabledStates is shown below:

Value
Role can view...

app

All pages

app.dashboards

All dashboards

app.dashboards.actions

Actions dashboard

app.dashboards.overview

Overview dashboard

app.dashboards.users

Users dashboard

app.projects

All projects

app.redirector

Redirector dashboard

app.resources

All types of platform resources

app.resources.actiontypes

Action Types dashboard

app.resources.batches

Batches dashboard

app.resources.collections

Collections dashboard

app.resources.products

Products dashboard

app.resources.thngs

Thngs dashboard

app.testing

Testing dashboard

app.userDashboards

My Dashboards section


Create an Operator Role

Roles are created with a POST request to the /roles resource containing a JSON document.

Notes

Only the account owner can create new roles.

You cannot create roles with the same name as the predefined admin and none roles.

POST /roles
Content-Type: application/json
Authorization: $OPERATOR_API_KEY

OperatorRoleDocument
curl -i -H "Content-Type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X POST 'https://api.evrythng.com/roles' \
  -d '{
    "name" : "RepairEngineer",
    "description" : "Engineer authorized to perform device repairs"
  }'
const role = {
  name: 'RepairEngineer',
  description: 'Engineer authorized to perform device repairs'
};

operator.role().create(role).then(console.log);
HTTP/1.1 201 Created
Content-Type: application/json

{
  "id": "5901c5d4b14d6d2c00dc420d",
  "name" : "RepairEngineer",
  "description" : "Engineer authorized to perform device repairs"
}

Read an Operator Role

Read a single Operator role by ID.

GET /roles/:roleId
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X GET 'https://api.evrythng.com/roles/593e8f3fcfe9a42c00bf1434'
const roleId = '593e8f3fcfe9a42c00bf1434';

operator.role(roleId).read().then(console.log);
HTTP/1.1 200 OK
Content-Type: application/json

{
  "id": "593e8f3fcfe9a42c00bf1434",
  "name": "OperatorRole",
  "description": "An example Operator role"
}

Update an Operator Role

Update an Operator role with a PUT request to the /roles/:roleId resource containing a JSON document that is some subset of the OperatorRoleDocument. For example, just a new description field.

Note

Once a role has been created, its type cannot be updated.

When updating customFields the entire object is replaced with the copy in the request payload.

PUT /roles/:roleId
Authorization: $OPERATOR_API_KEY
Content-Type: application/json

OperatorRoleDocument
curl -i -H "Content-Type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X PUT 'https://api.evrythng.com/roles/58cfc58d085b262800b78183' \
  -d '{
    "description": "Updated description"
  }'
const roleId = '58cfc58d085b262800b78183';

const roleUpdate = {
  description: 'Updated description'
};

operator.role(roleId).update(roleUpdate).then(console.log);
HTTP/1.1 200 Ok
Content-Type: application/json

{
  "id": "58cfc58d085b262800b78183",
  "name": "Repair Engineer",
  "description": "Updated description"
}

Application User Roles

In addition to account-level Operator roles, Application Users can be assigned Application User roles that dictate which resources they can view and interact with.

New Application User roles automatically inherit some default immutable permissions. See the Role Permissions page for more information.


Jump To ↓

ApplicationUserRoleDocument Data Model
Create an Application User Role
Read an Application User Role
Update an Application User Role
Application Default Role


ApplicationUserRoleDocument Data Model

.name (string)
    Friendly name of this resource.

.id (string, read-only)
    The role ID. May alse be 'none', 'admin', or 'base_app_user' 
    default roles.

.type (string, one of 'userInApp')
    The type of Role. Only 'userInApp' is currently available.

.version (integer, one of '2')
    The Role API version.

.description (string)
    Friendly description of this resource.

.customFields (object)
    Object of case-sensititve key-value pairs of custom fields 
    associated with the resource.

.scopes (RoleScopesDocument)

.createdAt (integer, read-only)
    Timestamp when the resource was created.

.updatedAt (integer, read-only)
    Timestamp when the resource was updated.
{
  "type": "object",
  "description": "An object describing a Platform role.",
  "properties": {
    "name": {
      "type": "string",
      "description": "Friendly name of this resource."
    },
    "id": {
      "type": "string",
      "description": "The role ID. May also be 'none', 'admin', or 'base_app_user' default roles.",
      "readOnly": true,
      "minLength": 4,
      "maxLength": 24
    },
    "type": {
      "type": "string",
      "description": "The type of Role. Only 'userInApp' is currently available.",
      "enum": [ "userInApp" ]
    },
    "version": {
      "type": "integer",
      "description": "The Role API version.",
      "enum": [ 2 ]
    },
    "description": {
      "type": "string",
      "description": "Friendly description of this resource."
    },
    "customFields": {
      "type": "object",
      "description": "Object of case-sensititve key-value pairs of custom fields associated with the resource."
    },
    "scopes": { "$ref": "RoleScopesDocument" },
    "createdAt": {
      "type": "integer",
      "description": "Timestamp when the resource was created.",
      "readOnly": true
    },
    "updatedAt": {
      "type": "integer",
      "description": "Timestamp when the resource was updated.",
      "readOnly": true
    }
  }
}
{
  "id": "59b8f735d3fa773000b27691",
  "version": 2,
  "type": "userInApp",
  "name": "Schema Test Role",
  "description": "An example Application User role",
  "customFields": {
    "admin": false
  },
  "createdAt": 1505294133126,
  "updatedAt": 1510920016139
}

RoleScopesDocument Data Model

.roles (array of string)
    The roles this role is scoped to.

.projects (array of string)
    The projects this role is scoped to.
{
  "type": "object",
  "description": "The roles and projects scopes this role is scoped to.",
  "properties": {
    "roles": {
      "type": "array",
      "description": "The roles this role is scoped to.",
      "items": {
        "type": "string",
        "description": "The ID of this resource.",
        "readOnly": true
      }
    },
    "projects": {
      "type": "array",
      "description": "The projects this role is scoped to.",
      "items": {
        "type": "string",
        "description": "The ID of this resource.",
        "readOnly": true
      }
    }
  }
}

Create an Application User Role

Notes

Only the account owner can create new roles.

You cannot create roles with the same name as the predefined admin and none roles.

POST /roles
Content-Type: application/json
Authorization: $OPERATOR_API_KEY

AppUserRoleDocument
curl -i -H "Content-Type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X POST 'https://api.evrythng.com/roles' \
  -d '{
    "type": "userInApp",
    "version": 2,
    "name" : "RepairEngineer",
    "description" : "Engineer authorized to perform device repairs"
  }'
const role = {
  type: 'userInApp',
  version: 2,
  name: 'RepairEngineer',
  description: 'Engineer authorized to perform device repairs'
};

operator.role().create(role).then(console.log);
HTTP/1.1 201 Created
Content-Type: application/json

{
  "id": "58d014554451ac280060f2db",
  "type": "userInApp",
  "version": 2,
  "name": "RepairEngineer",
  "description": "Engineer authorized to perform device repairs",
  "createdAt": 1490031701138,
  "updatedAt": 1490031701138
}

Read an Application User Role

Read a single Application User role by ID.

GET /roles/:roleId
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X GET 'https://api.evrythng.com/role/593e8f3fcfe9a42c00bf1434'
const roleId = '593e8f3fcfe9a42c00bf1434';

operator.role(roleId).read().then(console.log);
HTTP/1.1 200 OK
Content-Type: application/json

{
  "id": "593e8f3fcfe9a42c00bf1434",
  "type": "userInApp",
  "version": 2,
  "name": "RepairEngineer",
  "description": "Engineer authorized to perform device repairs",
  "createdAt": 1490031701138,
  "updatedAt": 1490031701138
}

Update an Application User Role

Update a role with a PUT request to the /roles/:roleId resource containing a JSON document that is some subset of the RoleDocument. For example, just a new description field.

Note

Once a role has been created, its type cannot be updated.

When updating customFields the entire object is replaced with the copy in the request payload.

PUT /roles/:roleId
Authorization: $OPERATOR_API_KEY
Content-Type: application/json

AppUserRoleDocument (subset)
curl -i -H "Content-Type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X PUT 'https://api.evrythng.com/roles/58cfc58d085b262800b78183' \
  -d '{
    "description": "Updated description"
  }'
const roleId = '58cfc58d085b262800b78183';

const update = {
  description: 'Updated role description'
};

operator.role(roleId).update(update).then(console.log);
HTTP/1.1 200 Ok
Content-Type: application/json

{
  "id": "58cfc58d085b262800b78183",
  "type": "userInApp",
  "version": 2,
  "name": "Test user role",
  "description": "Updated role description",
  "createdAt": 1490011533066,
  "updatedAt": 1490011533066
}

Read all Roles

Get a list of all available roles for the account. The result may be paginated if there are more than 30 items.

GET /roles
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X GET 'https://api.evrythng.com/roles'
operator.role().read().then(console.log);
HTTP/1.1 200 Ok
Content-Type: application/json

[
  {
    "id": "admin",
    "name": "admin"
  },
  {
    "id": "none",
    "name": "none"
  },
  {
    "id": "base_app_user",
    "version": 2,
    "type": "userInApp",
    "name": "base_app_user"
  },
  {
    "id": "592d70418c5e3f2800408a74",
    "version": 2,
    "type": "userInApp",
    "name": "RepairEngineer",
    "description": "Engineer authorized to perform device repairs",
    "customFields": {
      "foo": "bar"
    },
    "createdAt": 1496150081776,
    "updatedAt": 1496150097386
  },
  {
    "id": "592d70a18c5e3f2800408ab3",
    "name": "OperatorRole1",
    "description": "An example Operator role"
  }
]

Delete a Role

Remove a single role of either type by performing a DELETE request on the /roles/:roleId resource.

Note

The predefined none role will be assigned to all the users currently having the role about to be removed.

A userInApp role cannot be deleted until all users with that role have been assigned a different role.

DELETE /roles/:roleId
Authorization: $OPERATOR_API_KEY
curl -i -H "Content-Type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X DELETE 'https://api.evrythng.com/roles/58cfc58d085b262800b78183'
const roleId = '5805f5012229bd50693aac24';

operator.role(roleId).delete().then(() => console.log('Deleted'));
HTTP/1.1 200 Ok

Update a Role's Project Scope

Use the special notation shown below to update a role with multiple new project scopes at once. For each ID, prefix with + to add or - to remove. Including neither will replace the list with the payload. It is not permitted to mix the two update methods at once.

PUT /roles/:roleId
Content-Type: application/json
Authorization: $OPERATOR_API_KEY

{
  "scopes": {
    "project": [ "+U2As9WsMthW5F5aeFECpCeDs" ]
  }
}
curl -H "Content-Type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X PUT 'https://api.evrythng.com/roles/592d70418c5e3f2800408a74' \
  -d '{
    "scopes": {
      "projects": ["+U2As9WsMthW5F5aeFECpCeDs"]
    }
  }'

The role itself can be viewed with the scopes using the withScopes=true query parameter:

HTTP/1.1 200 OK
Content-Type: application/json

{
  "id": "592d70418c5e3f2800408a74",
  "version": 2,
  "type": "userInApp",
  "name": "RepairEngineer",
  "description": "Engineer authorized to perform device repairs",
  "customFields": {
    "foo": "bar"
  },
  "scopes": {
    "projects": [
      "U2As9WsMthW5F5aeFECpCeDs"
    ]
  }
  "createdAt": 1496150081776,
  "updatedAt": 1496744121980
}

Set a Role's Scoped Roles

Use a PUT request to update a role with a list of IDs of roles that can access that role. For example, a Repair Engineer Supervisor role can be added by ID to the roles scopes of the Repair Engineer role to enable the supervisor to see and assign it.

PUT /roles/:roleId
Content-Type: application/json
Authorization: $OPERATOR_API_KEY

{
  "scopes": {
    "roles": [ "596ccd9a1b0b282c00d5cfda" ]
  }
}
HTTP/1.1 200 OK
Content-Type: application/json

{
  "id": "592d70418c5e3f2800408a74",
  "version": 2,
  "type": "userInApp",
  "name": "RepairEngineer",
  "description": "Engineer authorised to perform device repair",
  "customFields": {
    "foo": "bar"
  },
  "scopes": {
    "roles": [
      "596ccd9a1b0b282c00d5cfda"
    ],
    "projects": [
      "U2As9WsMthW5F5aeFECpCeDs"
    ]
  },
  "createdAt": 1500297818393,
  "updatedAt": 1500302769632
}

Read a Role's Scoped Roles

The roles that are visible to any given role are those that their current role is scoped to, i.e: those that include the current role in their scopes.roles field. The current user is determined using the Application User API Key authenticating the request.

GET /roles
Authorization: $APPLICATION_USER_API_KEY
HTTP/1.1 200 OK
Content-Type: application/json

[
  {
    "id": "596cba5a79fd982c00d25fda",
    "version": 2,
    "type": "userInApp",
    "name": "RepairEngineer",
    "description": "Engineer authorised to perform device repair",
    "customFields": {},
    "createdAt": 1500297818393,
    "updatedAt": 1500302769632
  }
]

Read an Account's Role

Read the current role of an account, as well as other authentication information. The response contains a list of AccessDocument objects, each of which has a role field.

GET /accounts/:accountId/accesses
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X GET 'https://api.evrythng.com/accounts/UhpHrg39QCy9dsSddN8xhwnb/accesses'
const accountId = 'UhpHrg39QCy9dsSddN8xhwnb';
const operatorApiKey = '$OPERATOR_API_KEY';

EVT.api({
  url: `/accounts/${accountId}/accesses`,
  authorization: operatorApiKey
  method: 'GET'
}).then((accesses) => console.log(accesses[0].role));
HTTP/1.1 200 OK
Content-Type: application/json

[
  {
    "id": "57bc4f351976e18a306f47b5",
    "account": "UhpHrg39QCy9dsSddN8xhwnb",
    "operator": "UhpMetUeBegbkUDpGgsk4hyq",
    "apiKey": "9O0Tqi92f78bGX376d1Nw4sfC49J78HcEqv1HLzDw...",
    "role": "admin"
  }
]

Set an Account's Role

Set the role of an account with a roleId. This replaces the previously assigned role. The AccessDocument should contain the roleId of the role to be assigned.

PUT /accounts/:accountId/accesses/:accessId
Content-Type: application/json
Authorization: $OPERATOR_API_KEY

AccessDocument
curl -i -H "Content-Type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X PUT 'https://api.evrythng.com/accounts/UhpHrg39QCy9dsSddN8xhwnb/accesses/57bad69fc18104025b292da8' \
  -d '{
    "role": "5889cd38c4b3762e00667633"
  }'
const accountId = 'UhpHrg39QCy9dsSddN8xhwnb';
const accessId = '57bad69fc18104025b292da8';
const newRoleId = '58b0521a95cb272800a17a45';
const operatorApiKey = '$OPERATOR_API_KEY';

const update = {
  role: newRoleId
};

EVT.api({
  url: `/accounts/${accountId}/accesses/${accessId}`,
  authorization: operatorApiKey
  method: 'PUT',
  data: update
}).then(console.log);
HTTP/1.1 200 OK
Content-Type: application/json

{
  "id": "57bad69fc18104025b292da8",
  "account": "UhpHrg39QCy9dsSddN8xhwnb",
  "operator": "UEp4rDGsnpCAF6xABbys5Amc",
  "apiKey": "$OPERATOR_API_KEY",
  "role": "5889cd38c4b3762e00667633"
}

Application Default Role

When an application is created, it will contain a default role that Application Users in that application will be automatically assigned. The default application default role is the pre-defined "base_app_user" role. This allows Application Users to view all resources in that application's project. Custom Application User roles can be created to specify a different subset of permissions as required. See Role Permissions for more information.

If the application's defaultRole changes at some point in the future, all Application Users within that application will be assigned the new role automatically.


Set an Application's Default Role

PUT /projects/:projectId/applications/:applicationId
Content-Type: appplication/json
Authorization: $OPERATOR_API_KEY

{
  "defaultRole": "58f8773ffc7c042800fda32b"
}
curl -i -H "Content-Type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X PUT 'https://api.evrythng.com/projects/Uh5qRBqc4DcmbNbAANtMnrYm/applications/Uh6GfhFnmgScpnxwMsqmbbBk' \
  -d '{
    "defaultRole": "58f8773ffc7c042800fda32b"
  }'
const projectId = 'Uh5qRBqc4DcmbNbAANtMnrYm';
const applicationId = 'Uh6GfhFnmgScpnxwMsqmbbBk';
const defaultRoleId = '58f8773ffc7c042800fda32b';

const update = {
  defaultRole: defaultRoleId
};

operator.project(projectId).application(applicationId).update(update)
  .then(console.log);
HTTP/1.1 200 OK
Content-Type: application/json

{
  "id": "Uh6GfhFnmgScpnxwMsqmbbBk",
  "createdAt": 1480521204655,
  "customFields": {},
  "updatedAt": 1494509400307,
  "name": "Test Application",
  "description": "Main app for API testing and documentation",
  "project": "Uh5qRBqc4DcmbNbAANtMnrYm",
  "socialNetworks": {},
  "defaultRole": "58f8773ffc7c042800fda32b",
  "appApiKey": "8xolxjxxl64r7QeW5wP575745woBpd5Jy3uzTt12..."
}
Suggest Edits

Role Permissions

 

Role permissions determine what an Operator or Application User can see/do on an account according to their assigned role. Read the Roles section for more information on managing roles through the API.

Read Roles and Permissions in the Documentation section for more conceptual information on roles and permissions usage.

Note

If a role's permissions are changed while a user with that role has an open Pub/Sub Broker connection, the changes will not apply for those users until they reconnect.


API Status
Stable:
/roles/:roleId/permissions
/roles/:roleId/permission/:permissionName


.name (string, one of 'global_read', 'gl_resource_update', 'gl_project_update', 'project_read', 'project_update', 'project_resource_update')
    Name of the specific role permission.

.enabled (boolean)
    `true` if this permission is enabled, `false` otherwise.

.projectScoped (boolean)
    `true` if this permission if enabled only for specific 
    project(s), `false` otherwise.

.projects (array of string)
    Array of project IDs that this permission is enabled for.

.children (OperatorRolePermissionDocument)
{
  "type": "string",
  "description": "Object describing Operator role permissions",
  "properties": {
    "name": {
      "type": "string",
      "description": "Name of the specific role permission.",
      "enum": [ "global_read", "gl_resource_update", "gl_project_update", "project_read", "project_update", "project_resource_update" ]
    },
    "enabled": {
      "type": "boolean",
      "description": "`true` if this permission is enabled, `false` otherwise."
    },
    "projectScoped": {
      "type": "boolean",
      "description": "`true` if this permission if enabled only for specific project(s), `false` otherwise."
    },
    "projects": {
      "type": "array",
      "description": "Array of project IDs that this permission is enabled for.",
      "items": { "type": "string" }
    },
    "children": { "$ref": "OperatorRolePermissionDocument" }
  }
}
[
  {
    "name": "global_read",
    "enabled": false,
    "projectScoped": false,
    "children": [
      {
        "name": "gl_resource_update",
        "enabled": false,
        "projectScoped": false
      },
      {
        "name": "gl_project_update",
        "enabled": false,
        "projectScoped": false
      }
    ]
  },
  {
    "name": "project_read",
    "enabled": false,
    "projectScoped": true,
    "projects": [],
    "children": [
      {
        "name": "project_update",
        "enabled": false,
        "projectScoped": true,
        "projects": []
      },
      {
        "name": "project_resource_update",
        "enabled": false,
        "projectScoped": true,
        "projects": []
      }
    ]
  }
]

Enable/Disable an Operator Role Permission

Enable or disable a specific permission for a given Operator role. Set enabled to true to grant that permission, false to revoke it.

PUT /roles/:roleId/permissions/:permissionName
Content-Type: application/json
Authorization: $OPERATOR_API_KEY

OperatorRolePermissionDocument
curl -i -H "Content-Type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X PUT 'https://api.evrythng.com/roles/UhpHrg39QCy9dsSddN8xhwnb/permissions/global_read' \
  -d '{ 
    "name": "global_read", 
    "enabled": true 
  }'
const operatorAppiKey = '$OPERATOR_API_KEY';
const roleId = '59aeb3abb24413002c7d8651';
const permission = 'global_read';

EVT.api({
  url: `/roles/${roleId}/permissions/${permission}`,
  authorization: operatorApiKey,
  method: 'PUT',
  data: { name: permission, enabled: true }
}).then(console.log);
HTTP/1.1 200 OK
Content-Type: application/json

{ 
  "name": "global_read", 
  "enabled": true, 
  "projectScoped": false 
}

Enable/Disable a Project-scoped Operator Role Permission

PUT /roles/:roleId/permissions/:permissionName

For project_* type permissions, it is also possible to specify which projects the permission applies to. For example, to enable the project_read permission for a single project include an array of project IDs in the projects field of the request:

PUT /roles/:roleId/permissions/:permissionName
Content-Type: application/json
Authorization: $OPERATOR_API_KEY

OperatorRolePermissionDocument
curl -i -H "Content-Type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X PUT 'https://api.evrythng.com/roles/5805f5012229bd50693aac24/permissions/project_read' \
  -d '{ 
    "name": "project_read", 
    "enabled": true,
    "projects": [ "Uh5qRBqc4DcmbNbAANtMnrYm" ]
  }'
const operatorAppiKey = '$OPERATOR_API_KEY';
const roleId = '59aeb3abb24413002c7d8651';
const permission = 'project_read';

EVT.api({
  url: `/roles/${roleId}/permissions/${permission}`,
  authorization: operatorApiKey,
  method: 'PUT',
  data: { 
    name: permission, 
    enabled: true,
    projects: [ 'Uh5qRBqc4DcmbNbAANtMnrYm' ]
  }
}).then(console.log);
HTTP/1.1 200 OK
Content-Type: application/json

{ 
  "name": "project_read", 
  "enabled": true, 
  "projectScoped": true, 
  "projects": [ "Uh5qRBqc4DcmbNbAANtMnrYm" ] 
}

Read an Operator Role's Permissions

Read the state of all permission types for a given role. In the case of reading all permissions for an Operator type role the following response is returned, containing the state of all permissions for that role.

Since it is a collection of OperatorRolePermissionDocument objects, the fields themselves are the same as detailed in the OperatorRolePermissionDocument section above.

GET /roles/:roleId/permissions
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X GET 'https://api.evrythng.com/roles/5805f5012229bd50693aac24/permissions'
const roleId = '5805f5012229bd50693aac24';

operator.role(roleId).permission().read().then(console.log);
HTTP/1.1 200 OK
Content-Type: application/json

[
  {
    "name": "global_read",
    "enabled": false,
    "projectScoped": false,
    "children": [
      {
        "name": "gl_resource_update",
        "enabled": false,
        "projectScoped": false
      }, 
      {
        "name": "gl_project_update",
        "enabled": false,
        "projectScoped": false
      }
    ]
  }, 
  {
    "name": "project_read",
    "enabled": false,
    "projectScoped": true,
    "projects": [],
    "children": [
      {
        "name": "project_update",
        "enabled": false,
        "projectScoped": true,
        "projects": []
      }, 
      {
        "name": "project_resource_update",
        "enabled": false,
        "projectScoped": true,
        "projects": []
      }
    ]
  }
]

Application User Role Permissions

The Application User role permissions model is similar to that used for Operator roles, with one key difference: each role has a set of permissions that can be added or removed, instead of enabled or disabled. The set of paths and access modes a role has determines the resources it can see and interact with. See the ApplicationUserRolePermissionDocument Data Model section for how to specify this.


Jump To ↓

ApplicationUserRolePermissionDocument Data Model
Update an Application User Role's Permissions
Delete an Application User Role's Permissions
Read an Application User Role's Permissions
Default Application User Role Permissions


ApplicationUserRolePermissionDocument Data Model

.path (string)
    The API path the role can access. Must be a subset of the 
    default `base_app_user` permissions.

.access (string)
    A four letter combination of 'c', 'r', 'u', and 'd' 
    describing the access to create, read, update, or destroy 
    the resources in the 'path' of this permission.

.default (boolean)
    `true` for default permissions.
{
  "type": "object",
  "description": "An object representing an account role permission. Depending on the type of role, different fields may be returned.",
  "properties": {
    "path": {
      "type": "string",
      "description": "The API path the role can access. Must be a subset of the default `base_app_user` permissions."
    },
    "access": {
      "type": "string",
      "description": "A four letter combination of 'c', 'r', 'u', and 'd' describing the access to create, read, update, or destroy the resources in the 'path' of this permission.",
      "minLength": 1,
      "maxLength": 4
    },
    "default": {
      "type": "boolean",
      "description": "`true` for default permissions."
    }
  }
}
[
  {
    "path": "/access$",
    "access": "r",
    "default": true
  },
  {
    "path": "/accesses",
    "access": "crd",
    "default": true
  },
  {
    "path": "/auth/all/logout",
    "access": "c",
    "default": true
  },
  {
    "path": "/rateLimits$",
    "access": "r",
    "default": true
  },
  {
    "path": "/roles",
    "access": "r",
    "default": true
  },
  {
    "path": "/thngs",
    "access": "cru"
  },
  {
    "path": "/products",
    "access": "cru"
  },
  {
    "path": "/collections",
    "access": "cru"
  },
  {
    "path": "/collections/*/thngs/*",
    "access": "d"
  },
  {
    "path": "/actions/*",
    "access": "cr"
  },
  {
    "path": "/actions",
    "access": "r"
  },
  {
    "path": "/auth/evrythng/thngs",
    "access": "crd"
  },
  {
    "path": "/users/{user}",
    "access": "ru"
  },
  {
    "path": "/places",
    "access": "r"
  },
  {
    "path": "/connectors/*/auth",
    "access": "rd"
  },
  {
    "path": "/connectors/*/auth/token",
    "access": "c"
  },
  {
    "path": "/connectors",
    "access": "r"
  }
]

Update an Application User Role's Permissions

Use a PUT request to replace the role's current set of permissions with a new set. This must include any existing permissions, otherwise they will be deleted if not present in the new payload.

To remove a permission, simply omit it in the payload after reading from the role initially.

PUT /roles/:roleId/permissions
Content-Type: application/json
Authorization: $OPERATOR_API_KEY

[ ApplicationUserRolePermissionDocument, ... ]
curl -H "Content-Type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X PUT 'https://api.evrythng.com/roles/5805f5012229bd50693aac24/permissions' \
  -d '[
    {
      "path": "/thngs",
      "access": "cr"
    }
  ]'
const roleId = '5805f5012229bd50693aac24';
const update = [{
  path: '/thngs',
  access: 'cru'
}];

operator.role(roleId).permission().update(update).then(console.log);
HTTP/1.1 200 OK
Content-Type: application/json

[
  {
    "path": "/thngs",
    "access": "cru"
  }
]

Read an Application User Role's Permissions

Read an array of paths and access types that an Application User with the specified role can access.

GET /roles/:roleId/permissions
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X GET 'https://api.evrythng.com/roles/593e8f3fcfe9a42c00bf1434/permissions'
const roleId = '593e8f3fcfe9a42c00bf1434';

operator.role(roleId).permission().read().then(console.log);
HTTP/1.1 200 OK
Content-Type: application/json

[
  {
    "path": "/thngs",
    "access": "cru"
  },
  {
    "path": "/products",
    "access": "cru"
  }
]

Delete an Application User Role's Permissions

To delete all non-default permissions for an Application User role, simple perform an update with an empty array payload ([]).

PUT /roles/:roleId/permissions
Authorization: $OPERATOR_API_KEY

[]
curl -H "Content-Type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X PUT 'https://api.evrythng.com/roles/5805f5012229bd50693aac24/permissions' \
  -d '[]'
const roleId = '592d70418c5e3f2800408a74';

const update = [];  // Set to zero permissions

operator.role(roleId).permission().update(update).then(console.log);

Default Application User Role Permissions

When a new Application User role is created, it inherits some immutable permissions that all users in that role will get. These are identified by the default field. Any additional permissions the role requires can be added in addition to these default permissions:

[
  {
    "path": "/access$",
    "access": "r",
    "default": true
  },
  {
    "path": "/auth/all/logout",
    "access": "c",
    "default": true
  }, 
  {
    "path": "/rateLimits$",
    "access": "r",
    "default": true
  }, 
  {
    "path": "/roles",
    "access": "r",
    "default": true
  }
]

A Thng is the central data structure in the EVRYTHNG Platform. Thngs are extensible data containers designed to model physical objects such as packaged products, electronic devices, consumer electronics, buildings and spaces, etc. Thngs have various basic fields (name, description, tags, etc.), and a set of locations. More dynamic/specialised data can be stored in the properties, customFields, or identifiers fields as required.

Thngs can be the target of actions describing events that happen to them, or they can be added to collections to express logical groupings, such as a box of individual items, as a unit. In addition to specifying a Thng in an action, it is also possible to create an action through the Thng itself using aliased actions.


API Status
Stable:
/auth/evrythng/thngs
/thngs
/thngs/:thngId
/thngs/:thngId/actions
/thngs/:thngId/location
/thngs/:thngId/properties
/thngs/:thngId/redirector


ThngDocument Data Model

.name (string)
    Friendly name of this resource.

.id (string, read-only)
    The ID of this resource.

.createdAt (integer, read-only)
    Timestamp when the resource was created.

.updatedAt (integer, read-only)
    Timestamp when the resource was updated.

.activatedAt (integer, read-only)
    Timestamp when the Thng was activated.

.tags (array of string)
    Array of string tags associated with this resource.

.description (string)
    Friendly description of this resource.

.customFields (object)
    Object of case-sensititve key-value pairs of custom fields 
    associated with the resource.

.identifiers (object)
    Various identifiers (EPC, ISBN, etc.) as a JSON object with 
    one or more key-value pairs.

.product (string)
    The Platform ID of a product this Thng is a class of.

.location (LocationDocument)

.properties (object)
    A JSON object with key-value pairs describing properties of 
    the Thng.

.collections (array of string)
    An array of collection IDs of any collections this Thng 
    belongs to.

.createdByTask (string, read-only)
    The ID of this resource.

.batch (string, read-only)
    The ID of this resource.

.scopes (ScopesDocument)
{
  "type": "object",
  "description": "An object representing a Thng.",
  "properties": {
    "name": {
      "type": "string",
      "description": "Friendly name of this resource."
    },
    "id": {
      "type": "string",
      "description": "The ID of this resource.",
      "readOnly": true
    },
    "createdAt": {
      "type": "integer",
      "description": "Timestamp when the resource was created.",
      "readOnly": true
    },
    "updatedAt": {
      "type": "integer",
      "description": "Timestamp when the resource was updated.",
      "readOnly": true
    },
    "activatedAt": {
      "type": "integer",
      "description": "Timestamp when the Thng was activated.",
      "readOnly": true
    },
    "tags": {
      "type": "array",
      "description": "Array of string tags associated with this resource.",
      "items": {
        "type": "string",
        "maxLength": 60
      }
    },
    "description": {
      "type": "string",
      "description": "Friendly description of this resource."
    },
    "customFields": {
      "type": "object",
      "description": "Object of case-sensititve key-value pairs of custom fields associated with the resource."
    },
    "identifiers": {
      "type": "object",
      "description": "Various identifiers (EPC, ISBN, etc.) as a JSON object with one or more key-value pairs."
    },
    "product": {
      "type": "string",
      "description": "The Platform ID of a product this Thng is a class of."
    },
    "location": { "$ref": "LocationDocument" },
    "properties": {
      "type": "object",
      "description": "A JSON object with key-value pairs describing properties of the Thng."
    },
    "collections": {
      "description": "An array of collection IDs of any collections this Thng belongs to.",
      "type": "array",
      "items": { "type": "string" }
    },
    "createdByTask": {
      "type": "string",
      "description": "The ID of this resource.",
      "readOnly": true
    },
    "batch": {
      "type": "string",
      "description": "The ID of this resource.",
      "readOnly": true
    },
    "scopes": { "$ref": "ScopesDocument" }
  }
}
{
  "id": "UnRPNbfD6GsYh6wawhBMNdDe",
  "createdAt": 1510920534153,
  "customFields": {
    "region": "en_gb"
  },
  "tags": [
    "example",
    "thng"
  ],
  "updatedAt": 1510920534153,
  "name": "Example Thng",
  "description": "An example Thng resource.",
  "product": "UHR8MGsSCct4t9wwRgEKqrUr",
  "properties": {
    "is_use": false
  },
  "identifiers": {
    "ean_13": "4834838943"
  }
}

See also: ScopesDocument, LocationDocument


Create a Thng

Create a new Thng. It is recommended to link all Thngs to a product upon creation by specifying the product's ID in the product field.

POST /thngs
Content-Type: application/json
Authorization: $OPERATOR_API_KEY

ThngDocument
curl -i -H "Content-Type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X POST "https://api.evrythng.com/thngs" \
  -d '{
    "name": "Truck # L4-4",
    "description": "Truck L4-4. Delivery truck for North England.",
    "location": {
      "position": {
        "type": "Point",
        "coordinates": [ 2.05, 57.1 ]
      }
    },
    "properties": {
      "status": "Idle"
    }
  }'
const thng = {
 name: 'Truck # L4-4',
  description: 'Truck L4-4. Delivery truck for North England.',
  location: {
    position: {
      type: 'Point',
      coordinates: [ 2.05, 57.1 ]
    }
  },
  properties: {
    status: 'Idle'
  }
};

user.thng().create(thng).then(console.log);
Thng thng = new Thng();
thng.setName("Truck # L4-4");
thng.setDescription("Truck L4-4. Delivery truck for North England.");
apiManager.thngService().thngCreator(thng).execute();
HTTP/1.1 201 Created
Content-Type: application/json
Location: https://api.evrythng.com/thngs/UFhB5TENG6ffeHxUCRt8eNta

{
  "id": "UFhB5TENG6ffeHxUCRt8eNta",
  "createdAt": 1346856615704,
  "updatedAt": 1346856615704,
  "name": "Truck # L4-4",
  "description": "Truck L4-4. Delivery truck for North England.",
  "location": {
    "position": {
      "type": "Point",
      "coordinates": [ 2.05, 57.1 ]
    }
  },
  "properties": {
    "status": "Idle"
  }
} 

Read a Thng

Thng data is read using a GET request on the Thng URL including the thngId.

GET /thngs/:thngId
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X GET "https://api.evrythng.com/thngs/U2wm8b42MVNxTCX6MYDrRpyd"
const thngId = 'U2wm8b42MVNxTCX6MYDrRpyd';

user.thng(thngId).read().then(console.log);
String thngId = "U2wm8b42MVNxTCX6MYDrRpyd";

Thng thng = apiManager.thngService().thngReader(thngId).execute();
HTTP/1.1 200 OK
Content-Type: application/json

{
  "id": "U2wm8b42MVNxTCX6MYDrRpyd",
  "createdAt": 1346856615704,
  "updatedAt": 1346856615704,
  "name": "Truck # L4-4",
  "description": "Truck L4-4. Delivery truck for North England.",
  "location": {
    "position": {
      "type": "Point",
      "coordinates": [2.05, 57.1]
    }
  },
  "properties": {
    "status": "Idle",
    "last_repaired": "14. June 2014",
    "current_load_volume_m3": "0",
    "current_load_weight_kg": "0",
    "type": "40 Tons load"
  },
  "tags": ["Truck","Man TGS 3","Scotland","Diesel","Large","40 Tons"]
}

Read all Thngs

Read all available Thngs in scope of the authenticating API key. The result may be paginated if there are more than 30 items.

GET /thngs
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X GET 'https://api.evrythng.com/thngs'
user.thng().read().then(console.log);
Iterator<PVector<Thng>> thngs = apiManager.thngService().iterator().perPage(10).execute();

while (thngs.hasNext()) {
    PVector<Thng> page = thngs.next();
    for (Thng thng : page) {
        System.out.println("Thng: " + thng.toString());
    } 
}
HTTP/1.1 200 OK
Content-Type: application/json

[
  {
    "id":"504766a7c8f205a0744243c1",
    "createdAt":1346409633224,
    "updatedAt":1346409633224,
    "name": "Truck # L4-4",
    "description": "Truck L4-4. Delivery truck for North England.",
    "location": {
      "position": {
        "type": "Point",
        "coordinates": [ 2.05, 57.1 ]
      }
    },
    "properties": {
      "status": "Idle"
    }
  }
]

Update a Thng

Update a single Thng by ID.

Note

Only the basic fields (name, description, product, identifiers, tags, and customFields) can be updated in this manner.

Properties, location and collections must be updated via their respective /thngs/:thngId/location, /thngs/:thngId/properties and /collections/:collectionId/thngs endpoints.

PUT /thngs/:thngId
Content-Type: application/json
Authorization: $OPERATOR_API_KEY

ThngDocument (subset)
curl -i -H "Content-Type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X PUT 'https://api.evrythng.com/thngs/UkYVKThN36Cfe4xACatserPs' \
  -d '{
    "name": "Truck #F893-44 - IDLE",
    "description": "Truck F893-44. Delivery truck for North England. Currently being repaired."
  }'
const thngId = 'UkYVKThN36Cfe4xACatserPs';

// There are three ways of updating entities. 
// You can either update on the entity itself by fields:
user.thng(thngId).read().then((thng) => {
  thng.name = "New thng name";
  thng.update().then(console.log);
});

// By providing a ThngDocument JSON object to the entity .update() method:
user.thng(thngId).read().then((thng) => {
  console.log(thng);
  const thngDocument = { tags: ['tag1', 'tag2'] };

  // Return the update promise to use chaining
  return thng.update(thngDocument);
}).then(console.log);

// Or by providing the ThngDocument JSON object to the 
// resource .update() method straight away in a single call.
const thngDocument = { tags: ['tag1', 'tag2'] };

user.thng(thngId).update(thngDocument).then(console.log);
// thng from previous thngService reader invocation
thng.setDescription("Updated Thng description");

apiManager.thngService().thngUpdater(thng.getId(), thng).execute();
HTTP/1.1 200 OK
Content-Type: application/json

{
  "id": "UkYVKThN36Cfe4xACatserPs",
  "createdAt": 1484042877370,
  "updatedAt": 1497457027408,
  "name": "Truck #F893-44 - IDLE",
  "description": "Truck F893-44. Delivery truck for North England",
  "product": "U2qckwpNq3PEEMaaagPXxcVh",
  "identifiers": {
    "shortId": "UkYVKThN36Cfe4xACatserPs"
  },
  "batch": "U2EBHMxdAh6MTrSh4t26NqUf",
  "createdByTask": "UFhe5yE5Bd5Hesyphxb8Fydk"
}

Update Multiple Thngs

Using a suitable filter, it is possible to apply (PUT) an updated ThngDocument to multiple Thngs, providing they match the filter query parameter. For example, all Thngs that begin with "Smart" to contain the 'smart' tag.

PUT /thngs?filter=name%3DSmart*
Content-Type: application/json
Authorization: $OPERATOR_API_KEY

ThngDocument (subset)
curl -H "Content-Type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X PUT 'https://api.evrythng.com/thngs?filter=name%3DSmart*' \
  -d '{
    "tags": [ "smart" ]
  }'
const update = { 
  tags: [ "updated" ]
};

operator.thng().update(update, { 
  params: { 
    filter: 'name=Test*' 
  } 
}).then(console.log);

The update will be applied asynchronously, and the response will be 204 'No Content'.

HTTP/1.1 204 No Content

Delete a Thng

Delete a single Thng by ID. This action cannot be undone.

DELETE /thngs/:thngId
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X DELETE 'https://api.evrythng.com/thngs/UF6qwDbaVDPwQKRwagmnqyqs'
const thngId = 'UF6qwDbaVDPwQKRwagmnqyqs';

operator.thng(thngId).delete().then(() => console.log('Deleted!'));
String thngId = "UF6qwDbaVDPwQKRwagmnqyqs";

apiManager.thngService().thngDeleter(thngId).execute();
HTTP/1.1 200 OK

Thng Location

As Thngs model real-world resources, they have a special property called a location to keep track of the actual position over time.

LocationDocument Data Model

.position (object)
    A GeoJSON Point object. The coordinate order is longitude, 
    then latitude.

.createdAt (integer, read-only)
    Timestamp when the resource was created.

.timestamp (integer, read-only)
    The time the location update occurred, or filled 
    automatically by the Platform if omitted.

.longitude (number)
    The longitude.

.latitude (number)
    The latitude.

.place (string)
    The place ID.

.scopes (ScopesDocument)
{
  "type": "object",
  "description": "An object representing a location.",
  "properties": {
    "position": {
      "type": "object",
      "description": "A GeoJSON Point object. The coordinate order is longitude, then latitude.",
      "properties": {
        "type": {
          "type": "string",
          "description": "The type of the point.",
          "enum": [ "Point" ]
        },
        "coordinates": {
          "type": "array",
          "description": "The point coordinates",
          "items": { "type": "number" }
        }
      }
    },
    "createdAt": {
      "type": "integer",
      "description": "Timestamp when the resource was created.",
      "readOnly": true
    },
    "timestamp": {
      "type": "integer",
      "description": "The time the location update occurred, or filled automatically by the Platform if omitted.",
      "readOnly": true
    },
    "longitude": {
      "type": "number",
      "description": "The longitude."
    },
    "latitude": {
      "type": "number",
      "description": "The latitude."
    },
    "place": {
      "type": "string",
      "description": "The place ID."
    },
    "scopes": { "$ref": "ScopesDocument" }
  }
}
{
  "latitude": -37,
  "longitude": -179.3,
  "position": {
    "type": "Point",
    "coordinates": [
      -179.3,
      -37
    ]
  }
}

See also: ScopesDocument


Read a Thng's Location History

To access all the location updates for a thng, send a GET to the /location endpoint of the Thng.

GET /thngs/:thngId/location
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X GET 'https://api.evrythng.com/thngs/U2a4AsGtqerbcWD6qhYGfb5d/location'
const thngId = 'U2a4AsGtqerbcWD6qhYGfb5d';

// in a single request
user.thng(thngId).location().read().then(console.log);

// or on the resource itself
user.thng(thngId).read().then((thng) => {
  thng.location().read().then(console.log);
});
HTTP/1.1 200 OK
Content-Type: application/json

[
  {
    "timestamp":1347028945712,
    "createdAt":1347028945712,
    "position": {
      "type": "Point",
      "coordinates": [18.3,-36.4]
    }
  }
]

Update a Thng's Location

Just like properties, the location of a Thng can be updated at any time via the /location endpoint of the Thng. The payload must always be an array of LocationDocument objects, which allows you to send several locations (with different timestamps each) in a single request.

This is useful when a device wants to update a path cached over a certain period of time at once. An example would be a taxi that updates the whole set of locations it has been through once per hour instead of sending each GPS sensor reading individually.

Instead of specifying the location itself, it is also possible to specify a place to set the Thng's location.

PUT /thngs/:thngId/location
Content-Type: application/json
Authorization: $OPERATOR_API_KEY

[ LocationDocument, ... ]
curl -i -H "Content-Type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X PUT 'https://api.evrythng.com/thngs/U2wm8b42MVNxTCX6MYDrRpyd/location' \
  -d '[{
      "position": {
        "type": "Point",
        "coordinates": [ -17.3, 36 ]
      }
    }, {
      "timestamp": 1347028945711,
      "position": {
        "type": "Point",
        "coordinates": [ -179.3, -37 ]
      }
    }]'
const thngId = 'U2wm8b42MVNxTCX6MYDrRpyd';

const update = [{
  position: {
    type: 'Point',
    coordinates: [ -17.3, 36 ]
  }
}, {
  timestamp: 1347028945711,
  position: {
    type: 'Point',
    coordinates: [ -179.3, -37 ]
  }
}];

// in a single request
user.thng(thngId).location().update(update).then(console.log);

// or on the resource itself
user.thng(thngId).read().then((thng) => {
  thng.location().update(update).then(console.log);
});
HTTP/1.1 200 OK
Content-Type: application/json

[
  {
    "timestamp":1347550210375,
    "createdAt":1347028945712,
    "position": {
      "type": "Point",
      "coordinates": [-17.3, 36]
    }
  }, {
    "timestamp": 1347028945712,
    "createdAt": 1347028945712,
    "position": {
      "type": 'Point',
      "coordinates": [ -179.3, -37 ]
    }
  } 
]

Delete a Thng's Location History

Similar to properties, location updates are not individually identified, therefore the only way to delete them is by specifying a point in time before which all the updates will be deleted as a query param: ?to=Timestamp.

Important

If the ?to parameter is not specified, ALL location updates will be deleted for the Thng specified.

DELETE /thngs/:thngId/location[?to=Timestamp]
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X DELETE 'https://api.evrythng.com/thngs/U2wm8b42MVNxTCX6MYDrRpyd/location'
const thngId = 'U2wm8b42MVNxTCX6MYDrRpyd';

// in a single request
operator.thng(thngId).location().delete();

// or on the resource itself
operator.thng(thngId).read().then((thng) => thng.location().delete());
HTTP/1.1 200 OK

Create a Thng's Redirection

POST /thngs/:thngId/redirector
Content-Type: application/json
Authorization: $OPERATOR_API_KEY

RedirectionDocument
curl -H "Content-Type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X POST 'https://api.evrythng.com/thngs/Uk6qwgSReXPat5awagfNpHsk/redirector' \
  -d '{
    "defaultRedirectUrl": "https://google.com/{thngId}"
  }'
const operatorApiKey = '$OPERATOR_API_KEY';
const thngId = 'Uk6qwgSReXPat5awagfNpHsk';

EVT.api({
  url: `/thngs/${thngId}/redirector`,
  method: 'POST',
  authorization: operatorApiKey,
  data: { 
    defaultRedirectUrl: 'https://example.com/{thngId}'
  }
}).then(console.log);
HTTP/1.1 201 Created
Content-Type: application/json

{
  "createdAt": 1496668317836,
  "updatedAt": 1496668348257,
  "defaultRedirectUrl": "https://google.com/Uk6qwgSReXPat5awagfNpHsk",
  "evrythngUrl": "https://api.evrythng.com/thngs/Uk6qwgSReXPat5awagfNpHsk",
  "shortDomain": "tn.gg",
  "shortId": "799yeEi4qp",
  "hits": 0
}

Read a Thng's Redirection

GET /thngs/:thngId/redirector
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X GET 'https://api.evrythng.com/thngs/Uk6qwgSReXPat5awagfNpHsk/redirector'
const operatorApiKey = '$OPERATOR_API_KEY';
const thngId = 'Uk6qwgSReXPat5awagfNpHsk';

EVT.api({
  url: `/thngs/${thngId}/redirector`,
  authorization: operatorApiKey
}).then(console.log);
HTTP/1.1 200 OK
Content-Type: application/json

{
  "createdAt": 1496664729327,
  "updatedAt": 1496664729327,
  "defaultRedirectUrl": "https://google.com",
  "evrythngUrl": "https://api.evrythng.com/thngs/Uk6qwgSReXPat5awagfNpHsk",
  "shortDomain": "tn.gg",
  "shortId": "39aZNy4g",
  "hits": 0
}

Update a Thng's Redirection

PUT /thngs/:thngId/redirector
Content-Type: application/json
Authorization: $OPERATOR_API_KEY

RedirectionDocument
curl -H "Content-Type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X PUT 'https://api.evrythng.com/thngs/Uk6qwgSReXPat5awagfNpHsk/redirector' \
  -d '{
    "defaultRedirectUrl": "https://google.com/{thngId}"
  }'
const operatorApiKey = '$OPERATOR_API_KEY';
const thngId = 'Uk6qwgSReXPat5awagfNpHsk';

EVT.api({
  url: `/thngs/${thngId}/redirector`,
  method: 'PUT',
  authorization: operatorApiKey,
  data: { 
    defaultRedirectUrl: 'https://example.com/{thngId}'
  }
}).then(console.log);
HTTP/1.1 201 Created
Content-Type: application/json

{
  "createdAt": 1496668317836,
  "updatedAt": 1496668348257,
  "defaultRedirectUrl": "https://google.com/Uk6qwgSReXPat5awagfNpHsk",
  "evrythngUrl": "https://api.evrythng.com/thngs/Uk6qwgSReXPat5awagfNpHsk",
  "shortDomain": "tn.gg",
  "shortId": "799yeEi4qp",
  "hits": 0
}

Delete a Thng's Redirection

DELETE /thngs/:thngId/redirector
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X DELETE 'https://api.evrythng.com/thngs/Uk6qwgSReXPat5awagfNpHsk/redirector' 
const operatorApiKey = '$OPERATOR_API_KEY';
const thngId = 'Uk6qwgSReXPat5awagfNpHsk';

EVT.api({
  url: `/thngs/${thngId}/redirector`,
  authorization: operatorApiKey,
  method: 'DELETE'
}).then(() => console.log('Deleted!'));
HTTP/1.1 200 OK
Content-Type: application/json

Thngs as Devices

In the connected devices world, devices can be autonomous and behave as sensors and/or actuators, communicating directly with the EVRYTHNG platform as an actor. A Thng can be registered as a device by creating a Device API key, with which the device can manage the details of its corresponding Thng in the EVRYTHNG platform.


Create Device API Key

Creates an API Key for a given Thng (Device API key), which is useful when an embedded device needs to talk to its Thng in the Platform (update its properties, subscribe to actions, etc.).

A Device API key can only be created by Application Users or Operators. If called multiple times for the same Thng, the existing API key will be returned (a new one will not be generated). If you want to create a new API key for a Thng, you must delete the existing one first (see below).

POST /auth/evrythng/thngs
Content-Type: application/json
Authorization: $OPERATOR_API_KEY

{
  "thngId": "UBPWKPA3qqtyrPe7QQm9hrrf"
}
curl -i -H "Content-Type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X POST 'https://api.evrythng.com/auth/evrythng/thngs' \
  -d '{ 
    "thngId": "UBPWKPA3qqtyrPe7QQm9hrrf" 
  }'
const thngId = 'UBPWKPA3qqtyrPe7QQm9hrrf';
const operatorApiKey = '$OPERATOR_API_KEY';

EVT.api({
  url: '/auth/evrythng/thngs',
  method: 'POST',
  authorization: operatorApiKey,
  data: {
    thngId: thngId
  }
}).then(console.log);
HTTP/1.1 201 OK
Content-Type: application/json

{
  "thngId": "UBPWKPA3qqtyrPe7QQm9hrrf",
  "thngApiKey": "PEjcPTFS8Cr4CZenEg312234ialp1CiM7weR5Q..."
}

Note

Device API Keys are currently created on a per-project basis. This means that a Device API Key created with a Thng with no project scope will not be available if the Thng is later moved to a different project scope. Similarly, if a Thng already in a given project is scoped to other projects, new Device API Keys will need to be created for each new project.

In the future, Device API Keys will inherit the scope of the Thng they represent. When this change occurs this page will be updated.


Read Device API Key

Can be done by Application Users or Operators that have access rights to the Thng.

GET /auth/evrythng/thngs/:thngId
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X GET 'https://api.evrythng.com/auth/evrythng/thngs/UBPWKPA3qqtyrPe7QQm9hrrf'
const thngId = 'UBPWKPA3qqtyrPe7QQm9hrrf';
const operatorApiKey = '$OPERATOR_API_KEY';

EVT.api({
  url: `/auth/evrythng/thngs/${thngId}`,
  authorization: operatorApiKey
}).then(console.log);
HTTP/1.1 200 OK
Content-Type: application/json

{
  "thngId": "UBPWKPA3qqtyrPe7QQm9hrrf",
  "thngApiKey": "PEjcPTFS8CrCZenEg1234ialpodqSCiM7weR5Q..."
}

Delete Device API Key

This revokes access for a given Device API key. Can be done by Application Users or Operator that have access rights to the Thng.

DELETE /auth/evrythng/thngs/:thngId
Content-Type: application/json
Authorization: $OPERATOR_API_KEY
curl -i -H "Content-Type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X DELETE 'https://api.evrythng.com/auth/evrythng/thngs/UBPWKPA3qqtyrPe7QQm9hrrf'
const thngId = 'UBPWKPA3qqtyrPe7QQm9hrrf';
const operatorApiKey = '$OPAERATOR_API_KEY';

EVT.api({
  url: `/auth/evrythng/thngs/${thngId}`,
  method: 'DELETE',
  authorization: operatorApiKey
}).then(() => console.log('Deleted!'));
HTTP/1.1 200 OK

Using Evrythng.js in a Device

To make it easier for Javascript developers to write embedded device applications, we created the EVT.Device scope. Remember that conceptually a device is a Thng. Therefore the EVT.Device scope has direct access to everything related to the Thng (actions, properties, locations) - see _API Scope and Key Permissions for more information.

Example: evrythng.js

const device = new EVT.Device({
  apiKey: deviceApiKey,  // The API key created above
  id: thngId
});
device.update({
  customFields: {
    foo: 'bar'
  }
}).then(console.log);
// create
device.property().create([{
  key: 'temperature',
  value: 30
}]).then(console.log);

// read
device.property('temperature').read().then(console.log);

// update
device.property('temperature').update(52);
device.property('status').update(true);

// bulk update
device.property().update({
  temperature: 52,
  status: true
});

device.property('temperature').update([{
  value: 52,
  timestamp: 143022888800
}, {
  value: 70,
  timestamp: 143022890000
}]);

See Properties for more examples on using the property resource.

// create
device.action('scans').create().then(console.log);

// read
device.action('all').read().then(console.log);

See Actions for more examples on using the /actions resource.

The Time API provides an alternative to NTP and other time services to get the current time. In the general case, we recommend using services and protocols especially designed for time-related operations, such as NTP as opposed to the Time API. When resources such as memory local time-keeping hardware are limited or unavailable, you may find it useful to call our Time API to obtain the current time and perform local time-keeping from then on.

Please keep in mind that the time returned by the Time API is the time of our servers at the moment the request is processed. It does not take the round-trip time into account.


API Status
Stable: time.evrythng.com/time


TimeInfoDocument Data Model

.timestamp (integer, read-only)
    Number of milliseconds since the Epoch.

.localTime (string, read-only)
    The date, time and zone offset specified as per ISO 8601.

.nextChange (integer, read-only)
    when the next change in offset is expected.

.offset (integer, read-only)
    The current offset in milliseconds of the timezone compared 
    to UTC.
{
  "type": "object",
  "description": "Object describing the current time, with timezone info.",
  "properties": {
    "timestamp": {
      "type": "integer",
      "description": "Number of milliseconds since the Epoch.",
      "readOnly": true
    },
    "localTime": {
      "type": "string",
      "description": "The date, time and zone offset specified as per ISO 8601.",
      "readOnly": true
    },
    "nextChange": {
      "type": "integer",
      "description": "when the next change in offset is expected.",
      "readOnly": true
    },
    "offset": {
      "type": "integer",
      "description": "The current offset in milliseconds of the timezone compared to UTC.",
      "readOnly": true
    }
  }
}
{
  "localTime": "2017-11-17T12:12:00.696Z",
  "nextChange": 1521939600000,
  "offset": 0,
  "timestamp": 1510920720696
}

Query Parameters

  • ?tz - String
    The time zone to retrieve the local time for, as per TZDB.

The nextChange field can be used when the client is unable to compute the local time correctly. Instead of querying the Time API periodically, this field can be used by the client to know when it's relevant to query the Time API again.

Note that time zones like GMT+1 don't know anything about daylight saving time (DST). Usually, these are the cities that move from one time zone to another at specific times in the year. Hence if you want to track DST with the time API, use a city time zone, such as "Europe/Zurich" as opposed to "Etc/GMT+1".

Note that a Timestamp is by definition always in UTC, thus the timestamp field is not affected by the tz parameter. This means that nextChange is in UTC as well.


Read the current time

Read the current server time.

Note

This request is anonymous and must be sent with no Authorization header over HTTP.

GET /time
curl -X GET 'http://time.evrythng.com/time'
HTTP/1.1 200 OK
Accept: application/json

{
  "timestamp": 1428571989696
}

Read timezone time

Read the time for a specified timezone.

GET /time?tz=Europe%2FZurich
curl -X GET 'http://time.evrythng.com/time?tz=Europe%2FZurich'
HTTP/1.1 200 OK
Accept: application/json

{
  "timestamp" : 1428576687786,
  "offset" : 7200000,
  "localTime" : "2015-04-09T12:51:27.786+02:00",
  "nextChange" : 1445734800000
}
Suggest Edits

Enterprise API Introduction

 

The Enterprise API is a collection of APIs aimed at applications using the EVRYTHNG platform at a deeper level of integration, or on a larger scale such as beyond hundreds of ADIs. Functionality included here includes bulk ADI/action creation, rule-based logic, cloud integration, file storage, and OAuth integration.

See the Standard API Introduction for more basic information about how the API operates in general. That information relates to the Enterprise API as well.

Note

These APIs are only available to customers with an enterprise Platform subscription. Please get in touch to discuss enabling any of them on your account.

 

The Batch and Task APIs allows the generation of large quantities of Thngs quickly and easily. Creating large collections of Thngs works in the following manner:

  1. The Batch API is used to create a placeholder for a specific batch resource and associated metadata (the data that all Thngs in batch share), but not the Thngs themselves.

  2. Once you have created a batch object, you can populate it with Thngs using the Task API, where you can choose the parameters for the generation task such as the quantity of Thngs to generate, the product ID, input file name, and so on.

This page describes operations on the metadata of batches. For operations you can do on a batch refer to the Tasks API page.


API Status
Stable:
/batches
/batches/:batchId


BatchDocument Data Model

.id (string, read-only)
    The ID of this resource.

.createdAt (integer, read-only)
    Timestamp when the resource was created.

.updatedAt (integer, read-only)
    Timestamp when the resource was updated.

.name (string)
    Friendly name of this resource.

.description (string)
    Friendly description of this resource.

.status (string, read-only, one of 'EMPTY', 'IN_PROGRESS', 'COMPLETE', 'SEALED')
    The status of the batch.

.resourceCountsByProduct (object, read-only)
    Count of resources, grouped by product ID. Those with no 
    product come under `NO_PRODUCT`.

.identifiers (object)
    Various identifiers (EPC, ISBN, etc.) as a JSON object with 
    one or more key-value pairs.

.tags (array of string)
    Array of string tags associated with this resource.

.customFields (object)
    Object of case-sensititve key-value pairs of custom fields 
    associated with the resource.

.scopes (ScopesDocument)
{
  "type": "object",
  "description": "An object representing a Platform batch.",
  "properties": {
    "id": {
      "type": "string",
      "description": "The ID of this resource.",
      "readOnly": true
    },
    "createdAt": {
      "type": "integer",
      "description": "Timestamp when the resource was created.",
      "readOnly": true
    },
    "updatedAt": {
      "type": "integer",
      "description": "Timestamp when the resource was updated.",
      "readOnly": true
    },
    "name": {
      "type": "string",
      "description": "Friendly name of this resource."
    },
    "description": {
      "type": "string",
      "description": "Friendly description of this resource."
    },
    "status": {
      "type": "string",
      "description": "The status of the batch.",
      "enum": [ "EMPTY", "IN_PROGRESS", "COMPLETE", "SEALED" ],
      "readOnly": true
    },
    "resourceCountsByProduct": {
      "type": "object",
      "description": "Count of resources, grouped by product ID. Those with no product come under `NO_PRODUCT`.",
      "readOnly": true
    },
    "identifiers": {
      "type": "object",
      "description": "Various identifiers (EPC, ISBN, etc.) as a JSON object with one or more key-value pairs."
    },
    "tags": {
      "type": "array",
      "description": "Array of string tags associated with this resource.",
      "items": {
        "type": "string",
        "maxLength": 60
      }
    },
    "customFields": {
      "type": "object",
      "description": "Object of case-sensititve key-value pairs of custom fields associated with the resource."
    },
    "scopes": { "$ref": "ScopesDocument" }
  }
}
{
  "id": "U4asNCTB6GPEYMaaRYCwwBmd",
  "createdAt": 1510921508928,
  "customFields": {
    "region": "eu"
  },
  "tags": [
    "example",
    "batch"
  ],
  "updatedAt": 1510921536082,
  "name": "Example Batch",
  "description": "An example batch resource.",
  "identifiers": {
    "ean_13": "4389489790"
  },
  "status": "COMPLETE",
  "resourceCountsByProduct": {
    "NO_PRODUCT": {
      "thngs": 1,
      "urlBindings": 0
    }
  }
}

See also: ScopesDocument


Create a Batch

Batches are created by sending a POST request containing a JSON document to the /batches resource.

The Location header in the response returns the URL of the Batch resource that was created, which is always in the format /batches/:batchId, where batchId is the unique identifier of this batch assigned by the Platform.

POST /batches
Content-Type: application/json
Authorization: $OPERATOR_API_KEY

BatchDocument
curl -i -H "Content-Type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X POST "https://api.evrythng.com/batches" \
  -d '{
    "name": "Batch #R1-5",
    "identifiers": {
      "UPC": "027242784925"
    }
  }'
const batch = {
  name: "Batch #R1-5",
  identifiers: {
    UPC: "027242784925"
  }
};

operator.batch().create(batch).then(console.log);
Batch batch = new Batch();
batch.setName("Batch #R1-5");
apiManager.batchService().batchCreator(batch).execute();
HTTP/1.1 201 Created
Content-Type: application/json
Location: https://api.evrythng.com/batches/504766a7c8f205a0744243d2

{
  "id": "504766a7c8f205a0744243d2",
  "createdAt": 1346856615704,
  "updatedAt": 1346856615704,
  "name": "Batch #R1-5",
  "identifiers": {
    "UPC": "027242784925"
  },
  "status": "EMPTY"
}

Read All Batches

To retrieve the list of all batches in your account or project, simply do a GET on the /batches resource. The result may be paginated if there are more than 30 items. You can also use ?filter or ?ids query params to restrict the returned resources.

To retrieve a subset of all the batches you have created, simply use a filter. Filterable fields are: name, tags, identifiers, createdAt, and products.

GET /batches
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X GET 'https://api.evrythng.com/batches'
operator.batch().read().then(console.log);
List<Batch> batches = apiManager.batchService().batchesReader().execute();
for(Batch batch : batches) {
    System.out.println("Batch " + batch.getId() + " - " + batch.getName());
}
HTTP/1.1 200 OK
Content-Type: application/json

[
  {
    "id": "504766a7c8f205a0744243d2",
    "createdAt": 1346856615704,
    "updatedAt": 1346856615704,
    "name": "Batch #R1-5",
    "identifiers": {
      "UPC": "027242784925"
    },
    "status": "EMPTY"
  },
  {
    "id": "504766a7c8f205a0744861e5",
    "createdAt": 1526400111245,
    "updatedAt": 1526400923571,
    "name": "Batch #R1-7",
    "identifiers": {
      "UPC": "027242784946"
    },
    "status": "PENDING"
  }
]

Read a Batch

You can retrieve a batch document with a GET on the batch URL containing the batchId.

GET /batches/:batchId
Authorization: $OPERATOR_API_KEY

BatchDocument
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X GET "https://api.evrythng.com/batches/UFwm8bCUx7M2cpSdBQM8bfdn"
const batchId = 'UFwm8bCUx7M2cpSdBQM8bfdn';

operator.batch(batchId).read().then(console.log);
String batchId = "UFwm8bCUx7M2cpSdBQM8bfdn";

Batch batch = apiManager.batchService().batchReader(batchId).execute();
HTTP/1.1 200 OK
Content-Type: application/json

{
  "id": "UFwm8bCUx7M2cpSdBQM8bfdn",
  "createdAt": 1475587649371,
  "updatedAt": 1476450513026,
  "name": "Batch #R1-5",
  "status": "COMPLETE",
  "resourceCountsByProduct": {
    "NO_PRODUCT": {
      "thngs": 20,
      "urlBindings": 20
    }
  }
}

Update a Batch

You can update the data of a batch using any subset of a valid BatchDocument model.

Attention

The fields are entirely overwritten by the new values, so to add a tag for example you must submit the entire updated array with the new and old tags.

PUT /batches/:batchId
Content-Type: application/json
Authorization: $OPERATOR_API_KEY

BatchDocument (subset)
curl -i -H "Content-Type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X PUT 'https://api.evrythng.com/batches/UFwm8bCUx7M2cpSdBQM8bfdn' \
  -d '{
    "name": "Batch #R1-5 | 03-2015",
  }'
const batchId = 'UFwm8bCUx7M2cpSdBQM8bfdn';

const update = {
  name: 'Batch #R1-5 | 03-2015'
};

operator.batch(batchId).update(update).then(console.log);
String batchId = "UFwm8bCUx7M2cpSdBQM8bfdn";

Batch batch = apiManager.batchService().batchReader(batchId).execute();
batch.setName("Batch #R1-5 | 03-2015");
apiManager.batchService().batchUpdater(batchId, batch).execute();
HTTP/1.1 200 OK
Content-Type: application/json

{
  "id": "UFwm8bCUx7M2cpSdBQM8bfdn",
  "createdAt": 1346856615704,
  "updatedAt": 1346856615704,
  "name": "Batch #R1-5 | 03-2015",
  "identifiers": {
    "UPC": "027242784925"
  },
  "status": "EMPTY"
}

Delete a Batch

To delete a batch, simply send a DELETE request to the batch's URL.

Attention

Deleting the batch will not delete the tasks and ADIs in it, which you have to delete separately and manually.

DELETE /batches/:batchId
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X DELETE 'https://api.evrythng.com/batches/UFwm8bCUx7M2cpSdBQM8bfdn'
const batchId = 'UFwm8bCUx7M2cpSdBQM8bfdn';

operator.batch(batchId).delete().then(() => console.log('Deleted!'));
String batchId = "UFwm8bCUx7M2cpSdBQM8bfdn";

apiManager.batchService().batchDeleter(batchId).execute();
HTTP/1.1 200 OK

Running Tasks on a Batch

On its own, a batch describes metadata about a group of Thngs it will contain. To learn how to populate the batch with Thngs, see Tasks.

Suggest Edits

Cloud 2 Cloud

 

The EVRYTHNG Cloud 2 Cloud (C2C) Connector Framework is an extensible capability to integrate other third party cloud services with the EVRYTHNG Platform. Users can interact with devices/products/things that are connected to another cloud (e.g. Nest) from the EVRYTHNG Platform and vice versa. EVRYTHNG becomes the glue between different third party clouds and applications can leverage this to easily combine in their logical things from different platforms in a homogeneous way, and further enrich the end-user experience.


API Status
Stable:
/projects/:projectId/applications/:applicationId/connectors
/connectors
/connectors/:connectorName
/connectors/:connectorName/auth


Overview

To integrate a third party cloud with your EVRYTHNG account you have to:

  • Define the basic connector information (using the API defined below)

  • Define the authentication parameters for the third party cloud. This usually requires credentials for an OAuth authentication and authorization process that enables the connector instance to access your third party cloud account. The connector will provide a common endpoint to launch the authentication process from any application.

  • Create and manage a model that maps the properties of the "devices" in the third party cloud to the Thngs in the EVRYTHNG platform. The connector uses this mapping for various operations such as creation of Thngs, synchronization, monitoring and more.


Supported Third Party Clouds

Currently, the following clouds are supported:

  • Nest - Integrate with Nest smart devices to exchange data and trigger actions.

ConnectorDocument Data Model

.name (string, read-only, one of 'nest')
    The unique name of the connector.

.model (NestModelDocument)

.auth (OAuthDocument)

.displayName (string, read-only)
    Friendly name of the connector.

.url (string, read-only)
    The url of the 3rd party service.

.image (string, read-only)
    The location of an image associated with the 3rd party 
    service.

.authUrl (string, read-only)
    The authentication url for the 3rd party service.

.status (string, one of 'connected', 'disconnected')
    The status of the connector.
{
  "type": "object",
  "description": "An object describing a Cloud 2 Cloud Connector Framework connector.",
  "properties": {
    "name": {
      "type": "string",
      "description": "The unique name of the connector.",
      "readOnly": true,
      "enum": [ "nest" ]
    },
    "model": { "$ref": "NestModelDocument" },
    "auth": { "$ref": "OAuthDocument" },
    "displayName": {
      "type": "string",
      "description": "Friendly name of the connector.",
      "readOnly": true
    },
    "url": {
      "type": "string",
      "description": "The url of the 3rd party service.",
      "readOnly": true
    },
    "image": {
      "type": "string",
      "description": "The location of an image associated with the 3rd party service.",
      "readOnly": true
    },
    "authUrl": {
      "type": "string",
      "description": "The authentication url for the 3rd party service.",
      "readOnly": true
    },
    "status": {
      "type": "string",
      "description": "The status of the connector.",
      "enum": [ "connected", "disconnected" ]
    }
  }
}
{
  "name": "nest",
  "displayName": "Nest",
  "url": "http://www.nest.com",
  "image": "http://file.answcdn.com/answ-cld/image/upload/v1/tk/brand_image/c7d3fadd/c71c29fef0dd6c56a9b2777906bcf3245960c403.png",
  "authUrl": "https://api.evrythng.com/connectors/nest/auth",
  "model": {
    "structures": {
      "name": "Structure Name"
    },
    "devices": {
      "thermostats": {
        "name": "Thermostat Name"
      },
      "smoke_co_alarms": {
        "name": "Smoke Alarm Name"
      }
    }
  },
  "auth": {
    "type": "oauth",
    "oAuthUrl": "https://home.nest.com/login/oauth2",
    "tokenUrl": "https://api.home.nest.com/oauth2/access_token",
    "clientId": "6b5adf3d-4434-443b-b43a-756a9a7e58c6",
    "clientSecret": "27BCDgGp493BaB8GFHVmACBCB"
  }
}

See also: OAuthDocument, NestModelDocument


OAuthDocument Data Model

.clientId (string)
    The OAuth client ID for the 3rd party service.

.clientSecret (string)
    The OAuth client secret for the 3rd party service.

.type (string, one of 'oauth')
    The authentication type.

.oAuthUrl (string)
    The URL of the OAuth service.

.tokenUrl (string)
    The URL of the OAuth service access token.
{
  "type": "object",
  "description": "OAuth connector credentials",
  "properties": {
    "clientId": {
      "type": "string",
      "description": "The OAuth client ID for the 3rd party service.",
      "minLength": 36,
      "maxLength": 36
    },
    "clientSecret": {
      "type": "string",
      "description": "The OAuth client secret for the 3rd party service.",
      "minLength": 25,
      "maxLength": 25
    },
    "type": {
      "type": "string",
      "description": "The authentication type.",
      "enum": [ "oauth" ]
    },
    "oAuthUrl": {
      "type": "string",
      "description": "The URL of the OAuth service."
    },
    "tokenUrl": {
      "type": "string",
      "description": "The URL of the OAuth service access token."
    }
  }
}

Create a Connector

To enable a third party integration for a specific application, we need to POST a connector model describing the configuration of the connection with the third party cloud. Only the Operator can enable connectors in an application. For Nest connectors, the auth credentials can be found in the Nest Developers console.

POST /projects/:projectId/applications/:applicationId/connectors
Content-Type: application/json
Authorization: $OPERATOR_API_KEY

{
  "name": "nest",
  "model": {
    "structures": {
      "name": "Structure Name"
    },
    "devices": {
      "thermostats": {
        "name": "Thermostat Name"
      },
      "smoke_co_alarms": {
        "name": "Smoke Alarm Name"
      }
    }
  },
  "auth": {
    "clientId": "6b5adf3d-4434-443b-b43a-756a9a7e58c6",
    "clientSecret": "27BCDgGp493BaB8GFHVmACBCB"
  }
}
curl -i -H "Content-Type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X POST 'https://api.evrythng.com/projects/U2meqbNWegsaQKRRaDUmpssr/applications/UF3Vqb7D6G8EhMwaRYgQ2pFc/connectors'
  -d '{
  "name": "nest",
  "model": {
    "structures": {
      "name": "Structure Name"
    },
    "devices": {
      "thermostats": {
        "name": "Thermostat Name"
      },
      "smoke_co_alarms": {
        "name": "Smoke Alarm Name"
      }
    }
  },
  "auth": {
    "clientId": "6b5adf3d-4434-443b-b43a-756a9a7e58c6",
    "clientSecret": "27BCDgGp493BaB8GFHVmACBCB"
  }
}'
const operatorApiKey = '$OPERATOR_API_KEY';
const projectId = 'U3fAq5MDBgswQpwawXgK2pSr';
const applicationId = 'UmH2xEfGMGsEEqRaREtn3eCq';

const connector = {
  name: 'nest',
  model: {
    structures: { name: 'Structure Name' },
    devices: {
      thermostats: { name: 'Thermostat Name' },
      smoke_co_alarms: { name: 'Smoke Alarm Name' }
    }
  },
  auth: {
    clientId: '6b5adf3d-4434-443b-b43a-756a9a7e58c6',
    clientSecret: '27BCDgGp493BaB8GFHVmACBCB'
  }
};

EVT.api({
  url: `/projects/${projectId}/applications/${applicationId}/connectors`,
  authorization: operatorApiKey,
  method: 'POST',
  data: connector
}).then(console.log);
HTTP/1.1 201 Created
Content-type: application/json

{
  "name": "nest",
  "displayName": "Nest",
  "url": "https://www.nest.com",
  "image": "https://file.answcdn.com/answ-cld/image/upload/v1/tk/brand_image/c7d3fadd/c71c29fef0dd6c56a9b2777906bcf3245960c403.png",
  "authUrl": "https://api.evrythng.com/connectors/nest/auth",
  "model": {
    "structures": {
      "name": "Structure Name"
    },
    "devices": {
      "thermostats": {
        "name": "Thermostat Name"
      },
      "smoke_co_alarms": {
        "name": "Smoke Alarm Name"
      }
    }
  },
  "auth": {
    "type": "oauth",
    "oAuthUrl": "https://home.nest.com/login/oauth2",
    "tokenUrl": "https://api.home.nest.com/oauth2/access_token",
    "clientId": "6b5adf3d-4434-443b-2h2f-756a9a7e58c6",
    "clientSecret": "27BC2Dg5Gp1aB83GFHVmACBCB"
  }
}

Possible Error Codes Returned

Code
Description
Meaning

404

Not found

The application was not found;
No supported connector was found.

400

Bad request

No name was provided;
No model was provided;
No auth was provided;
The model provided is invalid;
The auth provided is invalid.

409

Conflict

The connector has already been enabled.


Read all Connectors

Returns a list of all connectors. The result may be paginated if there are more than 30 items.

GET /projects/:projectId/applications/:applicationId/connectors
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X GET 'https://api.evrythng.com/projects/U2meqbNWegsaQKRRaDUmpssr/applications/UF3Vqb7D6G8EhMwaRYgQ2pFc/connectors'
const operatorApiKey = '$OPERATOR_API_KEY';
const projectId = 'U3fAq5MDBgswQpwawXgK2pSr';
const applicationId = 'UmH2xEfGMGsEEqRaREtn3eCq';

EVT.api({
  url: `/projects/${projectId}/applications/${applicationId}/connectors`,
  authorization: operatorApiKey
}).then(console.log);
HTTP/1.1 200 Ok
Content-type: application/json

[
  {
    "name": "nest",
    "displayName": "Nest",
    "url": "https://www.nest.com",
    "image": "https://file.answcdn.com/answ-cld/image/upload/v1/tk/brand_image/c7d3fadd/c71c29fef0dd6c56a9b2777906bcf3245960c403.png",
    "authUrl": "https://api.evrythng.com/connectors/nest/auth",
    "model": {
      "structures": {
        "name": "Structure Name"
      },
      "devices": {
        "thermostats": {
          "name": "Thermostat Name"
        },
        "smoke_co_alarms": {
          "name": "Smoke Alarm Name"
        }
      }
    },
  	"auth": {
      "type": "oauth",
      "oAuthUrl": "https://home.nest.com/login/oauth2",
      "tokenUrl": "https://api.home.nest.com/oauth2/access_token",
      "clientId": "6b5adf3d-4434-443b-2h2f-756a9a7e58c6",
    	"clientSecret": "27BC2Dg5Gp1aB83GFHVmACBCB"
    }
  }
]

Read a Connector

Returns the connector description when specified by ID.

GET /projects/:projectId/applications/:applicationId/connectors/:connectorName
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X GET 'https://api.evrythng.com/projects/U2meqbNWegsaQKRRaDUmpssr/applications/UF3Vqb7D6G8EhMwaRYgQ2pFc/connectors/nest'
const operatorApiKey = '$OPERATOR_API_KEY';
const projectId = 'U3fAq5MDBgswQpwawXgK2pSr';
const applicationId = 'UmH2xEfGMGsEEqRaREtn3eCq';
const connectorName = 'nest';

EVT.api({
  url: `/projects/${projectId}/applications/${applicationId}/connectors/$connectorName}`,
  authorization: operatorApiKey
}).then(console.log);
HTTP/1.1 200 Ok
Content-type: application/json

{
  "name": "nest",
  "displayName": "Nest",
  "url": "https://www.nest.com",
  "image": "https://file.answcdn.com/answ-cld/image/upload/v1/tk/brand_image/c7d3fadd/c71c29fef0dd6c56a9b2777906bcf3245960c403.png",
  "authUrl": "https://api.evrythng.com/connectors/nest/auth",
  "model": {
    "structures": {
      "name": "Structure Name"
    },
    "devices": {
      "thermostats": {
        "name": "Thermostat Name"
      },
      "smoke_co_alarms": {
        "name": "Smoke Alarm Name"
      }
    }
  },
  "auth": {
    "type": "oauth",
    "oAuthUrl": "https://home.nest.com/login/oauth2",
    "tokenUrl": "https://api.home.nest.com/oauth2/access_token",
    "clientId": "6b5adf3d-4434-443b-2h2f-756a9a7e58c6",
    "clientSecret": "27BC2Dg5Gp1aB83GFHVmACBCB"
  }
}

Possible Error Codes Returned

Code
Description
Meaning

404

Not found

The connector has not been enabled


Update a Connector

Update a connector with new data.

Note

Not all the fields of a connector can be updated. If the authentication parameters changed, this is considered to be a new connector and it must to be created separately as a new resource. Only the Operator can modify the connector information.

PUT /projects/:projectId/applications/:applicationId/connectors/:connectorName
Content-Type: application/json
Authorization: $OPERATOR_API_KEY

ConnectorDocument (subset)
curl i -H "Content-Type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X PUT 'https://api.evrytng.com/projects/U2meqbNWegsaQKRRaDUmpssr/applications/UF3Vqb7D6G8EhMwaRYgQ2pFc/connectors/nest'
  -d '{
    "model": {
      "structures": {
        "name": "New Structure Name"
      }
    }
  }'
const operatorApiKey = '$OPERATOR_API_KEY';
const projectId = 'U3fAq5MDBgswQpwawXgK2pSr';
const applicationId = 'UmH2xEfGMGsEEqRaREtn3eCq';
const connectorName = 'nest';

const update = {
  model: {
    structures: { name: 'New Structure Name' }
  }
};

EVT.api({
  url: `/projects/${projectId}/applications/${applicationId}/connectors/${connectorName}`,
  authorization: operatorApiKey,
  method: 'PUT',
  data: update
}).then(console.log);
HTTP/1.1 200 Ok
Content-type: application/json

{
  "name": "nest",
  "displayName": "Nest",
  "url": "https://www.nest.com",
  "image": "https://file.answcdn.com/answ-cld/image/upload/v1/tk/brand_image/c7d3fadd/c71c29fef0dd6c56a9b2777906bcf3245960c403.png",
  "authUrl": "https://api.evrythng.com/connectors/pest/auth",
  "model": {
    "structures": {
      "name": "New Structure Name"
    }
  },
  "auth": {
    "type": "oauth",
    "oAuthUrl": "https://home.nest.com/login/oauth2",
    "tokenUrl": "https://api.home.nest.com/oauth2/access_token",
    "clientId": "6b5adf3d-4434-443b-2h2f-756a9a7e58c6",
    "clientSecret": "27BC2Dg5Gp1aB83GFHVmACBCB"
  }
}

Possible Error Codes

Code
Description
Meaning

400

Bad request

One or more fields provided in the payload cannot be updated;
The model provided is invalid.

404

Not found

The connector has not been enabled


Delete a Connector

Deletes a connector for an application.

Warning

Deleting a connector for an application will delete all the Thngs that were imported from the third party cloud.

DELETE /projects/:projectId/applications/:applicationId/connectors/:connectorName
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X DELETE 'https://api.evrythng.com/projects/U2meqbNWegsaQKRRaDUmpssr/applications/UF3Vqb7D6G8EhMwaRYgQ2pFc/connectors/nest'
const operatorApiKey = '$OPERATOR_API_KEY';
const projectId = 'U3fAq5MDBgswQpwawXgK2pSr';
const applicationId = 'UmH2xEfGMGsEEqRaREtn3eCq';
const connectorName = 'nest';

EVT.api({
  url: `/projects/${projectId}/applications/${applicationId}/connectors/${connectorName}`,
  authorization: operatorApiKey,
  method: 'DELETE'
}).then(console.log);
HTTP/1.1 200 Ok

Possible Error Codes Returned

Code
Description
Meaning

404

Not found

The connector has not been created, or is not enabled


Read Connectors Available for User

Returns list of connectors that are enabled for a specific Application User whose API key authenticates the request.

GET /connectors
Authorization: $APPLICATION_USER_API_KEY
curl -H "Authorization: $APPLICATION_USER_API_KEY" \
  -X GET 'https://api.evrythng.com/connectors'
const applicationUserApiKey = '$APPLICATION_USER_API_KEY';
const projectId = 'U3fAq5MDBgswQpwawXgK2pSr';
const applicationId = 'UmH2xEfGMGsEEqRaREtn3eCq';

EVT.api({
  url: '/connectors',
  authorization: applicationUserApiKey
}).then(console.log);
HTTP/1.1 200 Ok
Content-type: application/json

[
  {
    "name": "nest",
    "displayName": "Nest",
    "url": "https://www.nest.com",
    "image": "https://file.answcdn.com/answ-cld/image/upload/v1/tk/brand_image/c7d3fadd/c71c29fef0dd6c56a9b2777906bcf3245960c403.png",
    "authUrl": "https://api.evrythng.com/connectors/nest/auth",
    "status": "disconnected"
  }
]

Read a Connector as an Application User

Returns the connector description for the connector with name connectorName.

GET /connectors/:connectorName
Authorization: $APPLICATION_USER_API_KEY
curl -H "Authorization: $APPLICATION_USER_API_KEY" \
  -X GET 'https://api.evrythng.com/connectors/nest'
const applicationUserApiKey = '$APPLICATION_USER_API_KEY';
const projectId = 'U3fAq5MDBgswQpwawXgK2pSr';
const applicationId = 'UmH2xEfGMGsEEqRaREtn3eCq';
const connectorName = 'nest';

EVT.api({
  url: `/connectors/${connectorName}`,
  authorization: applicationUserApiKey
}).then(console.log);
HTTP/1.1 200 Ok
Content-type: application/json

{
  "name": "nest",
  "displayName": "Nest",
  "url": "https://www.nest.com",
  "image": "https://file.answcdn.com/answ-cld/image/upload/v1/tk/brand_image/c7d3fadd/c71c29fef0dd6c56a9b2777906bcf3245960c403.png",
  "authUrl": "https://api.evrythng.com/connectors/nest/auth",
  "status": "disconnected"
}

Possible Error Codes Returned

Code
Description
Meaning

404

Not found

The connector is not available


Authenticate With the 3rd Party Service

This endpoint is provided as a generic access to the authentication process. A successful request is redirected to the 3rd Party service's authentication mechanism. The URL is provided as well in the connector description.

GET /connectors/:connectorName/auth
Authorization: $APPLICATION_USER_API_KEY
curl -H "Authorization: $APPLICATION_USER_API_KEY" \
  -X GET 'https://api.evrythng.com/connectors/nest/auth'

Check the Location header for the redirection URL.

HTTP/1.1 301 Moved Permanently
location: https://home.nest.com/login/oauth2?client_id=df767635-fafa-afda-87ac-47fdaabcbc43&state=590af124dbdb0a1c00f22dd7

Possible Error Codes Returned

Code
Description
Meaning

404

Not found

The connector has not been enabled


Authenticate with an Existing Token

If an OAuth token for the 3rd party service has already been obtained, it can be used to shorten the authentication process.

POST /connectors/:connectorName/auth/token
Authorization: $APPLICATION_USER_API_KEY

{
  "access_token": "c.Kd7ahAwGn9wcZfRKEFqKlTpNU3ouQ...",
  "expires_in": 315360000
}
curl -H "Content-Type: application/json" \
  -H "Authorization: $APPLICATION_USER_API_KEY" \
  -X POST 'https://api.evrythng.com/connectors/nest/auth/token' \
  -d '{
    "access_token": "c.Kd7ahAwGn9wcZfRKEFqKlTpNU3ouQAwuujhJAGkVOBvlXCpOIyWacgi1zzsdjFeGbZivn6tVlSn856UpVMdTh8llKJLM8taMO4Hwr7YZB0Cm85uvOy0jQ7SOTnWJLEf5pFif51iGVJ8SKSBM",
    "expires_in": 315360000
  }'
HTTP/1.1 202 Accepted

Unlink User From a Connector

Application Users who want to revoke access to their third party cloud connector and remove all the information from EVRYTHNG can do so by invoking this endpoint.

Important

This will delete the connection between the clouds and remove any information and history from the users' Thngs that was imported from the third party cloud.

GET /connectors/:connectorName/auth
Authorization: $APPLICATION_USER_API_KEY
curl -H "Authorization: application/json" \
  -X GET 'https://api.evrythng.com/connectors/nest/auth'
HTTP/1.1 200 OK

Possible Error Codes Returned

Code
Description
Meaning

404

Not found

The connector has not been enabled;
Connector is not found for this user.

The File API manages storage and retrieval of files. Only Operators can perform these requests. An EVRYTHNG file resource only contains the metadata for the file (name, type, etc.). The actual file itself is uploaded separately as part of the workflow detailed below.

The process for storing a file within the Platform consists of two steps:

  1. Creating a FileDocument metadata resource detailing the file's format.
  2. Uploading the file itself to the URL specified in the created FileDocument in a separate request.

API Status
Stable:
/files
/files/:fileId


FileDocument Data Model

A FileDocument holds information about a file stored on a remote platform. This includes signed upload and download URLs for a file, and also whether the file has 'private' or 'public' access on remote storage.

.id (string, read-only)
    The ID of this resource.

.createdAt (integer, read-only)
    Timestamp when the resource was created.

.updatedAt (integer, read-only)
    Timestamp when the resource was updated.

.tags (array of string)
    Array of string tags associated with this resource.

.uploadUrl (string, read-only)
    The URL to upload file data to with an HTTP `PUT` request. 
    Will expire after 30 mins.

.contentUrl (string, read-only)
    The URL the file is available at for download.

.name (string)
    Name of the file. This is unique to the account, allowing a 
    file to be overwritten.

.type (string)
    The MIME type of the file. Must match the Content-Type 
    header used when uploading the file content.

.privateAccess (boolean)
    A boolean flag indicating whether a file should have 
    `private` or `public` access on remote storage.

.size (integer)
    The size of the uploaded file in bytes.

.scopes (ScopesDocument)
{
  "type": "object",
  "description": "An object containing remote file metadata.",
  "properties": {
    "id": {
      "type": "string",
      "description": "The ID of this resource.",
      "readOnly": true
    },
    "createdAt": {
      "type": "integer",
      "description": "Timestamp when the resource was created.",
      "readOnly": true
    },
    "updatedAt": {
      "type": "integer",
      "description": "Timestamp when the resource was updated.",
      "readOnly": true
    },
    "tags": {
      "type": "array",
      "description": "Array of string tags associated with this resource.",
      "items": {
        "type": "string",
        "maxLength": 60
      }
    },
    "uploadUrl": {
      "type": "string",
      "description": "The URL to upload file data to with an HTTP `PUT` request. Will expire after 30 mins.",
      "readOnly": true
    },
    "contentUrl": {
      "type": "string",
      "description": "The URL the file is available at for download.",
      "readOnly": true
    },
    "name": {
      "type": "string",
      "description": "Name of the file. This is unique to the account, allowing a file to be overwritten."
    },
    "type": {
      "type": "string",
      "description": "The MIME type of the file. Must match the Content-Type header used when uploading the file content."
    },
    "privateAccess": {
      "type": "boolean",
      "description": "A boolean flag indicating whether a file should have `private` or `public` access on remote storage."
    },
    "size": {
      "type": "integer",
      "description": "The size of the uploaded file in bytes."
    },
    "scopes": { "$ref": "ScopesDocument" }
  }
}
{
  "id": "UkHsQB7C6msEhqaaR2tsQbWh",
  "createdAt": 1487860949665,
  "updatedAt": 1487860949665,
  "uploadUrl": "https://s3.amazonaws.com/evtcdn_02/2/uf/UhpHrg39QCy9dsSddN8xhwnb/example-file.js?...",
  "contentUrl": "https://s3.amazonaws.com/evtcdn_02/2/uf/UhpHrg39QCy9dsSddN8xhwnb/example-file.js",
  "size": 35862,
  "name": "example-file.js",
  "type": "application/javascript",
  "privateAccess": false
}

See also: ScopesDocument


Create Remote File Metadata

Use this endpoint to create a metadata record for a file to be uploaded. This operation is the basis for a remote file upload. A successful HTTP POST will create a FileDocument record with a unique ID and a transient uploadUrl field.

This signed uploadUrl can then be used by a client with an HTTP PUT request to do the actual upload to remote storage. The uploadUrl will expire after 30 minutes. The input payload to this endpoint should also contain a setting for the privateAccess field to indicate whether the file should have 'private' or 'public' access on remote storage.

POST /files
Content-Type: application/json
Authorization: $OPERATOR_API_KEY

FileDocument
curl -i -H "Content-Type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X POST 'https://api.evrythng.com/files' \
  -d '{
    "name": "my_file1.txt",
    "type": "text/plain",
    "tags": [ "files" ],
    "privateAccess": false
  }'
const file = {
  name: 'my_file1.txt',
  type: 'text/plain',
  tags: [ 'files' ],
  privateAccess: false
};

operator.file().create(file).then(console.log);
HTTP/1.1 201 Created
Content-Type: application/json
Location: https://api.evrythng.com/files/U2k9enU2BD8RQKaaagME5dFn

{
  "id": "U2k9enU2BD8RQKaaagME5dFn",
  "createdAt": 1485785656730,
  "tags": [
    "files"
  ],
  "updatedAt": 1485785656730,
  "uploadUrl": "https://s3.amazonaws.com/evtcdn_02/2/uf/UhpHrg39QCy9dsSddN8xhwnb/my_file1.txt?AWSAccessKeyId=AKIAID2MOF7RYNLLMH7Q&Expires=1485787456&Signature=YWBUFgcyCKfcTxEg9tzxAxmvP%2FU%3D",
  "name": "my_file1.txt",
  "type": "text/plain",
  "privateAccess": false
}

Read Metadata for All Remote Files

Use this endpoint to get a list of FileDocuments associated with this account ID. The retrieved file metadata will have (among other attributes), a file's unique ID, and a transient contentUrl field which can be used to download the actual file represented by the resource using an HTTP GET request. The value of the privateAccess boolean field will indicate whether the file has 'public' or 'private' access on remote storage. The result may be paginated if there are more than 30 items.

Important

If the privateAccess flag is set to true, then the contentUrl will expire after 30 minutes.

This endpoint also supports filtering by name and tags filters. Details of filters can be found on the Filters page.

GET /files
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X GET 'https://api.evrythng.com/files"
operator.file().read().then(console.log);
HTTP/1.1 200 OK
Content-Type: application/json

[
  {
    "id": "UkYqry34eDswtKawaYMqwfbr",
    "createdAt": 1484575386979,
    "tags": [
      "files"
    ],
    "updatedAt": 1484575386979,
    "uploadUrl": "https://s3.amazonaws.com/evtcdn_02/2/uf/UhpHrg39QCy9dsSddN8xhwnb/test.txt?AWSAccessKeyId=AKIAID2MOF7RYNLLMH7Q&Expires=1485788741&Signature=eyJvv7wFCl8AFaG9G%2BDYapUdoZc%3D",
    "contentUrl": "https://s3.amazonaws.com/evtcdn_02/2/uf/UhpHrg39QCy9dsSddN8xhwnb/test.txt",
    "size": 67,
    "name": "test.txt",
    "type": "text/plain",
    "privateAccess": false
  }
]

Read Remote File Metadata by ID

Use this endpoint to get metadata for a specific remote file by ID. The fileId path parameter should be the value returned when the FileDocument was created.

The retrieved file metadata will have (among other attributes), a transient contentUrl field which can be used to download the physical file using an HTTP GET request. The value of the privateAccess boolean field will indicate whether the file has 'public' or 'private' access on remote storage.

GET /files/:fileId
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X GET 'https://api.evrythng.com/files/UFm5cMg7Bgsat5aawFS4nhHt'
const fileId = 'UFm5cMg7Bgsat5aawFS4nhHt';

operator.file(fileId).read().then(console.log);
HTTP/1.1 200 OK
Content-Type: application/json

[
  {
    "id": "UFm5cMg7Bgsat5aawFS4nhHt",
    "createdAt": 1484575386979,
    "tags": [
      "files"
    ],
    "updatedAt": 1484575386979,
    "uploadUrl": "https://s3.amazonaws.com/evtcdn_02/2/uf/UhpHrg39QCy9dsSddN8xhwnb/test.txt?AWSAccessKeyId=AKIAID2MOF7RYNLLMH7Q&Expires=1485788741&Signature=eyJvv7wFCl8AFaG9G%2BDYapUdoZc%3D",
    "contentUrl": "https://s3.amazonaws.com/evtcdn_02/2/uf/UhpHrg39QCy9dsSddN8xhwnb/test.txt",
    "size": 67,
    "name": "test.txt",
    "type": "text/plain",
    "privateAccess": false
  }
]

Delete Remote File by ID

Use this endpoint to delete a remote file by ID. The fileId path parameter should be the id value returned when the FileDocument was created.

Important

This endpoint will physically delete the file on remote storage, and will also remove the file's metadata record on the platform.

DELETE /files/:fileId
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X DELETE 'https://api.evrythng.com/files/UFm5cMg7Bgsat5aawFS4nhHt'
const fileId = 'UFm5cMg7Bgsat5aawFS4nhHt';

operator.file(fileId).delete().then(() => console.log('Deleted'));
HTTP/1.1 200 OK

File Upload Workflow

In order to upload a file you need to get a signed uploadUrl, which can then be used with an HTTP PUT request to perform a multipart or binary data upload.

Step 1: Create Remote File Metadata and note the uploadUrl in the response, including its query parameters.

Step 2: Perform an HTTP PUT request on the uploadUrl attribute, and specify the x-amz-acl header as private or public-read.

The example below shows uploading a text file to the Files API. For the cURL example, the --data-binary flag will also work for other files types, such as images.

PUT :uploadUrl
Content-Type: text/plain
x-amz-acl: private


- - - - - -WebKitFormBoundaryKJ2T3DyoTYBx6Byd
Content-Disposition: form-data; name="file"; filename="my_file.txt"
Content-Type: text/plain

My remote file text content


- - - - - -WebKitFormBoundaryKJ2T3DyoTYBx6Byd--
curl -H "Content-Type: text/plain" \
  -H "x-amz-acl: public-read" \
  -X PUT 'https://s3.amazonaws.com/evtcdn_02/2/uf/UhpHrg3837hgdsSddN8xhwnb/thng-prod.png?AWSAccessKeyId=AKIHD8YH9F7RYNLLMH7Q&Expires=1486726308&Signature=op3MlVJ46HF53%2F%2BDRKqAV2cJtYM%3D' \
  --data-binary @my_file.txt
HTTP/1.1 200 OK

File Download Workflow

In order to download a previously uploaded remote file you need to get a contentUrl by performing an HTTP GET request with the file's ID. The contentUrl can then be used in a subsequent HTTP GET request to download the actual file.

Step 1: Retrieve remote file metadata by ID.

Step 2: Perform an HTTP GET request on the contentUrl using the correct value of the x-amz-acl header, either "private" or "public-read", that was used when the file was uploaded. You must also specify the correct Content-Type header for the uploaded file.

GET :contentUrl
Content-Type: text/plain
x-amz-acl: private (or public-read)
Suggest Edits

OAuth Authorize

 

Third party applications can use OAuth 2.0 to authenticate their EVRYTHNG users.

Application owners set up an OAuth client and receive a clientId. They can then obtain a user token by performing OAuth 2.0 implicit grant type flow, as defined in RFC 6749 specification.

Once the users are authenticated and have agreed to give access to their account, their Application User API key is returned to User Agent using a redirection mechanism.

The client application can use it to interact with the EVRYTHNG API.


API Status
Stable: auth.evrythng.com/oauth/authorize


OAuth Client Authorize Request

Request a user token.

GET https://auth.evrythng.com/oauth/authorize?client_id=:clientId
        &redirect_uri=:redirect_uri&response_type=token&state=:state
HTTP/1.1 302 Found
Location: :redirect_uri#access_token=:userApiKey&token_type=apikey&state=:state

Query Parameters

  • client_id - Ref, Required
    ID of the registered OAuth client.

  • response_type - String, Required
    OAuth 2.0 grant type. Only token is supported.

  • redirect_uri - URI
    Possible override of the default redirectUrl defined in the OAuth client object. Only the query string of the URI can be overridden. Any non matching URI will be rejected.

  • state - String
    A custom value forwarded without transformation in the fragment of the redirect response. Recommended to prevent a Cross-Site Request Forgery.


Response Parameters (in fragment)

  • access_token - API Key
    The User API key.

  • token_type - String
    Fixed value: apikey.

  • state - String
    If present in the request, the same value is returned here.

  • error - String
    In case of an unsuccessful (but valid) request, the reason of the failure is returned here.

In case the users refuse to give access to their data, the fragment contains an error=access_denied parameter instead of the access_token. The state parameter is forwarded as-is in all cases:

GET https://auth.evrythng.com/oauth/authorize?client_id=:clientId
        &redirect_uri=:redirect_uri&response_type=token&state=:state
HTTP/1.1 302 Found
Location: :redirect_uri#error=access_denied&state=:state
Suggest Edits

OAuth Clients

 

To enable OAuth 2.0, developers needs to register an OAuth client Platform resource for their application. An OAuth client resource represents any web-based or native application that will interact with the platform on behalf of the Application User, using their API key.

The EVRYTHNG Platform allows registration of multiple OAuth clients for the application. The registration process requires a redirect URL where users will be directed after allowing or declining access for the application.

Each successfully registered client is granted unique credentials. As long as the platform supports only Implicit Grant authorization flow, the client ID is the only part of the credentials that is needed to obtain the Application User access token.


API Status
Stable:
/projects/:projectId/applications/:applicationId/oauthClients
/projects/:projectId/applications/:applicationId/oauthClients/:clientId


OAuthClientDocument Data Model

.id (string, read-only)
    The ID of the OAuth client.

.name (string)
    The name of the OAuth client.

.redirectUrl (string)
    The OAuth redirect URL. Must be a valid absolute URL under 
    HTTPS protocol.

.createdAt (integer, read-only)
    Timestamp when the resource was created.

.updatedAt (integer, read-only)
    Timestamp when the resource was updated.

.customFields (object)
    Object of case-sensititve key-value pairs of custom fields 
    associated with the resource.
{
  "type": "object",
  "description": "An object representing an OAuth client.",
  "properties": {
    "id": {
      "type": "string",
      "description": "The ID of the OAuth client.",
      "minLength": 45,
      "maxLength": 45,
      "readOnly": true
    },
    "name": {
      "type": "string",
      "description": "The name of the OAuth client."
    },
    "redirectUrl": {
      "type": "string",
      "description": "The OAuth redirect URL. Must be a valid absolute URL under HTTPS protocol."
    },
    "createdAt": {
      "type": "integer",
      "description": "Timestamp when the resource was created.",
      "readOnly": true
    },
    "updatedAt": {
      "type": "integer",
      "description": "Timestamp when the resource was updated.",
      "readOnly": true
    },
    "customFields": {
      "type": "object",
      "description": "Object of case-sensititve key-value pairs of custom fields associated with the resource."
    }
  }
}
{
  "id": "AViIrsX3zYYibHr4FZXAEIE0bdD1RLhff7nedB5bT8kr",
  "name": "Example Client",
  "redirectUrl": "https://examples.com/oauth/callback",
  "customFields": {
    "type": "Web Application",
    "homeUrl": "https://examples.com"
  },
  "createdAt": 1433508108657,
  "updatedAt": 1433508108657
}

Create an OAuth Client

Registers a new OAuth client granted with unique client ID.

POST /projects/:projectId/applications/:applicationId/oauthClients
Content-type: application/json
Authorization: $OPERATOR_API_KEY

OAuthClientDocument
curl -i -H "Content-type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X POST 'https://api.evrythng.com/projects/U2meqbNWegsaQKRRaDUmpssr/applications/UF3Vqb7D6G8EhMwaRYgQ2pFc/oauthClients' \
  -d '{
	"name": "Example Client",
    "redirectUrl": "https://examples.com/oauth/callback",
    "customFields": {
      "type": "Web Application",
      "homeUrl": "https://examples.com"
    }
  }'
const projectId = 'Uh5qRBqc4DcmbNbAANtMnrYm';
const applicationId = 'Uh6GfhFnmgScpnxwMsqmbbBk';
const operatorApiKey = '$OPERATOR_API_KEY';

const client = {
  name: 'Example Client',
  redirectUrl: 'https://examples.com/oauth/callback',
  customFields: {
    type: 'Web Application',
    homeUrl: 'https://examples.com'
  }
};

EVT.api({
  url: `/projects/${projectId}/applications/${applicationId}/oauthClients`,
  authorization: operatorApiKey,
  method: 'POST',
  data: client
}).then(console.log);
HTTP/1.1 201 Created
Content-type: application/json

{
  "id": "AViIrsX3zYYibHr4FZXAEIE0bdD1RLhff7nedB5bT8kr",
  "name": "Example Client",
  "redirectUrl": "https://examples.com/oauth/callback",
  "customFields": {
    "type": "Web Application",
    "homeUrl": "https://examples.com"
  },
  "createdAt": 1433508108657,
  "updatedAt": 1433508108657
}

Read an OAuth Client

Reads the OAuth client.

GET /projects/:projectId/applications/:applicationId/oauthClients/:clientId
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X GET 'https://api.evrythng.com/projects/U2meqbNWegsaQKRRaDUmpssr/applications/UF3Vqb7D6G8EhMwaRYgQ2pFc/oauthClients/AViIrsX3zYYibHr4FZXAEIE0bdD1RLhff7nedB5bT8kr'
const projectId = 'Uh5qRBqc4DcmbNbAANtMnrYm';
const applicationId = 'Uh6GfhFnmgScpnxwMsqmbbBk';
const oauthClientId = 'AMWefPQKd1jcQzgizqY2z22x4jhDUaij8d2V72Fq7565r';
const operatorApiKey = '$OPERATOR_API_KEY';

EVT.api({
  url: `/projects/${projectId}/applications/${applicationId}/oauthClients/${oauthClientId}`,
  authorization: operatorApiKey,
}).then(console.log);
HTTP/2.2 200 OK
Content-type: application/json

{
  "id": "AViIrsX3zYYibHr4FZXAEIE0bdD1RLhff7nedB5bT8kr",
  "name": "Example Client",
  "redirectUrl": "https://examples.com/oauth/callback",
  "customFields": {
    "type": "Web Application",
    "homeUrl": "https://examples.com"
  },
  "createdAt": 1433508108657,
  "updatedAt": 1433508108657
}

Update an OAuth Client

Updates the OAuth client. As soon as the redirect URL is modified, the old URL is not used for users redirection.

PUT /projects/:projectId/applications/:applicationId/oauthClients/:clientId
Content-Type: application/json
Authorization: $OPERATOR_API_KEY

OAuthClientDocument (subset)
curl -i -H "Content-type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X PUT 'https://api.evrythng.com/projects/U2meqbNWegsaQKRRaDUmpssr/applications/UF3Vqb7D6G8EhMwaRYgQ2pFc/oauthClients/AViIrsX3zYYibHr4FZXAEIE0bdD1RLhff7nedB5bT8kr' \
  -d '{
    "name": "Updated Example Client",
    "redirectUrl": "https://examples.com/oauth/updated/callback",
    "customFields": {
      "type": "Web Application",
      "homeUrl": "https://examples.com/updated"
    }
  }'
const projectId = 'Uh5qRBqc4DcmbNbAANtMnrYm';
const applicationId = 'Uh6GfhFnmgScpnxwMsqmbbBk';
const oauthClientId = 'AMWefPQKd1jcQzgizqY2z22x4jhDUaij8d2V72Fq7565r';
const operatorApiKey = '$OPERATOR_API_KEY';

const clientUpdate = {
  name: 'Updated Example Client',
  redirectUrl: 'https://examples.com/oauth/updated/callback',
  customFields: {
    type: 'Web Application',
    homeUrl: 'https://examples.com/updated'
  }
};

EVT.api({
  url: `/projects/${projectId}/applications/${applicationId}/oauthClients/${oauthClientId}`,
  authorization: operatorApiKey,
  method: 'PUT',
  data: clientUpdate
}).then(console.log);
HTTP/1.1 200 OK
Content-type: application/json

{
  "id": "AViIrsX3zYYibHr4FZXAEIE0bdD1RLhff7nedB5bT8kr",
  "name": "Updated Example Client",
  "redirectUrl": "https://examples.com/oauth/updated/callback",
  "customFields": {
    "type": "Web Application",
    "homeUrl": "https://examples.com/updated"
  },
  "createdAt": 1433508108657,
  "updatedAt": 1433937407207
}

Delete an OAuth Client

Delete the OAuth client. After the client is removed, it is not allowed to obtain the Application User access token anymore.

DELETE /projects/:projectId/applications/:applicationId/oauthClients/:clientId
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X DELETE 'https://api.evrythng.com/projects/U2meqbNWegsaQKRRaDUmpssr/applications/UF3Vqb7D6G8EhMwaRYgQ2pFc/oauthClients/AViIrsX3zYYibHr4FZXAEIE0bdD1RLhff7nedB5bT8kr'
const projectId = 'Uh5qRBqc4DcmbNbAANtMnrYm';
const applicationId = 'Uh6GfhFnmgScpnxwMsqmbbBk';
const oauthClientId = 'AMWefPQKd1jcQzgizqY2z22x4jhDUaij8d2V72Fq7565r';
const operatorApiKey = '$OPERATOR_API_KEY';

EVT.api({
  url: `/projects/${projectId}/applications/${applicationId}/oauthClients/${oauthClientId}`,
  authorization: operatorApiKey,
  method: 'DELETE'
}).then(() => console.log('Deleted!'));
HTTP/1.1 200 OK

Read All OAuth Clients

Returns an array of OAuth clients.

GET /projects/:projectId/applications/:applicationId/oauthClients
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X GET 'https://api.evrythng.com/projects/U2meqbNWegsaQKRRaDUmpssr/applications/UF3Vqb7D6G8EhMwaRYgQ2pFc/oauthClients'
const projectId = 'Uh5qRBqc4DcmbNbAANtMnrYm';
const applicationId = 'Uh6GfhFnmgScpnxwMsqmbbBk';
const operatorApiKey = '$OPERATOR_API_KEY';

EVT.api({
  url: `/projects/${projectId}/applications/${applicationId}/oauthClients`,
  authorization: operatorApiKey
}).then(console.log);
HTTP/1.1 200 OK
Content-type: application/json

[
  {
    "id": "AViIrsX3zYYibHr4FZXAEIE0bdD1RLhff7nedB5bT8kr",
    "name": "Example Client",
    "redirectUrl": "https://examples.com/oauth/callback",
    "customFields": {
      "type": "Web Application",
      "homeUrl": "https://examples.com"
    },
    "createdAt": 1433508108657,
    "updatedAt": 1433508108657
  }
]
 

Node Versions

Reactor scripts inside Platform applications created before April 13th 2017 run Node v4.3. Applications created after this date run Node v6.10.2.

Reactor allows execution of custom code in reaction to a supported event, such as when an action is created, or a Thng's property value is changed. Every application has a Reactor script attached, but by default this script is empty and does nothing.

Reactor scripts are written in Node.js and may use NPM packages via a conventional package.json file. They are automatically provided with the latest version of evrythng-extended.js via a global EVT object and app EVT.App scope for requests. You should not remove this basic dependency, or the script may fail to execute.

Once configured, Reactor scripts will run whenever an action is created, a Thng/product property changes value, or a scheduled event is triggered, depending on which callbacks appear in the script. This allows complex logic to occur in the context of virtually any Platform resource update, driving rich integrations and experiences.

Note

When an action or resource update occurs that will trigger a Reactor script, all application Reactor scripts within the resource's project scope will run for that event type, if applicable.


API Status
Stable:
/projects/:projectId/applications/:applicationId/reactor/script
/projects/:projectId/applications/:applicationId/reactor/script/status
/projects/:projectId/applications/:applicationId/reactor/schedules
/projects/:projectId/applications/:applicationId/reactor/logs


Scripts

There are two ways to upload Reactor scripts to EVRYTHNG Platform.

  1. The simple API allows a script file and an optional NPM dependencies descriptor file (aka package.json) to be uploaded in an update request body.
  2. The bundle API which allows the upload of a full Node.js project as a .zip file.

We'll only consider the bundle API in detail here, since the simple API consists of merely sending the script content. You can update the script through the API or via the application's page in the EVRYTHNG Dashboard.

Bundle Structure

Bundled scripts are provided in the form of a .zip file with the following structure.

bundle.zip
 - main.js
 - package.json
 - (otherfile.js)
 - (otherfolder/)
     - (anotherfile.js)
     ...
 ...

package.json is a standard NPM dependencies package.json file, and can be used to specify any other dependencies of the Reactor script.

Notes

  • By default we include evrythng-extended in the package.json file when an application is first created. You must not remove this, or the Reactor may stop working. It is provided to allow you to see which version you are using, and update if necessary.

  • You must not include any node_modules in the bundle. The package.json file can be used to specify these dependencies.

  • devDependencies in package.json are ignored by Reactor when a bundle is uploaded and installed.

If the file is not provided, or no dependency to evrythng-extended.js is specified, the latest version of evrythng-extended.js will be automatically linked. It is strongly advised to explicitly state which specific version of the library you want to use.


Event Types

This section lists the types of events a Reactor script can be launched in response to. For each, the object provided to the callback is defined and the available Reactor filter parameters listed.

Important

All event handlers must call done() just before exiting. This will tell the Platform that everything is complete and that we are not waiting on any other pending callback. This will also commit the log lines that have been recorded using logger, and make them available via the Dashboard and the API.

Function
Triggered

onActionCreated

When an action is created.

onProductPropertiesChanged

When one or more properties of a product have changed.

onThngPropertiesChanged

When one or more properties of a Thng have changed.

onScheduledEvent

When a scheduled event has occurred.

If function in the schedule uploaded is a custom function name, that name will be invoked instead, but only if it has been exported from the script.


onActionCreated

This occurs every time an action is created and is visible to the application. The event supplied to the handler is as follows:

.action (ActionDocument)
{
  "type": "object",
  "description": "The event parameter passed to an `onActionCreated` event callback.",
  "properties": {
    "action": { "$ref": "ActionDocument" }
  }
}
{
  "action": {
    "id": "Una8MNVreDswQKwRahUhdymr",
    "createdAt": 1510919501111,
    "customFields": null,
    "tags": null,
    "scopes": {
      "users": [
        "10ff93f061029f0a20273005"
      ],
      "projects": [
        "all"
      ]
    },
    "timestamp": 1510919501111,
    "type": "implicitScans",
    "user": "10ff93f061029f0a20273005",
    "location": {
      "place": null,
      "latitude": 51.4333,
      "longitude": 0.1833,
      "position": {
        "type": "Point",
        "coordinates": [
          0.1833,
          51.4333
        ]
      },
      "customFields": null
    },
    "locationSource": "geoIp",
    "device": null,
    "context": {
      "ipAddress": "141.0.154.202",
      "city": "Crayford",
      "region": "England",
      "countryCode": "GB",
      "userAgent": null,
      "referer": null,
      "language": null,
      "userAgentName": "Unknown",
      "userAgentVersion": null,
      "userAgentType": null,
      "deviceType": null,
      "device": null,
      "operatingSystemName": "Unknown",
      "operatingSystemFamily": null,
      "operatingSystemProducer": null,
      "operatingSystemVersion": null,
      "timeZone": "Europe/London"
    },
    "reactions": [
      {
        "type": "redirection",
        "customFields": null,
        "redirectUrl": "https://google.com",
        "redirectionContext": {
          "constants": {
            "constant_key": "constant_value"
          }
        }
      }
    ],
    "createdByProject": "10ff93f061029f0a20273005",
    "createdByApp": "10ff93f061029f1a20273005",
    "createdByThng": null,
    "identifiers": null,
    "thng": "UFqMRDbaqm8EEMRaRF5h7cEg",
    "product": "U2E9hmyCVg8atpRwwXgTqcrt",
    "shortId": null,
    "shortDomain": null
  },
  "triggerApp": "10ff93f061029f1a20273005",
  "triggerUser": "10ff93f061029f0a20273005",
  "triggerUserAnonymous": true
}

Reactor Filters: thng.*, product.*, user.*, time, Time, action.*, Location, place.*.

See also: ActionDocument


onProductPropertiesChanged

This occurs every time the properties of a product have been changed. The event supplied to the handler is as follows:

.product (ProductDocument)

.changes (object)
    Object containing a key for each changed property, with 
    value of a `ReactorPropertyChangeDocument`.

.triggerApp (string)
    The triggering application.

.triggerUser (string)
    The triggering Application User, if any.

.triggerUserAnonymous (boolean)
    `true` if the triggering Application User is anonymous, 
    `false` otherwise.
{
  "type": "object",
  "description": "The event parameter passed to an `onProductPropertiesChanged` event callback.",
  "properties": {
    "product": { "$ref": "ProductDocument" },
    "changes": {
      "type": "object",
      "description": "Object containing a key for each changed property, with value of a `ReactorPropertyChangeDocument`."
    },
    "triggerApp": {
      "type": "string",
      "description": "The triggering application."
    },
    "triggerUser": {
      "type": "string",
      "description": "The triggering Application User, if any."
    },
    "triggerUserAnonymous": {
      "type": "boolean",
      "description": "`true` if the triggering Application User is anonymous, `false` otherwise."
    }
  }
}
{
  "changes": {
    "is_packed": {
      "oldValue": true,
      "newValue": false,
      "timestamp": 1510930465583
    }
  },
  "product": {
    "id": "UGxqfDDpeg8aQKRaahWKKcSb",
    "createdAt": 1495029355783,
    "customFields": {
      "key": "value"
    },
    "tags": [
      "some",
      "tags"
    ],
    "scopes": {
      "users": [
        "all"
      ],
      "projects": [
        "UmxHK6K8BXsa9KawRh4bTbqc",
        "UmSqCDt5BD8atKRRagdqUnAa"
      ]
    },
    "updatedAt": 1510055403742,
    "brand": null,
    "categories": null,
    "properties": {
      "test": 84,
      "is_packed": false
    },
    "description": "Example description text",
    "fn": "Example Product",
    "name": "Example Product",
    "photos": null,
    "url": null,
    "identifiers": {
      "key": "value"
    }
  },
  "triggerApp": null,
  "triggerUserAnonymous": false,
  "triggerUser": null
}

Reactor Filters: product.*, time, Property.

See also: ProductDocument, ReactorPropertyChangeDocument


onThngPropertiesChanged

This occurs every time the properties of a Thng have been changed.

Note

If the new value is the same as the old value, this event will not fire.

The event supplied to the handler is as follows:

.thng (ThngDocument)

.changes (object)
    Object containing a key for each changed property, with 
    value of a `ReactorPropertyChangeDocument`.

.triggerApp (string)
    The triggering application.

.triggerUser (string)
    The triggering Application User, if any.

.triggerUserAnonymous (boolean)
    `true` if the triggering Application User is anonymous, 
    `false` otherwise.
{
  "type": "object",
  "description": "The event parameter passed to an `onThngPropertiesChanged` event callback.",
  "properties": {
    "thng": { "$ref": "ThngDocument" },
    "changes": {
      "type": "object",
      "description": "Object containing a key for each changed property, with value of a `ReactorPropertyChangeDocument`."
    },
    "triggerApp": {
      "type": "string",
      "description": "The triggering application."
    },
    "triggerUser": {
      "type": "string",
      "description": "The triggering Application User, if any."
    },
    "triggerUserAnonymous": {
      "type": "boolean",
      "description": "`true` if the triggering Application User is anonymous, `false` otherwise."
    }
  }
}
{
  "changes": {
    "is_online": {
      "oldValue": true,
      "newValue": false,
      "timestamp": 1510855251930
    }
  },
  "thng": {
    "id": "UnRKTWQ9MQtBhsaRah2sCBsg",
    "createdAt": 1510680063133,
    "customFields": {},
    "tags": null,
    "scopes": {
      "users": [
        "all"
      ],
      "projects": [
        "UmxHK6K8BXsa9KawRh4bTbqc"
      ]
    },
    "updatedAt": 1510763814549,
    "name": "Example Thng",
    "description": null,
    "location": null,
    "product": "UHaqwfVEq3PYEMwwa2Apyc7h",
    "properties": {
      "~connected": false,
      "is_online": false
    },
    "identifiers": {},
    "collections": null,
    "batch": null,
    "createdByTask": null
  },
  "triggerApp": null,
  "triggerUserAnonymous": false,
  "triggerUser": null
}

Reactor Filters: thng.*, product.*, time, Property.

See also: ThngDocument,
ReactorPropertyChangeDocument


onScheduledEvent

When a scheduled event occurs, the only argument provided to the callback will be the event that was specified in the Reactor schedule.

event = ReactorScheduleDocument.event
{
  "foo": "bar"
}

See also: ReactorScheduleDocument


ReactorPropertyChangeDocument Data Model

This object contains the changes that occurred as an object of keys, each containing the old and new values.

.oldValue (string|number|object)
    The previous property value.
    
.newValue (string|number|object)
    The new property value
    
.timestamp (integer)
    The time that the update occured.
{
  "type": "object",
  "description": "Object describing the property changes for this event.",
  "properties": {
    "oldValue": { "description": "The previous property value." },
    "newValue": { "description": "The new property value" },
    "timestamp": {
      "type": "integer",
      "description": "The time that the update occured."
    }
  }
}

Built-in Libraries

Reactor scripts are packaged with the latest version of evrythng-extended.js (unless you explicitly specify the version you want to use). It is pre-configured with the application's Trusted Application API Key.

The table below describes the global variable made available to you within the Reactor script context:

Name
Description

EVT

Preconfigured instance of evrythng-extended.js module. See SDK documentation.

app

Preconfigured instance of the EVT.App scope. See SDK documentation.

Note: Uniquely inside a Reactor script, the application's Trusted Application API Key can be read from the app.apiKey property.

done

Function that must be called when the script has finished, so that logs can be output. If this is not called, no logs will be shown.

logger

The logger available to use to output logs. console.log() will not be visible in output logs. Available methods are .info(), .error(), and .warn().

evrythng-extended.js will also forward the X-Evrythng-Reactor header, which is used to protect you against inadvertently create a recursive loop (see Limits: Recursion).

If you re-configure evrythng-extended.js or use an alternate way to communicate with the API, make sure you forward this header to prevent any possible recursive loops.


Debugging

You can create log messages from the Reactor script. A logger instance is globally defined and available anywhere in the Reactor script. Use the following functions available to create log messages of differing levels of severity.

Function
Desciption

logger.debug(message);

Write a log message with debug level

logger.info(message);

Write a log message with info level

logger.warn(message);

Write a log message with warning level

logger.error(message);

Write a log message with error level

Note

Reactor log entries will be available for up to seven (7) days after they are generated.


Filters

In most cases you don't want your Reactor scripts to be executed on every event (action creation or property change) but only if some conditions are met. For example, suppose we only want to react to action creation of type scans and ignore the others. One obvious solution is to use an if construction within the Reactor script.

function onActionCreated(event) {
  // BAD - Wasteful, scripts runs even if type is not 'scans'
  if (event.action.type === 'scans') {
    logger.debug('Scan action created');
  }
  done();
}

The problem with this example is that for non-scan actions the Reactor script is still executed but doing nothing useful. It is not a good practice since the Reactor script execution cost is non-trivial.

The recommended solution is to use the Reactor filters annotation syntax, an example of which is shown below to run the script only when the action's type is scans.

// @filter(onActionCreated) action.type=scans
function onActionCreated(event) {
  // GOOD - @filter means this is only called for action type 'scans'
  logger.debug('scan action created');
  done();
}

Reactor filters functionality is limited compared to full javascript but they are evaluated on an earlier step before a script is executed, and so are much less costly.


Syntax

Reactor filters are defined as Javascript comments (both // and /* */ styles are supported). The filter syntax is as described on the Filters page. Filters can be defined anywhere in the script, however, it is a good idea to place each directly above the corresponding callback function definition. If using the bundle Reactor type, filters should be defined in main.js file.

The templates for filtering the three main callback types are shown below.

Note

Unlike regular Platform filters, the logical OR operator is supported in Reactor filters for valid fields. Do this using the pipe (|) symbol, URL encoded to %7C. For example:

GET /thngs?filter=name%3DExample%20Thng%7CcreatedAt%3D1472815501280

// @filter(onActionCreated) <filter>
function onActionCreated(event) {
  // ...
}

// @filter(onProductPropertiesChanged) <filter>
function onProductPropertiesChanged(event) {
  // ...
}

// @filter(onThngPropertiesChanged) <filter>
function onThngPropertiesChanged(event) {
  // ...
}

We will do our best to extract filter definitions from your Reactor script. However, if we failed for any reason, no error is raised. Reactor script functions will be executed as if the filter has not been defined.

Note

Only one filter per function is allowed. A 'duplicate' error will be returned otherwise at the time the script is uploaded.

Filter Parameters

This is a list of parameters that can be used in the Reactor filter body, although not all of them are be available in a given context. See the Event Types section to see which are available for which callback.

Thng

  • thng.id
    The action Thng’s ID.

  • thng.identifiers.{identifierName}
    The action Thng’s identifiers.

  • thng.name
    The action Thng’s name.

  • thng.customFields.{customFieldName}
    The action Thng’s custom fields.

  • thng.tags
    The action Thng’s tags.

Product

  • product.id
    The action product’s ID.

  • product.identifiers.{identifierName}
    The action product’s identifiers.

  • product.name
    The action product’s name.

  • product.customFields.{customFieldName}
    The action product’s custom fields.

  • product.tags
    The action product’s tags.

User

  • user.gender
    The action user’s gender. Possible values are male and female.

  • user.age
    The action user’s years of age.

  • user.customFields.{customFieldName}
    The action user’s custom fields.

  • user.locale
    The action user’s locale.

Time

  • time
    Number of milliseconds elapsed since Epoch at the time the action occurred.

  • timeOfDay
    Number of milliseconds elapsed since midnight in the action’s timezone.

  • dayOfWeek
    Day of the week in the action’s timezone. Possible values are mon, tue, wed, thu, fri, sat, sun.

  • dayOfMonth
    Day of the month in the action’s timezone. Possible values are 1 to 31.

  • month
    Month in the action’s timezone. Possible values are 1 to 12.

  • year
    Year in the action’s timezone.

Action

  • action.customFields.{customFieldName}
    The action’s custom fields.

  • action.type
    The action type

Property

  • propertyChangeOld.{propertyKey}
    The old value of the property that changed.

  • propertyChangeNew.{propertyKey}
    The new value of the property that changed.

Location

  • timezone
    The timezone where the action occurred. See the list of possible timezones.

  • location.lat
    The location latitude in degrees where the action occurred.

  • location.lon
    The location longitude in degrees where the action occurred.

  • country
    ISO 3166-1 alpha-2 country code where the action occurred

Place

  • place.id
    The place’s ID.

  • place.tags
    The place’s tags.

  • place.customFields.{customFieldName}
    The place’s custom fields.

  • place.distance
    The place distance in meters from the scan.


Limits

Execution Timeout

When triggered, a Reactor script must complete within a certain amount of time. If this is not the case, the execution is aborted. See below for time constraints.

Recursion

Given that a Reactor script may trigger itself by doing an operation against the REST API, which causes the script to trigger again (directly, or