Marketing teams are under increasing pressure to drive measurable pipelines, improve conversion rates, and maximize return on digital spend. Yet a significant portion of website traffic remains anonymous. Visitors browse products, consume content, compare offerings, and demonstrate buying intent without identifying themselves. When that early engagement cannot be connected to a known customer profile, marketing loses valuable context that could accelerate conversion and improve personalization.
This creates a missed revenue opportunity. Without linking pre-identification behavior to known contacts, segmentation is incomplete, nurture journeys lack behavioral depth, and campaign attribution reflects only part of the customer story. Marketing decisions are then made on partial insight rather than full lifecycle visibility.
The opportunity is clear; connect anonymous engagement to known identities the moment a visitor converts. By capturing behavioral signals early and merging them into the customer profile at identification, marketing gains a complete view of intent from first interaction through opportunity creation. That unified insight enables more precise targeting, stronger personalization, improved journey performance, and a measurable lift in conversion rates and customer lifetime value.
Microsoft Dynamics 365 provides two key products that make this possible:
- Customer Insights – Data (CI Data): The Customer Data Platform (CDP) that collects and unifies behavioral, transactional, and demographic data across systems.
- Customer Insights – Journeys (CI Journeys): The marketing automation platform that delivers personalized, real-time engagement using known contacts and leads.
Each serves a different purpose but together creates a powerful closed loop between identity, behavior, and engagement.
In this blog, we will use Customer Insights – Data to:
- Track anonymous website activity in real time using a lightweight web tracking script
- Capture identifying information through a Dataverse form
- Merge anonymous browsing history with a known Contact record at the moment of identification
The tracking script logs page views, clicks, and other behavioral signals using cookies. Anonymous visitors are assigned a temporary profile tied to their browser session. When that same visitor later logs in, signs up, or submits a form, we call the setUser() function to associate the browser cookie with a Contact record in Dataverse.
To enable this identity resolution, identifying information must be collected through a Dataverse-backed form. In our example, the form is hosted on Power Pages, but the same pattern applies to any website capable of securely writing to Dataverse.
By combining these solutions, you get true end-to-end personalization. CI Data ensures that even before a visitor is identified, their interactions, pages viewed, actions taken and content downloaded are tracked. Once the user becomes known (for example, when their information is saved as a Contact in Dataverse), CI Data links their anonymous browsing history to that Customer profile. CI Journeys also immediately begin engaging that Contact through personalized, automated campaigns such as welcome emails, nurture sequences, or offers closing the loop between data collection, identity resolution, and engagement.
Customer Insights – Data Real-Time Web Personalization enables the customization of a customer's web experience either through a "pro-code" approach using APIs, or optionally through a "no-code" approach using the Optimizely Solution.
Pro-Code Web personalization relies on 3 main capabilities:
- Capture Web Activities from Website visitors in Real-Time
- Update customer profiles in real time based on these web activities
- Personalize your customers' web experience in real time
In this blog, we will demonstrate the basic contact capture pro-code approach on how to use Customer Insights Data, Customer Insights Journeys together on a Power Pages website for real-time personalization. Imagine a Power Pages site where visitors can either sign up/log in (becoming known users) or download gated content (providing their information). We want to track the user's journey in either case:
- If they are an existing customer and log in, the system recognizes them as a known Contact. The setUser() call links their current browser session to their existing Contact record, ensuring that all new activity continues to be tracked under their established profile. Their behavioral history continues to accumulate in real time, enriching segmentation and enabling ongoing personalized engagement.
- If they remain anonymous, their clicks/page views are tracked to an unknown profile.
- If they fill out a form (to register or download content), we capture them as a Contact (known profile) and immediately merge their anonymous browsing history into that known profile. At the same time, that Contact is available in CI Journeys for marketing outreach (e.g. a welcome email or adding to a journey).
To achieve this, we will set up the integration between Power Pages and Dataverse with two implementation approaches (Path A and Path B). Both paths will accomplish the goal of creating a Contact upon form submission and calling the Customer Insights web tracking API to merge profiles in real-time. The difference lies in how we handle duplicate contacts and the complexity of implementation.
Design Paths for Capturing Known Users
Path A – Basic Contact Capture: This simple approach uses a single-step Power Pages form and client-side JavaScript to ensure every submit results in a known identity for personalization. On submit, the script checks whether a Contact already exists in Dataverse (DV) using the email address. It then either reuses the existing Contact or creates a new one. Once the Contact ID (GUID) is available, the site calls the Customer Insights web tracking API to associate the current browser cookie with that Contact.
At a high level, If there is a need to create a new contact because it does not exist in DV Contact Table, the following happens:
- The Power Page form will create new contact in DV
- You use the new ContactID to execute MSCI.SetUser()
- Result: This User is still unknown from a CI-D perspective
This happens because when a new contact is created in DV, the DV data source in CI-D is not yet refreshed and Unification has not been run to unify new contacts originating from the DV data source. So, CI-D is still not aware of this new contact, consequently the MSCI.SetUser will not be able to "resolve" this in CI-D as it has not gone through unification. In this situation, the new contact will only become a known one (from a CI-D Perspective) after the next CI-D System refresh.
Pros: Straightforward implementation with minimal configuration and immediate identity resolution within the same browser session.
Cons: Introduces security and governance considerations when invoking the Dataverse Web API from the client. Also, Duplicate risks remain possible due to race conditions or inconsistent email normalization
Path B – Advanced Contact Capture with De-duplication: This approach uses a multi-step form and a backend Dataverse real-time Plugin for de-duplication. The form first creates a record in a custom Contact Request table (instead of directly in Contacts). A Dataverse triggers on the submission checks if a Contact exists (e.g. matching email).
- If yes, it links to the existing Contact.
- If not, it creates a new Contact and associates it with the Contact Request.
The form’s second step (the thank-you page) can then pull the linked Contact’s ID (via a hidden field) and invoke the identification script.Pros: Avoids creating duplicate Contact records by reusing existing contacts when possible.Cons: More complex to set up. Requires a Dataverse real-time Plugin for deduplication. Plugin has to be efficient to avoid performance bottlenecks.Both paths A and B leverage the same integration points between Customer Insights – Data:- Web Tracking Script: The CI Data tracking script is added to your Power Pages site so that all visitors (anonymous or signed‐in) generate web interaction events (page views, clicks) captured in the system
- Identity Merge (setUser): When a visitor becomes known either by logging in or submitting a form, the setUser() function is executed with the resolved Contact identifier.
| Javascript |
<script> function setUser() { window["MSCI"].setUser({ "authId": "<identifier>" }); } </script> |
| HTML |
| <button type="submit"onclick="setUser()">Submit</button> |
This tells CI Data: “This browser cookie now belongs to this known user.” At that moment the unknown profile merges into the known profile, so all the prior anonymous browsing history attaches to the known profile.
- Contact Creation: Your form (either Path A or Path B) creates a record in Dataverse Contact table (or lead/opportunity depending on your model). That Contact is the “known user” target. It also becomes usable by Dynamics 365 Customer Insights – Journeys for marketing actions (segments, journeys, etc.).
- Real‐time Web Personalization Tables: The Dataverse tables used to track user behavior and the cookie‐to‐contact linking are:
- PersonalizationUser: links an unknown (cookie) profile to a known profile when setUser is called.
- PersonalizationCookie: stores cookie values for unknown profiles.
- PersonalizationView: records page views (what pages the user visited).
- PersonalizationAction: records discrete actions (clicks, downloads, link clicks) taken by the user.
These tables give you detailed visibility into what visitors (unknown or known) are doing on the website. You can query them or use them via the personalization API to feed into your website logic or segmentation.
- Data Unification for new Known Profiles: What this really means is that after the new user is created, known and identified (step 2 with setUser), the user’s contact record must ultimately be part of the unified profile dataset in CI Data so that all behavioral data (from step 4) rolls up into one unified customer profile. The unification process in D365 Customer Insights Data involves refreshing your data sources (in this case the Contact table), deduplicating records, matching and merging into a customer profile table. Once unified, CI Data can automatically link Dynamics 365 app to unified customer profile via automatic CustomerId Backstamping Hydration.
Path A: Basic Contact Capture (Simple Integration)
Concept: In Path A, the Power Pages site uses a form with client-side logic to create or retrieve a Contact directly in Dataverse. Once the Contact is resolved, the script immediately invokes the setUser() function to associate the current browser session with that Contact. This merges any pre-authentication web interactions (tracked via the cookie) into the now-known profile in real time. From that point forward, Customer Insights – Data recognizes the visitor as a known Contact, and Customer Insights – Journeys can engage them accordingly.How it works: When the form creates a new Contact, Dataverse generates a Contact GUID. We capture that GUID(ContactId) which is the primary key of the contact table ingested into CI Data, then call the setUser API from the tracking script. The setUser call uses the identifier to link the current browser session’s cookie to the corresponding customer record in the contact table. As a result, the PersonalizationUser table in Dataverse gets an entry linking the cookie ID to the Contact’s ID. All past and future web interactions (stored in PersonalizationView and PersonalizationAction tables) are now associated with the known Contact profile. Downstream, any segments or measures that include this Contact will consider their prior anonymous behavior as well. Meanwhile, the Contact record is available to CI Journeys for outreach such as triggering a welcome journey or simply as part of your marketing database. Steps to Implement Path A:
- Enable CI Data Web Tracking on your site: In Customer Insights – Data, go to Web tracking & personalization (preview) and select the table that identifies your customers (e.g. Contact). Copy the provided tracking script and paste it into the <head> of your website. In Power Pages, you might do this by adding it to the master page or using a script component/snippet in the template, so it loads on all pages. This script will start creating unknown profiles for visitors and logging page views. You can verify it's working by browsing the site and then checking the Customers > Unknown tab in CI Data, you should see profiles with cookie IDs being created and their page hits recorded.
Image1 - web tracking script from Dynamics 365 Customer Insights Data
Image2 - web tracking script on PowerPages- Configure a Custom Form with Contact Upsert Logic: In Power Pages, create a form that captures the required fields (e.g., First Name, Last Name, Email, Mobile). Instead of relying on a standard Insert form with redirect, this implementation uses client-side JavaScript to securely call the Dataverse Web API.
When the user clicks Submit, the script:
- Validates required fields and prevents duplicate submissions.
- Retrieves a secure anti-forgery token for Web API access.
- Checks whether a Contact with the submitted email already exists in Dataverse.
- If found, retrieves the existing contactid.
- If not found, creates a new Contact and extracts the generated contactid.
- Stores the contactid in sessionStorage.
- Calls MSCI.setUser({ authId: contactId }) to immediately associate the current browser cookie with that Contact.
This approach ensures that existing customers are reused, new Contacts are created only when necessary, and identity merging with Customer Insights – Data occurs in real time
Image 3: Existing contact login
Image 4: New contact created
Image 5: New contact login
Image 6: Duplicate contact identified- Execute Identity Merge on Form Submission: Identity resolution and profile merging occur directly within the form submission process. Once the script determines the correct contactid—either by retrieving an existing Contact based on email or creating a new one—it immediately calls the Customer Insights identification API:
javascript <script> window["MSCI"].setUser({ authId: contactId }); <script> |
The contactid (GUID) is the primary key of the Contact table configured for web tracking and unification in Customer Insights – Data. Because the tracking configuration uses this primary key as the identifier, passing the contactid ensures the correct customer profile is identified.
When setUser() executes:
- The current browser cookie (anonymous profile) is linked to the identified Contact.
- All previously captured web interactions are merged into the known profile.
- All future web activity continues under that Contact.
- The anonymous-only profile is retired.
This enables real-time identity merging and continuous behavioral tracking from the moment the Contact is resolved.
Image 7: Known contact identity and cookie tracked in real time
Image 8: Unknown contact identity and cookie tracked in real timePrepare for the next unification run in CI Data: While setUser() performs real-time identity linking at the cookie level, newly created Contacts must still participate in the next data refresh and unification cycle in Customer Insights – Data. During this cycle, the Contact is matched, merged, and incorporated into the unified customer profile table, ensuring complete consolidation of behavioral, transactional, and demographic data for segmentation, analytics, and journey orchestration.
Image 9: Newly created contact identity and cookie tracked in real time
Path B: Advanced Contact Capture with De-duplication
Concept: Path B prevents duplicate Contacts by introducing a Contact Request table and a flow that runs before record creation. When the form is submitted:- Dataverse Plugin attached to the Create event that is real-time synchronously checks if a Contact already exists using a unique field (such as email).
- If found, it links to the existing Contact.
- If not, it creates a new one and sets the lookup reference.
The form’s second step retrieves this Contact ID and executes the setUser call, achieving the same merge behavior as Path A, but with higher data quality.Benefits- Prevents duplicates and maintains a clean master Contact dataset.
- Supports enterprise-grade personalization with precise identity resolution.
Conclusion
In both the basic and advanced implementations of contact capture with de-duplication, the overall flow remains consistent:
- The website tracking script initializes a session and generates an unknown profile (cookie) in Customer Insights – Data.
- The form submission either creates a new Contact or finds an existing one in Dataverse.
- The setUser function links the anonymous cookie profile to Contact or Lead, merging all prior web activity into the known customer profile.
- During the next data refresh and unification run, CI Data consolidates behavioral, demographic, transactional, and web interaction data into a single unified profile.
- This unified profile becomes the foundation for segmentation and engagement in Customer Insights Data and Journeys. With a consistent view of the customer, organizations can target users with personalized content and measure their full lifecycle from anonymous visitor to fully engaged customer.
It is important to call out that CI Journeys forms may seem like a natural choice, especially since they already support duplicate detection and integrate deeply with Dataverse. However, today CI Journeys forms work only with known Contacts and Leads. They cannot track anonymous visitor behavior or participate in the real-time identification and profile merging process required for end-to-end web personalization.
Microsoft is actively working on extending real-time web personalization capabilities, including enabling anonymous tracking through CI Journeys forms.
Together, Customer Insights – Data and Journeys enable real-time web personalization allowing your website to adapt instantly to each visitor, and your marketing journeys to follow up with rich context about who the user is, what they interacted with, and how best to continue the conversation.