Product resources are used to model a class of individual items, and as such should contain information common to all instances of that product. For example, a product resource could be used to describe a particular SKU or model of a physical product with specific characteristics including size, weight, color, market, GS1 identifiers etc.

Once created, each Thng that represents an individual instance of that product class is associated with the product resource containing the common information via its product field. 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).

Like Thngs, products can have properties and actions associated with them. In addition to specifying a product in a generic action request, it is also possible to create an action through the product itself using aliased actions.


API Status
General Availability:
/products
/products/:productId
/products/:productIdentifier
/products/:productId/actions/:actionType
/products/:productId/properties


ProductDocument Data Model

An object representing a platform product.

.name (string, required)
    Friendly name of this resource.

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

.color (string)
    Color of the product itself

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

.type (string)
    Type of this resource. Can be unset with an empty string.

.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 (IdentifiersDocument)
    Various identifiers (EPC, GTIN, etc.) as a JSON object with 
    one or more key-value pairs. Identifiers which have keys 
    mentioned in account unique identifiers configuration will 
    be concatenated and used as unique identifier for a given 
    product which means that user will not be able to create 
    two products with the same unique identifiers.

.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 (CustomFieldsDocument)
    Object of case-sensititve key-value pairs of custom fields 
    associated with the resource.

.fn (string, read-only)
    Friendly name of the product.

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

.scopes (ScopesDocument)
    Project and user scopes arrays.
{
  "additionalProperties": false,
  "type": "object",
  "description": "An object representing a platform product.",
  "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
    },
    "color": {
      "type": "string",
      "description": "Color of the product itself"
    },
    "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 product was activated.",
      "readOnly": true
    },
    "description": {
      "type": "string",
      "description": "Friendly description of this resource."
    },
    "type": {
      "type": "string",
      "description": "Type of this resource. Can be unset with an empty string.",
      "maxLength": 256
    },
    "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, GTIN, etc.) as a JSON object with one or more key-value pairs. Identifiers which have keys mentioned in account unique identifiers configuration will be concatenated and used as unique identifier for a given product which means that user will not be able to create two products with the same unique identifiers."
    },
    "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.",
      "readOnly": true
    },
    "brand": {
      "type": "string",
      "description": "The product's brand name."
    },
    "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": [
    {
      "name": "name",
      "type": "string",
      "operators": ["="]
    },
    {
      "name": "identifiers.<key>",
      "type": "string",
      "operators": ["="]
    },
    {
      "name": "tags",
      "type": "list of string",
      "operators": ["="]
    }
  ]
}
{
    "name":"T-shirt",
    "identifiers": {
        "gs1:01":"00995031756029",
        "gs1:22":"00117765745435"
    }
}

See also: ScopesDocument

Filterable Fields

This resource type can be filtered using the following fields and operators.

FieldTypeOperators
nameString=
identifiers.<key>String=
tagsList of string=

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 '{
    "tags": [
      "example",
      "product"
    ],
    "brand": "AS Garments Ltd.",
    "name": "Soft Blue Tee",
    "description": "An example product resource",
    "photos": [
      "https://upload.wikimedia.org/wikipedia/commons/8/84/Example.svg"
    ],
    "identifiers": {
      "gs1:01": "00043834898332"
    }
  }'
const payload = {
  tags: [
    'example',
    'product'
  ],
  brand: 'AS Garments Ltd.',
  name: 'Soft Blue Tee',
  description: 'An example product resource',
  photos: [
    'https://upload.wikimedia.org/wikipedia/commons/8/84/Example.svg'
  ],
  identifiers: {
    'gs1:01': '00043834898332'
  }
};

operator.product().create(payload)
  .then(console.log);
Product product = new Product();
product.setName("DLP-46EX");
product.setDescription("Example Product");
apiManager.productService().productCreator(product).execute();
HTTP/1.1 201 Created
Content-Type: application/json
Location: https://api.evrythng.com/products/UrahkCNHqc8p9bwRwmrfBe3t

{
  "id": "UrahkCNHqc8p9bwRwmrfBe3t",
  "createdAt": 1560774106285,
  "tags": [
    "example",
    "product"
  ],
  "updatedAt": 1560774106285,
  "brand": "AS Garments Ltd.",
  "description": "An example product resource",
  "fn": "Soft Blue Tee",
  "name": "Soft Blue Tee",
  "photos": [
    "https://upload.wikimedia.org/wikipedia/commons/8/84/Example.svg"
  ],
  "identifiers": {
    "gs1:01": "00043834898332"
  }
}

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/UrahkCNHqc8p9bwRwmrfBe3t'
const productId = 'UrahkCNHqc8p9bwRwmrfBe3t';

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

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

{
  "id": "UrahkCNHqc8p9bwRwmrfBe3t",
  "createdAt": 1560774106285,
  "tags": [
    "example",
    "product"
  ],
  "updatedAt": 1560774106285,
  "brand": "AS Garments Ltd.",
  "description": "An example product resource",
  "fn": "Soft Blue Tee",
  "name": "Soft Blue Tee",
  "photos": [
    "https://upload.wikimedia.org/wikipedia/commons/8/84/Example.svg"
  ],
  "identifiers": {
    "gs1:01": "00043834898332"
  }
}

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": "UrahkCNHqc8p9bwRwmrfBe3t",
    "createdAt": 1560774106285,
    "tags": [
      "example",
      "product"
    ],
    "updatedAt": 1560774106285,
    "brand": "AS Garments Ltd.",
    "description": "An example product resource",
    "fn": "Soft Blue Tee",
    "name": "Soft Blue Tee",
    "photos": [
      "https://upload.wikimedia.org/wikipedia/commons/8/84/Example.svg"
    ],
    "identifiers": {
      "gs1:01": "00043834898332"
    }
  }
]

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"
  }'
const productId = 'UXGmadd2EwqQkXe3F5Gg4hbg';

const payload = {
  name: 'New Product Name',
};

user.product(productId).update(payload)
  .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": "UrahkCNHqc8p9bwRwmrfBe3t",
  "createdAt": 1560774106285,
  "tags": [
    "example",
    "product"
  ],
  "updatedAt": 1560774106285,
  "brand": "AS Garments Ltd.",
  "description": "An example product resource",
  "fn": "New Product Name",
  "name": "New Product Name",
  "photos": [
    "https://upload.wikimedia.org/wikipedia/commons/8/84/Example.svg"
  ],
  "identifiers": {
    "gs1:01": "00043834898332"
  }
}

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!'));
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 "Test" to contain the 'test' tag.

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

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

operator.product().update(payload, { params })
  .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

Use the appropriate short domain API to create a product redirection. This example uses tn.gg.

POST https://<short domain>/redirections
Content-Type: application/json
Authorization: $OPERATOR_API_KEY

RedirectionDocument
curl -H "Content-Type: application/json" \
  -H "Authorization: $OPERATOR_API_KEY" \
  -X POST 'https://tn.gg/redirections' \
  -d '{
    "type": "product",
    "evrythngId": "Uk6qwgSReXPat5awagfNpHsk",
    "defaultRedirectUrl": "https://www.example.com?product={productId}"
  }'
const productId = 'Uk6qwgSReXPat5awagfNpHsk';

// 'type' and 'evrythngId' are handled by the SDK
const payload = { 
  defaultRedirectUrl: 'https://www.example.com?product={productId}'
};

operator.product(productId).redirection('tn.gg')
  .create(payload)
  .then(console.log);
HTTP/1.1 201 Created
Content-Type: application/json

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

Read a Product's Redirection

Use the appropriate short domain redirection API to read a product redirection.

GET https://<short domain>/redirections?evrythngId=:productId
Authorization: $OPERATOR_API_KEY
Accept: application/json
curl -H "Authorization: $OPERATOR_API_KEY" \
  -H "Accept: application/json" \ 
  -X GET 'https://tn.gg/redirections?evrythngId=Uk6qwgSReXPat5awagfNpHsk'
const productId = 'Uk6qwgSReXPat5awagfNpHsk';

operator.product(productId).redirection('tn.gg')
  .read()
  .then(console.log);
HTTP/1.1 200 OK
Content-Type: application/json

{
  "createdAt": 1496664729327,
  "updatedAt": 1496664729327,
  "defaultRedirectUrl": "https://www.example.com?product=Uk6qwgSReXPat5awagfNpHsk",
  "evrythngUrl": "https://api.evrythng.com/products/Uk6qwgSReXPat5awagfNpHsk",
  "shortDomain": "tn.gg",
  "shortId": "799yeEi4qp",
  "hits": 0
}

Update a Product's Redirection

Use the appropriate short domain redirection API and short ID to update a product 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/799yeEi4qp' \
  -d '{
    "defaultRedirectUrl": "https://www.brand.com?product={productId}"
  }'
const productId = 'Uk6qwgSReXPat5awagfNpHsk';
const payload = { 
  defaultRedirectUrl: 'https://www.brand.com?product={productId}'
};

operator.product(productId).redirection('tn.gg')
  .update(payload)
  .then(console.log);
HTTP/1.1 201 Created
Content-Type: application/json

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

Delete a Product's Redirection

DELETE https://<short domain>/redirections/:shortId
Authorization: $OPERATOR_API_KEY
curl -H "Authorization: $OPERATOR_API_KEY" \
  -X DELETE 'https://tn.gg/redirections/799yeEi4qp'
const productId = 'Uk6qwgSReXPat5awagfNpHsk';

operator.product(productId).redirection('tn.gg')
  .delete();
HTTP/1.1 200 OK
Content-Type: application/json