Using a CEDDL Data Layer with DTM


I feel that standards are a good thing. I think that each of us should strive to follow standard approaches, because it’ll make all of our lives easier. I also think that a Data Layer is an adornment on every site, and a svelte way of helping analysts and marketing do their work more flexibly.

Since December 2013, there is a standard, published by the W3C, called Customer Experience Digital Data Layer, or CEDDL. You can get the PDF from the W3C, if you want to learn about it.

Since pretty much every vendor in this world has also come up with their own standard (including Adobe with the ContextHub on AEM), the question is: why should I use CEDDL? The most important reason to me is: CEDDL is vendor-agnostic.

So today, let’s look at the best way to use CEDDL along with DTM, shall we?


First thing to do is to add the “AEM ContextHub” tool to your DTM Web Property.

Adding the tool means there’ll be a new Data Element type called “AEM ContextHub” in DTM. Those DEs allow your friendly marketer to select the data they want in a DE from a drop down, which is populated from the structure of the Data Layer.

By using those DEs, plus the “dataelementchanged” trigger, you can create Event-based Rules that fire when some (definable) part of the Data Layer changes, perfect for the event structure in CEDDL.

The ContextHub tool was originally designed to work with the AEM ContextHub, as the name suggests. Luckily, the tool is flexible enough to work with any data layer structure, as long as you have a JSON Schema that describes your DL.

For CEDDL, you can find the schema (as you can so many other things) on github: CEDDL JSON Schema.

So let’s do this!

Start by clicking the “Add Tool” button on the “Overview” tab of your Web Property.

Select “AEM ContextHub” from the drop down (it should actually be selected), give your tool a name (like “Data Layer”), then click the “Create Tool” button.

Create Tool Step 1 – Type & Name
On the AEM ContextHub Settings screen, you can decide whether you want to use the default AEM ContextHub data layer, or another structure. We’ll go for the latter, so select “[x] Customize ContextHub Data Layer”, tell it where to find the DL by typing window.digitalData into the “Data Layer Root” field, then click the “Open Editor” button.

Create Tool Step 2 – Structure
You’ll see a mostly black editor window with some code in it.

Editor with AEM ContextHub Schema
That code is the AEM data layer schema, and we need to replace it with a CEDDL-compatible schema.

Press CTRL-A or Command-A to select all code in the editor,

Code Editor – selected
then hit your Backspace or Delete key.

Code Editor – empty
Cool. We want that editor empty. Nice.

Next, get a CEDDL schema. There is one available on mkohlmyr’s github. You can jump straight to the raw code, then select all (CTRL-A on Windows, Command-A on Mac) and copy the lot (CTRL-C or Command-C).

Raw Schema, selected
Back in DTM, paste the schema code into the editor window (CTRL-V or Command-V).

Code Editor with CEDDL Schema
Save the Tool, and we’re done here.

Data Elements

Now that we have the tool, we can use it to more easily define Data Elements.

As an example, let’s build a “Pagename” DE.

Head over to the “Rules” tab, then click “Data Elements” on the left. Now use the “Create Data Element” button.

Create Data Element – step 1
Give your DE a name, say “Pagename”, then select “AEM ContextHub” in the Type drop down.

Create Data Element – step 2
The dialog changes slightly now.

Note that there are two new drop down in the dialog window now:

  1. Source — if you have multiple DLs on your site (totally possible), this one lets you choose one of them
  2. Object — this drop down is where you pick an element from the DL, say page.pageInfo.pageName

The “Object” drop down is what links the DE to the DL.

Create Data Element – step 3
So select “page.pageInfo.pageName”, and you’re done.

Create Data Element – step 4


CEDDL contains a sub-structure called “event”, which is meant to be dynamically populated as things happen on an already loaded page.

You’ll use this for cart adds, email address submits, tab switches within the page, and other user activity, but also for Javascript-driven content changes.

Each time something happens, there’ll be one more event added to the DL, along with some meta data that explains what happened.

For a cart add, as an example, that meta data would be a product ID, quantity, and maybe other data. For a login, it could be simply whether the login was successful, or why not, and so on.

We build a Data Element that points to the event part of the DL, then we have an EBR listen to changes of that DE.

That allows us to properly handle interaction and dynamic changes on any page. Whether the EBR collects all data, determines the right events and “variables”, and tracks directly, or whether it works as a dispatcher, calling other EBRs or DCRs, is entirely up to you.

Event-based Rule for handling CEDDL events
And that’s it. That’s how you use CEDDL with DTM.

(Btw: I think this article might be used beyond the usual developer audience, so that’s why I specified how to select, copy, and paste. HTH)

25 thoughts on “Using a CEDDL Data Layer with DTM

      1. Sure, websites range massively in their requirements for data layers. The W3C Standard is a good place to start but might require quite a bit of modification in order to be properly scalable to enterprise organisations and websites—of course you might disagree but I wondered what your opinion is.


      2. You’re right, people will modify and amend the standard structure to meet their needs.

        I guess the good thing is that you can, plus other Data Layers need to be amended / modified, too. I don’t think there’s any DL out there that covers everything. So this is neither a pro nor a con for this specific structure.

        And I don’t think it is a reason NOT to use a DL, either. Especially if you have a complex requirement, modelling that via a DL means it is made explicit, which can only be good.

        What are your thoughts?


      3. I agree, I thinks there’s a quite a bit of custom DL design going on in the industry too, right from the ground up and often done by a FED rather than an Analytics Developer.

        Often this is at the expense of optimisation and personalisation tools which can really flex their muscles when supercharged with a good data layer.


  1. Hi all, i’m a little stuck with setting up the schema to match my existing digitalData object – specifically where I have arrays.

    Here’s how my digitalData model looks in pure JS:

    digitalData = window.digitalData || {};
    digitalData.user = window.digitalData.user || [];
    digitalData.user[0] = window.digitalData.user[0] || {};
    digitalData.user[0].profile = window.digitalData.user[0].profile || {};
    digitalData.user[0].profile.profileInfo = window.digitalData.user[0].profile.profileInfo || {}; = || {}; = || {}; = || {}; = || []; = || {};

    note that scope is an array with two objects inside.

    Would anyone be able to assist me with getting the right CEDDL schema for my configuration?

    Any help appreciated! 🙂


      1. Thanks Jan, very helpful! I guess i’m just having some issues with modifying the CEDDL schema to match my existing digitalData object. I just can’t get the arrays to work for some reason, everything else is fine.


      2. How have you done it? Here’s what I did: copy your code, paste it into my browser console on an empty page. That created a digitalData object for me. Then I used JSON.stringify(digitalData) to get it as a string. I copied that string (minus the surrounding “) into the site I quoted before and hit “Generate”.


  2. Yes, that’s what I did however when I go to create a data element in Adobe DTM, and choose “AEM ContextHub” as the type, i’m limited for the “user” scope.

    Here’s a screenshot of what I see:


      1. Ah. I think that is normal.

        Since an array can have 0 to many elements, and since for a specific part of the element, you have to specify the index, you’ll have to build custom JS Data Elements.

        My suggestion: build a DE that retrieves “user”, then build DEs that retrieve individual parts of the user by first reading the other DE (using _satellite.getVar(‘user’)), then checking everything is available, then output the part you need.

        Does that make sense?


  3. Yes, definitely makes sense! I think i’m going to restructure my digitalData object slightly and drop the array from the “user” scope as it seems pointless anyway (i inherited, not designed, the digitalData config here at work lol).


  4. Hi Kevin! I have an issue using AEM ContextHub in cases when the dataLayer is dynamically populates on the sit. If I copy-past the entire dataLayer schema into the tool, and some element do not exist (on the first page load, for example) I’ll get a spinning “SATELLITE: TypeError – Cannot read property ‘xxx’ of undefined” error int the console. Is there a way around this? Or does the ContextHub only work with a static dataLayers? Thank you in advance!


  5. Hi Jan,

    Thanks for this valuable article – as always 🙂

    I’m dealing with a well defined CEDDL in one of my projects right now.

    Coming from GTM and wondering about how to solve this with DTM:

    I have an event array which holds event objects 1-n. There is also an additional last-event object, which mirrors the last event object that got pushed and gets updated after every new push within the event array.

    In case new event objects are pushed one-by-one due to on-site user interactions with a slight delay between them, I’m able to listen to them with a ‘dataelementchanged’ EBR and trigger the associated tags. Everything is working fine.

    My problem:

    There are cases when new events are pushed with less than 1000 ms between them (sometimes even during page load). As far as I can see, with ‘dataelementchanged’ EBR I will lose these intermediate events and can’t execute any tags.


    Event Array
    First push:


    Second push (less than 1000 ms later)


    Last Event Object (get updated with every push)

    ‘dataelementchanged’ EBR on event[1].lastEvent.eventInfo.eventName would only be fired once.

    What would be ‘your way’ to solve this?

    Thanks in advance and best,


    1. Hi Jens,

      Interesting situation, and you won’t like the answer 🙂

      Looking at it from the point of view of an Analytics person, I’d say the fact that only the last one in a quick succession of events triggers an EBR is _exactly_ what I want. Main reason:
      If every event triggered an EBR, I would potentially have a lot of server calls for something that to me sounds like it was triggered by a single visitor action. My analytics heart says “track only one time per action”.

      Now I don’t know what the other events are, exactly, but I was thinking of a simple situation “visitor clicks ‘add to cart’, system sends ‘cart add clicked’ event, ‘cart created’ event, and ‘article added to cart’ event.
      In this situation, I’d fire one call, adding cart add and cart new into the tracking.
      From the perspective of the EBR, I’d probably trigger it off of the ‘article added to cart’ event, and I’d use JS code to go through the elements just before to check whether I would have to add cart new, too.

      Is your use case different?


  6. Hi Jan,

    Thanks a lot for your swift and insightful reply. Your ‘article added to cart’ example makes sense, of course.

    Our CEDDL was built with the intention to completely tool agnostic, so the events that get emitted are not exclusively directed to DTM and tracking.

    Let’s take the following simplified example:

    – User enters a product detail page with a product and a chat-service overlay
    – The CEDDL recieves the following order of events during pageload:

    Event-Name 1: ‘Product-View’
    Event-Name 2: ‘Chat-Service available’
    Last-Event-Name: ‘Chat-Service available’

    I have a EBR with data-element-changed listener for ‘Last-Event-Name’ and an additional check for the value of the Last-Event-Name. In case’Last-Event-Name’ matches ‘Product-View’, I want to set and fill the product object for Adobe Analytics call.

    The EBR and data-element-changed listener for ‘Last-Event-Name’ only receives ‘Chat-Service available’ as a value because of less than 100 ms between both CEDDL additions, so the “Product-View” event is out of reach in this case.

    Like said, I’m coming from GTM: GTM’s dataLayer.push with included event-key enables me to evaluate every event-key that get pushed even when they occur with less than 100 ms between them.

    Knowing this, do you think a script that monitors every change to event array and call _satellite.track(Event-Name(n)) for every single added event object would provide a similar robust solution for this use-case? Are you aware if _satellite.track have similar timing constraints as data-element-changed?

    Thanks a lot in advance and best,


    1. Thought so 😉

      From the perspective of DTM and Analytics, the scenario you describe would be covered via Page Load Rule, not EBR or DCR.
      You’d try to make it so the data layer events are done before the PLR fires, maybe by triggering the PLR on document ready or even window onload.
      It really doesn’t make sense to track every single one of your events in Analytics, and it would be too expensive to do so.
      I get the idea of being non-tool-dependent. For the use case you listed, a PLR would be the answer, I think. In other cases, a single EBR might do it, or you’d have multiple DCRs (and the associated cost due to multiple server calls).
      Not sure whether calling DCRs in rapid succession would work, but you can try in the console using _satellite.track(“a”) inside a for loop…


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.