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. The end result of a redirection that involves Redirector rules is resolved on each and every scan or short URL access.

To achieve this, each account and contained application contains a set of Redirector rules which process all incoming scan and implicitScan actions (custom action types can also trigger rules if they meet the rule match criteria). The rules contained in the Redirector are processed every time one of these actions is created, by the application, an Application User, or other API key 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 individual application if required.

When a rule is triggered by an action of type implicitScans and delegates the final redirection to an individual application, the action and any associate Thng and/or product will be scoped to that project. This is important when campaigns are being run using serialised codes and means that you do not need to scope anything to the project, when the redirector rule detects that the Thng or product scanned is part of a campaign, the items are automatically scoped.


API Status
General Availability:
/redirector
/projects/:projectId/applications/:applicationId/redirector


Redirection Process

If a short URL associated with a Thng or product is requested by a web browser/app or other request, and a redirection is present, the Redirector will return this redirection URL. If there are any Redirector rules in place for the account/application associated with the resource, these will be run to apply any conditional modifications to the redirection. For example, returning a different URL in different countries or at certain times of day or days of the week. It is this contextual data that makes the Redirector so powerful. Data on the product or Thng scanned can also be used programmatically to influence Redirector rules.

The order of precedence between the three types of redirection available when a match is to be determined is as follows. Note that the scope of the API key used to make the request (if any) plays a part in this process:

1842

Here are some common scenarios, and the type of redirection that would result:

Scenario 1 - A user scans a product QR code with a third-party scanner app, such as Google Lens or the iOS Camera app. Since this request is anonymous, and there are no account-level Redirector rules that match, the product/Thng redirection is output.

Scenario 2 - Same as the above scenario, but there are now account-level rules that match if the product is scanned in Italy. The rule dictates that the Italian product information page is returned, and so the user is directed there instead.

Scenario 3 - A brand-specific web app uses an Application API key or Application User API key to scan a QR code (such as using scanthng.js). Since this is an application-scoped API request, the application-level Redirector rules are consulted. If one matches, it determines the final URL output. if not, the product/Thng's redirection is output instead.


Reactions

The Redirector also adds redirection reaction data to actions after they are created. In the case of the action type implicitScan actions are created by the API when a short URL is resolved (either by accessing the short URL, or scanning the QR code first), and the resolved long URL will be read from the action's reactions and used to redirect the browser. This all happens synchronously upon action creation.

The action will contain a reactions object including details of how the redirection was determined. In the example below, the match expression was thng.tags=offer:

{
  "id": "UmeSC6ebeDPwtpwawFKkgFcc",
  "createdAt": 1497455681787,
  "timestamp": 1497455681787,
  "type": "implicitScans",
  "user": "U4D9dt8WBg8w95wwRE2sNktb",
  "location": {
    "latitude": 51.45,
    "longitude": 0.2167,
    "position": {
      "type": "Point",
      "coordinates": [
        0.2167,
        51.45
      ]
    }
  },
  "locationSource": "geoIp",
  "reactions": [
    {
      "type": "redirection",
      "redirectUrl": "https://google.com",
      "redirectionContext": {
        "constants": {},
        "thng.tags": [
          "offer"
        ]
      }
    }
  ],
  "createdByProject": "UHD9dQVtBXsw9pRaaFy6bheq",
  "createdByApp": "U4g9dQWkeg8wtKRawhgVxHmt",
  "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 new redirection URL or to delegate the decision to one or more application Redirectors if it is configured to do so. Each delegate Redirector may produce a redirection URL in response, but they don't have to. Else, the product/Thng's redirection URL is used.

In case of implicitScan, if no redirection is produced, the default redirect URL for that product or Thng is used.

🚧

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.


Rule Configuration

A Redirector's configuration consists of a list of rules. The rules are processed one after the other in sequence from the first to the last. 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, including Regex Match (~) operator (see Filters). An example is shown below.

product.identifiers.upc=12345&dayOfWeek=sun

In addition, * wildcard can be used with Equal (=) operator to match any symbols. * should be the first or last character of the right-hand side value of the expression. For example:

product.name=*xampl*

Regular expression match example:

parameters.header.User-Agent~\\.*\\(Android\\|iOS\\)\\.*

Redirect URL

The redirect URL in each rule is used to redirect the user if a rule matches. The URL can be any valid URI protocol, not just https. Some other examples of resources a redirection rule could direct users are shown below:

  • mailto:[email protected] - Open a draft email in the native email client.
  • market://details?id=org.example.foo - Open an app in the Google Play Store.
  • tel:07794478002 - Open a phone number in the native phone app.

The redirect URL can also reference fields from its context (such as the resource involved in producing the redirection action) in the path, the query and the fragment parts of the URL. In order to reference a field, you must put it inside braces. Braces are not allowed for any other usage.

For example, to automatically insert the ID of the Thng an action was created on, the redirect URL should include {thng.id}:

https://www.example.com?thng={thng.id}

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 below for a full list of available template fields that you can use in this fashion.


Redirection Context

During the process of a redirection, there is a redirectionContext attached to the action, which contains information about various data 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: which Application User created the action. User-related context parameters are empty for account level Redirector rules.
  • place: the action's place, if specified. Must be in project scope when used in application level Redirector rules. See below for a full example of using this context.
  • time: when the action was created. Additional values for time elements are also available (see below).
  • constants: "key: value" pairs (as many as required).
  • parameters: HTTP Headers and Query Parameters, part of the original request. Note: at the moment only supported are: linkType query parameter and accept, accept-language and user-agent headers.

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

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/GroupDescriptionFields
thngThe action's target thngid, name, description, identifiers.<key>, customFields.<key>, tags
productThe action's target productid, name, description, identifiers.<key>, customFields.<key>, tags
userThe Application User that created the actiongender, age, customFields.<key>, locale
TimeThe current time & datetime, timeOfDay, dayOfWeek, dayOfMonth, month, year, timezone
actionThe action createdcustomFields.<key>, type
locationThe action's locationlat, lon
placeThe action's place, if specifiedid, tags, customFields.<key>, distance (m)
ConstantsThe Redirector rule constantsAs specified in the rule
projectThe application's projectid, name, description, customFields.<key>, identifiers.<key>, tags
applicationThe applicationid, name, description, customFields.<key>, tags
CountryAction's location countrycountry
parametersHeaders and Query ParametersqueryString.<key>, header.<key>

Using the Place Context

The place Redirector context fields will only resolve if an action contains a place and/or a relative position to compare it to in the location property. The locationSource must be place or sensor for the comparison to occur.

For example, the action below was submitted a distance away from a known place. In this case, the {place.distance} field will resolve to the distance between the position and the place.

{
  "type": "scans",
  "thng": "Un8xQkeAVgPw9KRaRYHb6n5n",
  "locationSource": "sensor",
  "location": {
    "place": "Unaxsf2P6G8hEqaaREPHmHsn",
    "position": {
      "type": "Point",
      "coordinates": [
        -0.032958984375, 45.4524242413431
      ]
    }
  }
}

For a given redirectUrl containing place context elements:

https://evrythng.com?id={place.id}&distance={place.distance}

The resulting action will contain a reactions list with a redirectUrl with the placeholders suitably filled in:

"reactions": [
  {
    "type": "redirection",
    "redirectUrl": "https://evrythng.com?id=Unaxsf2P6G8hEqaaREPHmHsn&distance=676617.8091136938",
    "redirectionContext": {
      "place.distance": 676617.8091136938,
      "place.id": "Unaxsf2P6G8hEqaaREPHmHsn",
      "thng.name": "test"
    }
  }
]

Redirection Scoping

Redirection scoping allows consumer interactions on a product or Thng to be scoped to an equivalent campaign or project in the EVRYTHNG platform. It is essential that this is used for campaigns involving serialised codes, but also may be beneficial for non serialised campaigns.

For example: for all scans in December in the USA of Toys, run Santas Campaign, and automatically scope the action, the Thng and the product to the resolved campaign.

898

The following default action types will trigger project scoping:

  • implicitScans
  • scans

Other action types will not scope resources when a redirector rule matches. Custom action types, by default will not perform redirection scoping. To enable this additional functionality, add the tag scope-on-redirect to the action type itself.

850

Account Level Redirector

The account level Redirector handles all redirections that are not associated with an application, or where no application-scoped API key was used to create the action (i.e: it was created anonymously).


AccountRedirectorRulesDocument Data Model

.rules (array of AccountRedirectorRuleDocument, required)
    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.
{
  "additionalProperties": false,
  "type": "object",
  "description": "An object representing the account's Redirector rules",
  "required": ["rules"],
  "properties": {
    "rules": {
      "description": "The rules set up for a Redirector.",
      "type": "array",
      "items": {
        "additionalProperties": false,
        "type": "object",
        "description": "An object containing a single Redirector rule set.",
        "required": ["match", "redirectUrl"],
        "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": {
              "additionalProperties": false,
              "type": "object",
              "description": "A single app/project delegate.",
              "required": ["app", "project"],
              "properties": {
                "app": {
                  "type": "string",
                  "description": "The delegate application ID.",
                  "pattern": "^[abcdefghkmnpqrstwxyABCDEFGHKMNPQRSTUVWXY0123456789]{24}$"
                },
                "project": {
                  "type": "string",
                  "description": "The delegate project ID.",
                  "pattern": "^[abcdefghkmnpqrstwxyABCDEFGHKMNPQRSTUVWXY0123456789]{24}$"
                }
              }
            }
          },
          "constants": {
            "type": "object",
            "description": "Key-value pairs (both must be strings). User can add as many as needed."
          }
        }
      }
    },
    "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
    }
  }
}

AccountRedirectorRuleDocument Data Model

.match (string, required)
    Filter used to evaluate this rule. Will always match if this 
    field is not set.

.name (string)
    Friendly name of this resource.

.redirectUrl (string, required)
    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.
{
  "additionalProperties": false,
  "type": "object",
  "description": "An object containing a single Redirector rule set.",
  "required": ["match", "redirectUrl"],
  "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": {
        "additionalProperties": false,
        "type": "object",
        "description": "A single app/project delegate.",
        "required": ["app", "project"],
        "properties": {
          "app": {
            "type": "string",
            "description": "The delegate application ID.",
            "pattern": "^[abcdefghkmnpqrstwxyABCDEFGHKMNPQRSTUVWXY0123456789]{24}$"
          },
          "project": {
            "type": "string",
            "description": "The delegate project ID.",
            "pattern": "^[abcdefghkmnpqrstwxyABCDEFGHKMNPQRSTUVWXY0123456789]{24}$"
          }
        }
      }
    },
    "constants": {
      "type": "object",
      "description": "Key-value pairs (both must be strings). User can add as many as needed."
    }
  }
}

DelegateDocument Data Model

.app (string, required)
    The delegate application ID.

.project (string, required)
    The delegate project ID.
{
  "additionalProperties": false,
  "type": "object",
  "description": "A single app/project delegate.",
  "required": ["app", "project"],
  "properties": {
    "app": {
      "type": "string",
      "description": "The delegate application ID.",
      "pattern": "^[abcdefghkmnpqrstwxyABCDEFGHKMNPQRSTUVWXY0123456789]{24}$"
    },
    "project": {
      "type": "string",
      "description": "The delegate project ID.",
      "pattern": "^[abcdefghkmnpqrstwxyABCDEFGHKMNPQRSTUVWXY0123456789]{24}$"
    }
  }
}

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'
operator.redirector().read()
	.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 "Authorization: $OPERATOR_API_KEY" \
  -X PUT 'https://api.evrythng.com/redirector' \
  -d '{
    "rules": [{
      "match": "thng.tags=shipped",
      "delegates": [{
        "app": "Ue9G3eMPNVMgesY6GVfGadBg",
        "project": "UB9mGBRy7e6XB8hM3BfGwded"
      }]
    }]
  }'
const payload = {
  rules: [{
    name: 'Shipped Rule',
    match: 'thng.tags=shipped',
    redirectUrl: 'https://www.brand.com/inspection/shipped?id={thngId}',
    deletgates: [{
      app: 'Uh6GfhFnmgScpnxwMsqmbbBk',
      project: 'Uh5qRBqc4DcmbNbAANtMnrYm',
    }],
  }],
};

operator.redirector().update(payload)
	.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.tags=shipped",
      "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, required)
    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.
{
  "additionalProperties": false,
  "type": "object",
  "description": "An object representing an Application Redirector's rules.",
  "required": ["rules"],
  "properties": {
    "rules": {
      "description": "The rules set up for a Redirector.",
      "type": "array",
      "items": {
        "additionalProperties": false,
        "type": "object",
        "description": "An object containing a single Redirector rule set.",
        "required": ["redirectUrl"],
        "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."
          }
        }
      }
    },
    "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
    }
  }
}

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, required)
    URL to redirect to if the rule matches.

.constants (object)
    Key-value pairs (both must be strings). User can add as many 
    as needed.
{
  "additionalProperties": false,
  "type": "object",
  "description": "An object containing a single Redirector rule set.",
  "required": ["redirectUrl"],
  "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';

operator.project(projectId).application(applicationId)
	.redirector()
	.read()
	.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.tags=shippedt",
      "name": "Shipped Rule",
      "redirectUrl": "https://www.brand.com/inspection/shipped?id={thngId}",
      "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 "Authorization: $OPERATOR_API_KEY" \
  -X PUT 'https://api.evrythng.com/projects/U2meqbNWegsaQKRRaDUmpssr/applications/UF3Vqb7D6G8EhMwaRYgQ2pFc/redirector' \
  -d '{
    "rules": [{
      "match": "thng.tags=shipped",
      "redirectUrl": "https://www.brand.com/inspection/shipped?id={thngId}"
    }]
  }'
const projectId = 'U2meqbNWegsaQKRRaDUmpssr';
const applicationId = 'UF3Vqb7D6G8EhMwaRYgQ2pFc';

const payload = {
  rules: [{
    name: 'Shipped Rule',
    match: 'thng.tags=shipped',
    redirectUrl: 'https://www.brand.com/inspection/shipped?id={thngId}',
    deletgates: [{
      app: 'Uh6GfhFnmgScpnxwMsqmbbBk',
      project: 'Uh5qRBqc4DcmbNbAANtMnrYm',
    }],
  }],
};

operator.project(projectId).application(applicationId)
	.redirector()
	.update(payload)
	.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.tags=shipped",
      "redirectUrl": "https://www.brand.com/inspection/shipped?id={thngId}"
    }
  ]
}