Two products, one customer picture
Dynamics 365 Customer Insights is really two applications sold under one name. Customer Insights – Data is the customer data platform: it ingests data from your CRM, webshop, ERP or loyalty system, unifies it into one profile per customer and lets you calculate measures and build segments on top of that profile. Customer Insights – Journeys is the marketing automation engine: segments, emails, SMS, forms, consent and journeys that react to what customers do.
You can run Journeys without Data, and many Dynamics 365 Sales customers do exactly that, working straight on Dataverse contacts and leads. The moment your customer data lives in more than one system, though, unification becomes the foundation everything else stands on. This article walks through how I approach both sides on projects, with extra attention to consent, because for Belgian and other EU customers that is where most of the real design decisions sit.
Where things stand
If you still have assets in the old marketing app, the most important fact to know is that the outbound marketing module of Customer Insights – Journeys has been removed. Real-time journeys is now the only journey engine, and anything you plan today should be built there.
Outbound marketing is gone
Outbound marketing was removed in May 2026. Most tables and their data remain accessible, but they are no longer refreshed. Outbound segments are still visible yet unusable, events created in outbound must be recreated, and old Power BI reports on outbound analytics need to be rebuilt on the new data sources. Microsoft documents exactly what remains in its notes about outbound marketing removal.
Customer Insights – Data: Getting unification right
Start with the sources, not the tool
Before anyone opens the unification wizard, I want a simple inventory per source: which system owns it, what identifies a customer there (email, customer number, phone), how complete those fields are, and how often the data changes. Ingest only profile data into unification. Orders, cases, web visits and other one-to-many data belong in activities and relationships, not in the customer data step. Clean up obvious junk rows at the source or in the ingestion layer first; unification is not a data quality tool.
The four unification steps
Unification runs in four steps. Understanding what each one does saves you a lot of trial and error. Microsoft's data unification overview covers the screens; here is what matters in practice:
- Customer data: select the source tables and columns that describe the customer and map them to semantic types such as email, phone, name and address.
- Deduplication: define rules per table to find multiple rows for the same customer and choose which row wins, for example the most recently updated one.
- Matching conditions: define how rows in different tables are matched to each other. Tables can only match against tables higher in the match order, and the first table is the primary table that forms the basis of your profiles.
- Unified data view: decide which columns are kept, excluded or merged, for instance five email columns merged into one with a clear winner policy.
Choose the primary table deliberately
The primary table should be the source with the most complete and reliable profiles that shares the most identifying columns with the others. In a CE-centric landscape that is usually the Dataverse contact table. Use Include all records only for tables whose unmatched rows genuinely represent customers; otherwise you inflate your profile count with singletons that nobody can act on.
Designing match rules
Rules are evaluated in order: the engine only tries rule two when rule one finds no match. So start with the strongest, cheapest signal (an exact, normalized email or customer number) and only then fall back to combinations such as last name plus postal code plus phone. Conditions within a rule are combined with AND. The matching rules documentation lists all normalization options. I document the design in a worksheet like the one below before configuring anything, so business owners can review it:
# Match design worksheet (design documentation, not an import format)
Match order
1. Contact (Dataverse) primary table: most complete profiles
2. WebshopCustomer Include all records: yes
3. LoyaltyMember Include all records: no
Deduplication: WebshopCustomer
Rule 1 Email Normalize: Whitespace, Text to lower case Precision: Exact
Winner most recently modified row
Matching: WebshopCustomer -> Contact
Rule 1 Email Normalize: Whitespace, Text to lower case Precision: Exact
Rule 2 LastName Normalize: Type - Name, Unicode to ASCII Precision: High
AND PostalCode Normalize: Whitespace Precision: Exact
AND Phone Normalize: Type - Phone Precision: Exact
Exception: FirstName is different -> do not match
Custom bypass (values that must never cause a match)
info@yourcompany.be
test@example.com
+32000000000
unknownNormalization details that bite in Belgium
- Phone numbers: the Type - Phone normalization treats the country code as significant, so +32 3 123 45 67 does not match 03 123 45 67. Store numbers in international format at the source, or you will lose matches silently.
- Names with accents: combine Unicode to ASCII with Type - Name so that names typed with and without accents (Hélène, Helene) or with name variations still compare well.
- Shared and dummy values: generic B2B mailboxes, test addresses and placeholder phone numbers will glue unrelated people together. Put them in a custom bypass list.
- Fuzzy precision: use Exact for identifiers and reserve fuzzy precision (High at 80% rather than Low at 30%; Medium is 60%) for names and addresses inside a rule that also contains an exact condition.
The CustomerId is stable, until it is not
Every unified profile gets a CustomerId that survives refreshes, except when profiles merge or split because data or rules changed. In those cases the PreviousCustomerId column tells you where a profile came from. If you export profiles to other systems or keep IDs in Dataverse, design downstream processes to handle merges and splits instead of assuming the ID is permanent.
Measures and segments on unified profiles
Once profiles exist, measures turn activity data into numbers you can use: total revenue in the last twelve months, days since last order, number of open service cases. Segments then combine profile attributes and measures. Two habits keep this manageable:
- Name for the business: a measure called Revenue12M with a description of its source and filter is reusable; Measure 7 is not.
- Keep segments few and owned: every segment needs an owner and a purpose. Dozens of near-identical segments make refresh times longer and nobody trusts the numbers anymore.
- Respect the refresh chain: segments depend on measures, which depend on unification, which depends on ingestion. Schedule source refreshes so the chain completes before the journeys that use the result start.
Consent and GDPR for EU customers
This section describes how the product handles consent; it is not legal advice. Which lawful basis applies to which communication is a decision for your organization and its data protection officer. My job as consultant is to make sure the system can enforce whatever they decide, and that the decision is recorded somewhere other than in my head.
The consent model in Journeys
Consent in Customer Insights – Journeys is stored per contact point, not per contact record. Read the consent management overview before designing anything; the building blocks are:
- Contact point consent: consent belongs to an email address or phone number. If two contacts share an address, they share its consent.
- Compliance profile: groups purposes, topics, the preference center and the company address, typically one per brand, legal entity or line of business.
- Purpose: why you communicate, for example commercial, transactional or tracking. Each purpose has an enforcement model.
- Topic: optional finer subscriptions under a purpose, such as newsletter or event invitations.
Enforcement models
Each message is tied to a compliance profile, a purpose and optionally a topic, and consent is checked immediately before sending. The enforcement model on the purpose decides what happens when no consent is recorded:
| Enforcement model | No consent recorded | Opted out | Typical use |
|---|---|---|---|
| Restrictive | Blocked | Blocked | Commercial messages that require explicit opt-in |
| Nonrestrictive | Sent | Blocked | Communication on a basis other than opt-in, where opt-out must still be honored |
| Disabled | Sent | Sent | Strictly transactional messages |
Check the defaults before go-live
Out of the box, the Commercial purpose is Nonrestrictive and the Transactional purpose is Disabled. Microsoft explicitly states these are product defaults, not legal recommendations. Disabled ignores opt-outs entirely, so never route marketing content through a transactional purpose to get around consent. Also note that tracking (opens, clicks, website interactions) has its own purpose and is not in the default preference center; add it if your design relies on it.
Double opt-in
Double opt-in sends a confirmation email after a form submission, and only creates or updates the contact and consent records once the link is clicked (the link expires after 28 days). It is enabled through a feature switch and then configured per compliance profile. It applies to forms created after you turn it on (older forms are switched per form), each form uses one compliance profile, and it is not available for event registration or preference center forms. For B2C newsletters in the EU I treat it as the default choice: it gives you proof that the address owner asked for the subscription, and it keeps typos and fake addresses out of your database. See double opt-in in real-time journeys.
Lawful basis and record-keeping
The system stores opt-in and opt-out status, source and dates, but it does not know why you are allowed to send. Agree per purpose which lawful basis applies, write it down in the solution design, and make sure every channel that collects consent (forms, the preference center, imports, the service desk) writes it in the same way. Consent you import from legacy systems needs a source and date as well, or it is very hard to defend later.
Data subject requests
Requests for access, correction and erasure touch more places than people expect. In my runbooks I include at least:
- The source systems, because Customer Insights – Data rebuilds profiles from them on every refresh. Deleting only the unified profile is pointless.
- Dataverse contacts and leads, plus their contact point consent records and interaction history used by Journeys.
- Exports and downstream copies: files, data lakes or other systems that received profiles or segments.
- A log of what was done and when, so the request can be evidenced.
Customer Insights – Journeys: Segments and real-time triggers
Segments
Journeys can use segments built in its own segment builder on Dataverse contacts and leads, and, when the environments are connected, segments and profiles from Customer Insights – Data. Exclusion segments on entry and suppression segments as exit criteria are the cleanest way to keep people out of a journey. Live segments can be edited while a journey runs, which is convenient but deserves a change log. The segmentation overview explains the options. I sketch segments as pseudo-logic in the design document before building them:
// Pseudo-logic for the design document; built in the segment builder, not in code
Segment: "BE - renewal due in 90 days (NL)"
Contacts
where Address 1: Country/Region = "Belgium"
and Language (custom column) = "Dutch"
and has related Subscription (custom table)
where Status = Active
and End date within the next 90 days
Journey entry: this segment
Exclusion segment: "Open complaint cases"
Exit criteria: trigger "Renewal confirmed"Trigger-based or segment-based?
Trigger-based journeys react to something an individual customer does: submitting a form, abandoning a cart, a record changing in Dataverse. Segment-based journeys address an audience. Business events that hit many customers at once, such as a billing run or an annual renewal mailing, belong in a segment-based journey: trigger throughput is capped at roughly 2,500 triggers per minute, and excess triggers are slowed down or can fail. A useful rule of thumb:
- Customer-initiated, spread over time: trigger-based journey, using out-of-the-box Dataverse triggers or a custom trigger.
- Company-initiated, many people at once: segment-based journey driven by a column that puts contacts into the segment.
- Exit and goals: use triggers as exit criteria (for example a completed purchase) so customers do not receive reminders for something they already did.
Custom triggers
A custom trigger is a signal you define yourself, with typed attributes (text, number, true/false, date/time or entity reference) that journeys can branch on and use for personalization. Every custom trigger carries the customer it applies to, a contact, lead or Customer Insights – Data profile. After you create the trigger, the app generates a code snippet for your developers, and once events arrive the trigger becomes ready to use. Details are in create custom triggers.
Treat the ingestion key as a secret
The generated snippet contains an ingestion key that identifies your Journeys instance. Anyone who has it can raise triggers. Keep it out of public front-end code where possible, raise triggers from a server-side component instead, and keep trigger attributes that end up in message content to a minimum so they cannot be abused for script injection.
Common pitfalls
- Unifying transactional data: putting orders or visits in the customer data step instead of activities, which produces strange matches and slow runs.
- Phone numbers in mixed formats: national and international notation side by side means fewer matches than expected, without any error.
- Generic mailboxes in B2B data: info@ and sales@ addresses merge colleagues into one profile and also share one consent record in Journeys.
- Fuzzy matching everywhere: low precision on single columns creates false merges that are much harder to undo than missed matches.
- Consent stored on the contact only: legacy contact fields such as Do not allow Bulk Emails are not the same thing as contact point consent. By default, real-time journeys ignore Do not email, Do not allow Bulk Emails and Do not track; they are only evaluated, for contacts, when you turn on the feature switch Check contact consent in real-time journeys, and the preference center never writes them back. Migrate them into consent records deliberately instead of assuming journeys respect them.
- Transactional purpose as a shortcut: sending marketing content under a Disabled purpose bypasses every opt-out.
- Triggers for batch events: raising thousands of triggers at once for a business event instead of using a segment.
- Leftovers from outbound: old outbound segments still appear in lists but are not updated; make sure nobody selects them.
A pragmatic rollout order
- Agree on the consent design: compliance profiles, purposes, topics, enforcement models and lawful basis, signed off by the DPO.
- Inventory and clean sources; fix phone formats and dummy values at the source.
- Unify with two sources first, review sample profiles with the business, then add the rest.
- Build a small set of measures and segments that an actual journey needs.
- Go live with one trigger-based journey (for example a form follow-up) and one segment-based journey, including exit criteria.
- Write and test the data subject request runbook before the first campaign, not after the first request.
Conclusion
Customer Insights delivers value when the foundations are boring and solid: clean sources, simple and documented match rules, a consent model that reflects decisions your organization has actually made, and journeys that use triggers and segments for what each is good at. Fancy personalization built on wrong profiles or doubtful consent only gets you into trouble faster.
Start small, get unification and consent right, and expand from there. That order is less exciting in a kick-off meeting, but it is the one that still works a year later.