Web Sitemap

A sitemap is a JavaScript configuration file that instructs the Data 360 SDK which pages to track, what events to capture, and how to extract data from the page.

[ACTION REQUIRED: Review Note] - NOTE: You can use the Sitemap Generator (Coming Soon) tool to build your sitemap configuration without writing code.

Sitemap Basics

  • The sitemap is wrapped inside SalesforceInteractions.init(), which handles SDK initialisation and consent before the sitemap runs.
  • Consent is passed as a Promise that resolves when a user opts in or out. Each consent object requires purpose, provider, and status.
  • SalesforceInteractions.initSitemap() accepts three keys: global (cross-site listeners), pageTypeDefault (fallback for unmatched pages), and pageTypes (page-specific interactions and listeners).
  • Each page type uses an isMatch function or regex to identify the current URL. Define one page type per distinct page template on your site.
  • Resolvers extract values from the page at event-fire time. Use resolvers.fromSelector(), resolvers.fromSelectorAttribute(), and resolvers.fromHref() to pull data from DOM elements.
  • Every event must include an eventType property — without it, the SDK fires the event but Data 360 will not translate or ingest it.

[ACTION REQUIRED: Review Note] - WARNING: If an event does not have eventType mapped, it will not appear in Data 360. Always verify eventType is present on every event, especially custom events, before uploading your schema.

Basic Sitemap Example

The following example demonstrates a complete sitemap covering consent management, a global email capture listener, and page types for a product page, cart page, and order confirmation page.

SalesforceInteractions.init({
  consents: new Promise((resolve) => {
    const { OptIn, OptOut } = SalesforceInteractions.ConsentStatus;
    const purpose = SalesforceInteractions.ConsentPurpose.Tracking;
    const provider = "Test Provider";

    // user clicks button that grants consent
    document
      .getElementById("opt-in")
      .addEventListener(
        "click",
        () => resolve([{ purpose, provider, status: OptIn }]),
        {
          once: true,
        },
      );

    // user clicks button that revokes consent
    document
      .getElementById("opt-out")
      .addEventListener(
        "click",
        () => resolve([{ purpose, provider, status: OptOut }]),
        {
          once: true,
        },
      );
  }),
}).then(() => {
  // set the log level during sitemap development to see potential problems
  SalesforceInteractions.log.level = "debug";

  const {
    cashDom,
    listener,
    resolvers,
    sendEvent,
    util,
    CartInteractionName,
    CatalogObjectInteractionName,
    OrderInteractionName,
  } = SalesforceInteractions;

  const global = {
    listeners: [
      // capture email address when a user signs up
      listener("submit", ".user-signup-form", (actionEvent) => {
        const emailAddress = cashDom("#user_email").val();
        if (emailAddress) {
          sendEvent({
            interaction: {
              name: "Email Sign Up",
            },
            user: {
              attributes: {
                email: emailAddress,
                eventType: "contactPointEmail",
              },
            },
          });
        }
      }),
    ],

    // attach optional data to every actionEvent that is sent out
    onActionEvent: (actionEvent) => {
      const email = window && window._userInfo && window._userInfo.email;
      if (email) {
        actionEvent.user = actionEvent.user || {};
        actionEvent.user.attributes = actionEvent.user.attributes || {};
        actionEvent.user.attributes.emailAddress = email;
      }
      return actionEvent;
    },
  };

  const productIdResolver = resolvers.fromSelectorAttribute(
    ".product",
    "data-id",
  );

  const productPage = {
    name: "product",
    isMatch: () => /products/.test(window.location.pathname),
    // capture the product being viewed when the page is opened
    interaction: {
      name: CatalogObjectInteractionName.ViewCatalogObject,
      catalogObject: {
        type: "Product",
        id: productIdResolver,
        attributes: {
          name: resolvers.fromSelector(".product-title"),
          url: resolvers.fromHref(),
          imageUrl: resolvers.fromSelectorAttribute(".product img", "src"),
        },
        relatedCatalogObjects: {
          Color: resolvers.fromSelectorAttributeMultiple(
            ".color-value",
            "data-attr-value",
          ),
        },
      },
    },
    listeners: [
      // capture when the user adds this product to their cart
      listener("click", ".add-to-cart", () => {
        sendEvent({
          interaction: {
            name: CartInteractionName.AddToCart,
            lineItem: {
              catalogObjectType: "Product",
              catalogObjectId: productIdResolver(),
              quantity: parseInt(cashDom(".product .quantity input").val(), 10),
              price: parseFloat(cashDom(".product .price").text().trim()),
            },
          },
        });
      }),
      // capture when the user shares the product to social media
      listener("click", ".share", () => {
        sendEvent({
          interaction: {
            name: CatalogObjectInteractionName.ShareCatalogObject,
            catalogObject: {
              type: "Product",
              id: productIdResolver(),
            },
          },
        });
      }),
    ],
  };

  const cartPage = {
    name: "Cart",
    isMatch: () => /^\/cart/.test(window.location.href),
    listeners: [
      // capture when a user removes an item from their cart
      listener("click", ".remove-from-cart", (event) => {
        const $cartItem = cashDom(event.target).parents(".cart-item").first();
        sendEvent({
          interaction: {
            name: CartInteractionName.RemoveFromCart,
            lineItem: {
              catalogObjectType: "Product",
              catalogObjectId: $cartItem.attr("data-id"),
              quantity: parseInt($cartItem.find(".quantity").text().trim(), 10),
            },
          },
        });
      }),
    ],
  };

  const orderConfirmationPage = {
    name: "Order Configuration",
    isMatch: /\/confirmation/.test(window.location.href),
    // capture when a user completes an order
    interaction: {
      name: OrderInteractionName.Purchase,
      order: {
        id: resolvers.fromSelectorAttribute(".order", "data-id"),
        totalValue: parseFloat(resolvers.fromSelector(".order .total").trim()),
        lineItems: () =>
          cashDom(".order .line-items").map((index, el) => {
            const $lineItem = cashDom(el);
            return {
              catalogObjectType: "Product",
              catalogObjectId: $lineItem.attr("data-id"),
              quantity: parseInt($lineItem.find(".quantity").text().trim(), 10),
            };
          }),
      },
    },
  };

  const pageTypeDefault = {
    name: "default",
  };

  SalesforceInteractions.initSitemap({
    global,
    pageTypeDefault,
    pageTypes: [cartPage, orderConfirmationPage, productPage],
  });
});

Uploading the Sitemap to Data 360

Once your sitemap code is ready, upload it to your web connector in Data 360 Setup.

  1. Create a JavaScript file named sitemap.js and paste your sitemap code into it.
  2. Go to Data 360 Setup and click Web & Mobile Apps.
  3. Click on the connector you created in the previous step.
  4. Navigate to the Sitemap section of the connector setup page.
  5. Click Upload and select your sitemap.js file.
  6. Review the sitemap for errors and click Save.

[ACTION REQUIRED: Update Image Here] - Original Context/URL: Screenshot of the Sitemap section on the connector setup page showing the Upload button and error review panel.

Summary

A complete sitemap covers consent management, global listeners, and page-specific interactions. Every event must include an eventType property to be translated and ingested by Data 360. Once your sitemap is in place, validate that events are firing correctly in the browser console before uploading your schema and creating a Data Stream.