October 15, 2020 | 10 Min

How to A/B test using the Amplience Headless CMS and Dynamic Yield Experience APIs

John Williams
AuthorJohn Williams
Default hero
GuidesEcommerce

At last, you can use the powerful capabilities of Dynamic Yield with Amplience Dynamic Content. This summer Dynamic Yield announced it was releasing a complete set of APIs for its Personalization Anywhere™ platform. These new experience APIs allow customers to implement Dynamic Yield’s full suite of personalization capabilities on the server-side. Previously the only way to implement AB testing and content personalization was by using JavaScript libraries in the browser. Although the client-side method is easy to integrate and Dynamic Yield’s new JavaScript library supports Single Page Application (SPA) frameworks, we recommend leveraging the new APIs as you build a composable commerce architecture. 

As soon as I found out these APIs were available, I signed up to the Dynamic Yield trial and gave them a spin. In this blog, I will walk you through simple ways of combining Amplience and Dynamic Yield capabilities using an API integration that builds on the Dynamic Yield Pet shop tutorial.

You will learn how to:

  • Integrate Dynamic Yield and Amplience on the server-side to support headless architecture approaches

  • Set up Dynamic Yield Campaigns to use Amplience Content

  • Create content variants for A/B testing in Amplience

  • Assign Amplience content variants into Dynamic Yield Campaigns

  • Use Amplience to place content into Dynamic Yield A/B test experiences

Set up the content variants in Amplience Dynamic Content

For this demo we are going to create a new content type to represent the homepage banner. To keep things simple, we are going to use the same banner rendering code used in the pet shop example. We’ll use Amplience Dynamic Content to deliver the content for the banners and Dynamic Yield to choose which content to choose. Ideally we want to deliver the same shape of JSON as the Dynamic Yield example so we only have to make minimal code changes.

This is the JSON object defined in the Dynamic Yield pet shop tutorial.

1{
2  "image": "../image_url",
3  "title": "banner title",
4  "subtitle": "banner subtitle",
5  "cta": "Call to action text",
6  "link": "link for cta"
7}

Fortunately, this is simple in Amplience as you model the JSON object for your content using a JSON Schema. To create a schema In Amplience Dynamic Content, choose "Content type schemas" from the "Development" menu and then click the "Create Schema" button at the top of the schema list window.

You'll be asked to choose whether you want to create a schema using one of our examples, or code one of your own. Choose "Code from scratch" and click the "Get started" button.

Now give your schema an ID. This is a JSON schema, so the ID is in the format of a URL. For this example I have used is: example.amplience.com/DY/DY-Banner.

Click "Create schema".

The schema editor window opens and shows a blank boilerplate schema definition without any properties. To make the schema the same shape as the Dynamic Yield example we need to add 5 properties using the "Add property" drop down.

Below you can see I have added one Image property and four text properties (title, subtitle, cta, link). Note that each property name in the schema must match the corresponding property name in the Dynamic Yield JSON object.

The title and description for each property are used to make a more friendly experience for the business user when authoring content.

1{
2    "$schema": "http://json-schema.org/draft-07/schema#",
3    "$id": "http://example.amplience.com/DY-Banner",
4
5    "title": "dynamic yield banner",
6    "description": "example banner for dynamic yield",
7
8    "allOf": [
9        {
10            "$ref": "http://bigcontent.io/cms/schema/v1/core#/definitions/content"
11        }
12    ],
13    
14    "type": "object",
15    "properties": {
16        "image": {
17            "title": "banner image",
18            "allOf": [
19                { "$ref": "http://bigcontent.io/cms/schema/v1/core#/definitions/image-link" }
20            ]
21        },
22        "title": {
23            "title": "banner title",
24            "description": "main title for the banner",
25            "type": "string",
26            "minLength": 0,
27            "maxLength": 100
28        },
29        "subtitle": {
30            "title": "subtitle",
31            "description": "secondary title for the banner",
32            "type": "string",
33            "minLength": 0,
34            "maxLength": 200
35        },
36        "cta": {
37            "title": "cta",
38            "description": "call to action text",
39            "type": "string",
40            "minLength": 0,
41            "maxLength": 100
42        },
43        "link": {
44            "title": "link",
45            "description": "link for cta",
46            "type": "string",
47            "minLength": 0,
48            "maxLength": 300
49        }
50        
51    },
52    "propertyOrder": []
53}

By using the built in image property the business user does not have to enter a image URL, instead they can select images loaded into Content Hub (the Amplience Digital Asset Management system) and take advantage of the Amplience Dynamic Media image API. I will show you later how we can format the image object returned in the JSON into an image URL to keep the JSON in the correct format for the pet shop demo.

If you have used the same schema name as I have you could copy and paste the above schema definition. Once the schema is complete register it as a content type. To do this choose "Save and register as content type" from the schema editor's "Save" menu.

A content schema defines the shape of the JSON object, but a content type allows the schema to be used by business users in the Amplience CMS. To complete the content type simply give it a name (e.g. Dynamic Yield Banner Example), add an icon to make it easier to identify this content type when create content, and assign the repository where you want business users to create content for this demo.

Create Content Variants

For this demo we are going to create three content variants: a generic pets banner, a dog lovers banner and a cat lovers banner. To create the content, go to the repository on which the Dynamic Yield Banner Example Content Type is enabled and click the "Create content" button. You will be presented with a dialog showing all the content types you have created. Select the Dynamic Yield Banner Example Content type

Now author the content for one of your banners- let's say the dog lovers banner. Select an appropriate image. I uploaded several images to Amplience from the unsplash.com site where Dynamic Yield sourced their images for the tutorial. I liked this image with lots of different breeds and an Amplience pink background. Add content to the rest of the fields. Be creative and have some fun with it.
Once done click "Save" and give the content a name of "dog gift promotion".

When you are happy with this content you can publish it. Choose "Publish" from the drop down menu at the top right of the screen. Once published the content is live and can be retrieved using the content delivery API.

To see this in action, click "i" icon (next to the "Back" button at the top of the window) to open the content properties pane. Then select "Content Delivery 2 URL" in the "Content Delivery" section. The JSON content for the banner will open in a new tab and look something like this:

1{"content":
2{"_meta":{"name":"dog gift promotion",
3"schema":"http://example.amplience.com/DY-Banner","deliveryId":"a8795733-7356-4785-9d20-38d9fe4d98c7"},
4"image":{"_meta":{"schema":"http://bigcontent.io/cms/schema/v1/core#/definitions/image-link"},
5    "id":"c89445d4-4350-408f-9c12-8b66943e2885","name":"hannah-lim-U6nlG0Y5sfs-unsplash",
6    "endpoint":"jwdemo",
7    "defaultHost":"i1.adis.ws"},
8"title":"The Usual doggy Suspects",
9"subtitle":"gifts for all dogs",
10"link":"/category/dogs",
11"cta":"pamper your pooch"}}
Now repeat the process for a cat lovers banner.
And create a generic pets banner.

You now have three content variants live to used with Dynamic Yield.

Set up Dynamic Yield for an Amplience campaign

Now we have content variants in Amplience we need to create a campaign in Dynamic Yield to select these variants for AB testing or prisonization. The first step is to create a new template that represents an Amplience Content Variant. All we need is really one field to allow the business user to drop in the identifier for the content variant they created in Amplience. The default content ID is a GUID and this is fine for now. Later we will show how we can user definable keys which are much easier to read and understand. This is the template I used to represent the Amplience content Variant – I added a title field to allow the business user to add a friendly description in Dynamic Yield but it isn’t really necessary for this demo.

1{
2  "amplienceID": "${Amplience_ID}",
3  "title": "${title}"
4}
  1. Go to Assets > Templates and click "Add New".

  2. Name the new template " Amplience content", and select the template type Custom API Campaign.

  3. Clicking on Next will take you to the template editor. Switch to the JSON tab to edit the raw JSON structure.

  4. Paste the template JSON into the JSON editor. It should look something like this:

Use Dynamic Yield to A/B test using Amplience content

In our first integrated scenario we are going to perform a simple A/B test using a custom API campaign in Dynamic Yield that will choose which piece of Amplience Content to deliver for a user session.

  • Go to API Campaigns and click Add New->Custom.

  • Name your campaign "Amplience Banner".

  • Click Next. For now we will A/B test and not define any targeting, so let's name our experience "Default Experience" and leave the targeting as is: "All users". Click Next again to proceed from the targeting step to the variations step.

  • Create a variant. Click "New Variation" and choose the template "Amplience content "

  • Name the variation whatever you'd like

  • Add the Amplience content ID to the variant

  • Add a title for readability and Save

Here is an easy way to get a content Item ID from Amplience:

  1. In the Content Library in Dynamic Content click the ellipsis ("...") dropdown on the content item for which you want to find the ID. For this variant we'll use the generic pet gift promotion content item.

  2. Select "Get content ID" from the menu. A dialog is displayed showing the content ID of the item you chose.

  3. Click the "Copy to clipboard" button. You can now paste the content ID into Dynamic Yield.

To complete the A/B test campaign add a second variant using the same process, this time use the Amplience content ID for the Dog promotion content. Ensure you have A/B test selected, a 50% distribution, and Click Through Rate as the Primary metric. Save the Experience and let's move on to seeing it in action.

Set up the technical delivery environment

To make the setup easy and to build on existing Dynamic Yield concepts, I decided to base my walk-through on the Dynamic Yield pet shop tutorial Node Express app.

  • To get started follow the pet shop tutorial instructions to build a tutorial technical environment.. Alternatively you can use one you have used previously.

After the pet shop tutorial installation is complete you need to Install the Amplience Content Delivery SDK which wraps up the Amplience Dynamic Content APIs.

  • Under the tutorial file path -
    1\api-tutorial-petshop\after
    \
    1npm install dc-delivery-sdk-js –save

The Amplience Content Delivery SDK repository can also be found on GitHub.

Your environment is now ready to integrate Dynamic Yield and the Amplience Dynamic Content APIs.

Integrate APIs in the server-side code

For this example there are three steps for integrating the APIs in the server-side:

  1. Make a request to the Dynamic Yield choose API to get the Amplience content ID from a decision.

  2. Make the request to Amplience Dynamic Content using the content ID to retrieve the content referenced in the Dynamic Yield decision.

  3. Reformat the Amplience content object into a JSON format that works with the existing tutorial code.

Make a request to the Dynamic Yield

We are going to target the home page banner with specific content pulled from Amplience. Let’s take a look at the homepage handler 

1routes/homepage.js
 . The function 
1getPageContent
 is where the call to the Dynamic Yield API is done. It is currently calling the tutorial demo campaign "HP Hero Banner".

1async function getPageContent(req) {
2  req.dyContext.page.type = 'HOMEPAGE';
3  const apiResponse = await DYAPI.choose(req.userId, req.sessionId, req.dyContext, 
4                                    ['HP Hero Banner', 'HP Recommendations', 'HP Overlays']);
5  const content = {
6    heroBanner: apiResponse['HP Hero Banner'] || defaultHeroBanner,
7    recommendations: apiResponse['HP Recommendations'] || defaultRecommendations,
8    overlay: apiResponse['HP Overlay'],
9  };
10  return content;
11}

We need to change the code to use the Amplience banner campaign we have just setup in Dynamic Yield.

  1. Change the "HP Hero Banner" passed in the DYAPI.choose call to "Amplience Banner"

  2. Change the

    1heroBanner
    property in the
    1content
    constant JSON definition to
    1heroBanner: apiResponse['Amplience Banner'] || defaultHeroBanner,

  3. Save and try out the changes

  4. Run

    1npm start
    in the Node console to run the pet shop environment

  5. Now, open a browser and navigate to

    1http://localhost:3000

Take a look at the console and you can see we have a returned an Amplience Banner variant referencing an Amplience content item. You will notice that no banner appears in the browser, that’s fine as we haven’t added the code that takes the Amplience ID from the Dynamic Yield result to retrieve the full content Item from the Amplience Dynamic Content API.

Make a request to Amplience to retrieve content

Now let’s wire in the code to retrieve the content from the Dynamic Content Delivery API using a content ID.

  • In the base directory of the application (directly under "before/"), create a new JavaScript file called 

    1amplienceAPI.js

  • Add the following code to the top of the file that sets up the Amplience SDK. Note that the content repository in which your content variants are stored sits within a Hub. Use the name of this hub for the 

    1hubName
     property. You can find your hub name by choosing "Properties" from the "Settings" menu.

1// Setup the Amplience SDK use your dynamic hub name
2const ContentClient = require('dc-delivery-sdk-js').ContentClient;
3const client = new ContentClient({
4    hubName: 'put the name of you Amplience hub here'
5});
  • Add the function

    1getContentItemById(contentID)
    to retrieve the content from Amplience using a content ID

  • Copy the code below into the

    1amplienceAPI.js
    file. It calls the Amplience Content Delivery API using the Amplience SDK.

1async function getContentItemById(contentID) {
2    let variant = {};
3    try{
4        const response = await client.getContentItemById(contentID);
5        variant = response;
6        console.log("Amplience content variant:  "+JSON.stringify(variant));
7    }catch(error){
8        console.log('Amplience content not found', error);
9    }
10    return variant;
11}
12module.exports = {
13    getContentItemById
14};

The next step is to make a call to the

1getContentItemById
function from the homepage server-side handler using the content ID retrieved from the Dynamic Yield choose call.

  1. Open

    1routes/homepage.js

  2. Add the code to

    1require
    the
    1DYAPI
    module.

  3. Modify the

    1getPageContent
    function to call
    1getContentItemById
    passing in the content ID returned in the choose call response:
    1apiResponse['Amplience Banner ab'].amplienceID

  4. Replace the property value for heroBanner in the

    1content
    const with the content retrieved from Amplience

1heroBanner: amplienceResponse['Amplience Banner’] || defaultHeroBanner,

The code should look something like this:

1const DYAPI = require('./../DYAPI');
2const AmplienceAPI = require('./../amplienceAPI')
3//Later in the file
4async function getPageContent(req) {
5    req.dyContext.page.type = 'HOMEPAGE';
6    const apiResponse = await DYAPI.choose(req.userId, req.sessionId,req.dyContext, ['Amplience Banner']);
7    const amplienceResponse = await AmplienceAPI.getContentItemById(apiResponse['Amplience Banner'].amplienceID)
8    const content = {
9        heroBanner: amplienceResponse || defaultHeroBanner, // <== Here!
10        recommendations: defaultRecommendations,
11    };
12    if (req.originalUrl.includes('SALE')) {
13        content.overlay = defaultOverlay;
14    }
15    return content;
16}
If you refresh the browser on the pet shop Homepage the console will show the Amplience Content object being returned. However, there is one small task left to get the homepage banner to render correctly and that is to slightly reformat the Amplience content object to match the format the tutorial code expects.

Reformat the Amplience JSON object to work with the existing tutorial code

The final step is to change the Amplience

1getContentItemById
function so that it reformats the JSON object to look the same as the one expected by the existing Pug rendering templates.

  1. To do this take just the body element (our JSON Schema was designed to ensure that the shape of JSON in the body matches that of the example).

  2. Because we use images hosted by Amplience and delivered by Dynamic Content, we need to build an image URL based on the image object in the JSON. This is simple to do with the SDK helper function. In this example I have kept it very basic, but you may want to try adding other Dynamic Media parameters such as size or even cropping. See the full range of features available in the Dynamic Media image APIs.

The new function looks like this:

1async function getContentItemById(contentID) {
2    let variant = {};
3    try{
4        const response = await client.getContentItemById(contentID);
5        variant= response.body;
6        //build a dynamic image URL from the JSON
7        variant.image =  response.body.image.url().build();
8        console.log(variant);
9    }catch(error){
10            console.log('Amplience content not found', error);
11    }
12    return variant;
13}
14module.exports = {
15    getContentItemById
16};
If we now refresh the homepage you will see either
Or this.

Depending on which Variant you are served by Dynamic Yield. To see the different variants, you need to open a new incognito tab until you see the other variant. This is because once you are assigned a variant it will stay as long as the test runs. See more detail in the Dynamic Yield documentation.

Summary and next steps

After completing this tutorial you can define A/B tests in Dynamic Yield that uses content managed by Amplience Dynamic Content. You will now have a simple method for implementing Amplience and Dynamic Yield in a headless architectural way. It is very easy to evolve this approach work with a SPA framework such as Vue.js or React, or even implement it using a GraphQL layer.

This approach was purposefully made simple for developers to implement, but there are capabilities in Amplience and Dynamic Yield that could make the implementation more friendly for business users such as:

  • Using Keys (user definable friendly names) assigned to content items instead of IDs. This will enable users to create naming conventions that are easily remembered across the systems.

  • A single Amplience slot could be used per Dynamic Yield Campaign and modelled to hold each content variant that is switched depending on the Dynamic Yield Decision.

  • Amplience content variants could be loaded into Dynamic Yield as a feed, maybe using Amplience webhooks to synchronize the content.

In the next blog I’ll move on to driving Amplience content into Dynamic Yield Audience targeted personalization. I will explore how some of the alternative approaches expressed above can be used to simplify the integration from a business user perspective.