Headless Integration

Updated on October 2, 2024

Integrate Releva in 12 easy steps for Apps and headless e-commerce platforms. #

This page will guide you through a full integration of your shop or app with Releva using APIs only. If you have a standard e-commerce shop or platform and have access to your frontend, please refer to our standard integration guide instead.

Please note that you should only call Releva’s API on behalf of clients who have given you consent to do so under GDPR.

Step 1: On each page (screen) #

Please make the following API call.

  • It’s important to set the x-forwarded-for header with the visitor’s IP in order for Releva’s geolocation to work properly.
curl -H 'X-Forwarded-For: <clientIp>' -H 'Content-Type: application/json' -H 'Authorization: Bearer <accessToken>' -XPOST https://releva.ai/api/v0/push -d'{
  context: {
    "profileId": ."..",
    "sessionId": "...",
    "mergeProfileId": "...",
    "profileIdChanged": true/false,
    "sessionId": "...",
    "isFirstSession": true/false,
    "group": "123,
    "cart": {
        "products":  [
          {
            "id": "",
            "price": 0,
            "currency": "",
            "quantity": 0,
            "custom": {
              "string": [
                {"key": "color", "values": ["red"]}
              ],
              "numeric": [
                {"key": "size", "values": [18]}
              ]
            }
          }
       ]
    },
    "wishlist": {
      "products": [
        {"id": "..."}
      ]
    },
    "profile": {
      "firstName": "",
      "lastName": "",
      "email": "",
      "phoneNumber": "...",
      "registeredAt": "YYYY-mm-ddT00:00:00.000Z",
      "location": {"lat": 39.9352959, "lon": 23.5825895}
    },
    "profileChanged": true/false,
    "cartChanged": true/false
  },
  options: {}
}'

Note that you need to convert the phone number to a valid international format (e.g. if your shop operates in the U.S., you need to change 585-766-1234 to +15857661234; if the phone number cannot be converted in this way, do not include it in the request).

Hint: Whenever the user’s cart has changed, please send the same request as above with the updated cart contents. If a user removes the last item from their cart, and their cart is deleted, please send a request with the same body but with an empty products list. Example below:

...
"cart": {
  "products": []
},
...

The context Object #

The Context Object has the following structure. All fields are required unless stated otherwise.

FieldTypeDescription
profileIdStringA unique profile ID identifying the visior (e.g. visitor’s cookie.) It should be valid for 365 days after the last visit of the visitor
mergeProfileIdString (Optional)An existing profile ID to merge into the profile’s history. If Sarah has profileId: A on her phone where she is logged in , and profileId: B on her laptop where she hasn’t logged in, when she logs in from her laptop, you should send a request with profileId: A and mergeProfileId: B once, and after that you should send profileId: A for Sarah on both her devices.
profileIdChangedBooleanThis flag should be set if profileId has been changed since the last request
sessionIdStringA unique session ID identifying the visior (e.g. visitor’s cookie with session scope.) If you be valid for the duration of the session – until the browser/app is closed.
isFirstSessionStringTrue if this is the first time this user is visiting the app/site. This should stay set as “true” until the first session has ended (see sessionId for explanation of session duration).
groupIntegerA random integer between 0 and 9,999. The number should not change for a given profileId and be valid for 365 days after the last visit of ths visitor.
profileChangedBooleanThis flag should be set if any of the information in the profile has changed. For example, after a user changes their name or email.
cartChangedBooleanThis flag should be set if any of the information in the cart has changed. 
wishlistChangedBooleanThis flag should be set if any of the information in the wishlist has changed.

The context.cart Object #

This object describes the current state of the visitor’s cart. It should be set whenever a visitor’s cart is not empty.

FieldTypeDescription
productsArray[Object]A list of products in the cart.
products[].idStringA unique product id. Should be identical to context.product.id for a given product. Note that if you have a multi-locale shop ( you sell in multiple languages / currencies), this id will need to be unique across all locales.
products[].priceFloatThe unit price at which this product was purchased.
products[].currencyISO-4217 String (Optional)The product currency. This field is required if you sell products in multiple currencies.
products[].quantityIntegerThe quantity purchased.
products[].customObject (Optional)The custom fields are associated with the product.

The context.profile Object #

This object describes the currently logged-in visitor. It should be set whenever a visitor is logged in.

FieldTypeDescription
firstNameString (Optional)Visitor’s first name.
lastNameString (Optional)Visitor’s last name.
emailString (Optional)Visitor’s email address.
phoneNumberString (Optional)Visitor’s phones number. Phone numbers must start with “+” followed by a sequence of numbers. Valid phone number: +49875345976.
registeredAtISO-8601 String (Optional)The date and time when this visitor created their account.
consentString (Optional)Visitor’s consent, may be one of these:
– legitimateInterest – this is the default one, and every user has this consent by default
– privacyPolicy
– mandatoryCookies
– cookiesPolicy
customObject (Optional)The custom fields are associated with the profile.
locationObject (Optional)The location of the user represented as coordinates in decimal notation
location.latFloatThe latitude of the visitor, e.g. 23.45
location.lonFloatThe longitude of the visitor, e.g. 45.56

The context.wishlist Object #

This object describes the wishlist.

FieldTypeDescription
products[].idString (Optional)The id of a product in the wishlist
products[].customObject (Optional)The custom fields are associated with the wishlist product – for ex. chosen size and color

Step 2: On product pages #

Find the call to /api/v0/push you added in Step 1. Then, add the following to the context parameter:

"page": {
  "token": "<yourProductPageToken>",
  "categories": ["men/shoes/sport"]
},
"product": {
  "id": ""
}

The context.page Object #

The Context Object describes the page the user is currently viewing. All fields are required unless stated otherwise.

FieldTypeDescription
tokenString (Optional)The page token of the currently visited page (e.g. home page, product page, cart page, etc.) Consult with your admin panel to find the right token to use. If you do not wish to obtain personalized content, you may omit this.
localeISO 639-1 StringThe page locale (e.g. enbg, etc.)
currencyISO-4217 String (Optional)The page currency. This field is required if you sell products in multiple currencies.
idsArray[String]If the user is visiting a listing page, set this to the list of product ids currently displayed. Otherwise, it should be omitted. Note that if you have a multi-locale shop ( you sell in multiple languages / currencies), each id will need to be unique across all locales.
categoriesArray[String]If the user is visiting a listing page, set this to the current categories selected.
customObject (Optional)The custom field filters are associated with the currently viewed product listing (for instance – shoe sizes 42, 43, 44).
blocksObject (Optional)The selection criteria for elements which should be returned by the API and rendered on the page
blocks.tagsArray[String]If passed, only product and banner blocks containing at least one of the tags passed would be rendered on the page

The context.product Object #

This object describes the currently viewed product if the visitor is on a product page. In all other cases, it should not be set.

FieldTypeDescription
idStringA unique product ID identifying your product. Note that if you have a multi-locale shop ( you sell in multiple languages / currencies), this id will need to be unique across all locales.
customObject (Optional)The custom fields associated with the currently viewed product, for example the currently selected shoe size.

Step 3: On the product listing page (e.g. category page) #

Find the call to /api/v0/push you added in Step 1. Then, add the following to the context parameter:

"page": {
  "token": "<yourProductListingPageToken>",
  "ids": [],
  "categories": ["men/shoes/sport"],
  "filter": {
    "operator": "and",
    "nested": [
      {"key": "price", "operator": "gte", "value": "10", "action": "include"},
      {"key": "price", "operator": "lte", "value": "50", "action": "include"},
      {
        "operator": "or", 
        "nested": [
          {"key": "custom.string.size", "operator": "eq", "value": "42", "action": "include"},
          {"key": "custom.string.size", "operator": "eq", "value": "43", "action": "include"},
        ]
      }
    ]
  }
}

The context.page Object #

The Context Object describes the page the user is currently viewing. All fields are required unless stated otherwise.

FieldTypeDescription
tokenString (Optional)The page token of the currently visited page (e.g. home page, product page, cart page, etc.) Consult with your admin panel to find the right token to use. If you do not wish to obtain personalized content, you may omit this.
localeISO 639-1 String (Optional)The product locale (e.g. enbg, etc.) This field is required if you sell products in multiple languages.
currencyISO-4217 String (Optional)The page currency. This field is required if you sell products in multiple currencies.
idsArray[String] (Optional)If the user is visiting a listing page, set this to the list of product ids currently displayed. Othersise, it should be omitted. Note that if you have a multi-locale shop ( you sell in multiple languages/currencies), each id will need to be unique across all locales.
queryString (Optional)The user query.
categoriesArray[String] (Optional)If the user is visiting a listing page, set this to the current categories selected.
customObject (Optional)The custom field filters associated with the currently viewed product listing (for instance – shoe sizes 42, 43, 44).
filterObject (Optional)The dynamic fields that should be applied
blocksObject (Optional)The selection criteria for elements which should be returned by the API and rendered on the page
blocks.tagsArray[String]If passed, only product and banner blocks containing at least one of the tags passed would be rendered on the page

The context.page.filter object #

This is an object with the following properties:

FieldTypeDescription
operatorStringIt has two values:
– “or” – used when we want only one condition from the sibling “nested” array to be true in order to return products
– “and” – used when we want every condition from the sibling “nested” array to be true in order to return products
nestedArrayIt contains all of the conditions you can check the conditions in the below table for context.nested. Also, you can nest inner conditions with a different “operator” if you append an object inside this array with the same fields “operator” and “nested”

The context.page.filter.nested array #

This is an array of conditions used for the filter with the following properties:

FieldTypeDescription
keyStringOption one you can use default fields like ‘price’.
Option two you can use the custom fields in the product like this -> custom.[type].[key], for example, custom.string.estate_type.
(Custom fields have three types – “string”, “numeric” and “date”)
operatorStringWhat operator do you want to use to check the value given in the “value” field with the value from the products by the key given above.
We support these operators:
– eq – Equal to
– lt – Less than
– gt – Greater than
– lte – Less than or equal to
– gte – Greater than or equal to
actionStringWhat action do you want to perform if the value checked with the operator has found products by the given key.
We support these actions:
– include – Include only the products that match the condition
– exclude – Exclude only the products that match the condition
– bury – Will make the products appear at the bottom of the search if they match the condition
– boost – Will make the products appear at the top of the search if they match the condition
valueStringThe value that you want to be checked in the products by the operator and key
weightInteger (Optional)Weight is used only if you choose boost or bury in the action field and if you have more than one “boost” or “burry”, they will be ordered by the given weight.

Step 4: On search and search result pages #

Find the call to /api/v0/push you added in Step 1. Make sure that your context.page object contains the ids property that will show the result from a search and a query object that defines the query of the user. Also include the filter object (see the previous step for detailed specification).

"page": {
  "token": "<tokenOfAnyPageWithSearchCapability>",
  "query": "my simple query",
  "ids": ["productId_id1", "productId_2", "productId_3"],
  "filter": {
    "operator": "and",
    "nested": [
      {"key": "price", "operator": "gte", "value": "10", "action": "include"},
      {"key": "price", "operator": "lte", "value": "50", "action": "include"},
      {
        "operator": "or", 
        "nested": [
          {"key": "custom.string.size", "operator": "eq", "value": "42", "action": "include"},
          {"key": "custom.string.size", "operator": "eq", "value": "43", "action": "include"},
        ]
      }
    ]
  }
},
...

Note that if you add the optional query parameter, the system will also add a search event. We highly recommend adding the ids field that must represent the result of the search query. The locale must represent both the page and query locale. In the rare event when they differ you shall describe search locale as a custom property.

Step 5: On checkout success pages #

Find the call to /api/v0/push you added in Step 1.Then, change your cart[].products to contain not the list of products in the user’s cart, but the list of products that were part of the newly placed order.
Include the profile object with email and phoneNumber, if you have them (at least one is required, send both if you have both). Please ensure that the profile object is passed even if the order was placed via guest checkout and not by a registered user.

Note that you need to convert the phone number to a valid international format (e.g. if your shop operates in the U.S., you need to change 585-766-1234 to +15857661234; if the phone number cannot be converted in this way, do not include it in the request).

...
     "cart": {
        "products":  [
          {
            "id": "id_of_ordered_product",
            "price": 1,
            "currency": "USD",
            "quantity": 2,
            "custom": {
              "string": [
                {"key": "color", "values": ["red"]}
              ],
              "numeric": [
                {"key": "size", "values": [18]}
              ]
            }
          }
       ]
    },
    "profile": {
      "firstName": "",
      "lastName": "",
      "email": "",
      "phoneNumber": "+15857661234",
      "registeredAt": "YYYY-mm-ddT00:00:00.000Z",
    }
...

Step 6: Render Personalization Results #

A successful request to the /api/v0/push endpoint will return a response with the following structure (below):

  • Recommenders – this array contains a list of recommenders enabled on this page (the page is identified by the context.page.token you included in the request). Each reocmmender will have a name, a unique token, and a list of products under responses. Each response represends a product with information that Releva has for this product. In addition, there is a mergeContext field which we use to attribute user actions to Releva. This information should be sufficient to visualize the products in your website or app. IMPORTANT: If the visitor clicks on a recommended product, you need to merge the mergeContext values into the context object the next time you call the Push API.
  • Banners – this array contains a list of banners enabled on this page (the page is identified by the context.page.token you included in the request). Each banner will have a name, a unique token, and an HTML body. . In addition, there is a mergeContext field which we use to attribute user actions to Releva. This information should be sufficient to visualize the banner in your website or app. IMPORTANT: If the visitor clicks on a recommended product, you need to merge the mergeContext values into the context object the next time you call the Push API.
  • Push – this contains the public key (vapidPublicKey) that Releva will use to sign push notifications.
{
  "recommenders": [
    {
      "token": "916c829e-c1eb-4f49-81ec-106ab8471f3b",
      "name": "Trending from This Category",
      "meta": {
        "algorithm": "hot"
      },
      "tags": ["t1", "t2"],
      "cssSelector": "body",
      "displayStrategy": "befeoreend",
      "template": {"id": 123, "body": "..."},
      "response": [
        {
          "available": true,
          "categories": [
            "cat1"
          ],
          "createdAt": "2019-08-18T05:48:42.419Z",
          "currency": "BGN",
          "custom": {
            "string": [
              {
                "values": [
                  "bar"
                ],
                "key": "f"
              }
            ]
          },
          "data": {
            "foo": "bar"
          },
          "description": "Some Description",
          "discount": 0,
          "discountPercent": 0,
          "discountPrice": 0,
          "id": "3847012384816",
          "imageUrl": "https://cdn.shopify.com/s/files/1/0263/8328/6320/products/yes-me-me-me-me_286x381.jpg?v=1565038619",
          "listPrice": 2500,
          "locale": "bg",
          "mergeContext": {
            "rid": "...",
            "rpid": "..."
          }
          "name": "Test Product",
          "price": 2500,
          "publishedAt": "2019-12-25T12:41:30.457Z",
          "updatedAt": "2019-12-25T12:41:30.457Z",
          "url": "https://releva-dev.myshopify.com/products/test-product?rlv_rid=916c829e-c1eb-4f49-81ec-106ab8471f3b",
        }
      ]
    },
    ...
  ],
  "banners": [
    {
      "token": "9f4625ea-4ef1-4503-b55c-f11c13e25a60",
      "name": "Test Banner",
      "html": "<html>\n  Hello World!\n</html>",
      "tags": ["t1", "t2"],
      "mergeContext": {
        "bid": "..."
      },
      "displayType": "popup",
      "delaySeconds": 123,
      "scrollPercentage": 23,
      "cssSelector": "body",
      "trigger": null
    },
    ...
  ],
  "push": {
    "vapidPublicKey": "..."
  }
}

Step 7: Register users for push notifications #

If you do not plan on using Releva to send push notifications, you may skip this step.

Please make the following API call. In order to generate the keys required for Releva to send the push notification for each user, you would need to add Releva’s public key, which you can obtain from the push.vapidPublicKey in Releva’s response (see previous step).

curl -H 'Content-Type: application/json' -XPOST https://releva.ai/api/v0/pushSubscriptions/<accessToken> -d'{
  "context": {
    "profile": {
      "email": "...",
      "phoneNumber": "..."
    }
  },
  "subscription": {
      "endpoint": "...",
      "expirationTime": null,
      "keys": {
        "auth": "...",
        "p256dh": "..."
      }
   }
}'

The context Object #

This object describes the currently logged-in visitor. It should be set whenever a visitor is logged in.

FieldTypeDescription
profile.emailString (Optional)Visitor’s email address. At least onf ne of email, phoneNumber is required.
profile.phoneNumberString (Optional)Visitor’s phones number. Phone numbers must start with “+” followed by a sequence of numbers. Valid phone number: +49875345976. At least onf ne of email, phoneNumber is required.

The subscription Object #

This object describes the currently logged-in visitor. It should be set whenever a visitor is logged in.

FieldTypeDescription
subscription.endpointStringURL of push services where notification should be sent
subscription.expirationTimeString (Optional)Expiry time of subscription
subscription.keys.authStringSubscription key
subscription.keys.p256dhStringSubscription key

Step 8: Call the poduct update API call when changes in your catalogue occur #

This is necessary for ensuring that changes in your catalogue are reflected in Releva in a timely manner. See Product Update for implementation details.

Step 9: Call the cart paid API when a customer places an order #

See Cart Paid API for implementation details.

Step 10: Implement the periodical product sync API #

This ensures that Releva will learn about changes in your catalogue even if your Product Update call does not work. It is highly recommended to implement this fallback mechanism. See Periodical Product Sync for implementation details.

Step 11: Send subscribes and unsubscribes to Releva #

If you do not plan on using Releva to send emails or text messages, you may skip this step.

  1. When a user wishes to unsubscribe from receiving email or other marketing communication through Releva, you need to call the Unsubscribe API to notify Releva.
  2. When a user wishes to subscribe to receiving email or other marketing communication through Releva, you need to call the Subscribe API to notify Releva.

Step 12: Send newly registered users to Releva. #

When a new user registers on your shop, you need to call the Profile Register API to notify Releva.

All Set! #

Congratulations! This concludes the integration of your shop. Releva offers some advanced features that will help you achieve even better results. Feel free to explore the API Specification for information about how to send custom product and profile data, which can later be used in personalization.