— Adobe, AEP, Experience Platform, Data Collection, Web SDK, Target, at.js, alloy.js — 5 min read
This guide assumes familiarity with:
This guide requires access to the following Adobe solutions:
Experience Data Model (XDM) schemas are the building blocks, principles, and best practices for composing schemas in Adobe Experience Platform.
Platform Web SDK uses your schema to standardize your web event data, send it to the Platform Edge Network, and ultimately forward the data to any Experience Cloud applications configured in the datastream. This step is critical as it defines a standard data model required for ingesting customer experience data into Experience Platform and enables downstream services and applications built on these standards.
:point_right: Checklist |
---|
For Target, add the recommended pre-defined field groups for web data collection:
When defining your Schema, your data will be passed in the xdm
object of payload and visible to all configured Adobe solution.
Alternatively, you can pass data to Target only via data
object.
If Target custom parameters are moved to the XDM Schema, the path in the Audience builder will differ and needs to be updated for Audiences and Profile Scripts.
The XDM is serialized using dot notation (for example, web.webPageDetails.name
).
:point_right: Checklist |
---|
The Adobe Experience Platform Identity Service sets a common visitor ID across all Adobe solutions in order to power Experience Cloud capabilities such as audience-sharing between solutions. You can also send your own customer IDs to the Service to enable cross-device targeting and integrations with other systems, such as your Customer Relationship Management (CRM) system.
:point_right: Checklist |
---|
targetMigrationEnabled
and idMigrationEnabled
parameters to true
in the configuration. This helps where some pages still coninue to use Visitor.js
and at.js
.Code below sends identity data to AEP:
1alloy("sendEvent", {2 xdm: {3 "identityMap": {4 "ID_NAMESPACE": [5 {6 "id": "1234",7 "authenticatedState": "ambiguous",8 "primary": true9 }10 ]11 }12 }13});
Target must be enabled in the datastream configuration before any Target activities can be delivered by the Platform Web SDK.
:point_right: Checklist |
---|
There are three supported ways to use Adobe Experience Platform Web SDK:
For technical clarification purposes, this guide references the CDN approach even though loading Web SDK via the Data Collection UI would be the most simple and recommended way.
Web SDK's alloy.js can be loaded synchronously or asynchronously.
:point_right: Checklist |
---|
1<script>2 !function(n,o){o.forEach(function(o){n[o]||((n.__alloyNS=n.__alloyNS||3 []).push(o),n[o]=function(){var u=arguments;return new Promise(4 function(i,l){n[o].q.push([i,l,u])})},n[o].q=[])})}5 (window,["alloy"]);6</script>7<script src="https://cdn1.adoberesources.net/alloy/2.6.4/alloy.min.js"></script>8<script>9alloy("configure", {10 "edgeConfigId": "ebebf826-a01f-4458-8cec-ef61de241c93",11 "orgId":"ADB3LETTERSANDNUMBERS@AdobeOrg",12 "prehidingStyle": "body { opacity: 0 !important }"13});14</script>
It is recommended to set prehidingStyle
to body { opacity: 0 !important }
. This ensures that the whole page is pre-hidden during personalization call.
:point_right: Checklist |
---|
1<script>2 !function(n,o){o.forEach(function(o){n[o]||((n.__alloyNS=n.__alloyNS||3 []).push(o),n[o]=function(){var u=arguments;return new Promise(4 function(i,l){n[o].q.push([i,l,u])})},n[o].q=[])})}5 (window,["alloy"]);6 //Pre-hiding snippet7 !function(e,a,n,t){8 if (a) return;9 var i=e.head;if(i){10 var o=e.createElement("style");11 o.id="alloy-prehiding",o.innerText=n,i.appendChild(o),12 setTimeout(function(){o.parentNode&&o.parentNode.removeChild(o)},t)}}13 (document, document.location.href.indexOf("adobe_authoring_enabled") !== -1, "body { opacity: 0 !important }", 3000);14</script>15<script src="https://cdn1.adoberesources.net/alloy/2.6.4/alloy.min.js" async></script>16<script>17alloy("configure", {18 "edgeConfigId": "ebebf826-a01f-4458-8cec-ef61de241c93",19 "orgId":"ADB3LETTERSANDNUMBERS@AdobeOrg"20});21</script>
renderDecisions
to EventsTo enable Visual Experience Composer (VEC) experiences delivery, add renderDecisions: true
to Web SDK event calls.
This also delivers custom code experiences set up via VEC.
There is no need to include decisionScopes: __view__
as it is automatically added by the Web SDK.
This __view__
scope is a signal to fetch all the page-load activities from Target and prefetch all views.
:point_right: Checklist |
---|
renderDecisions
option for VEC-based Activities. 1alloy("sendEvent", {2 renderDecisions: true,3 xmd: {},4 data: {}5});
decisionScopes
to EventsSetting decisionScopes
with a list of custom scopes (a.k.a. mboxes
or Target Locations
in older terms) retrieves qualifying Target Activities created with the form-based composer.
:point_right: Checklist |
---|
decisionScopes
option for the Form-based Activities.1alloy("sendEvent", {2 decisionScopes: ["scope1", "scope2"],3 xmd: {},4 data: {}5});
Note that custom scope experiences will need to be rendered manually using code. See more information below.
It is advised to move custom parameters to XDM Schema. This also changes how Audiences work because the path to a custom parameter from XDM becomes different (eg., web.webPageDetails.name
).
However, free form data is also supported inside data.__adobe.target
object:
:point_right: Checklist |
---|
xdm
or data
objects as needed. 1alloy("sendEvent", {2 renderDecisions: true,3 xdm: {},4 data: {5 __adobe: {6 target: {7 "profile.gender": "female",8 "entity.id": 1234,9 "user.categoryId": "outerwear,jackets",10 "foo": "bar"11 }12 }13 }14 });
For response tokens, subscribe to alloy.js sendEvent promise, iterate through propositions
and extract the details from items -> meta
.
:point_right: Checklist |
---|
1alloy("sendEvent", {2 renderDecisions: true,3 xdm: {},4 data: {}5 }).then(function(result) {6 handleResponseTokens(result); //custom method that processes response tokens, see definition example below7 });
Recommnedations entity data is sent as custom parameters in data object.
:point_right: Checklist |
---|
entity.id
and any other required entity.
data to data.__adobe.target
object.1alloy("sendEvent", {2 renderDecisions: true,3 data: {4 __adobe: {5 target: {6 "entity.id": "123",7 "entity.genre": "Drama"8 }9 }10 }11 });
On the first sendEvent()
call, all XDM Views that should be rendered to the end-user will be fetched and cached. Subsequent sendEvent()
calls with XDM Views passed in will be read from the cache and rendered without a server call.
:point_right: Checklist |
---|
1alloy("sendEvent", {2 "renderDecisions": true, //<--3 "xdm": {4 "web": {5 "webPageDetails": {6 "viewName":"home" //<--7 }8 }9 }10 });
There may be a case where we want to prefetch content and display on demand
1alloy("sendEvent", {2 renderDecisions: false,3}).then(function(result) {4 return alloy("applyPropositions", {5 propositions: retrievedPropositions,6 metadata: getMetadata(scopes)7 }).then(function(applyPropositionsResult) {8 alloy("sendEvent", { // Send the display notifications9 xdm: {10 eventType: "decisioning.propositionDisplay",11 _experience: {12 decisioning: {13 propositions: applyPropositionsResult.propositions14 }15 }16 }17 });18 handleResponseTokens(applyPropositionsResult);19 });20});21
22function getMetadata(scopes){23 let metadata = {};24 scopes.forEach(function(scope){25 metadata[scope] = {26 selector: "head",27 actionType: "appendHtml"28 }29 });30 return metadata;31}
If decisions are not set to render, they are pre-fetched and we would need to apply content manually and send a display notification event.
:point_right: Checklist |
---|
1function getContentForPropositions(data, props){2 let content = [];3 if (data.propositions) {4 data.propositions.forEach(proposition => {5 proposition.items.forEach(item => {6 if (item.schema === "https://ns.adobe.com/personalization/html-content-item") {7 content.push({8 scope: "",9 });10 }11 });12 });13
14 }15 return content;16}17
18alloy("sendEvent", {19 xdm: {...},20 decisionScopes: ["__view__"]21 }).then(function(result) {22 getContentForPropositions(result, ["myScope1"]);23 if (result.propositions) {24 result.propositions.forEach(proposition => {25 proposition.items.forEach(item => {26 if (item.schema === HTML_SCHEMA) {27 // manually apply offer28 document.getElementById("form-based-offer-container").innerHTML =29 item.data.content;30 const executedPropositions = [31 {32 id: proposition.id,33 scope: proposition.scope,34 scopeDetails: proposition.scopeDetails35 }36 ];37 // manually send the display notification event, so that Target/Analytics impressions are increased38 alloy("sendEvent",{39 "xdm": {40 "eventType": "decisioning.propositionDisplay",41 "_experience": {42 "decisioning": {43 "propositions": executedPropositions44 }45 }46 }47 });48 }49 });50 });51 }52 });
You can prefetch scopes, send notifications, track events and user actions by calling the sendEvent
command, populating the _experience.decisioning.propositions
XDM fieldgroup, and setting the eventType to one of 2 values:
decisioning.propositionDisplay
: Signals the rendering of the Target Activity.decisioning.propositionInteract
: Signals a user interaction with the Activity, like a mouse click.Track a decisioning.propositionDisplay
event after rendering an Activity
1alloy("sendEvent", {2 xdm: {},3 decisionScopes: ['discount']4}).then(function(result) {5 var propositions = result.propositions;6 var discountProposition;7 if (propositions) {8 // Find the discount proposition, if it exists.9 for (var i = 0; i < propositions.length; i++) {10 var proposition = propositions[i];11 if (proposition.scope === "discount") {12 discountProposition = proposition;13 break;14 }15 }16 }17 if (discountProposition) {18 // Find the item from proposition that should be rendered.19 // Rather than assuming there a single item that has HTML20 // content, find the first item whose schema indicates21 // it contains HTML content.22 for (var j = 0; j < discountProposition.items.length; j++) {23 var discountPropositionItem = discountProposition.items[i];24 if (discountPropositionItem.schema === "https://ns.adobe.com/personalization/html-content-item") {25 var discountHtml = discountPropositionItem.data.content;26 // Render the content27 var dailySpecialElement = document.getElementById("daily-special");28 dailySpecialElement.innerHTML = discountHtml;29
30 // For this example, we assume there is only a single place to update in the HTML.31 break;32 }33 }34 // Send a "decisioning.propositionDisplay" event signaling that the proposition has been rendered.35 alloy("sendEvent", {36 xdm: {37 eventType: "decisioning.propositionDisplay",38 _experience: {39 decisioning: {40 propositions: [41 {42 id: discountProposition.id,43 scope: discountProposition.scope,44 scopeDetails: discountProposition.scopeDetails45 }46 ]47 }48 }49 }50 });51 }52});
Track a decisioning.propositionInteract
event after a click metric occurs
1alloy("sendEvent", {2 xdm: { ...},3 decisionScopes: ["hero-banner"]4}).then(function (result) {5 var propositions = result.propositions;6
7 if (propositions) {8 // Find the discount proposition, if it exists.9 for (var i = 0; i < propositions.length; i++) {10 var proposition = propositions[i];11 for (var j = 0; j < proposition.items.length; j++) {12 var item = proposition.items[j];13
14 if (item.schema === "https://ns.adobe.com/personalization/measurement") {15 // add metric to the DOM element16 const button = document.getElementById("form-based-click-metric");17
18 button.addEventListener("click", event => {19 const executedPropositions = [20 {21 id: proposition.id,22 scope: proposition.scope,23 scopeDetails: proposition.scopeDetails24 }25 ];26 // send the click track event27 alloy("sendEvent", {28 "xdm": {29 "eventType": "decisioning.propositionInteract",30 "_experience": {31 "decisioning": {32 "propositions": executedPropositions33 }34 }35 }36 });37 });38 }39 }40 }41 }42});
1alloy("sendEvent", {2 "documentUnloading": true, // sends as a beacon3 "xdm": {4 "eventType": "commerce.purchases",5 "commerce": {6 "order": {7 "purchaseID": "a8g784hjq1mnp3",8 "purchaseOrderNumber": "VAU3123",9 "currencyCode": "USD",10 "priceTotal": 999.9811 }12 }13 }14});
mbox3rdpartyId
to Target:point_right: Checklist |
---|
1alloy("sendEvent", {2 xdm: {3 "identityMap": {4 "ID_NAMESPACE": [5 {6 "id": "1234",7 "authenticatedState": "authenticated"8 }9 ]10 }11 }12 });
The Adobe Experience Platform Web SDK supports two types of Analytics logging for Analytics for Target (A4T) use cases:
:point_right: Checklist |
---|
If you want to add, remove, or modify fields from the event globally, you can configure an onBeforeEventSend callback. This callback is called every time an event is sent. This callback is passed in an event object with an xdm field. Modify content.xdm to change the data that is sent with the event.
1alloy("configure", {2 "edgeConfigId": "ebebf826-a01f-4458-8cec-ef61de241c93",3 "orgId": "ADB3LETTERSANDNUMBERS@AdobeOrg",4 "onBeforeEventSend": function(content) {5 // ignores events from bots6 if (MyBotDetector.isABot()) {7 return false;8 }9 // Change existing values10 content.xdm.web.webPageDetails.URL = xdm.web.webPageDetails.URL.toLowerCase();11 // Remove existing values12 delete content.xdm.web.webReferrer.URL;13 // Or add new values14 content.xdm._adb3lettersandnumbers.mycustomkey = "value";15 //sets a query parameter in XDM16 const queryString = window.location.search;17 const urlParams = new URLSearchParams(queryString);18 content.xdm.marketing.trackingCode = urlParams.get('cid')19 }20});
Remove at.js JavaScript library if previously used and any references to at.js API functions.
:point_right: Checklist |
---|
at.js
codeadobe.targe.getOffers
and/or adobe.targe.getOffer
if anyadobe.target.trackEvent
if anywindow.targetGlobalSettings
if anywindow.targetPageParams
and/or window.targetPageParamsAll
if any