— Adobe, AEP, AEP SDK, Mobile, Server-side, Proxy — 4 min read
During my consulting practice, I am often asked to share best practices on how Adobe Target can work outside of mobile AEP SDK in the apps. The reason is that some enterprise apps are architectured in a way where content is fully or partially driven by a web server API - the external server that delivers content. Therefore, implementing AEP SDK client-side in the app is not an option, even though Adobe recommends client-side implementaiton as a best practice. To implement Target server-side, we need to let SDK generate and pass Target-related data into an external API service.
This document describes how Adobe Target can operate in the mobile app where Target calls are re-routed to a server-side API service responsible for communicating with Target edge network.
The following technical requirements must be met to leverage Target solution via server-side implementation.
Note that legacy Target API does not support A4T integration so it cannot be used. Use the Delivery API only.
The key to this implementation is to extract Target functionality from the AEP SDK running client-side into an external server-side API service, or Proxy service. Because this is not a recommended approach, we must be careful about what information is being passed to the Proxy server. SDK generates all necessary data about the visitor to correctly pass it to Target and other Adobe solutions. Passing incorrect data will result in product malfunction.
Here is a list of identifiers required for all Target calls:
Your organization Customer ID with an authenticated state, if known, is superior to any other ID such as ECID and TNT ID. However, it is only known when visitor logs in to authenticate. Get and pass it to the Proxy call every time when it is known together with the user authenticated state.
IMPORTANT: if multiple Customer IDs are set, Target only supports the first authenticated ID on the list.
1Identity.getIdentifiers { customerIds, error in2 if let firstCustomerId = customerIds.first {3 print("Visitor ID : \(firstCustomerId)")4 }5}
Known as ECID, Experience Cloud ID or formerly Marketing Cloud ID, this identifier is used across multiple Adobe solutions and is considered the most important ID when your Customer ID is unknown. Target call without ECID may result in unexpected behavior. Getting ECID can be achieved using this method:
1Identity.getExperienceCloudId { (ecid, error) in2 print("ECID is: \(ecid)")3}
TNT ID is the key identifier for Target. Passing it is required. However, when ECID is available, and in most cases it will be, the TNT ID would only be an alias ID. Note, that the very first call to Target may omit TNT ID, in which case Target will generate a new TNT ID and send it back in response. This new TNT ID will need to be used for all future Target calls. AEP SDK handles this automatically.
TNT ID also contains mbox edge number, which is important for routing to the nearest edge location.
1Target.getTntId({ tntId, error in2 print("TNT ID is \(tntId)")3})
Session ID is required for constructing a Target request. Passing inconsistent session ID would result in duplicate visitor sessions and, in return, incorrectly personalized experience.
1Target.getSessionId { (sessionId, err) in2 print("Session ID is \(sessionId)")3}
Mbox edge host allows routing Target call to the nearest edge location.
It looks like mboxedgeNN.tt.omtrdc.net
where NN is the edge number.
The very first request to Target Delivery API has the following URL where mbox host is replaced by client code
:
https://<CLIENT_CODE>.tt.omtrdc.net/rest/v1/delivery/
After the initial Target response, SDK starts using the nearest mbox edge to route new calls to. It will then look like below.
https://mboxedge34.tt.omtrdc.net/rest/v1/delivery/
mboxedge34
is now the nearest mbox edge host. Mbox host can be retrieved from TNT ID.
For example, TNT ID "B70FF281-E87D-4FAC-9D5E-E84E53242D9C.34_0" contains 34
for mbox edge hint that will become mboxedge34
.
Extract TNT ID and parse it to get your edge hint and pass it to the Proxy call. Mbox edge must always be consistent when known or, otherwise, connecting to a different edge would result in duplicate visitor profile and inconsistant personalized experience.
If mbox edge host is unknown, pass client code
instead.
Adobe Target Delivery API is to be used for this server-side implementation. The Delivery API also supports A4T integration so Target Activities can report to Analytics.
Target has 2 content delivery methods: execute
and prefetch
.
The server-side implementation works best with the execute
method.
Here is an example of the app-generated Target call specific to mobile environment:
1curl -H 'Host: mboxedge34.tt.omtrdc.net' -H 'accept: */*' -H 'content-type: application/json' -H 'user-agent: Mozilla/5.0 (iPhone; CPU OS 14_5_1 like Mac OS X; en_US)' -H 'accept-language: en-us' --data-binary '{2 "execute" : {3 "mboxes" : [4 {5 "parameters" : {6 "a.HourOfDay" : "12",7 "a.CarrierName" : "AT&T",8 "a.CrashEvent" : "CrashEvent",9 "a.DeviceName" : "iPhone10,6",10 "previousosversion" : "iOS 14.5.1",11 "a.Resolution" : "1125x2436",12 "a.locale" : "en-US",13 "mycustomdata" : "ASUDYTF67WEUYSGYDVWE6TFWES",14 "a.AppID" : "AEP Demo 1.0 (1)",15 "a.RunMode" : "Application",16 "a.DaysSinceFirstUse" : "243",17 "previousappid" : "AEP Demo 1.0 (1)",18 "type" : "gold",19 "a.DaysSinceLastUse" : "0",20 "a.OSVersion" : "iOS 14.5.1",21 "a.LaunchEvent" : "LaunchEvent",22 "page" : "GlobalPage",23 "a.ignoredSessionLength" : "-1623862502",24 "a.DayOfWeek" : "4",25 "a.Launches" : "3"26 },27 "profileParameters" : {28 "extraPrefetchProfileKey" : "extraPrefetchProfileValue"29 },30 "name" : "sdk-demo-4",31 "index" : 032 }33 ]34 },35 "experienceCloud" : {36 "audienceManager" : {37 "blob" : "j8Odv6LonN4r3an7LhD3WZrU1bUpAkFkkiY1ncBR96t2PTI",38 "locationHint" : "7"39 },40 "analytics" : {41 "logging" : "client_side"42 }43 },44 "id" : {45 "tntId" : "74BEFBA7-819A-4D99-9D6A-F64011FB2654.34_0",46 "marketingCloudVisitorId" : "10238278281262063009146396392721108540",47 "customerIds" : [48 {49 "id" : "146396392721108345",50 "authenticatedState" : "authenticated",51 "integrationCode" : "guid"52 },53 {54 "id" : "827828126206300914",55 "authenticatedState" : "authenticated",56 "integrationCode" : "hhid"57 }58 ]59 },60 "property" : {61 "token" : "4b962579-c709-d8e0-2752-c2ef3c9ed3ea"62 },63 "context" : {64 "screen" : {65 "colorDepth" : 32,66 "height" : 2436,67 "width" : 1125,68 "orientation" : "portrait"69 },70 "mobilePlatform" : {71 "deviceName" : "iPhone10,6",72 "deviceType" : "phone",73 "platformType" : "ios"74 },75 "timeOffsetInMinutes" : 27064379.699999999,76 "application" : {77 "id" : "com.vues.aep-demo",78 "name" : "AEP Demo",79 "version" : "1"80 },81 "channel" : "mobile",82 "userAgent" : "Mozilla\/5.0 (iPhone; CPU OS 14_5_1 like Mac OS X; en_US)"83 },84 "environmentId" : 506285}' --compressed 'https://mboxedge34.tt.omtrdc.net/rest/v1/delivery/?client=adobeinternalags300&sessionId=74BEFBA7-819A-4D99-9D6A-F64011FB2654'
Here is Response for the above Request.
1{2 "status": 200,3 "requestId": "0ad6de40-f953-46e9-83f4-9e484c5cbc09",4 "client": "adobeinternalags300",5 "id": {6 "tntId": "74BEFBA7-819A-4D99-9D6A-F64011FB2654.34_0",7 "marketingCloudVisitorId": "10238278281262063009146396392721108540"8 },9 "edgeHost": "mboxedge34.tt.omtrdc.net",10 "execute": {11 "mboxes": [{12 "index": 0,13 "name": "sdk-demo-4",14 "state": "cWPpH5/slkSPMe...ZNNk8Ba6QQPVmUUGyVdI=",15 "options": [{16 "content": {17 "exp": "B"18 },19 "type": "json",20 "eventToken": "pIe/Ag35pmQBL7O...rWQTvE54PwSXpSSS2Q==",21 "responseTokens": {22 "experience.id": "1",23 "offer.name": "/app_prod_dealsscreengrossordersa4t92721aacopy/experiences/1/pages/0/zones/0/1634149051932",24 "activity.name": "HP - Banner - PROD",25 "activity.id": "590517",26 "option.name": "Offer3",27 "experience.name": "Experience B",28 "option.id": "3",29 "offer.id": "736344"30 },31 "sourceType": "target"32 }],33 "analytics": {34 "payload": {35 "pe": "tnt",36 "tnta": "590517:1:0|2,590517:1:0|1"37 }38 }39 }]40 }41}
Adobe mobile AEP SDK passes a lot of useful data to Target as seen above.
Request parameters contain lifecycle events prefixed with "a.
".
This data is not available for extraction so some server-side functionality may be limited when re-routing Target calls to a Proxy.
See Lifecycle metrics for awareness.
This Target Request may be the bare minimum template for HTTP Request to Target Delivery API:
1curl -H 'Host: mboxedge34.tt.omtrdc.net' -H 'accept: */*' -H 'content-type: application/json' -H 'user-agent: Mozilla/5.0 (iPhone; CPU OS 14_5_1 like Mac OS X; en_US)' -H 'accept-language: en-us' --data-binary '{2 "execute" : {3 "mboxes" : [4 {5 "parameters" : {6 "<ANY_CUSTOM_PARAM>" : "<ANY_CUSTOM_VALUE>"7 }8 "name" : "<ANY_LOCATION_NAME>",9 "index" : 010 }11 ]12 },13 "experienceCloud" : {14 "analytics" : {15 "logging" : "client_side"16 }17 },18 "id" : {19 "tntId" : "<TNT_ID_EXTRACTED_FROM_SDK>",20 "marketingCloudVisitorId" : "ECID_EXTRACTED_FROM_SDK",21 "customerIds" : [22 {23 "id" : "<AUTHENTICATED_CUSTOMER_ID>",24 "authenticatedState" : "authenticated",25 "integrationCode" : "<CUSTOMER_ID_CODE>"26 }27 ]28 },29 "property" : {30 "token" : "<PROPERTY_TOKEN_IF_KNOWN>"31 },32 "context" : {33 "channel" : "mobile"34 },35 "environmentId" : <TARGET_ENVIRONMENT_ID>36}' --compressed 'https://<CLIENT_CODE_OR_MBOX_HOST>.tt.omtrdc.net/rest/v1/delivery/?client=<CLIENT_CODE>&sessionId=<SESSION_ID>'
In the next sections we will review how to:
After sending the Target HTTP Request as shown above, the Response may contain personalized experiences that can be retrieved from this path:
1execute -> mboxes -> [<ARRAY_INDEX>] -> options -> content
Content will contain the Target Offer, or experience, which will need to be sent back and applied to the app's view as needed. It is developer's responsibility to parse, extract and apply the experience in the app from Target Response.
Your Target experience may be any object: JSON, HTML, or text. JSON is more common for mobile apps. Often, developers use feature flag approach where Target experience carries a flag with what UI change is required. It may also contain the entire meaningful component that app will consume as expected.
If Adobe customer is using A4T (Analytics for Target) to report Target Activities to Analytics, it is developer's responsibility to extract Analytics Payload from Target Response and send it to Analytics using Data Insertion API.
Note that A4T calls are service calls and do not count towards billable quota.
1https://<DATA_COLLECTION_HOST>.sc.omtrdc.net/b/ss/<RSID>/0/<CODEVERSION>?pe=tnt&tnta=<ANALYTICS_PAYLOAD_FROM_TARGET_RESPONSE>&mid=<ECID>&vid=<VID>&aid=<AID>
1https://demo.sc.omtrdc.net/b/ss/myCustomRsid/0/MOBILE-1.0?pe=tnt&tnta=285408:0:0|2&mid=2304820394812039
https://developers.adobetarget.com/api/delivery-api/ https://developer.adobe.com/target/implement/server-side/sdk-guides/integration-with-experience-cloud/a4t-reporting/ https://developer.adobe.com/target/implement/server-side/sdk-guides/integration-with-experience-cloud/a4t-reporting/#query-string-parameters-and-variables https://github.com/AdobeDocs/analytics-1.4-apis/blob/master/docs/data-insertion-api/sample_code/r_sample_http_get.md https://github.com/AdobeDocs/analytics-1.4-apis/blob/master/docs/data-insertion-api/reference/r_supported_tags.md https://www.softcrylic.com/blogs/adobe-analytics-for-target-a4t-a-look-under-the-hood/