Set Sitecore contact behavior profile scores using API

Sitecore 9.3 / .NET Framework 4.7.2 / 05-07-2022

 

Functional requirements

One of my customers requested an API capable of changing Xdb contact profile card behavior scores created by marketeers.

Goal was to create profile cards in Sitecore, have an external system periodically update the score based on their own user identifier and metrics, and finally personalize content using behavior profile scores.

 

Afbeelding met tekst

Automatisch gegenereerde beschrijving

 

Technical requirements

- API must be authenticated so it can only be used by authorized external parties.

- API must be able to map an external user Id to a Sitecore Xdb contact Id.

 

Solution

Updating Profile cards scores is possible through the Sitecore.Analytics.Tracker.Current.Interaction.Profiles[XX].Score property. But this relies on user interaction, a valid session and active tracker. None of these are available through a web service / API call.

As Profile cards are created and changed over time, creating custom Xdb facets is not an option here. They require a developer and deployments to XConnect service(s).

Instead, I chose to set scores on the contact behavior profile facet using the XConnect client following these steps:

 

1.       Create a Sitecore profile card and key

2.       Develop authenticated API

-          Maps external system user Id to Sitecore contact Id.

-          Sets behavior profile facet score directly into Xdb using the XConnect client.


Implementation

Create Sitecore Profile card and key

Starting with creating a profile card in Sitecore.

1.       On the Sitecore Launchpad, click Marketing Control Panel.

2.       In the content tree, expand the Marketing Control Panel node and the Profiles node.

3.       Expand the node for the profile you are interested and then click the Profile Cards folder.

4.       On the Home tab, in the Insert group, click Profile Card to create a new profile card.

5.       After creating the profile card, add a profile key to it with the same name.

Afbeelding met tekst

Automatisch gegenereerde beschrijving

Implement the Update Profilecard score API

The API must map the external user identifier to a Sitecore contact Id. I removed this piece of code from the example since it is very customer specific and not relevant for the implementation. Assuming we have the Sitecore contact Id, the API must perform a few steps:

Ø  Create new instance of XConnectClient

Ø  Get contact from xConnect

Ø  Get ContactBehaviorProfile facet

Ø  Get the marketing profile definition by name

Ø  Add or update facet profile and profile key score

Ø  Submit

Ø  Remove contact from session to enforce a reload

The final implementation of the API looks like this.

using Sitecore.Analytics.Tracking;

using System.Web.Http;

using System.Web.Mvc;

using Sitecore.XConnect.Collection.Model;

using Sitecore.XConnect.Client;

using Sitecore.XConnect;

using System.Linq;

using Sitecore.Marketing.Definitions.Profiles;

using Sitecore.Analytics;

using System.Text;

using System;

using XXX.Exceptions;

 

namespace XXX.ContactBehaviorProfile.Controllers

{

    /// <summary>

    /// This controller handles profile card scores

    /// </summary>

    [Authorize]

    public class ContactBehaviorProfileApiController : Controller

    {

        [System.Web.Mvc.HttpPost]

        public ActionResult SetProfileCardScores(string id, [FromBody] Models.ProfileCardDataModel profileCardDataModel)

        {

            try

            {

                var responseMessageStringBuilder = new StringBuilder();

 

                using (XConnectClient client = Sitecore.XConnect.Client.Configuration.SitecoreXConnectClientConfiguration.GetClient())

                {

                    // Get contact using XConnectClient

                    var xconnectContact = client.Get(new IdentifiedContactReference("extranet", id), new ContactExpandOptions(Sitecore.XConnect.Collection.Model.ContactBehaviorProfile.DefaultFacetKey));

                    if (xconnectContact == null)

                    {

                        throw new ContactBehaviorProfileApiException($"Contact with identifier ${id} could not be found.");

                    }                   

 

                    // Iterate through profile cards

                    foreach (var profileCard in profileCardDataModel.ProfileCards)

                    {                      

                        // Get marketing profile card definition from Sitecore

                        var profileDefinition = (ProfileDefinition)Tracker.MarketingDefinitions.Profiles[profileCard.Name];

                        if (profileDefinition == null)

                        {

                            throw new ContactBehaviorProfileApiException($"Profile card {profileCard.Name} could not be found.");

                        }

 

                        // Get contact behavior profile facet

                        var xConnectehaviorProfileFacet = xconnectContact.Facets.ContainsKey(Sitecore.XConnect.Collection.Model.ContactBehaviorProfile.DefaultFacetKey)

                            ? xconnectContact.Facets[Sitecore.XConnect.Collection.Model.ContactBehaviorProfile.DefaultFacetKey] as Sitecore.XConnect.Collection.Model.ContactBehaviorProfile

                            : new Sitecore.XConnect.Collection.Model.ContactBehaviorProfile();

 

                        // Get the current profile card score

                        var currentProfileScore = (double)0;

                        if (xConnectehaviorProfileFacet.Scores.ContainsKey(profileDefinition.Id))

                        {

                            currentProfileScore = xConnectehaviorProfileFacet.Scores[profileDefinition.Id].Score;

                        }

 

                        // Only continue if score has changed

                        if (currentProfileScore == profileCard.Score)

                        {

                            continue;

                        }

 

                        // Remove any existing profile score

                        xConnectehaviorProfileFacet.Scores.Remove(profileDefinition.Id);

 

                        // Create new profile score

                        var profileScore = new ProfileScore

                        {

                            ProfileDefinitionId = profileDefinition.Id,

                            Score = profileCard.Score,

                            ScoreCount = 1

                        };

 

                        // Add profile key

                        profileScore.Values.Add(profileDefinition.Keys.First().Id, profileCard.Score);

 

                        // Add profile score to behavior profile facet

                        xConnectehaviorProfileFacet.Scores.Add(profileDefinition.Id, profileScore);

 

                        // Set behavior profile facet on contact

                        client.SetFacet(xconnectContact, Sitecore.XConnect.Collection.Model.ContactBehaviorProfile.DefaultFacetKey, xConnectehaviorProfileFacet);

 

                        // Submit changes

                        client.Submit();

 

                        responseMessageStringBuilder.Append($"{profileCard.Name} score updated from {currentProfileScore} to {profileCard.Score}.");

                    }                 

 

                    // Remove the contact from session.

                    var contactManager = Sitecore.Configuration.Factory.CreateObject("tracking/contactManager", true) as ContactManager;

                    contactManager.RemoveFromSession(xconnectContact.Id.Value);

                }

 

                return Content(responseMessageStringBuilder.ToString());

            }

            catch (Exception exception) when (!(exception is ContactBehaviorProfileApiException))

            {

                throw new ContactBehaviorProfileApiException($"ContactBehaviorProfileApi failed with exception message: {exception.Message}", exception);

            }

        }

    }

}

 

Results

Now that the scores have been set. We can use the build-in personalization rule on a behavior profile key, example:

Afbeelding met tekst

Automatisch gegenereerde beschrijving

 

Final thoughts

When I started with this functionality and went through the Sitecore XConnect documentation, it seemed that the request was quite easy to implement. Just get the contact, change some facets and submit it back to Xdb. However, every time a contact got identified, the new score values got reset back to their initial values. I've spent a lot of time trying to figure out what was wrong, even hooked into the startTracking pipeline to debug the flow. At the end it seemed that I was updating the profile score, but was not updating the profile key score using the API. After updating both, everything worked as expected. The Sitecore documentation on these processes are quite poor so feel free to copy this code and/or share some feedback :)



Tony Hendriks / .Net & Sitecore Developer