Server-side integration with Sitecore Personalize API
Tags: sitecore, sitecorepersonalize, webdev, personalization
Published: 2023-09-25 08:26:51

As promised, in this post I will walk you through an example of API integration with Sitecore Personalize full stack experiences. While I am using C# for demonstration purposes, keep in mind that the underlying principles are the same for applications written in JavaScript, Kotlin, Swift or any other programming language.

In [my previous article](https://dev.to/annagevel/using-freemarker-template-engine-in-sitecore-personalize-5c) I explained how to configure API response in Sitecore Personalize. Now, let's take the next step to read this response in our application.

Imagine you have a website that requires personalisation functionality, for example displaying content tailored to the website visitors’ behaviour. Some scenarios can be achieved with web experiences and JavaScript injected on the page. However, in this example I will show you how it can be done with interactive experiences for scenarios that require a deeper level of integration between the application and personalisation engine.

## Preparation Steps

Before we start, I would recommend creating a Postman request for testing purposes. This allows you experiment with different request types and parameters to make sure API behaves as expected and your custom API response is correctly formatted.

You should already have an interactive experience configured in Sitecore Personalize. In case you need a quick refresher, refer to the useful video guide in [this article](https://www.linkedin.com/pulse/full-stack-experience-sitecore-personalize-content-kate-orlova/). Go to your experience details and grab the friendly ID from the Setup Details section. You will also need a browser ID or an identifier of a guest that exists in CDP.

![Friendly ID in Sitecore CDP user interface](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/btux2c90yne2cjifr9gp.png)

With these prerequisites in place, open Postman or a similar tool and set up a POST request to `/v2/callFlows`. The full specification for this API request is available in Sitecore documentation [here](https://doc.sitecore.com/personalize/en/developers/api/call-the-personalize-api.html).

You must include one of the following attributes: `browserId` or `email` or `identifiers` to identify the guest you are sending this request for. Optionally, if your experience supports custom fields, you can include them using the `params` object:

```
{
    "clientKey": "…",
    "channel": "WEB",
    "language": "en",
    "currencyCode": "EUR",
    "pointOfSale": "…",
    "browserId": "…",
    "params": {
      "someKey":"someValue"
    },
    "friendlyId": "…"
}
```

This is how my test request looks in Postman:

![Request details in Postman](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k5ktzd4e4qrjpc24u658.png)

## Writing Code

Once you have verified that the API responds as expected via Postman, it is time to start developing the integration. I have set up a vanilla Sitecore website with basic Sitecore CDP tracking, generating page view events and guest profiles.

API integration code consists of the following elements:

**1. FlowExecutionService.cs**

This class is responsible for sending API requests to `/v2/callFlows`. I am using the standard `System.Net.Http.HttpClient` for simplicity but you are free to choose your preferred REST API library. Here is a code snippet:

```
public class FlowExecutionService
{
        private readonly HttpClient _httpClient;

        public FlowExecutionService(HttpClient httpClient)
        {
            _httpClient = httpClient;
            _httpClient.BaseAddress = new Uri(ConfigSettings.APIEndpoint);

        }

        public async Task<T> ExecuteFlow<T>(FlowExecutionRequest request)
        {
            using (var httpRequest = new HttpRequestMessage(HttpMethod.Post, "callFlows"))
            {
                var json = JsonConvert.SerializeObject(request);
                httpRequest.Content = new StringContent(json, System.Text.Encoding.UTF8, "application/json");

                using (var response = await _httpClient.SendAsync(httpRequest).ConfigureAwait(false))
                {
                    var content = await response.Content.ReadAsStringAsync();
                    var result = JsonConvert.DeserializeObject<T>(content);
                    return result;
                }
            }
        }
}
```

**2.** Then there are two model classes: **FlowExecutionRequest.cs** and **FlowExecutionResult.cs**. `FlowExecutionRequest` is quite generic and can be reused for calling different experiences.

```
public class FlowExecutionRequest
{
        [JsonProperty("clientKey")]
        public string ClientKey { get; set; }

        [JsonProperty("channel")]
        public string Channel { get; set; }

        [JsonProperty("language")]
        public string Language { get; set; }

        [JsonProperty("currencyCode")]
        public string CurrencyCode { get; set; }

        [JsonProperty("pointOfSale")]
        public string PointOfSale { get; set; }

        [JsonProperty("email")]
        public string Email { get; set; }

        [JsonProperty("browserId")]
        public string BrowserId { get; set; }

        [JsonProperty("friendlyId")]
        public string FriendlyId { get; set; }
}
```

`FlowExecutionResult` should be specific to your experience and match the API response your configured with FreeMarker. That’s why I defined `ExecuteFlow<T>` as a generic method so that it can return different response models. This is just one example of how this model can look like:

```
public class FlowExecutionResult
{
        [JsonProperty("guestEmail")]
        public string GuestEmail { get; set; }

        [JsonProperty("guestName")]
        public string GuestName { get; set; }

        [JsonProperty("contentItemId")]
        public string ContentItemId { get; set; }
}
```

**3.** Finally, models and service calls are combined in a controller. Take a look at the method `Banner()` below, it prepares the `FlowExecutionRequest` model, populates it with API configuration settings and passes email address of the logged-in user. If you want to use browser Id instead of email, it can be passed from the front-end code or read from the `bid_` cookie like this:

```
BrowserId = HttpContext.Request.Cookies["bid_" + ConfigSettings.ClientKey];
```

The controller calls the `FlowExecutionService` and retrieves `FlowExecutionResult` which in my case contains guest’s name and email address as well as personalised component data source ID. This data source is taken from the website database and passed to the view for rendering the output:

```
public class BannerController : Controller
{
        private readonly FlowExecutionService _flowExecutionService;

        public BannerController(FlowExecutionService flowExecutionService)
        {
            _flowExecutionService = flowExecutionService;
        }

        public ActionResult Banner()
        {

            var request = new FlowExecutionRequest()
            {
                Channel = ConfigSettings.Channel,
                ClientKey = ConfigSettings.ClientKey,
                CurrencyCode = ConfigSettings.Currency,
                Email = Sitecore.Context.User.Profile?.Email,
                FriendlyId = ConfigSettings.ExperienceId,
                Language = Sitecore.Context.Item.Language.Name,
                PointOfSale = ConfigSettings.POS
            };

            var result = _flowExecutionService.ExecuteFlow<FlowExecutionResult>(request).GetAwaiter().GetResult();
            var model = new BannerModel()
            {
                GuestEmail = result?.GuestEmail,
                GuestName = result?.GuestName
            };

            if (result != null && ID.TryParse(result.ContentItemId, out var id))
            {
                model.ContentItem = Sitecore.Context.Database.GetItem(id);
            }

            return View("~/Views/FullStackExperience/Banner.cshtml", model);
        }
}
```

## Results

By creating a simple proof-of-concept like this, you will be able to integrate with much more complex interactive experiments and experiences in Sitecore Personalize. Depending on your business requirements, you can build a framework that allows specifying experience’s friendly ID and other parameters in the calling application. For instance, it can be a data source in Sitecore content tree or a configurational content item in a headless CMS. Building a foundation layer can streamline the process of adding new experiences for your marketing teams.

---

For a complete working example, feel free to explore the code repository on [GitHub](https://github.com/kate-orlova/sitecore-cdp).