Experience APIs Best Practices

Limit the scope of API keys and avoid them in clients

Take care to grant your API keys only the permissions needed for your use case, avoiding excess rights.

If you're pushing product updates via API, create dedicated keys with these permissions and make them available only to the relevant server-side components.

Another method of increasing security to consider is proxying all requests to our API through your server-side, so that no API Keys are found in the client at all.

Use batching to minimize the number of network calls

If you're running multiple campaigns on a single page: Instead of making the round-trip of calling the Choose endpoint for each campaign, use the selector parameter to request the desired variations for all relevant campaigns at once. This usually requires some planning.

Page types and campaigns

Take the Product Details Page (PDP) of an e-commerce website as an example. This page type might typically have one or more promo banners at the top, a recommendations bar below the fold, and perhaps the marketer also wants to show some targeted overlays or notifications to relevant users.

You can define a pretty straightforward list of campaigns that you can implement only once, and the marketer will then be able to control them dynamically. Let's call them "PDP Top Banners", "PDP Recs", "PDP Overlays", and "PDP Notifications".

Each of these campaigns would have a JSON template containing variables for each attribute the marketer can tweak. Some of these campaigns, or maybe all of them, might have no default experience at all unless the current user falls into some specific targeting rule. Your code composing the PDP would be built to handle that, doing either nothing or falling back to a hard-coded default, depending on the case.

A more dynamic (and complex) layout

Having a list of relevant campaign names by page type is neat. However, things might get a bit more complex if you're working with a Headless CMS platform.

With such platforms, page layouts can be changed dynamically and dramatically by marketers and content editors at any time. A layout might be a complex nested structure of components and child components, and contain reusable components shared by multiple layouts. Because the layout can change at any time, the rendering code doesn't know which components there are, and which relevant Dynamic Yield campaigns they might refer to, before actually fetching the up-to-date layout. In other words, the list of campaigns has to be discovered and the correct variations resolved before actual rendering is done.

In such a case, you can still implement efficient personalization by tweaking the CMS component topology a bit. Assume that you have a banner component that can be placed in any page layout and holds various data attributes (for example, background image, title, subtitle, target URL, and so on). By itself, it does not provide for testing or targeting of its attribute values. So how can you enable experimenting with multiple variations?

Instead of having the banner component directly hold its attributes, define a parent-child relationship between a banner campaign component and 1..n banner variation child components.

  • At the banner campaign level, hold the name of a matching Dynamic Yield campaign and a collection of child banner variations.

  • Each banner variation has a unique name, plus all the actual banner attributes.

  • The Dynamic Yield campaign holds a variation for each banner variation CMS component. Each variation payload then refers to that component's unique name.

Now, tweak the rendering process a bit. After the logical page layout is fetched, the rendering code should:

  • Search for any banner campaign components in the layout.
  • Get the Dynamic Yield campaign name from all found components into a list.
  • Call Choose to get the chosen variations for the list of campaign names. For each campaign, a selected variation is returned, holding the unique name of the appropriate banner variation component!

Now, our code can proceed to render the specific banners that were chosen.

Use asynchronous calls for recommendations

While the core layout of your page is often rendered immediately, we recommend that you consider loading some elements asynchronously to speed up the initial rendering.

Here are two great candidates for this:

  • Recommendations: Recommendation widgets can be placed on the page in various ways, some more inventive than others. Widgets are typically simple, inline components, or they can reside within floating carts and sliding drawers on mobile to conserve precious screen real estate.
    Common to all types is that they're usually not visible immediately upon page load, because they are either located below the fold, or because further user interaction is required to expand them. Therefore, you're advised to fetch a recommendation campaign after or in parallel to rendering the "core" part of the page.
  • Overlays and notifications: If it fits your application, elements shown on top of the page can be loaded and then rendered asynchronously as well.

Handle timeouts and errors gracefully

Although our service is designed and maintained to be healthy and fast, you have full control over how to respond to calls that take a long time to complete or return with an error.

Always set a timeout when calling our API. If a request times out or fails for any reason, ensure you know what the proper fallback should be. In some cases, it should simply be not taking any action at all, or not rendering an optional component – as long as this doesn't break the page. For elements drawn on top of the page (overlays and notifications), a "do nothing upon error" approach is typically the right choice.

When default content must appear, have this default ready in code, or preferably, in the configuration.

If you receive an error from the API, write to the log not only the error code and when it happened but also the unique request ID that's returned in the response header ('DY-Trace-ID'). Or, use the API Logs screen in the Dynamic Yield console to retrieve this data. This helps us analyze the issue by tracking the journey of that request within our system.

Review your code for consistency

Most real-life projects involve more than one developer, increasing the chance for some inconsistencies, which are not always apparent until some discrepancy is detected in reports for live campaigns.

Therefore, make sure that:

  • The userId and sessionId you choose are consistent across your application.

  • The SKUs passed in (a) the Product feed, (b) the parameters of the Choose call, and (c) predefined events, are all formatted exactly the same way. It's not uncommon to have more than one developer involved in integrating Dynamic Yield with your system. For example, the developer implementing the feed integration is often not the one integrating it in client apps.

  • Have a common approach to storing the variation identifiers received from API calls (decisionId, variationId, and slotId where applicable) and using them to report engagement.

Use user and session IDs that match your application (for API-type sections)

In most cases, your application would already have the notion of a User ID. This ID actually represents a specific device or browser used by someone whose identity you often do not know at all. In addition, there is often the notion of a Session ID that is limited in time and invalidated after a set period of inactivity. If you already have both, great! Make sure to consistently use the same IDs across all API calls you're making from various places in your codebase.

If you do not have a user or session ID, here are a few things to consider when designing your solution:

  1. Use secure storage to prevent hijacking of IDs: on the web, prefer using HttpOnly cookies, if possible, to hide sensitive identifiers from client-side code. In this scenario, you allocate the user ID in the server and return it in a cookie that is not accessible to the client code at all. You can use this technique for JWTs as well.

  2. Use only secure HTTPS connections between your client and server to prevent Man-In-The-Middle (MITM) attacks. This also protects your cookies while in transit.

  3. If you need to define a session identifier, make sure that a new session is created after a fixed inactivity duration and that sessions always have some maximum duration so that they don't last forever. A common value for inactivity time is 30 minutes.

Session definitions in analytics platforms

Various analytics platforms have their own nuanced definitions of a session, which may cause a slight difference in session counts between what you see in your analytics tool of choice and reports within Dynamic Yield.

For example, Google Analytics has a 30-minute inactivity period, but sessions also always end at midnight. You don't have to follow this last rule, but it will inherently generate some difference in session counts.