[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 Adobe Target, by exporting AEM Content Fragments to Adobe Target, and use them to personalize headless experiences using the Adobe 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.

In this tutorial, we’ll take a look at how we can export content fragments from AEM to Adobe Target in order to personalize headless experiences. We’ll be using the sample Weekend React app that is an AEM headless app, so the majority of the content on this app is powered by content fragments over AEM’s GraphQL APIs. But what we’ll do is we’ll add a promoted adventure here at the top in yellow that will be injected via Adobe Target in the Experience Platform mobile SDK. So we’ll take a look at how to create an Adobe Target cloud service in AEM, and then how we can export content fragments from AEM to Adobe Target as Content Fragment offers. And then we’ll also take a look at how we can create activities in Adobe Target that leverage the exported content fragments. And then we’ll wrap it up by implementing some code in our Weekend React app that makes a call to Adobe Target via the Web SDK and pulls in the Content Fragment offer that Target has selected for the activity. So you can see over here on the left, Target has served this browser, the Experience B, and over here on the right, we have the same app and Target has chosen to serve Experience A to it. While this tutorial uses a simple A-B test to demonstrate the personalization, you can use content fragment offers in any target activity, such as recommendations or personalized offers. Alright, I’ll let you get to it, and I hope this tutorial helps you understand how you can personalize your headless experiences using AEM and Target.

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

Adobe IMS Configuration adobe-ims-configuration

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

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

So let’s take a look at how to create an Adobe target integration in AMS Cloud Service. So we’re logged into our Author Services Administrator, and we’ll head over to Tools, Security, and select Adobe IMS Configurations. Next, we’ll want to create a new configuration here. From the Cloud solution, we’ll select Target, and now we need to specify a certificate. You can provide your own certificate if you want. A nice, easy way to do this, though, is to create a new certificate within AEM. So let’s just go ahead and do this. We can give this an alias, so we know what certificate is being used. I’ll just go ahead and give it a year, and then Adobe Target. But let’s go ahead and create the certificate. And now it’s created a new certificate in AEM, so the private key is stored in AEM. And we can go ahead and download the public key, which we’ll use a little bit later on. So go ahead and download this. All right, after we’ve done this, tap Next. And next, we’re going to provide some information that we get from developer.adobe.com. But before we do that, let’s just give this a nice title. This is Adobe Target as our technical account configuration name. And the rest of this information we will get from developer.adobe.com. So let’s head over there. So the user that does this will need to have developer access set via the Adobe Admin console. But we’ll first go click into Console, and then we’ll head over to Projects. So let’s go ahead and create a new project. We can do an empty project, since this is just going to allow us to access Target. I always like to first edit the project and give it a meaningful title. And then next, let’s add the Target API to this project. So we can go to the Click Experience Cloud, select Target, and then select Next. So we have two options here. The first is for Adobe Developer Console to generate our public-private key pair for us. And the second option is to provide our public key. Since we already had AEM Generator key pair, let’s go ahead and upload the public key that AEM generated here. We can select it from our downloads. And there we go. Now we’ve uploaded the public key that AEM generated. I’m going to review that. It in fact exists as well as that expiration date, which is 10 years in the future. And next, we need to assign access to Target via the product profile, which maps to a workspace. So we’re going to go ahead and select the default workspace from Target. And we can save our configuration. Next, we’ll head over to the service credentials. And this is where we will get the information that we need to provide to AEM. So if we head back to AEM, we can see that we need an authorization server, an API key, a client secret, as well as a payload. And I’m going to go slightly out of order. I will start with the API key. So we’ll head back to developer console. The API key is listed in developer console as the client ID. So we’ll go ahead and copy this and simply paste that in. Client secret. We retrieve that. We can copy this, put this in. To get the payload field at the bottom, we’re going to head over to the generate JWT tab. And we’ll copy this value. And then lastly, we have this authorization server at the top. So this is going to simply be this host name here under the AUD key. So we can take that from the payload and put it in this field. So this is all we need to provide. So we’ll hit create. And you can see that we have a new Adobe IMS technical account for Adobe Target here. If we select it, we can do a check health. And there we go. We have a healthy configuration.

Adobe Target Cloud Service adobe-target-cloud-service

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

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

Now that we have the Adobe IMS technical account for Adobe Target created, let’s go ahead and create the Adobe Target Cloud Service in AEM. So for this, we’ll head back to tools. And first we’re going to go to general and configuration browser. So this is where you can edit your context aware configurations. And we’ll just double check that the context aware configuration that we want to associate our Target cloud service with, in fact, allows cloud services. So we’ll select it, hit properties. And you can see here that this had not been enabled yet. So I’m going to go ahead and select cloud configurations. Save our changes. Now we can go back to tools, cloud services, and select Adobe Target. Now when we select the context aware configuration that we’ve enabled to support cloud service configs, we have a create button available. We can click this. We can give our cloud service configuration a name. So we need to provide the Adobe Target client code as well as Target tenant ID. And the easiest way to locate these are by going to Target. So let’s go ahead and head over to Experience Cloud and open up Target. So these values are simply going to be the segment of the URL prefixed by the at sign. So I can simply grab those, head back to AEM and paste them in. Next, we need to select the IMS configuration that we just created under tool security, Adobe IMS configurations. And we had named that Adobe Target. So let’s go ahead and select that. We have some extra configuration options, but we’ll leave these blank for now. But you can always go back and change these if you need. Click Connect. This should verify that we can connect to Adobe Target. So this looks good.

Configure asset folders configure-asset-folders

The Adobe 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 Adobe 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 Adobe Target Cloud Services configuration.
  6. Select Adobe Target from the Cloud Service Configurations dropdown.
  7. Select Save & Close in the top right
Now that we have the context-aware cloud service configuration created, we need to make sure that our context-aware configuration is applied to some content. So let’s head over and go to assets, files, and we want to verify that this is applied globally to our weekend shared folder. So we can select it, select properties, click cloud services. We can see that this folder tree has a cloud configuration set to conf weekend shared, which is the configuration that we added the Adobe target cloud service to. And because of that, when we click this cloud services configuration drop down, we will see our Adobe Target cloud service configuration that we just created. So we can select that and save and close.

Permission the AEM Target integration permission

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

Expand for step-by-step instructions
  1. Log in to Experience Cloud as user that can administer the Adobe Target product in Adobe Admin Console
  2. Open the Adobe Admin Console
  3. Select Products and then open Adobe 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
Now, when we click into some of our assets, locate a content fragment, select it. And now we have this export to Adobe target offers button in the top action bar. So there’s one last step that we’ll need to do before we can use this button. So if I click this button now, you’ll see down at the bottom, we get an error. If we were to take a look at the AEM error logs on AEM Author Service, we’d see that our AEM integration doesn’t have editor access and thus cannot write our offer into Adobe target. So we need to resolve this by heading over to the admin console. So again, we’ll head over to experience.adobe.com. We’ll open up the admin console, go to products, find target, click into target. And since our AEM integration is pushing to the default workspace, we’ll select the default workspace under the product profiles for target. Since we’ve configured AEM to use our developer.adobe.com project to access target’s APIs, we’ll head over to API credentials so we can properly permission them. And this is going to list all of your developer.adobe.com projects that have target as an API. And you can see over here on the right side that we have a target product role associated with each project. So what we need to do is locate this project that we’re using as part of our AEM integration with target in this list and give it the editor role. So let’s go ahead and see if we can find it. And here we go down at the bottom. We see that we have our AEM target integration project. And we see that it only has the observer permissions, which are insufficient to write to target. So let’s go ahead and give it the editor permissions.

Export Content Fragments to Target export-content-fragments

Content Fragments that exist under the configured AEM Assets folder hierarchy can be exported to Adobe 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 “Adobe Target enabled” folder

  3. Select the Content Fragments to export to Adobe Target

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

    • This action exports the fully hydrated JSON representation of the Content Fragment to Adobe 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 Adobe Target displays in the main view
  5. Log in to Adobe Experience Cloud with a user in the Editor role for Adobe Target

  6. From the Experience Cloud, select Target from the product switcher in top right to open Adobe 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
Now if we head back to AEM and we export the Adobe Target offer, you’ll see that the sync succeeded and the content fragment was pushed to target. Let’s quickly export a few other adventure content fragments that we might want to use and target offers. Simply head back, export, and maybe just one more. So now we have our offers synced to Adobe Target and now when we head over to Adobe target, we should see our three adventure offers listed as content fragment offers. We can select the type filter, filter by content fragments. You can see that we have a number of content fragments that have been published to this workspace, including our ski touring Mont Blanc, Tahoe skiing, and our Yosemite backpacking content fragments. If we select a content fragment and get info, we have the AEM path that that content fragment originated from, as well as a quick deep link back to that content fragment on the AEM as a cloud service author.

Target Activity using Content Fragment Offers activity

In Adobe 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 Adobe Target, select Activate from the Inactive/Activate/Archive dropdown in the top right.

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

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 Adobe 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 Adobe 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 Adobe Experience Platform Datastream ID is required for AEM Headless apps to interact with Adobe Target using the Adobe Web SDK.

Expand for step-by-step instructions
  1. Navigate to Adobe Experience Cloud

  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 Adobe Web SDK to serve personalized Content Fragments Offers.
    • Event Schema: Leave blank
  5. Select Save

  6. Select Add Service

  7. In Service select Adobe Target

    • Enabled: Yes
    • Property Token: Leave blank
    • Target Environment ID: Leave blank
      • The Target Environment can be set in Adobe 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 Adobe Web SDK configuration call.

With our activity created and activated, we’re almost ready for our developers to take over and integrate our content fragment offers via the activity into the app. The last piece of information that we’ll have to provide to our developers is the ID of an Adobe Experience Platform data stream that has been configured for Adobe Target. So let’s go ahead and do that real quick. Again, we’ll head over to the Experience Cloud homepage, at experience.adobe.com, and we’ll click into Experience Platform. On the left side, there will be a data collection section. So we expand this, select Data Streams, and we’ll create a new data stream. We’ll give it a name, and we won’t set an event schema. Since we’re not doing any data sharing through a digital data layer into an XDM schema, and then back into Adobe Target, so we can leave this blank. We can save it.
We have our data stream created up here, and we need to add a service, and that service is going to be Adobe Target. So we’ll simply select Adobe Target, and we want to mark it as enabled. Since we’re not using any property restrictions in Target, we can leave the property token blank. The Target environment ID, we can also leave blank since we’re actually managing that in Target administration hosts, and we’re not using a third-party ID namespace. So we can actually leave all of these blank. As we save it, we’ll have our new data stream created, integrated with Target, and we have a data stream ID here. We’ll want to make sure that we copy this data stream ID and provide it to our developers as they begin integrating our content fragment offers into the app.

Add personalization to an AEM Headless app code

This tutorial explores personalizing a simple React app using Content Fragment Offers in Adobe Target via Adobe 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 Adobe’s Mobile SDK.


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

Set up

  1. Download the source code for sample React app from Github.com

    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
  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 Adobe 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 Adobe Org Id that can be found at Experience Cloud > Profile > Account info > Current Org ID

    When invoking the Web SDK, the Adobe 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", { ... });


  1. Create a React component AdobeTargetActivity.js to surface Adobe Target activities.


    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
      'debugEnabled': true,
    export default function AdobeTargetActivity({ activityLocation, OfferComponent }) {
      const [offer, setOffer] = React.useState();
      useEffect(() => {
        async function sendAlloyEvent() {
          // Get the activity offer from Adobe Target
          const result = await alloy('sendEvent', {
            // decisionScopes is set to an array containing the Adobe 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 Adobe 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
                // Record the Content Fragment Offer as displayed for Adobe 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]
      }, [activityLocation, OfferComponent]);
      if (!offer) {
        // Adobe 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.
        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 AdobeTargetActivity React component is invoked using as follows:

    code language-jsx
    <AdobeTargetActivity activityLocation={"wknd-adventure-promo"} OfferComponent={AdventurePromo}/>
  2. Create a React component AdventurePromo.js to render the adventure JSON Adobe 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 Adobe Target Content Fragment Offers can be as varied and complex as required based on the Content Fragments that are exported to Adobe Target.


    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">{title}</h2>
                    <p className="adventure-promo-description">{description}</p>
                <div className="adventure-promo-image-wrapper">
                    <img className="adventure-promo-image" src={image} alt={title} />


    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 AdobeTargetActivity component to React app’s Home.js above the list of adventures.


    code language-javascript
    import AdventurePromo from './AdventurePromo';
    import AdobeTargetActivity from './AdobeTargetActivity';
    export default function Home() {
            <div className="Home">
              <AdobeTargetActivity 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 Adobe Target’s logic.

    Experience offers


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