1. Limit the Scope of API Keys and Avoid Them in Clients
Take care to grant your API keys with only the permissions needed for your use case, avoiding excess rights.
In case you're using the Unified Customer Profile API or 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 you should consider is proxying all requests to our API through your server-side, so that no API Keys are found in the client at all.
2. Use User and Session IDs Matching Your Application (API-Only Mode)
Note: this section applies only to an API-Only Implementation.
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:
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.
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, ensure a new session is created after a fixed inactivity duration and that sessions always have some maximum duration so they will not 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.
3. Use Batching to Minimize the Number of Network Calls
Having multiple campaigns running on the same page is, of course, common. In this case, instead of making the round-trip of calling the choose endpoint for each campaign, you should use the selector parameter to request the chosen variations for all relevant campaigns at once. This usually requires some planning ahead.
Page Types and Campaigns
Let's take the Product Details Page (PDP) of an eCommerce 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 potentially the marketer would also like to show some targeted overlays or notifications for relevant users.
In such a case, we can define a pretty straightforward list of campaigns which the developer can implement once and the marketer will be able to dynamically control. 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, may 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 may 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 may be a complex nested structure of components and child-components, and contain re-usable components shared by multiple layouts. Since the layout may change at any time, the rendering code does not 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, we can still implement efficient personalization by tweaking our CMS component topology a bit. Assume we have a banner component which can be placed in any page layout and holds various data attributes (e.g. background image, title, sub-title, target URL, etc.). By itself, it does not provide for testing nor targeting of its attribute values. How do we enable experimenting with multiple variations?
Instead of having the banner component directly hold its attributes, we can define a parent-child relationship between a banner campaign component and 1..n banner variation child components.
At the banner campaign level, we will 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 will hold a variation for each banner variation CMS component. Each variation payload will refer to that component's unique name.
Now, we need to 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 chosen variation will be returned - holding the unique name of the appropriate banner variation component!
Now, our code can proceed to render the specific banners that were chosen.
4. Use Asynchronous Calls for Recommendations
While the core layout of your page is often rendered at once, it is recommended to consider loading some elements asynchronously to speed up the initial rendering.
Here are two great candidates for this:
1. Recommendations: Recommendations widgets may be placed on the page in various ways, some more inventive than others. Widgets are typically simple, in-line components, or they may reside inside 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 on page load, as 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.
2. Overlays and Notifications: If it fits your application, elements shown on top of the page may be loaded and then rendered asynchronously as well.
5. Handle Timeouts and Errors Gracefully
Needless to say, we're making a great effort to keep our service healthy and fast. However, since no one (yet) is perfect, you have full control over how to respond to calls taking a long time to finish or returning with an error.
Always set a timeout when calling our API. In case 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 (i.e. overlays and notifications), a "do nothing on error" approach is typically the right behavior.
In cases where default content must appear, have this default at the ready in code, or preferably in the configuration.
If you receive an error from the API, please write to 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 admin to retrieve this data. This will help us to analyze the issue by tracking the journey of that request within our system.
6. Review Your Code for Consistency
Most real-life projects involve more than one developer, increasing the chance for some issues of inconsistency - which are not always apparent until some discrepancy is detected in reports for live campaigns.
Please 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 to choose, 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.