Skip to content

Creating rules-based item recommendations

Learn how to create a rules-based recommendation engine from items in your catalog.

About rules-based item recommendations

A rules-based recommendation engine uses user data and product information to suggest relevant items to users within messages. It uses Liquid and either Braze catalogs or Connected Content to dynamically personalize content based on user behavior and attributes.

Recommendation engine options

When deciding which recommendation engine suits your available resources and use cases, refer to this table of considerations:

Creating a recommendation engine

Create your recommendation engine using either a catalog or Connected Content:

To create your recommendation engine using a catalog:

  1. Create a catalog of products.
  2. For each product, add a list of recommended products as a string separated by a delimiter (like a pipe |) in a column named “product_recommendations”.
  3. Pass the product ID that you want to find recommendations for to the catalog.
  4. Get the product_recommendations value for that catalog item and split it by the delimiter with a Liquid split filter.
  5. Pass one or more of those IDs back to the catalog to collect the other product details.

Example

Let’s say you have a health food app and want to create a Content Card campaign that sends different recipes based on how long a user has been signed up for your app. First, create and upload a catalog through an CSV file that includes the following information:

After the catalog is uploaded to Braze, check the preview of a select number of catalog items to confirm the information imported accurately. The items may be randomized in the preview, but this won’t affect the output of the recommendation engine.

Example catalog in Braze.

Create a Content Card campaign. In the composer, enter Liquid logic to determine which users should receive the campaign, and which recipe and image should display. In this use case, Braze will pull the user’s start_date (or sign-up date) and compare it to the current date. The difference in days will determine which Content Card is sent.

1
2
3
4
5
6
{% assign start_date = {{custom_attribute.${start_date}}} | date: "%s" %}
{% assign current_date = "now" | date: "%s" %}
{% assign diff = {{current_date}} | minus: {{start_date}} | divided_by: 86400 %}
{% assign days = {{diff}} | round %}
{% catalog_items Healthy_Recipe_Catalog_SMB {{days}} %}
{{ items[0].title }}
1
2
3
4
5
6
7
8
9
10
{% assign start_date = {{custom_attribute.${start_date}}} | date: "%s" %}
{% assign current_date = "now" | date: "%s" %}
{% assign diff = {{current_date}} | minus: {{start_date}} | divided_by: 86400 %}
{% assign days = {{diff}} | round %}
{% catalog_items Healthy_Recipe_Catalog_SMB {{days}} %}
{% if items[0].title != blank %}
{{ items[0].body }}
{% else %}
{% abort_message('no card for today') %}
{% endif %}
1
2
3
4
5
6
{% assign start_date = {{custom_attribute.${start_date}}} | date: "%s" %}
{% assign current_date = "now" | date: "%s" %}
{% assign diff = {{current_date}} | minus: {{start_date}} | divided_by: 86400 %}
{% assign days = {{diff}} | round %}
{% catalog_items Healthy_Recipe_Catalog_SMB {{days}} %}
{{ items[0].image_url }}

For example:

An example message composer from a Content Card campaign.

In the On click behavior section, enter Liquid logic for where users should be redirected when they click the Content Card on iOS, Android, and Web devices.

1
2
3
4
5
6
{% assign start_date = {{custom_attribute.${start_date}}} | date: "%s" %}
{% assign current_date = "now" | date: "%s" %}
{% assign diff = {{current_date}} | minus: {{start_date}} | divided_by: 86400 %}
{% assign days = {{diff}} | round %}
{% catalog_items Healthy_Recipe_Catalog_SMB {{days}} %}
{{ items[0].link }}

For example:

An example on-click behavior block in the composer.

Go to the Test tab and select Custom user under Preview message as user. Enter a date in the Custom attribute field to preview the Content Card that would be sent to a user who signed up on that date.

An example custom attribute named 'start_date'.

To create your recommendation engine using Connected Content, first create a new endpoint using one of the following methods:

Next, use Liquid in your message that calls your endpoint to match a custom attribute value with a user’s profile and pull the corresponding recommendation.

1
2
3
4
5
6
7
8
{% connected_content YOUR_API_URL :save items %}

{% assign recommended_item_ids_from_user_profile = custom_attribute.${RECOMMENDED_ITEM_IDS} | split: ';' %}

{% for item_id in recommended_item_ids_from_user_profile %}
  {% assign recommended_item = items | where: "ITEM_ID", ITEM_ID | first %}
  recommended_item.item_name
{% endfor %}

Replace the following:

Example

Let’s say you want to pull restaurant recommendations from the Zomato Restaurants database and save the result as a local variable called restaurants. You can make the following Connected Content call:

1
2
3
4
{% connected_content https://developers.zomato.com/api/v2.1/search?entity_id={{city_id}}&entity_type=city&count=20&cuisines={{food_type}}&sort=rating:headers{“user-key”:“USER_KEY”} :save restaurants %}

{{city_food.restaurants[0]}}

Next, let’s say you want to pull restaurant recommendations based on a user’s city and food type. You can do so by dynamically inserting the custom attributes for the user’s city and food type to the beginning of the call, and then assigning the value of restaurants to the variable city_food.restaurants.

The Connected Content call would look like this:

1
2
3
4
5
6
7
8
{% assign city_id = {{custom_attribute.${city_id} | default: ‘306’}} %}
{% assign food_type = {{custom_attribute.${food_type} | default: ‘471’}} %}

{%- connected_content https://developers.zomato.com/api/v2.1/search?entity_id={{city_id}}&entity_type=city&count=20&cuisines={{food_type}}&sort=rating:headers{“user-key”:“USER_KEY”} :save restaurants %}

{% assign restaurants = city_food.restaurants %}

{{city_food.restaurants[0]}}

If you want to tailor the response to retrieve only the restaurant name and rating, you can add filters to the end of the call, like so:

1
2
3
4
5
6
7
8
{% assign city_id = {{custom_attribute.${city_id} | default: ‘306’}} %}
{% assign food_type = {{custom_attribute.${food_type} | default: ‘471’}} %}

{%- connected_content https://developers.zomato.com/api/v2.1/search?entity_id={{city_id}}&entity_type=city&count=20&cuisines={{food_type}}&sort=rating:headers{“user-key”:”USER_KEY”} :save restaurants %}
{% assign restaurants = city_food.restaurants %}

{{city_food.restaurants[0].restaurant.name}}
{{city_food.restaurants[0].restaurant.user_rating.rating_text}}

Finally, let’s say you want to group together the restaurant recommendations by rating. Do the following:

  1. Use assign to create blank arrays for rating categories of “excellent”, “very good”, and “good”.
  2. Add a for loop that examines the rating of each restaurant in the list.
    • If a rating is “Excellent”, append the restaurant name to the excellent_restaurants string, then add a * character at the end to separate each restaurant name.
    • If a rating is “Very Good”, append the restaurant name to the very_good_restaurants string, then add a * character at the end.
    • If a rating is “Good”, append the restaurant name to the good_restaurants string, then add a * character at the end.
  3. Limit the number of restaurant recommendations returned to four per category.

This is what the final call would look like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
{% assign city_id = {{custom_attribute.${city_id} | default: ‘306’}} %}
{% assign food_type = {{custom_attribute.${food_type} | default: ‘471’}} %}
{%- connected_content https://developers.zomato.com/api/v2.1/search?entity_id={{city_id}}&entity_type=city&count=20&cuisines={{food_type}}&sort=rating:headers{“user-key”:”USER_KEY”} :save restaurants %}
{% assign restaurants = city_food.restaurants %}
{% assign excellent_restaurants = “” %}
{% assign very_good_resturants = “” %}
{% assign good_restaurants = “” %}
{% for list in restaurants %}
{% if {{list.restaurant.user_rating.rating_text}} == `Excellent` %}
{% assign excellent_restaurants = excellent_restaurants | append: list.restaurant.name | append: `*` %}
{% elseif {{list.restaurant.user_rating.rating_text}} == `Very Good` %}
{% assign very_good_restaurants = very_good_restaurants | append: list.restaurant.name | append: `*` %}
{% elseif {{list.restaurant.user_rating.rating_text}} == `Good` %}
{% assign good_restaurants = good_restaurants | append: list.restaurant.name | append: `*` %}
{% endif %}
{% endfor %}
{% assign excellent_array = excellent_restaurants | split: `*` %}
{% assign very_good_array = very_good_restaurants | split: `*` %}
{% assign good_array = good_restaurants | split: `*` %}

Excellent places
{% for list in excellent_array %}

{{list}}
{% assign total_count = total_count | plus:1 %}
{% if total_count >= 4 %}
{% break %}
{% endif %}
{% endfor %}

Very good places
{% for list in very_good_array %}

{{list}}
{% assign total_count = total_count | plus:1 %}
{% if total_count >= 4 %}
{% break %}
{% endif %}
{% endfor %}

Good places
{% for list in good_array %}

{{list}}
{% assign total_count = total_count | plus:1 %}
{% if total_count >= 4 %}
{% break %}
{% endif %}
{% endfor %}

See the below screenshot for an example of how the response displays on a user’s device.

Rendering of a restaurant list generated by the example final call.

HOW HELPFUL WAS THIS PAGE?
New Stuff!