A Thng is the central data structure in the EVRYTHNG Platform. Thngs are extensible data containers designed to model physical objects such as packaged consumer products, electronic devices, or other saleable goods that are manufactured in large quantities, etc. Thngs have various basic fields including name
, description
, tags
, etc. More dynamic data can be stored in the properties
, and specialised use-case specific data in 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
General Availability:
/auth/evrythng/thngs
/thngs
/thngs/:thngId
/thngs/:thngIdentifier
/thngs/:thngId/actions/:actionType
/thngs/:thngId/location
/thngs/:thngId/properties
ThngDocument Data Model
Create a Thng
Read a Thng
Read all Thngs
Update a Thng
Update Multiple Thngs
Delete a Thng
Thng Location
Create a Thng's Redirection
Read a Thng's Redirection
Update a Thng's Redirection
Delete a Thng's Redirection
Thngs as Devices
Create Device API Key
Read Device API Key
Delete Device API Key
Using Evrythng.js in a Device
ThngDocument Data Model
.name (string, required)
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.
.type (string)
Type of this resource. Can be unset with an empty string.
.tags (array of string)
Array of string tags associated with this resource.
.description (string)
Friendly description of this resource.
.customFields (CustomFieldsDocument)
Object of case-sensititve key-value pairs of custom fields
associated with the resource.
.identifiers (IdentifiersDocument)
Various identifiers (EPC, GTIN, 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)
An object representing a location. Either 'position' or
'place' us required.
.properties (object, read-only)
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)
Project and user scopes arrays.
{
"additionalProperties": false,
"type": "object",
"description": "An object representing a Thng.",
"required": ["name"],
"properties": {
"name": {
"type": "string",
"description": "Friendly name of this resource."
},
"id": {
"type": "string",
"description": "The ID of this resource.",
"pattern": "^[abcdefghkmnpqrstwxyABCDEFGHKMNPQRSTUVWXY0123456789]{24}$",
"readOnly": true
},
"createdAt": {
"type": "integer",
"description": "Timestamp when the resource was created.",
"readOnly": true,
"minimum": 0
},
"updatedAt": {
"type": "integer",
"description": "Timestamp when the resource was updated.",
"readOnly": true,
"minimum": 0
},
"activatedAt": {
"type": "integer",
"description": "Timestamp when the Thng was activated.",
"readOnly": true
},
"type": {
"type": "string",
"description": "Type of this resource. Can be unset with an empty string.",
"maxLength": 256
},
"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, GTIN, 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.",
"pattern": "^[abcdefghkmnpqrstwxyABCDEFGHKMNPQRSTUVWXY0123456789]{24}$"
},
"location": {
"additionalProperties": false,
"type": "object",
"description": "An object representing a location. Either 'position' or 'place' us required.",
"properties": {
"position": {
"type": "object",
"description": "A GeoJSON Point object. The coordinate order is longitude, then latitude.",
"required": ["type", "coordinates"],
"properties": {
"type": {
"type": "string",
"description": "The type of the point.",
"enum": ["Point"]
},
"coordinates": {
"type": "array",
"description": "The point coordinates",
"items": {
"type": "number",
"minimum": -180,
"maximum": 180
}
}
}
},
"createdAt": {
"type": "integer",
"description": "Timestamp when the resource was created.",
"readOnly": true,
"minimum": 0
},
"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.",
"pattern": "^[abcdefghkmnpqrstwxyABCDEFGHKMNPQRSTUVWXY0123456789]{24}$"
},
"scopes": {
"additionalProperties": false,
"type": "object",
"description": "Project and user scopes arrays.",
"required": ["users", "projects"],
"properties": {
"users": {
"type": "array",
"description": "An array of Application User IDs this resource is scoped to.",
"items": { "type": "string" }
},
"projects": {
"type": "array",
"description": "An array of project IDs this resource is scoped to.",
"items": {
"type": "string",
"description": "The ID of this resource.",
"pattern": "^[abcdefghkmnpqrstwxyABCDEFGHKMNPQRSTUVWXY0123456789]{24}$",
"readOnly": true
}
}
}
}
},
"x-filterable-fields": ["timestamp"]
},
"properties": {
"type": "object",
"description": "A JSON object with key-value pairs describing properties of the Thng.",
"readOnly": true
},
"collections": {
"description": "An array of collection IDs of any collections this Thng belongs to.",
"type": "array",
"items": {
"type": "string",
"pattern": "^[abcdefghkmnpqrstwxyABCDEFGHKMNPQRSTUVWXY0123456789]{24}$"
}
},
"createdByTask": {
"type": "string",
"description": "The ID of this resource.",
"pattern": "^[abcdefghkmnpqrstwxyABCDEFGHKMNPQRSTUVWXY0123456789]{24}$",
"readOnly": true
},
"batch": {
"type": "string",
"description": "The ID of this resource.",
"pattern": "^[abcdefghkmnpqrstwxyABCDEFGHKMNPQRSTUVWXY0123456789]{24}$",
"readOnly": true
},
"scopes": {
"additionalProperties": false,
"type": "object",
"description": "Project and user scopes arrays.",
"required": ["users", "projects"],
"properties": {
"users": {
"type": "array",
"description": "An array of Application User IDs this resource is scoped to.",
"items": { "type": "string" }
},
"projects": {
"type": "array",
"description": "An array of project IDs this resource is scoped to.",
"items": {
"type": "string",
"description": "The ID of this resource.",
"pattern": "^[abcdefghkmnpqrstwxyABCDEFGHKMNPQRSTUVWXY0123456789]{24}$",
"readOnly": true
}
}
}
}
},
"x-filterable-fields": ["collections", "createdAt", "identifiers.<key>", "name", "product", "tags"]
}
{
"id": "UnRPNbfD6GsYh6wawhBMNdDe",
"createdAt": 1510920534153,
"tags": [
"example",
"thng"
],
"updatedAt": 1510920534153,
"name": "Soft Blue T-Shirt",
"description": "Single manufactured Soft Blue T-Shirt for sale.",
"type": "tagged",
"product": "UHR8MGsSCct4t9wwRgEKqrUr",
"identifiers": {
"gs1:21": "4834838943"
}
}
See also: ScopesDocument
, LocationDocument
Filterable Fields
This resource type can be filtered using the following fields and operators.
Field | Type | Operators |
---|---|---|
collections | List of string | = |
createdAt | Number | < , > |
identifiers.<key> | String | = |
name | String | = |
product | String | = |
tags | List of string | = |
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://$EVT_API_DOMAIN/thngs" \
-d '{
"tags": [
"example",
"thng"
],
"name": "Soft Blue T-Shirt",
"description": "Single manufactured Soft Blue T-Shirt for sale.",
"product": "UHR8MGsSCct4t9wwRgEKqrUr",
"identifiers": {
"gs1:21": "4834838943"
}
}'
const payload = {
tags: [
'example',
'thng'
],
name: 'Soft Blue T-Shirt',
description: 'Single manufactured Soft Blue T-Shirt for sale.',
product: 'UHR8MGsSCct4t9wwRgEKqrUr',
identifiers: {
'gs1:21': '4834838943'
}
};
user.thng().create(payload)
.then(console.log);
Thng thng = new Thng();
thng.setName("Box of cereals");
thng.setDescription("A simple box of cereals");
apiManager.thngService().thngCreator(thng).execute();
HTTP/1.1 201 Created
Content-Type: application/json
Location: https://api.evrythng.com/thngs/UNaE5DWVFKgsmwaww3y7Hakk
{
"id": "UNaE5DWVFKgsmwaww3y7Hakk",
"createdAt": 1560786516270,
"tags": [
"example",
"thng"
],
"updatedAt": 1560786516270,
"name": "Soft Blue T-Shirt",
"description": "Single manufactured Soft Blue T-Shirt for sale.",
"product": "UHR8MGsSCct4t9wwRgEKqrUr",
"identifiers": {
"gs1:21": "4834838943"
}
}
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://$EVT_API_DOMAIN/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": "UNaE5DWVFKgsmwaww3y7Hakk",
"createdAt": 1560786516270,
"tags": [
"example",
"thng"
],
"updatedAt": 1560786516270,
"name": "Soft Blue T-Shirt",
"description": "Single manufactured Soft Blue T-Shirt for sale.",
"product": "UHR8MGsSCct4t9wwRgEKqrUr",
"identifiers": {
"gs1:21": "4834838943"
}
}
You can also specify a valid custom identifier instead; such as reading the thng with identifier ser
value 12345678901234
:
GET /thngs/ser:12345678901234
Authorization: $OPERATOR_API_KEY
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://$EVT_API_DOMAIN/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": "UNaE5DWVFKgsmwaww3y7Hakk",
"createdAt": 1560786516270,
"tags": [
"example",
"thng"
],
"updatedAt": 1560786516270,
"name": "Soft Blue T-Shirt",
"description": "Single manufactured Soft Blue T-Shirt for sale.",
"product": "UHR8MGsSCct4t9wwRgEKqrUr",
"identifiers": {
"gs1:21": "4834838943"
}
}
]
Update a Thng
Update a single Thng by ID.
Note
Only the basic fields (
name
,description
,product
,identifiers
,tags
, andcustomFields
) 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://$EVT_API_DOMAIN/thngs/UkYVKThN36Cfe4xACatserPs' \
-d '{
"tags": ["updated"]
}'
const thngId = 'UkYVKThN36Cfe4xACatserPs';
const payload = { tags: ['updated'] };
user.thng(thngId).update(payload)
.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": "UNaE5DWVFKgsmwaww3y7Hakk",
"createdAt": 1560786516270,
"tags": [
"updated"
],
"updatedAt": 1560786516270,
"name": "Soft Blue T-Shirt",
"description": "Single manufactured Soft Blue T-Shirt for sale.",
"product": "UHR8MGsSCct4t9wwRgEKqrUr",
"identifiers": {
"gs1:21": "4834838943"
}
}
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://$EVT_API_DOMAIN/thngs?filter=name%3DSmart*' \
-d '{
"tags": [ "smart" ]
}'
const update = { tags: ["updated"] };
const params = { filter: 'name=Test*' };
operator.thng().update(update, { params })
.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://$EVT_API_DOMAIN/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. See the Locations section for more information.
Create a Thng's Redirection
Use the appropriate short domain API to create a Thng redirection. This example uses tn.gg
.
POST https://<short domain>/redirections
Content-Type: application/json
Authorization: $OPERATOR_API_KEY
Accept: application/json
RedirectionDocument
curl -H "Content-Type: application/json" \
-H "Authorization: $OPERATOR_API_KEY" \
-X POST 'https://tn.gg/redirections' \
-d '{
"type": "thng",
"evrythngId": "Uk6qwgSReXPat5awagfNpHsk",
"defaultRedirectUrl": "https://google.com/{thngId}"
}'
const thngId = 'Uk6qwgSReXPat5awagfNpHsk';
// 'type' and 'evrythngId' are handled by the SDK
const payload = { defaultRedirectUrl: 'https://example.com/{thngId}' };
operator.thng(thngId).redirection('tn.gg')
.update(payload)
.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
Use the appropriate short domain redirection API to read a Thng redirection.
GET https://<short domain>/redirections?evrythngId=:thngID
Authorization: $OPERATOR_API_KEY
Accept: application/json
curl -H "Content-Type: application/json" \
-H "Authorization: $OPERATOR_API_KEY" \
-X GET 'https://tn.gg/redirections?evrythngId=Uk6qwgSReXPat5awagfNpHsk'
const thngId = 'Uk6qwgSReXPat5awagfNpHsk';
operator.thng(thngId).redirection('tn.gg')
.read()
.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
Use the appropriate short domain redirection API and short ID to update a Thng redirection.
PUT https://<short domain>/redirections/:shortId
Content-Type: application/json
Authorization: $OPERATOR_API_KEY
RedirectionDocument
curl -H "Content-Type: application/json" \
-H "Authorization: $OPERATOR_API_KEY" \
-X PUT 'https://tn.gg/redirections/3kdh38sa' \
-d '{
"defaultRedirectUrl": "https://google.com/{thngId}"
}'
const thngId = 'Uk6qwgSReXPat5awagfNpHsk';
const payload = { defaultRedirectUrl: 'https://example.com/{thngId}' };
operator.thng(thngId).redirection('tn.gg')
.update(payload)
.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 https://<short domain>/redirections/:shortId
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
-X DELETE 'https://tn.gg/redirections/s87dfhs2'
const thngId = 'Uk6qwgSReXPat5awagfNpHsk';
operator.thng(thngId).redirection('tn.gg')
.delete();
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://$EVT_API_DOMAIN/auth/evrythng/thngs' \
-d '{
"thngId": "UBPWKPA3qqtyrPe7QQm9hrrf"
}'
const thngId = 'UBPWKPA3qqtyrPe7QQm9hrrf';
evrythng.api({
url: '/auth/evrythng/thngs',
method: 'POST',
apiKey,
data: { 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://$EVT_API_DOMAIN/auth/evrythng/thngs/UBPWKPA3qqtyrPe7QQm9hrrf'
const thngId = 'UBPWKPA3qqtyrPe7QQm9hrrf';
evrythng.api({
url: `/auth/evrythng/thngs/${thngId}`,
apiKey,
}).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://$EVT_API_DOMAIN/auth/evrythng/thngs/UBPWKPA3qqtyrPe7QQm9hrrf'
const thngId = 'UBPWKPA3qqtyrPe7QQm9hrrf';
evrythng.api({
url: `/auth/evrythng/thngs/${thngId}`,
method: 'DELETE',
apiKey,
}).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 Device
scope. Remember that conceptually a device is a Thng. Therefore the Device
scope has direct access to everything related to the Thng (actions, properties, locations) - see API Key Scopes and Permissions for more information.
Example: evrythng.js
const device = new evrythng.Device({ apiKey });
const payload = {
customFields: { foo: 'bar' },
};
device.update(payload).then(console.log);
// create
const payload = [
{ key: 'temperature', value: 30 },
];
device.property().create(payload)
.then(console.log);
// read
device.property('temperature').read()
.then(console.log);
// update
device.property('temperature').update(52);
device.property('status').update(true);
// bulk update
const payload = { temperature: 52, status: true };
device.property().update(payload);
const payload = [
{ value: 52, timestamp: 1430228888000 },
{ value: 70, timestamp: 1430228900000 },
];
device.property('temperature').update(payload);
See Properties for more examples on using the property resource.
// create
const payload = { type 'scans' };
device.action('scans').create(payload)
.then(console.log);
// read
device.action('all').read()
.then(console.log);
See Actions for more examples on using the /actions
resource.