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

.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.

FieldTypeOperators
collectionsList of string=
createdAtNumber<, >
identifiers.<key>String=
nameString=
productString=
tagsList 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, 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://$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.