Skip to main content

Overview

fetchExperiences is a client-side method on the MoEngage Personalize SDK. Call it at runtime typically on page load or component mount - with a list of experience keys, and it returns the personalized content that MoEngage has resolved for the current user. Experience keys are identifiers configured in the MoEngage dashboard for Personalize API experiences. Each key maps to a placement on your website - a homepage banner, a cart offer slot, a product listing card, and so on. MoEngage evaluates the current user against your active experiences and returns only the experiences the user qualifies for. Experiences that cannot be resolved for the user are returned in a separate failures array with a structured reason code. Your website owns the rendering layer. MoEngage provides the decision and the content payload.

Implementing fetchExperiences

Pre-requisites

SDK installation

  1. Refer to this article to integrate the Web SDK on your website.
  2. Refer to this article to integrate the Personalize SDK on your website.

Creating a Personalize API experience

Refer to this article to create a Personalize API experience.

When to use

Use fetchExperiences when your website renders personalized content using its own components and MoEngage provides the content payload and decisioning. Common use cases include personalized banners, offer cards, loyalty modules, product listing promotions, cart upsells, and dynamic recommendation blocks.
Implement through a centralized personalization layer rather than independently from unrelated page components. This makes it easier to manage fallback behaviour, attribution, and experience key governance across your site.

Method signature

Moengage.personalize.fetchExperiences(experienceKeys, customAttributes?)
ParameterTypeRequiredDescription
experienceKeysstring[]RequiredArray of experience key strings. Pass specific keys to evaluate only those experiences. Pass [] to evaluate all active experiences - MoEngage returns up to 25 eligible results.
customAttributesObjectOptionalKey-value pairs for runtime audience decisioning. Omit entirely when not needed - do not pass an empty object.
The method returns a Promise that resolves with a response object containing experiences and failures.

Fetching experiences

Fetch specific experiences

Use this when the page has fixed placements and you know exactly which experience keys to request.
<script type="text/javascript">
Moengage.personalize
  .fetchExperiences(['homepage-banner', 'cart-offers', 'listing-promo'])
  .then((result) => {
    // result.experiences - resolved content keyed by experience key
    // result.failures   - unresolved keys with structured reason codes
  })
  .catch((error) => {
    // SDK, network, or initialization error - render fallback content
  });
</script>

Fetch experiences with custom attributes

Pass custom attributes when your MoEngage audiences use runtime values for targeting - such as locale, page type, or country.
<script type="text/javascript">
Moengage.personalize
  .fetchExperiences(
    ['homepage-banner', 'cart-offers'],
    {
      "page_type": "cart",
      "locale": "en-US",
      "country": "US"
    }
  )
  .then((result) => {
    // result.experiences
    // result.failures
  })
  .catch((error) => {
    // Render fallback content
  });
</script>

Fetch all eligible active experiences

Pass an empty array to evaluate the current user against all active experiences. MoEngage returns up to 25 eligible results.
<script type="text/javascript">
Moengage.personalize
  .fetchExperiences([])
  .then((result) => {
    // result.experiences - up to 25 eligible active experiences
    // result.failures
  })
  .catch((error) => {
    // Render fallback content
  });
</script>
Use explicit experience keys for fixed placements. Pass empty-array only when your frontend is built to handle a dynamic set of returned experiences - such as a dynamic offer feed or a discovery-style content area. With empty-array mode, your rendering logic must decide where each returned experience belongs, what to do when a required placement is not returned, and how to handle more results than available slots.

How it works

  1. The page initializes the MoEngage SDK and establishes user identity and consent.
  2. Your code calls fetchExperiences with the relevant experience keys and any custom attributes.
  3. MoEngage evaluates the user against active campaign and associated segment rules, control groups, A/B variants, and decision policies.
  4. The Promise resolves with a response object containing experiences and failures.
  5. Your code reads, validates, and renders the payload from each resolved experience.
  6. Your code passes the experience_context and offering_context (if present in the response) back to MoEngage impression and click tracking calls.

Response

Response structure

The resolved response contains two top-level keys: experiences and failures. Both can be present in the same response - a single call can return some resolved experiences and some failures simultaneously.
{
  "experiences": {
    "homepage-banner": {
      "payload": {
        "title":          { "value": "Dress Like You Mean It",          "data_type": "string" },
        "imageURL":       { "value": "https://cdn.example.com/banner.jpg", "data_type": "string" },
        "ctaText":        { "value": "Shop Men's Edit",                 "data_type": "string" },
        "redirectionURL": { "value": "/collections/mens-fashion",       "data_type": "string" }
      },
      "experience_context": {
        "cid":               "6a268ba7_F_T_WP_AB_2_P_0_AU_9",
        "experience":        "Home Page Banner Update",
        "moe_variation_id":  "2",
        "audience_name":     "Category viewed - Mens Fashion",
        "audience_id":       "9",
        "moe_locale_id":     "0",
        "type":              "Web Personalization",
        "experience_type":   "API based Experience"
      }
    }
  },
  "failures": [
    {
      "code": "E003",
      "msg":  "user not in segment",
      "keys": ["cart-offers"]
    }
  ]
}

Response fields

FieldDescription
experiencesMap of resolved experiences keyed by experience key string. Only experiences the user qualifies for appear here.
experiences.<key>The resolved experience object. Use bracket notation for keys containing hyphens - for example, result.experiences['homepage-banner']. Dot notation (result.experiences.homepage-banner) treats - as the subtraction operator and produces a runtime error that can be difficult to trace.
payloadObject containing the content fields configured for the experience in MoEngage. Field names match what was configured in the dashboard.
payload.<field>.valueThe value for a payload field. May be a direct scalar such as a title or URL, or a serialized JSON string - depending on how the experience is configured. See Payload formats.
payload.<field>.data_typeData type of the field. For example, string.
experience_contextCampaign-level metadata used for impression and click attribution. Pass this object as-is to MoEngage tracking calls — do not modify, omit, or restructure any of its fields. The fields below describe what each value represents, but none of them should be altered by your implementation. See Personalize API Experience events tracking.
experience_context.cidComposite impression ID used by MoEngage to attribute the event to the correct campaign, variant, and audience.
experience_context.experienceName of the experience or campaign as configured in MoEngage.
experience_context.moe_variation_idA/B variation the user is assigned to.
experience_context.audience_nameName of the matched audience segment.
experience_context.audience_idID of the matched audience segment.
experience_context.typeExperience channel - for example, Web Personalization.
experience_context.experience_typeExperience implementation type - for example, API based Experience.
failuresArray of experience keys that could not be resolved for the current user. See Handling failures.
failures[].codeMachine-readable failure code.
failures[].msgHuman-readable failure message.
failures[].keysThe experience key(s) affected by this failure.

Payload formats

The shape of the payload depends on how the experience is configured in MoEngage. There are two formats.

Format 1 - Direct field values

Each content field is returned as a separate key under payload. Read values directly - no parsing required.
{
  "payload": {
    "title":          { "value": "Dress Like You Mean It",          "data_type": "string" },
    "imageURL":       { "value": "https://cdn.example.com/banner.jpg", "data_type": "string" },
    "ctaText":        { "value": "Shop Men's Edit",                 "data_type": "string" },
    "redirectionURL": { "value": "/collections/mens-fashion",       "data_type": "string" }
  }
}
Read values directly using optional chaining:
<script type="text/javascript">
const experience = result.experiences?.['homepage-banner'];

const title          = experience?.payload?.title?.value;
const imageURL       = experience?.payload?.imageURL?.value;
const ctaText        = experience?.payload?.ctaText?.value;
const redirectionURL = experience?.payload?.redirectionURL?.value;
</script>
Use bracket notation for experience keys that contain hyphens - result.experiences['homepage-banner']. Dot notation (result.experiences.homepage-banner) treats - as the subtraction operator and produces a runtime error that can be difficult to trace.

Format 2 - Serialized JSON offer array

One payload field contains a JSON-encoded string representing a ranked list of offers returned by a Decision Policy. Parse it with JSON.parse() before use.
{
  "payload": {
    "promo_card": {
      "value": "[{\"dp_offering_id\":\"...\",\"offering_content\":{\"payload\":{\"title\":\"Voucher Zone\",\"cTAText\":\"Explore\",\"imageURL\":\"https://...\"}}}]",
      "data_type": "string"
    }
  }
}
<script type="text/javascript">
const experience = result.experiences?.Homepage_offers_experience;
const rawValue   = experience?.payload?.promo_card?.value;
const offers     = JSON.parse(rawValue); // Array of ranked offer objects
</script>
Do not call JSON.parse() on every payload field. Parse only fields you know contain serialized JSON. Calling JSON.parse() on a plain string value such as a title or URL will throw an error.

Handling failures

When an experience key appears in failures, it was not served to the current user. Your implementation should suppress the placement or render default content.
CodeReasonRecommended handling
E001User is in campaign control groupSuppress placement or serve default content
E002User is in global control groupSuppress placement or serve default content
E003User is not in the segmentSuppress placement or serve default content
E004Invalid experience keyLog as a configuration error
E005Maximum limit breached for experience keyLog as a configuration error
E006Experience is not activeLog as a configuration error
E007Experience is expiredLog as a configuration error
E008Personalization failed for userServe default/fallback content
E001–E003 and E008 are expected runtime states. E004–E007 indicate a configuration issue in MoEngage and should trigger a log entry or alert for investigation.
<script type="text/javascript">
result.failures?.forEach(({ code, keys }) => {
  keys.forEach((key) => {
    if (['E004', 'E005', 'E006', 'E007'].includes(code)) {
      console.error(`MoEngage config error [${code}] for experience: ${key}`);
    } else {
      renderDefault(key); // Suppress or show fallback content
    }
  });
});
</script>

Implementation examples

Full example - reading a direct field-value payload

The click handler below uses attachClickHandler() as a sample reference. Replace this with whichever event binding pattern your frontend framework or component library uses.
<script type="text/javascript">
Moengage.personalize
  .fetchExperiences(['homepage-banner'])
  .then((result) => {

    // Check if the experience was resolved
    const experience = result.experiences?.['homepage-banner'];
    if (!experience) {
      renderDefault('homepage-banner');
      return;
    }

    // Read payload field values directly
    const banner = {
      title:          experience.payload?.title?.value,
      imageURL:       experience.payload?.imageURL?.value,
      ctaText:        experience.payload?.ctaText?.value,
      redirectionURL: experience.payload?.redirectionURL?.value
    };

    // Validate required fields before rendering
    if (!banner.title || !banner.imageURL || !banner.ctaText || !banner.redirectionURL) {
      renderDefault('homepage-banner');
      return;
    }

    // Render using your own components
    renderBanner(banner);

    // Track experience impression — must be called for every resolved experience key.
    // Pass experience_context exactly as returned in the response. Do not modify its fields.
    Moengage.personalize.trackImpression(experience.experience_context);

    // Track experience click when the user interacts with the rendered content.
    // extraAttributes is optional — use it to pass additional context about the CTA clicked.
    attachClickHandler(() => {
      Moengage.personalize.trackClick(experience.experience_context, {
        button_name: banner.ctaText
      });
    });
  })
  .catch(() => {
    renderDefault('homepage-banner');
  });
</script>

Full example - reading a serialized JSON offer array payload

When an experience payload contains offering data (Format 2), each offer in the array has its own offering_context. Track an impression and click for each offer that is shown or interacted with. Refer to Offerings events tracking for the full method reference. The click handler below uses attachClickHandler(offer, index) as a sample reference. Replace this with whichever event binding pattern your frontend framework or component library uses.
<script type="text/javascript">
Moengage.personalize
  .fetchExperiences(['Homepage_offers_experience'])
  .then((result) => {

    const experience = result.experiences?.Homepage_offers_experience;
    if (!experience) {
      renderDefault('Homepage_offers_experience');
      return;
    }

    // Parse the serialized JSON offer array
    const offers = parseJsonArrayPayloadField(experience, 'promo_card');

    if (!offers.length) {
      renderDefault('Homepage_offers_experience');
      return;
    }

    // Render each offer and track its impression.
    // offering_context must be passed as-is from each offer object — do not modify its fields.
    offers.forEach((offer, index) => {
      renderOffer(offer.offering_content);
      Moengage.personalize.offeringShown(offer.offering_context);

      // On offer click — pass both offeringContext and experienceContext.
      // Passing both means MoEngage automatically tracks the click for the offering
      // AND the parent experience in a single call.
      attachClickHandler(offer, index, () => {
        Moengage.personalize.offeringClicked(
          offer.offering_context,
          experience.experience_context  // Optional — omit if tracking experience click separately
        );
      });
    });

    // Track experience impression — must be called once for the resolved experience key.
    // Pass experience_context exactly as returned in the response. Do not modify its fields.
    Moengage.personalize.trackImpression(experience.experience_context);
  })
  .catch(() => {
    renderDefault('Homepage_offers_experience');
  });
</script>

Tracking impressions and clicks

Call the appropriate MoEngage SDK tracking methods after rendering each resolved experience and its offers.
  • For every resolved experience key, call Moengage.personalize.trackImpression(experience_context) after rendering.
  • When a user interacts with the rendered experience, call Moengage.personalize.trackClick(experience_context, extraAttributes). extraAttributes is optional — use it to pass additional context about the CTA clicked.
  • For experiences that contain offering data, call Moengage.personalize.offeringShown(offering_context) for each offer rendered. Each offer in the array has its own offering_context — pass it as-is and do not reuse contexts across offers.
  • On offer click, call Moengage.personalize.offeringClicked(offering_context, experience_context). Passing both contexts tracks the click for the offering and the parent experience in a single call.
Refer to Personalize API Experience events tracking and Offerings events tracking for the full method reference. For further assistance, please contact your MoEngage Customer Success Manager (CSM) or the Support team.