Experience APIs Best Practices
Limit the scope of API keys and avoid them in clients
Ensure that you grant your API keys only the permissions needed for your use case, avoiding excess rights.
If you're syncing your product feed via API, create dedicated keys with these permissions and make them available only to the relevant server-side components.
To increase security, also consider 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 the same time. 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 relevant users some targeted overlays or notifications.
You can define a pretty straightforward list of campaigns that you implement only once, and the marketer can then 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 situation.
A more dynamic (and complex) layout
Creating a campaign list as described is useful. However, things can get a bit more complex if you're working with a headless CMS platform.
With these 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 also contain reusable components shared by multiple layouts. Because the layout can change at any time, the rendering code doesn't know which components are included and which relevant Dynamic Yield campaigns they refer to before fetching the up-to-date layout.
In other words, the list of campaigns must be discovered and the correct variations resolved before the campaign is rendered.
In this case, you can still implement efficient personalization by tweaking the CMS component topology a bit. Let's say 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 doesn't 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 and make 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 generally 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, we recommend that you 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 that you know the appropriate fallback. In some cases, it's 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 the 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 Experience OS 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
andsessionId
you use 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.
- There is a common approach to storing the variation identifiers received from API calls (
decisionId
,variationId
, andslotId
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 already includes some sort of 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's 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 make from various places in your codebase.
If you don't have a user or session ID, here are some things to consider when designing your solution:
- Use secure storage to prevent ID hijackings: On the web, if possible, prefer using HttpOnly cookiesto 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.
- 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.
- 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.
Updated 3 months ago