Written by The Jahia Team
   Estimated reading time:

Apache Unomi gathers information about users actions, information that is processed and stored by Unomi services. The collected information can then be used to personalize content, derive insights on user behavior, categorize the profiles into segments along user-definable dimensions or acted upon by algorithms.

Items and scope

Unomi structures the information it collects using the concept of Item which provides the base the context server needs to process and store the data. Items are persisted according to their type (structure) and identifier (identity). This base structure can be extended, if needed, using properties in the form of key-value pairs.

Additionally, tracked items are also gathered by scope which allows the context server to group together related items. Scopes usually pertain to a given site being analyzed, though they can span across sites depending on the desired analysis granularity. Scopes allow clients accessing the context server to filter data to only see relevant data.

    itemType: <type of the item>,
    scope: <scope>,
    itemId: <item identifier>,
    properties: <optional properties>

Unomi defines a built-in scope (called systemscope) that clients can use to share data across scopes.


Users' actions are conveyed from clients to the context server using events. Of course, the required information depends on what is collected and users' interactions with the observed systems but events minimally provide a type, a scope and source and target items. You can imagine an event as being a sentence, the event's type being the verb, the source the subject and the target the object:

    eventType: <type of the event>,
    scope: <scope of the event>,
    source: <Item>,
    target: <Item>,
    properties: <optional properties>


By processing events, Unomi progressively builds a picture of who the user is and how they behave. This knowledge is embedded in Profile objects. A profile is an Item with any number of properties and optional segments and scores. Unomi provides default properties to cover common data (name, last name, age, email, etc.) as well as default segments to categorize users. Unomi users are, however, free and even encouraged to create additional properties and segments to better suit their needs.

Items and types

Any information that is processed by and stored in Unomi is structured as an Item. However, items only define a basic, generic structure. Types provide additional structure and semantics to generic items. By defining a new type, users specify which properties (including the type of value they accept) are available to items of that specific type.

Some types can be dynamically defined at runtime by calling the REST API while other extensions are done via Unomi plugins. Part of extending Unomi, therefore, is a matter of defining new types and specifying which kind of Unomi entity (e.g. profiles) they can be affected to. For example, the following JSON document can be passed to Unomi to declare a new property type identified (and named) tweetNb, tagged with the social tag, targeting profiles and using the integer value type.

    itemId: 'tweetNb', 
    itemType: 'propertyType',
    metadata: {
        id: 'tweetNb',
        name: 'tweetNb'
    tags: ['social'],
    target: 'profiles',
    type: 'integer'

Unomi defines default value types: date, email, integer and string, all pretty self-explanatory. While you can think of these value types as "primitive" types, it is possible to extend Unomi by providing additional value types.

Providing context to Unomi

In order to be able to build a profile based on user actions, Unomi requires data. This data is provided by clients which can choose how to do so. A convenient way to store and provide that contextual information is to leverage the Customer Experience Digital Data Layer digitalData object that is injected into the browser’s window object.

Using Marketing Factory

Jahia Marketing Factory makes it easier to provide the required information to the context server by automatically populating and injecting the digitalData object for the benefit of any javascript application running on a site for which Marketing Factory has been activated.

Marketing Factory populates the digitalData object with metadata that can be sent to the context server, notably the scope configured for the site (which is usually the site's key), metadata about the site and the current page. Marketing Factory also adds to this data the base URL for the context server:

window.digitalData = {
  "scope": <current scope>,
  "site": {
    "siteInfo": {
      "siteID": <site identifier>
  "page": {
    "pageInfo": {
      "pageID": <page identifier>,
      "pageName": <page name>,
      "pagePath": <relative path of the page from the site root>,
      "destinationURL": <the current URL>,
      "referringURL": <the URL from which we came from if any>,
      "language": <the current language>
    "category": {},
    "attributes": {}
  "contextServerPublicUrl": <base URL where the context server is configured>

After Marketing Factory loads the initial page the user is viewing, it contacts the context server to retrieve default context information automatically. This results in a cxs object being injected in the global javascript variable space, thus making it available to any javascript code running on the page:

cxs = {
    profileId: <identifier of the current user's profile>,
    sessionId: <identifier of the current user's session>,
    profileProperties: <optional, mapping profile property names to their values>,
    sessionProperties: <optional, mapping session property names to their values>,
    profileSegments: <optional array of segments the user matches>,
    filteringResults: <optional array of filtering results>,
    trackedConditions: <optional array of tracked conditions>

Since the cxs object contains the identifiers for the profile and session associated with our user, we could use this information to retrieve more details from the context server.

Without Marketing Factory

If you are not using Marketing Factory, it is still possible (of course!) to provide context to Unomi. You, however, require a little bit more work. In order to replicate the digitalFactory object as provided by Marketing Factory in your Digital Experience Manager components, you could use the following code in your JSPs:

<%--@elvariable id="renderContext" type="org.jahia.services.render.RenderContext"--%>
<%--@elvariable id="resource" type="org.jahia.services.render.Resource"--%>

<c:set var="scriptURL" value='${renderContext.request.secure ? "https://localhost:9443" : "http://localhost:8181"}'/>
<c:set var="pageName" value='${fn:escapeXml(resource.node.displayableName)}'/>

<template:addResources type="inlinejavascript">
    <script type="application/javascript">
        window.digitalData = window.digitalData || {
                "scope": "${renderContext.site.siteKey}",
                "site": {
                    "siteInfo": {
                        "siteID": "${resource.node.resolveSite.identifier}"
                "page": {
                    "pageInfo": {
                        "pageID": "${resource.node.identifier}",
                        "pageName": "${pageName}",
                        "pagePath": "${resource.node.path}",
                        "destinationURL": document.location.href,
                        "referringURL": document.referrer,
                        "language": "${resource.locale}"
                "contextServerPublicUrl": "${scriptURL}"

Note, however, that we hardcode the Unomi server’s URL in the javascript.

Using neither Marketing Factory, nor Digital Experience Manager

Of course, you can still interact with Unomi without either Marketing Factory or Digital Experience Manager, you would just need to provide the appropriate contextual information based on your application needs and constraints.