۶Ƶ

[Integration]{class="badge positive"}

Integrate AEM Headless and Target

[AEM Headless as a Cloud Service]{class="badge informative"}

Learn how to integrate AEM Headless with ۶Ƶ Target, by exporting AEM Content Fragments to ۶Ƶ Target, and use them to personalize headless experiences using the ۶Ƶ Experience Platform Web SDK’s alloy.js. The React WKND App is used to explore how a personalized Target activity using Content Fragments Offers can be added to the experience, to promote a WKND adventure.

video poster

The tutorial covers the steps involved in setting up AEM and ۶Ƶ Target:

۶Ƶ IMS Configuration adobe-ims-configuration

An ۶Ƶ IMS Configuration that facilitates the authentication between AEM and ۶Ƶ Target.

Review the documentation for step-by-step instructions on how to create an ۶Ƶ IMS configuration.

۶Ƶ Target Cloud Service adobe-target-cloud-service

An ۶Ƶ Target Cloud Service is created in AEM to facilitate the exporting of Content Fragments to ۶Ƶ Target.

Review the documentation for step-by-step instructions on how to create an ۶Ƶ Target Cloud Service.

video poster

Configure asset folders configure-asset-folders

The ۶Ƶ Target Cloud Service, configured in a context-aware configuration, must be applied to the AEM Assets folder hierarchy that contains the Content Fragments to export to ۶Ƶ Target.

Expand for step-by-step instructions
  1. Log in to AEM Author service as a DAM administrator
  2. Navigate to Assets > Files, locate the asset folder that has the /conf applied to
  3. Select the asset folder, and select Properties from the top action bar
  4. Select the Cloud Services tab
  5. Ensure that the Cloud Configuration is set to the context-aware config (/conf) that contains the ۶Ƶ Target Cloud Services configuration.
  6. Select ۶Ƶ Target from the Cloud Service Configurations dropdown.
  7. Select Save & Close in the top right

video poster

Permission the AEM Target integration permission

The ۶Ƶ Target integration, which manifests as a developer.adobe.com project, must be granted the Editor product role in ۶Ƶ Admin Console, in order to export Content Fragments to ۶Ƶ Target.

Expand for step-by-step instructions
  1. Log in to Experience Cloud as user that can administer the ۶Ƶ Target product in ۶Ƶ Admin Console
  2. Open the
  3. Select Products and then open ۶Ƶ Target
  4. On the Product Profiles tab, select DefaultWorkspace
  5. Select the API Credentials tab
  6. Locate your developer.adobe.com app in this list and set its Product Role to Editor

video poster

Export Content Fragments to Target export-content-fragments

Content Fragments that exist under the configured AEM Assets folder hierarchy can be exported to ۶Ƶ Target as Content Fragment Offers. These Content Fragment Offers, a special form of JSON offers in Target, can be used in Target activities to serve personalized experiences in headless apps.

Expand for step-by-step instructions
  1. Log in to AEM Author as DAM user

  2. Navigate to Assets > Files, and locate Content Fragments to export as JSON to Target under the “۶Ƶ Target enabled” folder

  3. Select the Content Fragments to export to ۶Ƶ Target

  4. Select Export to ۶Ƶ Target Offers from the top action bar

    • This action exports the fully hydrated JSON representation of the Content Fragment to ۶Ƶ Target as a “Content Fragment Offer”

    • The Fully hydrated JSON representation can be reviewed in AEM

      • Select the Content Fragment
      • Expand the Side Panel
      • Select Preview icon in the left Side Panel
      • The JSON representation that is exported to ۶Ƶ Target displays in the main view
  5. Log in to with a user in the Editor role for ۶Ƶ Target

  6. From the , select Target from the product switcher in top right to open ۶Ƶ Target.

  7. Ensure that the Default Workspace is selected in the Workspace switcher in the top right.

  8. Select the Offers tab in the top navigation

  9. Select the Type dropdown, and selecting Content Fragments

  10. Verify the Content Fragment exported from AEM appears in the list

    • Hover over the offer, and select the View button
    • Review the Offer Info and see the AEM deep link that opens the Content Fragment directly in AEM Author service

video poster

Target Activity using Content Fragment Offers activity

In ۶Ƶ Target, an Activity can be created that uses Content Fragment Offer JSON as the content, allowing personalized experiences in headless app with content created and managed in AEM.

In this example, we use a simple A/B activity, however any Target activity can be used.

Expand for step-by-step instructions
  1. Select the Activities tab in the top navigation

  2. Select + Create Activity, and then select the type of activity to create.

    • This example creates a simple A/B Test but Content Fragment Offers can power any activity type
  3. In the Create Activity wizard

    • Select Web
    • In Choose Experience Composer, select Form
    • In Choose Workspace, select Default Workspace
    • In Choose Property, select the Property the Activity is available in, or select No Property Restrictions to allow it to be used in all Properties.
    • Select Next to create the Activity
  4. Rename the Activity by selecting rename in the top left

    • Give the activity a meaningful name
  5. In the initial Experience, set Location 1 for the Activity to target

    • In this example, target a custom location named wknd-adventure-promo
  6. Under Content select the Default content, and select Change Content Fragment

  7. Select the exported Content Fragment to serve for this experience, and select Done

  8. Review the Content Fragment Offer JSON in the Content text area, this is the same JSON available in AEM Author service via the Content Fragment’s Preview action.

  9. In the left rail, add an Experience, and select a different Content Fragment Offer to serve

  10. Select Next, and configure the Targeting rules as required for the activity

    • In this example, leave the A/B testing as a manual 50/50 split.
  11. Select Next, and complete the activity settings

  12. Select Save & Close and give it a meaningful name

  13. From the Activity in ۶Ƶ Target, select Activate from the Inactive/Activate/Archive dropdown in the top right.

The ۶Ƶ Target activity that targets the wknd-adventure-promo location can now be integrated and exposed in an AEM Headless app.

video poster

Transcript
Now that we have some content fragment offers exported to target, let’s create an activity that uses them and then use that activity in an AEM headless app. So head over to activities. We’ll create an activity and for this we’re just going to keep it simple and do an AB test. We’ll select web since we’ll be using the Experience Platform web SDK. You can of course do the same thing using the ۶Ƶ Experience Platform mobile SDK. Let’s go ahead and select forms. Since we’ll use the form composer. We can choose a property. In this case we have no property restrictions. So let’s select that. Click next. And now we can create our experiences. So what we’ll want to do is give a custom location for this experience. So we’ll call this weekend adventure promo. Since we’ll be using these content fragment offers to promote an adventure. We’ll select the content. You can change content fragment. And then from here we can select the content fragment to use. Once we’ve selected it we can see the actual JSON data that will be returned to our app if this offer is selected. Do note that you can also see the same JSON in AEM. So I head back to AEM. I’ll head back to the ski touring Mont Blanc. Click into the content fragment. In the left side rail there’s the preview option. If we click this this shows us the exact same JSON data that gets exported to ۶Ƶ target. Alright let’s add one more experience. Since we’re doing an A-B test we’ll keep that location the same. Change our content. Again we’ll select the change content fragment. And in this case let’s do Tahoe skiing. Alright that should do it. We’ll just do a simple split for this example 50-50. We’ll pick a success metric as engagement and maybe some page views. And we can just leave everything else as is. We’ll call this weekend adventure promo. And we can save it. And now before we can integrate it into our application we’ll need to make sure that we select it and activate it. Alright so now we can work with our development team to integrate content fragment offers and personalize our AEM headless experiences.

Experience Platform Datastream ID datastream-id

An ۶Ƶ Experience Platform Datastream ID is required for AEM Headless apps to interact with ۶Ƶ Target using the ۶Ƶ Web SDK.

Expand for step-by-step instructions
  1. Navigate to

  2. Open Experience Platform

  3. Select Data Collection > Datastreams and select New Datastream

  4. In the New Datastream wizard, enter:

    • Name: AEM Target integration
    • Description: Datastream used by the ۶Ƶ Web SDK to serve personalized Content Fragments Offers.
    • Event Schema: Leave blank
  5. Select Save

  6. Select Add Service

  7. In Service select ۶Ƶ Target

    • Enabled: Yes
    • Property Token: Leave blank
    • Target Environment ID: Leave blank
      • The Target Environment can be set in ۶Ƶ Target at Administration > Hosts.
    • Target Third-Party ID Namespace: Leave blank
  8. Select Save

  9. On the right side, copy the Datastream ID for use in ۶Ƶ Web SDK configuration call.

video poster

Add personalization to an AEM Headless app code

This tutorial explores personalizing a simple React app using Content Fragment Offers in ۶Ƶ Target via ۶Ƶ Experience Platform Web SDK. This approach can be used to personalize any JavaScript-based web experience.

Android™ and iOS mobile experiences can be personalized following similar patterns using the .

Prerequisites

  • Node.js 14
  • Git
  • installed on AEM as a Cloud Author and Publish services

Set up

  1. Download the source code for sample React app from

    code language-shell
    $ mkdir -p ~/Code
    $ git clone git@github.com:adobe/aem-guides-wknd-graphql.git
    
  2. Open code base at ~/Code/aem-guides-wknd-graphql/personalization-tutorial in your favorite IDE

  3. Update the AEM service’s host that you want the app to connect to ~/Code/aem-guides-wknd-graphql/personalization-tutorial/src/.env.development

    code language-none
    ...
    REACT_APP_HOST_URI=https://publish-p1234-e5678.adobeaemcloud.com/
    ...
    
  4. Run the app, and ensure it connects to the configured AEM service. From the command line, execute:

    code language-shell
    $ cd ~/Code/aem-guides-wknd-graphql/personalization-tutorial
    $ npm install
    $ npm run start
    
  5. Install the ۶Ƶ Web SDK as an NPM package.

    code language-shell
    $ cd ~/Code/aem-guides-wknd-graphql/personalization-tutorial
    $ npm install @adobe/alloy
    

    The Web SDK can be used in code to fetch the Content Fragment Offer JSON by activity location.

    When configuring the Web SDK, there are two IDs required:

    • edgeConfigId which is the Datastream ID
    • orgId the AEM as a Cloud Service/Target ۶Ƶ Org Id that can be found at Experience Cloud > Profile > Account info > Current Org ID

    When invoking the Web SDK, the ۶Ƶ Target activity location (in our example, wknd-adventure-promo) must be set as the value in the decisionScopes array.

    code language-javascript
    import { createInstance } from "@adobe/alloy";
    const alloy = createInstance({ name: "alloy" });
    ...
    alloy("config", { ... });
    alloy("sendEvent", { ... });
    

Implementation

  1. Create a React component ۶ƵTargetActivity.js to surface ۶Ƶ Target activities.

    src/components/۶ƵTargetActivity.js

    code language-javascript
    import React, { useEffect } from 'react';
    import { createInstance } from '@adobe/alloy';
    
    const alloy = createInstance({ name: 'alloy' });
    
    alloy('configure', {
      'edgeConfigId': 'e3db252d-44d0-4a0b-8901-aac22dbc88dc', // AEP Datastream ID
      'orgId':'7ABB3E6A5A7491460A495D61@۶ƵOrg',
      'debugEnabled': true,
    });
    
    export default function ۶ƵTargetActivity({ activityLocation, OfferComponent }) {
      const [offer, setOffer] = React.useState();
    
      useEffect(() => {
        async function sendAlloyEvent() {
          // Get the activity offer from ۶Ƶ Target
          const result = await alloy('sendEvent', {
            // decisionScopes is set to an array containing the ۶Ƶ Target activity location
            'decisionScopes': [activityLocation],
          });
    
          if (result.propositions?.length > 0) {
            // Find the first proposition for the active activity location
            var proposition = result.propositions?.filter((proposition) => { return proposition.scope === activityLocation; })[0];
    
            // Get the Content Fragment Offer JSON from the ۶Ƶ Target response
            const contentFragmentOffer = proposition?.items[0]?.data?.content || { status: 'error', message: 'Personalized content unavailable'};
    
            if (contentFragmentOffer?.data) {
              // Content Fragment Offers represent a single Content Fragment, hydrated by
              // the byPath GraphQL query, we must traverse the JSON object to retrieve the
              // Content Fragment JSON representation
              const byPath = Object.keys(contentFragmentOffer.data)[0];
              const item = contentFragmentOffer.data[byPath]?.item;
    
              if (item) {
                // Set the offer to the React state so it can be rendered
                setOffer(item);
    
                // Record the Content Fragment Offer as displayed for ۶Ƶ Target Activity reporting
                // If this request is omitted, the Target Activity's Reports will be blank
                alloy("sendEvent", {
                    xdm: {
                        eventType: "decisioning.propositionDisplay",
                        _experience: {
                            decisioning: {
                                propositions: [proposition]
                            }
                        }
                    }
                });
              }
            }
          }
        };
    
        sendAlloyEvent();
    
      }, [activityLocation, OfferComponent]);
    
      if (!offer) {
        // ۶Ƶ Target offer initializing; we render a blank component (which has a fixed height) to prevent a layout shift
        return (<OfferComponent></OfferComponent>);
      } else if (offer.status === 'error') {
        // If Personalized content could not be retrieved either show nothing, or optionally default content.
        console.error(offer.message);
        return (<></>);
      }
    
      console.log('Activity Location', activityLocation);
      console.log('Content Fragment Offer', offer);
    
      // Render the React component with the offer's JSON
      return (<OfferComponent content={offer} />);
    };
    

    The ۶ƵTargetActivity React component is invoked using as follows:

    code language-jsx
    <۶ƵTargetActivity activityLocation={"wknd-adventure-promo"} OfferComponent={AdventurePromo}/>
    
  2. Create a React component AdventurePromo.js to render the adventure JSON ۶Ƶ Target serves.

    This React component takes the fully hydrated JSON representing an adventure content fragment, and displaying in a promotional manner. The React components that display the JSON serviced from ۶Ƶ Target Content Fragment Offers can be as varied and complex as required based on the Content Fragments that are exported to ۶Ƶ Target.

    src/components/AdventurePromo.js

    code language-javascript
    import React from 'react';
    
    import './AdventurePromo.scss';
    
    /**
    * @param {*} content is the fully hydrated JSON data for a WKND Adventure Content Fragment
    * @returns the Adventure Promo component
    */
    export default function AdventurePromo({ content }) {
        if (!content) {
            // If content is still loading, then display an empty promote to prevent layout shift when Target loads the data
            return (<div className="adventure-promo"></div>)
        }
    
        const title = content.title;
        const description = content?.description?.plaintext;
        const image = content.primaryImage?._publishUrl;
    
        return (
            <div className="adventure-promo">
                <div className="adventure-promo-text-wrapper">
                    <h3 className="adventure-promo-eyebrow">Promoted adventure</h3>
                    <h2 className="adventure-promo-title"></h2>
                    <p className="adventure-promo-description">{description}</p>
                </div>
                <div className="adventure-promo-image-wrapper">
                    <img className="adventure-promo-image" src={image} alt= />
                </div>
            </div>
        )
    }
    

    src/components/AdventurePromo.scss

    code language-css
    .adventure-promo {
        display: flex;
        margin: 3rem 0;
        height: 400px;
    }
    
    .adventure-promo-text-wrapper {
        background-color: #ffea00;
        color: black;
        flex-grow: 1;
        padding: 3rem 2rem;
        width: 55%;
    }
    
    .adventure-promo-eyebrow {
        font-family: Source Sans Pro,Helvetica Neue,Helvetica,Arial,sans-serif;
        font-weight: 700;
        font-size: 1rem;
        margin: 0;
        text-transform: uppercase;
    }
    
    .adventure-promo-description {
        line-height: 1.75rem;
    }
    
    .adventure-promo-image-wrapper {
        height: 400px;
        width: 45%;
    }
    
    .adventure-promo-image {
        height: 100%;
        object-fit: cover;
        object-position: center center;
        width: 100%;
    }
    

    This React component is invoked as follows:

    code language-jsx
    <AdventurePromo adventure={adventureJSON}/>
    
  3. Add the ۶ƵTargetActivity component to React app’s Home.js above the list of adventures.

    src/components/Home.js

    code language-javascript
    import AdventurePromo from './AdventurePromo';
    import ۶ƵTargetActivity from './۶ƵTargetActivity';
    ...
    export default function Home() {
        ...
        return(
            <div className="Home">
    
              <۶ƵTargetActivity activityLocation={"wknd-adventure-promo"} OfferComponent={AdventurePromo}/>
    
              <h2>Current Adventures</h2>
              ...
        )
    }
    
  4. If the React app is not running, re-start using npm run start.

    Open the React app in two different browsers so allow the A/B test to serve the different experiences to each browser. If both browsers show the same adventure offer, try closing/re-opening one of the browsers until the other experience displays.

    The image below shows the two different Content Fragment Offers displaying for the wknd-adventure-promo Activity, based on ۶Ƶ Target’s logic.

    Experience offers

Congratulations!

Now that we’ve configured AEM as a Cloud Service to export Content Fragments to ۶Ƶ Target, used the Content Fragments Offers in a ۶Ƶ Target Activity, and surfaced that Activity in an AEM Headless app, personalizing the experience.

recommendation-more-help
4859a77c-7971-4ac9-8f5c-4260823c6f69