# Martin P. Lopez — Writing, full text
> Independent Dynamics 365 Architect and Customer Insights expert, specialized in Customer Insights – Journeys. I help marketing teams at medium-to-large enterprises ship CI-J properly.
All articles from https://martinplopez.com/writing/ as raw markdown. Index: https://martinplopez.com/llms.txt
---
# 4 Learnings from Multi-Session Events in Customer Insights - Journeys
- URL: https://martinplopez.com/writing/learnings-multi-session-events/
- Published: 2026-08-21
- Summary: Four practical lessons from implementing multi-session events in Customer Insights - Journeys, covering registration forms, session visibility, confirmation emails, and cancellations.
I'm standing at my desk with the air from the fan hitting my back. In front of me, my client asks how we could improve registration for an event with three workshops running at the same time.
For me, the answer was clear: sessions. But my client had never worked with them before.
And lately, they haven't been the only client asking about this kind of event. That encouraged me to revisit session management in Dynamics 365 Customer Insights - Journeys and see how it behaves in real implementations.
Here are four things I learned along the way.
---
> [!NOTE]
> **TL;DR:**
>
> - Only publish sessions attendees should actually see and register for; draft sessions are useful for internal agenda items.
>
> - For simultaneous sessions, the standard registration experience may need custom logic to prevent attendees from choosing conflicting time slots.
>
> - Session registration data works well in confirmation emails, but a small Dataverse customization can make sorting by session start time much easier.
---
## Why use sessions in the first place
Sessions are a structured way to manage an event agenda. They're especially useful for events with multiple presentations or workshops, as they allow you to track how many attendees have registered for each one.
They also help with event coordination by letting you manage capacity for individual sessions. Instead of keeping the agenda in a separate document or note, everything stays connected to the event and visible in the calendar.
And sessions aren't limited to in-person events. Customer Insights - Journeys also supports online sessions with different Microsoft Teams meetings, making it possible to run simultaneous virtual sessions with separate registrations.
---
## My 4 Implementation learnings
### 1. If you don't want to show some sessions, just leave them in draft
When you're in an event and you click on the agenda tab, it will display a calendar view with a sub-grid underneath. The sub-grid will show the sessions that you have linked to this event, and even a summary of all the people registered to each session.

This overview might not be for every user. So to prevent users from registering for the lunch pause, it's as simple as **leaving the sessions as draft**, and **only Live sessions will be available for registration**.
### 2. Adapting the form is a must, especially with simultaneous sessions
It's not a surprise that the form that Microsoft provides to register sessions is neither looking great, nor very functional with some requirements of some customers.

For instance, if you are displaying different sessions that happen at the same time, in the standard form people can subscribe to different sessions that happen at the same time. In order to prevent this, you have to directly **edit the HTML by adding some CSS and custom JavaScript** to allow the one-session-per-time-slot logic to take place.
Customer Insights - Journeys does have an option to allow only one session registration, but that doesn't solve this scenario: attendees should still be able to select multiple sessions throughout the day, just not overlapping ones.
Luckily, **AI can speed up the JavaScript and CSS work considerably**, but the important part is defining and testing the registration logic correctly. Especially around overlapping times, mobile behaviour, and different event configurations, but it can give you great results like the following one:

The good thing for making a nice-looking form for sessions is that you can **reuse it for multiple events**, especially once you've implemented the one-session-per-time-slot logic.
> [!TIP]
> **Thank you Page**
>
> Mind the Thank you page, if you reuse the default form, it will automatically display the sessions that you subscribed to.
>
> You can hide the sessions with a JavaScript or customize the thank you and add the sessions as I described [in this article](/writing/dynamic-thank-you-page-dynamics-365)
### 3. Lists are fantastic for emailing session details, you just have to know how
To include the sessions an attendee registered for in an email, make sure you're working with the **Session Registration** table rather than the **Session** table, with the following structure: `Marketing Event Registration Reference --> Session Registrations`:

The challenge is that the session start time belongs to the Session record, not the Session Registration record. Because of that, I couldn't reliably sort my session-registration list chronologically using the field I actually needed.
My workaround for this was to create a field on the session registration table called session start. It is a formula field that will automatically get the start time from the session and put it on your session registration.

This allows you to sort the session registrations by start date, making the agenda in the email much more natural for the attendee.

### 4. Be intentional about allowing attendees to cancel individual sessions
When testing to cancel my registrations for the event or the sessions, I discovered first that the phone view of the standard cancellation form is terrible.

And second, it will allow you, when you have registered sessions, to cancel your session registrations. The problem is that this is a standard form, so it will display all the live session registrations of the event, even the ones that the person didn't register for.
Clicking a session the user isn't registered for has no effect. Clicking a session they are registered for cancels their registration.
Most of my clients don't want attendees to have the option to cancel individual sessions, especially since they can't register for a replacement session from the same page.
My recommendation here is to:
1. Make the form responsive for phones 🙏
2. Hide or disable the session-level cancellation controls with JavaScript so attendees can only cancel their entire event registration.
I normally recommend this because there is only one standard cancellation form. I try to avoid heavily customizing it, as it will be reused for all the cancellation options of all your events. When your event doesn't have sessions, it will also use this one.
There are certain ways to prevent this by linking emails to a certain cancellation form, but it makes the logic a bit more complex than what the clients are normally looking for.
---
## Conclusion
Sessions are one of those Customer Insights - Journeys features that look straightforward until you start applying real event requirements to them.
If I were implementing a multi-session event tomorrow, I would spend most of my time thinking about the attendee experience: Can they accidentally select overlapping sessions? Is their agenda clear in the confirmation email? And do the cancellation options match what the event organizer actually wants?
The standard session functionality gives you a good foundation, but don't assume the default registration and cancellation experiences will cover every requirement. Test the complete journey from registration to confirmation to cancellation before going live.
And if your event has three workshops happening at 10:00, make sure your attendees can't somehow be in all three. 😉
---
# Teams Webinar V2 in Customer Insights Journeys: First Lessons Learned
- URL: https://martinplopez.com/writing/teamsv2-customer-insights-journeys-lessons/
- Published: 2026-08-07
- Summary: Two months after setting up Teams Webinar V2 authentication for Customer Insights - Journeys, here is what actually broke, what the documentation does not tell you about real customer setups and the checks I now run before every webinar.
In June I wrote about [how to set up Teams Webinar V2 authentication for Customer Insights - Journeys](/writing/teamsv2-customer-insights-journeys/): the app registration, the federated credentials, the Graph permissions, the access policy.
Since then, that article has done something I didn't fully expect. People don't ask me *how* to set it up anymore. **They ask why it still doesn't work** 🙋
And the interesting part is that it's almost always the same short list of things. Not the setup itself, which most admins get through fine, but the gap between "the configuration is saved" and "my webinar actually behaves the way I promised the business it would".
So this is the follow-up: **the first lessons from running this in the wild**, and the checklist I now run before anyone books a webinar.
---
> [!NOTE]
> **TL;DR:**
>
> **The Teams Webinar V2 setup itself is not what breaks.** Two months of follow-ups later, the configuration from the June article has held up. The problems all sit in the gap between "the configuration is saved" and "the webinar behaves the way I promised the business it would".
>
> **Most custom event forms are missing the Meeting Options control.** They were last edited before Webinar V2 existed, so the button simply isn't there. It's not a drag-and-drop control: you have to copy `MeetingOptions` from the out-of-the-box form into your own form XML.
>
> **A greyed-out Meeting Options button is a security role gap.** Custom event management roles usually lack the read privilege on **Event teams properties** (`msevtmgt_eventteamsproperties`).
>
> **The Graph permissions must be Application permissions, not Delegated.** Adding them as delegated looks complete and saves without complaint, but the app can't use them and the error you get downstream never mentions consent.
>
> **If everything is by the book and it still fails, clear the browser cache and sign in again.** And plan for the real bottleneck: the setup touches Customer Insights, Entra ID and Teams, which in most organisations means three owners, three change processes and three weeks for one hour of technical work.
---
## What held up
Two things from the June article I'd write exactly the same way again.
**One app registration for the whole tenant is enough.** Nobody has come back to me with a case that needed one app per Customer Insights environment. Adding a separate federated credential per environment on the same registration has been the right shape every time, and it keeps the permission grant and the consent in one place instead of three.
**The setup is a one-time cost.** Once it's correct, it stays correct. None of the problems below came from the configuration drifting on its own. They came from customer setups that differ from the out-of-the-box configuration, which, in my experience, is much more common than not.
---
## The lessons
### 1. The Form doesn't display the Meeting Options
This is the most common error and it happens to people that followed the recommended guidelines and do their form customizations in a separate form, not the out-of-the-box form. The problem with this is that probably, by the time that the form was last edited, the Webinar V2 option was not available, so many customers have the problem that the Meeting Options button directly doesn't show:

The problem with this issue is that it's not a drag-and-drop control that you can put in a form directly, so you will have to go through the form.xml underneath to do this.
If you ever edited an XML Form of Dynamics 365, you'll know they are very easy to break. So I used the help of AI to insert the control named MeetingOptions inside the existing XML.
The best way I found to do this (so far) is to provide the AI with the out-of-the-box form and then your custom form that you use normally, and ask it to copy the MeetingOptions control into your normal custom form. This will have a good result in my experience.
I'm a bit old school, so I used a tool in the XrmToolbox called [Form Xml Editor](https://www.xrmtoolbox.com/plugins/MscrmTools.FormXmlEditor/) to extract the Xml of the forms and to upload the form with the Meeting Options control
> [!TIP]
> **This is the (new) way**
>
> This issue will very likely not be solved with an export in the near future.
>
> With the evolution of the [Dataverse Skills](https://github.com/microsoft/Dataverse-skills), you can do this with an AI directly, I just didn't try it this use case
If you take one thing from this note: **Make sure to add the controls that you use from the out-of-the-box event form to your custom form (if you use one)**.
### 2. Meeting Options appears, but the button is disabled

If your marketing people are using a security role that is not out of the box, for instance, if you have defined an event management security role, it might look like you have a meeting options button that shows disabled. They will need the permission to read "Event teams properties" (msevtmgt_eventteamsproperties), which you can give directly to the group.

This will give the permissions that were missing for it to work properly.
### 3. I set up the Graph permissions but it still doesn't work
The Graph permissions step is where I see the most silent failures.
Adding delegated permissions only to the app registration is sadly not making the cut. An admin normally would doubt to give Application Permissions as it can lead to a security threat and it might require an approval process, so he normally adds everything the documentation lists, saves, sees the full list as delegated permissions, and reasonably concludes the step is done.
It isn't. Until **Application permissions** is granted for the tenant, the app can't actually use those permissions, and the failure you get downstream doesn't say "consent is missing". It says something much vaguer about the webinar not being created.
**Make sure the Application Permissions are not Delegated permissions.** That's the whole lesson.
### 4. We did everything in the documentation, we still get the error message
Sometimes it can take a bit of time for Dynamics 365 to apply these application setups, but mostly what I see is just a cache problem.
Make sure that after you did everything in the documentation, you [clear the cache completely in the browser](https://its.uiowa.edu/services/how-clear-cache-and-cookies-your-web-browser), or even sign out and sign in again. That normally makes it work.
### 5. The real bottleneck is the security process
I said this at the end of the June article and two months of follow-ups have only made me more confident about it.
The setup touches Customer Insights, Entra ID and Teams, and in most organisations those are three different people with three different change processes. The technical work is maybe an hour. **Getting those three people in the same conversation, make them understand which permissions they really need and get approval is the part that takes three weeks.**
If you're a marketer or a functional consultant, the useful move is not to learn the setup, it's to start the conversation early and bring the June article with you. If you're the admin who owns all three, you already know how much easier your life is 😅
---
## My lessons learned checklist
The short version I now run through before anyone commits to a date:
1. **Open the event form your marketers actually use and confirm the Meeting Options control is there** → if it's a custom form, assume it isn't until you've seen it.
2. **Check the security role behind a greyed-out button** → read on Event teams properties (`msevtmgt_eventteamsproperties`), not just the standard event privileges.
3. **Confirm the Graph permissions are Application permissions and granted for the tenant** → the permission list looking complete is not the same as the app being able to use it.
4. **Clear the cache and sign out and in again before declaring it broken** → a good part of the "we did everything and it still fails" cases end here.
5. **Get the Customer Insights, Entra and Teams owners into the same conversation early** → before a date is promised to anyone.
Steps 1 to 4 are maybe an hour of work. Step 5 is the one that decides whether that hour happens this week or in three weeks, and it's the one people start last.
---
## Conclusion
None of these are hard problems. What do they have in common?
The form saves without the control it needs. The role saves without the privilege it needs. The permissions save as delegated and the list looks exactly as complete as it would if they were right. Everything looks like it worked, right up until someone asks why they don't see the meeting Options button or they see it, but is greyed out. That's a bad place to discover a misconfiguration, luckily it happens before the webinar goes live and you have the chance to fix it.
I wish these kinds of problems would be included in the official documentation, but until then, if this article can help someone, I'm happy to write it 😊
If you haven't done the setup yet, start with the [full walkthrough from June](/writing/teamsv2-customer-insights-journeys/) → this note assumes you've already been through it.
---
# Beyond the Default: Set Up and Brand a Customer Insights - Journeys Event Web App
- URL: https://martinplopez.com/writing/cij-webapp-setup-branding/
- Published: 2026-07-17
- Summary: Learn how to configure, customize, and deploy a Dynamics 365 Customer Insights - Journeys event web app using AI-assisted branding and static hosting.
After writing the article on how to create a [branded portal in 20 minutes in Power Pages](/writing/build-event-website-power-pages-dynamics-365), I discovered this feature from Customer Insights Journeys.
You can create a web app directly from Customer Insights Journeys and then deploy it to your hosting service, like Azure or Vercel. To test it, I created [the event page of Roasting Flow](https://events.roastingflow.com) which will display the events of my fake coffee shop 😉
I find the idea super interesting especially with the possibilities of extending and branding the application with AI. Because you host the site yourself, you do not need Power Pages capacity for the public event website. You are still responsible for your hosting costs and the licensing of Customer Insights - Journeys.
In this article, I go over how to set up a web app, run it locally, how I branded it so it aligns with my branding, and then tips to publish it online.
---
> [!NOTE]
> **TL;DR:**
>
> Customer Insights – Journeys can generate a lightweight event web app built with HTML, CSS, and JavaScript. Create separate web application records for `http://localhost:3000` and your production HTTPS domain, authenticate both domains, and download the application.
>
> You can then run it locally with Node.js, customize its branding, and deploy the contents of the `public` directory to a static hosting provider such as Azure Static Web Apps. Before deployment, verify the production values in `public/js/config.js`, and make sure each live event is published to the correct web application.
---
## What are the Event Web Apps from Customer Insights Journeys?
The event web apps from Customer Insights Journeys are a feature where you can easily create a lightweight website to display your events directly integrated with dynamics customer insights. When you create one web application, you are able to download a zip file that contains the whole website in a lightweight web application built with HTML, CSS, and JavaScript. Once you have it downloaded locally, you can edit it and brand it to your needs.

This allows you to be pretty flexible with regards to what you can do in the portal with the events, or not, or how to display them, apply some filters, sorting options, or searching options, as you are fully in control of your website. As well as multi-language options, which I think it's better handled than in Power Pages.
## Why not just stick to Power Pages?
Power Pages provides a low-code, managed option within the Power Platform and includes an event registration template for Customer Insights - Journeys. It is a good option when you want to build and maintain the website primarily through Power Pages Studio rather than working directly with the source code.
However, hosting your own web application gives you more control over the front-end experience, deployment process, and hosting provider. You can modify how events are displayed, introduce custom functionality, and use the development tools and frameworks that best fit your requirements.
Licensing is another consideration. Power Pages requires additional capacity for authenticated or anonymous users, whereas a self-hosted event web app uses the hosting plan you select. The most cost-effective option will depend on your expected traffic, maintenance requirements, and the capabilities your event website needs.
## Yes, I vibe-coded the branding
In my case, I vibe-coded the branding for my web application. It simplifies the process of branding on the web portal, but most importantly, it will respect the structure that Customer Insights has created for the event portal and just build on top of that, avoid unnecessarily restructuring the application.
As with any AI-generated code, I reviewed and tested the changes before deploying them.
---
## How I set it up
### Configuring Dynamics
First thing we do in Customer Insights Journeys is go to our settings and scroll down to the event management section to click on web applications and then create a new one.
We will give that web application a name and an origin. The origin must match the domain where the application is hosted so that the browser’s CORS requests to the Customer Insights - Journeys API are allowed.
I recommend creating two web applications:
- One with the origin to your actual domain, which in my case is `https://events.roastingflow.com`
- One with the origin pointing to `http://localhost:3000`
This one will be entirely for your local calls, so you can see that it shows your events and it shows your event forms correctly.
Once it gets saved, the token is displayed, and the endpoints are also displayed like this:

And under the endpoint documentation field, we will have an event portal web app field with a button attached to it, which allows us to download the zip file. This contains the actual website zip, we download it at this step.
In order to see the registration forms, it is needed to first verify the domain in Customer Insights Journeys. We will go to Settings Domains and enter the external form hosting for your actual domain and localhost.

Open the event, go to General > Publishing, select Event portal using web application, choose the appropriate web application, and publish the event. Only live events published to the web application will appear on the site.
### Running it locally
Once we download the zip, we can extract it and will find a README.md which is super useful with instructions on how to use the web app, here the prerequisites we have to run it locally:
- Set up a web application record for your domain in *Customer Insights - Journeys > Settings > Web applications* (required to allow the portal to call the public API)
- Authenticate your domains in *Customer Insights - Journeys > Settings > Domains* (required for embedded event registration forms)
- Node.js v22 or higher (optional; only needed if you use the provided development server)
The first two we did in the previous step. So after we installed node.js, we can test our web app by opening a terminal in the directory of the webapp and running first `npm install`. After the packages are installed, we can run `npm start`, and we'll get the server that is working locally:

If we go to the address in a browser in our computer (the one after "Server running at "), we'll be able to see our event website and navigate it:

Once we have this we can setup the branding of the web app.
### Brand it with AI
I will not go deep in this part, I'll just say, that I used Claude Code to brand the website using the following prompt (for my case):
`This folder contains a webapp of Dynamics Customer Insights. It shows the events of this coffee shop "https://www.roastingflow.com/" Can you make it coherent with the style, logos and structures of the original webpage? It will be events.roastingflow.com in the future`
This generated the plan you can find in my [GitHub repository of the website](https://github.com/martinplopez/RoastingFlowEventsWeb/blob/main/plan.md).
The result you can find in [the Event Portal of Roasting Flow](https://events.roastingflow.com)
### Deploy it to your hosting service
Once we got out portal correctly running locally , we can deploy it to our hosting provider. This means that the portal will be accessible to the rest of the world. In my case I use GitHub to host my code and Azure Static Web Apps to host the website. But Vercel, Netlify or Cloudflare Pages would be fine as well.
Before deployment, verify that `BASE_URL`, `ORG_ID`, `TOKEN`, and `WEBAPP_ID` in `public/js/config.js` contain the values from your production web application record.
The website will only show the events if these values come from the Web App Record we created with the domain for production (in my case events.roastingflow.com). If we use the ones from localhost it will not work.
> [!WARNING]
> **IMPORTANT**
>
> Before deployment, verify that `BASE_URL`, `ORG_ID`, `TOKEN`, and `WEBAPP_ID` in `public/js/config.js` contain the values from your production web application record.
>
> The production domain must match the origin configured for that record. Values created for `http://localhost:3000` are intended for local development and should not be used by the production website.
>
> `WEBAPP_ID` is optional. When it is provided, the website only displays events published to that specific web application.
---
## Tips and Tricks
- If you're not familiar with Azure Static Web Apps (SWA), you're a developer and didn't consider it as an option. I recommend it especially for this use case. Depending on your needs, it can get hosted for free even. Here the [Microsoft Learn Documentation](https://learn.microsoft.com/en-us/azure/static-web-apps/overview).
- If you use Azure SWA and want to avoid problems with the token and webapp_id, you can take advantage of the environment variables of the app to have the right token and webapp_id dynamically filled out. Feel free to fork [my repo in GitHub](https://github.com/martinplopez/RoastingFlowEventsWeb) where it's done already.
- If you want to dig deeper in the API options, Web Applications leverage the same API that Jonas Schwarzwälder talks about in [this amazing article](https://jonasschwarzwaelder.com/blog/introduction-to-cij-event-api/)
---
## Conclusion
Customer Insights - Journeys event web apps provide an interesting alternative when you need more control than the standard event experience offers. The generated application already includes event listings, search, event details, embedded registration forms, and multilingual support, while still giving you complete control over its appearance and hosting.
Using AI made the initial branding work much faster, but branding is only the beginning. Because the source code is yours to customize, you can introduce different event layouts, advanced filtering and sorting, additional languages, and experiences tailored to your audience.
Over the next few weeks, I will continue exploring what can be built with this architecture. Particularly its internationalization capabilities and ways to create a more flexible event discovery experience.
---
# Advanced Connector Policies: Don’t Break Customer Insights - Journeys Publishing
- URL: https://martinplopez.com/writing/acp-customer-insights-journeys/
- Published: 2026-07-03
- Summary: Learn why Advanced Connector Policies can block Customer Insights - Journeys from publishing journeys, which connector needs to be allowed, and how to configure your policy without interrupting marketers.
Some days start like any other: morning cup of coffee, daily meeting with colleagues, solve some problems from customers, etc.
But today I got a message from a user. He **couldn't publish a customer journey** and he didn't know why. He sent me the weirdest screenshot. This is more or less what he sent me:
 policy: 'Advanced Connector Policy'. Contact your administrator to update the policy. Further information can be found here: https://go.microsoft.com/fwlink/?linkid=2181032")
My first thought was, "When did we set up the Advanced Connector Policies?"
But then I remembered that it was recently and that I wasn't involved in the process of setting it up, so I could not give any feedback on adding Customer Insights connectors to the Advanced Connector Policies. Luckily, as soon as I did, the journey started running just as before.
In this article, I'll go over what **Advanced Connector Policies** (ACP) are, why it is good to implement them and how to do it without interrupting your Customer Insights - Journeys
And if you are facing a similar issue, you can send your system administrator this article directly. Unless it is you, in that case you can go directly to "How did I do it".
---
> [!NOTE]
> **TL;DR:**
>
> If Customer Insights - Journeys fails when publishing a journey after Advanced Connector Policies are enabled, check whether the required Customer Insights - Journeys connector is allowed in your policy.
>
> The key connector to look for is the native Journeys connector, currently documented by Microsoft as Dynamics 365 Marketing V2 with the ID `shared_d365marketingforapps`.
>
> Without it, journey publishing can be blocked by governance rules even though the journey itself looks valid.
---
## What are the Advanced Connector Policies
Advanced Connector Policies are rules that govern how connectors can be used within Power Apps, Power Automate and Copilot Studio.
Each connector can be classified as allowed or blocked. By default it will block most of the connectors and only leave the Microsoft standard ones, but you can any time adapt this allowed connectors list to your needs.
This is the evolution of the idea of Data Loss Policies (DLPs), which allowed to classify the connectors and allow only certain categories. Microsoft simplified this by giving just an allowed and blocked list

Advanced Connector Policies can be configured at the environment group level for bulk governance or directly on individual environments for targeted governance. They are especially powerful with Managed Environments, where administrators get more control over connectors and actions.
It is not obligatory to implement these rules to all the environments, but it is recommended if you want to apply governance to your power platform environments.
### Why should we set them up?
Advanced Connector policies are intended to reduce the risk of users unintentionally exposing organizational data through connectors used by Power Apps, Power Automate, and Copilot Studio.
#### But we use Customer Insights Journeys, not any of those tools
Yes and no. When you use journeys, Microsoft is using Power Automate flows behind the curtains to do the actions in those journeys. So that means that if a connector that they use in their flows isn't approved, by the time of publishing, the flow will fail.

---
## How I set it up
I will not go very deep into how to set it up, because there's a perfectly valid Microsoft article around [how to set up the advanced connector policies](https://learn.microsoft.com/en-us/power-platform/admin/advanced-connector-policies#configure-an-advanced-connector-policy). But I will give some pointers on how I set it up for Customer Insights Journeys.
[Microsoft explains](https://learn.microsoft.com/en-us/dynamics365/customer-insights/journeys/real-time-marketing-admin-settings) that it used to have another connector called **Dynamics 365 Customer Insights - Journeys**, but now the one to allow is the **Dynamics 365 Customer Insights - Journeys V2** connector (with ID `shared_d365marketingforapps`).

And for those of you who might think "*isn't the normal Dynamics Customer Insights connector necessary?*"

In this case, this connector refers to the Customer Insights - Data connectors of Power Automate.
If you're not using it for any of your Power Apps, Power Automate flows or Copilot Studio Agents, it can remain blocked.
> [!TIP]
> **Note**
>
> If you do have Customer Insights Data and use segments in Customer Insights Journeys, your segments **will still synchronize even with this connector blocked**.
>
> In this case, Dataverse is synced with Dynamics Customer Insights Data not by Power Automate, but by system job.
---
## Conclusion
Advanced Connector Policies are a useful step forward for Power Platform governance. They give administrators a cleaner way to control which connectors are allowed and reduce the risk of data being used in places where it should not be.
But in this specific case, I think the experience could be better.
Most Customer Insights - Journeys users are marketers. They should not need to know which internal connector is required to publish a journey, or why a governance setting in Power Platform suddenly blocks their work.
If Customer Insights - Journeys is installed in an environment, the required native connector should either be clearly highlighted during ACP configuration or automatically suggested as part of the allowed list.
Hopefully, Microsoft improves this experience and this article becomes obsolete soon. Until then, I hope this helps you avoid a confusing publishing error and keeps your journeys running.
---
# Invite Leads to Your Customer Insights Journeys Events
- URL: https://martinplopez.com/writing/leads-customer-insights-journeys-events/
- Published: 2026-06-19
- Summary: Learn how to invite Leads to Customer Insights Journeys events while keeping the generated Contact and original Lead connected using Power Automate.
In Customer Insights Journeys, **events run on Contacts**. Registrations, check-ins, reminders, the whole journey, all of it is contact-shaped.
Which is fine, until you remember that plenty of the people you'd love to have at your event aren't contacts at all: **they're sitting in your Leads**. Prospects Sales is already working with, or people who raised their hand on a form but are not in your contact base yet.
So you invite those leads to the event anyway and Customer Insights Journeys happily registers them as contacts. Now there's a subtle catch: the new **Event Registration points at a contact, while the Lead it actually belongs to is left on the sidelines, disconnected**. 🙋

In this article, we explore a solution on how to solve this problem easily with a Power Automate Flow. **This flow keeps the contact and the lead together**whenever a new registration is created, it finds the matching open Lead and links it to the registrant’s Contact. If no open Lead exists yet, it creates one and connects it back to the event.
---
> [!NOTE]
> **TL;DR:**
>
> Events register **Contacts**, but you want your **Leads** at them too, so this **Power Automate** flow keeps the two linked.
>
> - It triggers whenever an **Event Registration** is added (Dataverse, scope **Organization**).
> - It searches for an **open Lead** (`statecode eq 0`) with the registrant's email. Found one? → it links that Lead to the **Contact** (sets *Parent Contact*). None yet? → it **creates a new Lead**, stamped with the **Originating Event**.
> - The payoff: invite leads to your events and keep every registrant's contact tied to the lead it belongs to. 🔗
---
## How the out-of-the-box behavior works
Microsoft will by default link the contact and the lead in the following way:
1. It will first associate the lead with a contact, setting up a parent contact field with the contact assigned.
2. Then it will create a new record on the connection entity, where it will make the contact be related to the lead as the role of a stakeholder.
```mermaid
graph LR
LEAD["LEAD"]
CONTACT["CONTACT"]
LEAD -->|"Parent Contact"| CONTACT
CONTACT -->|"Connection (Role: Stakeholder)"| LEAD
```
Something I discovered while writing this article is that when you make an association of the lead via the parent contact field, **it will automatically create a connection record as a stakeholder for the contact**. So there is no need for an extra step in the flow where we create this record.
---
## The flow logic
Before clicking around, it helps to picture the decision:
```mermaid
flowchart TD
A([New event registration]) --> B[Get Contact]
B --> C["List open Leads with same email oldest first · take 1"]
C --> D{Open Lead found?}
D -->|Yes| E["Update Lead: set Parent Contact"]
D -->|No| F[Get Event]
F --> G["Create new Lead: Topic = event · set Originating Event"]
E --> H([Done])
G --> H
```
---
## Build it
### 1. The trigger
Add the Dataverse trigger **When a row is added, modified or deleted** and configure it:
- **Change type:** Added
- **Table name:** Event Registrations *(`msevtmgt_eventregistration`)*
- **Scope:** Organization
The **Scope** is the part people get wrong. With **User** scope the flow only fires for registrations *you* own, useless here. **Organization** means it catches every registration in the environment, which is what we want when this runs under a service account.
> 💡 If you want to limit the events in which this happen I would recommend **creating a field on the event table** which will activate or deactivate the generation of leads. This can also be added as a filter in the `Filter rows` option in a trigger.

### 2. Get the Contact
Add **Get a row by ID** (Dataverse):
- **Table name:** Contacts
- **Row ID:** the contact from the registration. In the expression editor:
`triggerOutputs()?['body/_msevtmgt_contactid_value']`
The registration stores the contact as a lookup, and `_..._value` is how you read a lookup's GUID straight off the trigger output. We need the full contact record next, mainly for the **email** and the **name**.
### 3. Find an existing open Lead
Add **List rows** (Dataverse) on the **Leads** table and open **Show advanced options**:
- **Filter rows:**
`emailaddress1 eq '@{outputs('Get_Contact')?['body/emailaddress1']}' and statecode eq 0`
- **Sort by:** `createdon asc`
- **Row count:** `1`
Read it left to right: same email **and** still active. We sort by **creation date ascending** and take **one**, so if there are several matches we deterministically grab the *oldest* one (the original) instead of a random duplicate. But here the **logic should be adapted to your organization way of handling leads**.

### 4. The condition
Add a **Condition**. On the left side, use this expression (not dynamic content, type it in the expression editor):
`length(outputs('Retrieve_Leads_with_same_email')?['body/value'])`
Operator **is greater than**, right side `0`.
`List rows` always returns an array under `body/value`. `length()` counts it: greater than zero means we found a Lead. This is cleaner and faster than looping.
### 5a. If YES → link the existing Lead
In the **If yes** branch, add **Update a row** (Dataverse):
- **Table name:** Leads
- **Row ID:**
`first(outputs('Retrieve_Leads_with_same_email')?['body/value'])?['leadid']`
- **Parent Contact for lead (Contacts):** set this lookup to the contact:
`/contacts/@{outputs('Get_Contact')?['body/contactid']}`
Two small things trip people up here. First, `first(...)` grabs the single Lead out of that array (we asked for one, but it's still an array). Second, a **lookup** in *Update a row* isn't a plain GUID, you bind it as a path: `/contacts/`. The field is **Parent Contact for lead**, which is the standard Lead → Contact relationship.

*The **Row ID** uses `first()` to pull the one Lead from the list, and **Parent Contact for lead** is bound as `/contacts/`, not just the raw GUID.*
### 5b. If NO → create a new Lead
The **If no** branch has two actions. Order matters: get the event **first**, then create the Lead.
**Get a row by ID** (Dataverse) - *Get Event*:
- **Table name:** Events *(`msevtmgt_events`)*
- **Row ID:** `triggerOutputs()?['body/_msevtmgt_eventid_value']`
**Add a new row** (Dataverse) - *Create new Lead*:
- **Topic** *(`subject`)* → the event's name: `outputs('Get_Event')?['body/msevtmgt_name']`
- **Email** → `outputs('Get_Contact')?['body/emailaddress1']`
- **Last Name** → `outputs('Get_Contact')?['body/lastname']`
- **First Name** → `outputs('Get_Contact')?['body/firstname']`
- **Parent Contact for lead (Contacts)** → `/contacts/@{outputs('Get_Contact')?['body/contactid']}`
- **Originating Event (Events)** → `/msevtmgt_events/@{triggerOutputs()?['body/_msevtmgt_eventid_value']}`
That **Originating Event** field is key. It's the native CIJ field on the Lead, so once you set it, the platform itself knows this Lead came from this event and it shows up in the event's insights without any extra work from you.

*Both lookups **Parent Contact for lead** and **Originating Event** are bound as paths (`/contacts/...` and `/msevtmgt_events/...`). The **Topic** carries the event's name so Sales instantly sees the context.*
---
## Things I'd watch out for
A few pointers before you flip this on for real:
- **Test in a sandbox first.** Register a couple of test contacts one brand new, one who already has an open Lead, and confirm both paths behave (create vs. link).
- **Run it as a service account** with the right Dataverse privileges (create/update on Leads and Connections), not under your personal user. Not sure how to do it? It's pretty well explained in this article: [Power Automate with a Service Principal](https://www.powercommunity.com/power-automate-with-a-service-principal-app-registration/)
- **Wrap it in a solution** and use a **connection reference** so you're not rebuilding connections when you ship to Prod. The exported flow already does this.
- **Lead matching logic.** This example uses the oldest open Lead with the same email address, but adapt this to your organization’s lead process. In some setups, you may want to match by campaign, event, business unit, owner, or source instead.
- **Bulk registrations.** This fires once per registration row. If you ever import registrations in big batches, keep an eye on your flow run volume.
---
## Conclusion
Customer Insights Journeys does a good job of handling event registrations, but the Lead-to-Contact relationship can easily become disconnected when you invite Leads to your events.
With a small Power Automate flow, you can close that gap: every new event registration checks for an existing open Lead, links it to the registered Contact, or creates a new Lead when needed. The result is **cleaner data, better event attribution, and a much clearer handover between marketing and sales**.
But the main idea stays the same: do not let your event registrations live only on the Contact side. Keep the Lead connected too, so Sales can see the full context of who registered, where they came from, and how they already interacted with your company.
---
# Upgrade Your Customer Insights Journeys Events to Teams Webinar V2
- URL: https://martinplopez.com/writing/teamsv2-customer-insights-journeys/
- Published: 2026-06-05
- Summary: Learn how to configure Teams Webinar V2 authentication for Dynamics 365 Customer Insights Journeys, including the required Entra app registration, Graph permissions, federated credentials, and Teams access policy.
For a while now, clients who run webinars in Customer Insights Journeys have been reaching out to me for help setting up Teams Webinar V2.
Their current Teams webinar option is being marked as *“legacy”*, which is basically like saying **Beetlejuice** for software products 😬
The timeline is also important: **Microsoft is retiring Teams live events and the related Graph APIs effective June 30, 2026**. For legacy Teams Webinar and Teams Meeting events, Microsoft honors already scheduled events through February 28, 2027. After that period, Teams Webinar V2 or Teams Meeting V2 should be used.
Most clients contact me when they try to save a new event as a Teams Webinar V2 and see a message like this:

Luckily, Microsoft warns us in the [documentation of the Teams V2](https://learn.microsoft.com/en-us/dynamics365/customer-insights/journeys/teams-web-version-2#create-a-teams-webinar-v2) that before using it, you need to set up authentication for Teams webinars v2.
They also documented in [this Microsoft Learn page](https://learn.microsoft.com/en-us/dynamics365/customer-insights/journeys/teams-authentication), which is the foundation of this article, but in here I share my learnings implementing it, including a script-assisted approach for admins who prefer to automate part of the setup.
If you're this person, **you can send this article to your IT Administrator**, as it can get a bit technical.
If you're brave enough (and have the right permissions), **you can also try it out yourself** 😉
---
> [!NOTE]
> **TL;DR:**
>
> Teams Webinar V2 in Customer Insights Journeys **requires an additional authentication setup before registrations and attendance tracking can work correctly**.
>
> The setup **connects Customer Insights Journeys with Microsoft Teams** through an Entra app registration, federated credentials, Microsoft Graph permissions, and a Teams Application Access Policy.
>
> My recommendation: **follow Microsoft’s documented setup first**, but pay special attention to the federated credential subject, the required Graph permissions, and the PowerShell access policy.
>
> **You do not necessarily need one app registration per Customer Insights environment**; in my implementation, I used one app registration with separate federated credentials for different environments.
>
> Also, remember that **attendance/check-in data depends on sending registrants the proper Join in Teams link from the Customer Insights email designer**.
---
## Why is this required?
Teams does not allow Customer Insights Journeys to create webinar registrations or download attendance reports using the standard integration alone.
That is why an **additional authentication setup is required** between our Customer Insights and Teams to send Teams registrations to specific attendees and keep track of the people who attended your webinar.
This allows 2 pretty cool features:
- Each registered person in Customer Insights **can be seen from the Teams App**
- When the Event ends, all the attendees will be checked in with their registrations in Customer Insights Journeys, so **you'll know exactly which people attended your webinar automatically**!
I did some tests and it works very well:

A little note on this: these features depend on using the **Join in Teams** link from the Customer Insights email designer. So, **be mindful to add the button in your Emails!**
---
## How I implemented it
I originally followed the article of Microsoft, but I stumbled upon the following questions:
1. they don't give you any guidance on **how to handle multiple environments of CIJ** (Customer Insights Journeys). Do I have to repeat the process for each registration?
2. I required PowerShell at the last stage, so not everything can be done by clicking which led me to think, **can this be a simple script**?
### Now my conclusions:
1. No, **there is no need to register an app for every environment**. You can register an app registration, give the right permissions for virtual events and when configuring the Federated credentials in your App, **you can give different federated credentials for the different environments**:

2. Yes, **especially the azure and teams part**, I think it's easier to generate your own credential in CIJ.
## The Microsoft way (manual)
My plan is not to rewrite something that Microsoft has already documented well. Instead, I’ll summarize the main steps in order and link directly to the relevant Microsoft Learn sections.
1. [Create the App Registration and copy the App-ID](https://learn.microsoft.com/en-us/dynamics365/customer-insights/journeys/teams-authentication#2-register-an-app-in-microsoft-entra)
2. [Create the teams authentication with the App-ID in CIJ](https://learn.microsoft.com/en-us/dynamics365/customer-insights/journeys/teams-authentication#3-add-the-app-id-in-customer-insights---journeys) and [copy the federated Federated Identity Credential Subject](https://learn.microsoft.com/en-us/dynamics365/customer-insights/journeys/teams-authentication#3-add-the-app-id-in-customer-insights---journeys)
3. [Configure the federated credentials for the App Registration](https://learn.microsoft.com/en-us/dynamics365/customer-insights/journeys/teams-authentication#4-configure-federated-credentials-in-your-app-in-microsoft-entra)
4. [Give all the API Permissions needed](https://learn.microsoft.com/en-us/dynamics365/customer-insights/journeys/teams-authentication#5-grant-api-permissions)
5. [Set an access policy with Powershell](https://learn.microsoft.com/en-us/dynamics365/customer-insights/journeys/teams-authentication#set-up-the-required-access-policy)
### My implementation notes
- The App-ID can be found here:

- When configuring the Federated Credentials, you should substitute the tenant in the `` when setting the Issuer `https://login.microsoftonline.com//v2.0`
- For the PowerShell step should install the [Microsoft Teams PowerShell Module](https://learn.microsoft.com/en-us/microsoftteams/teams-powershell-install). You can just run the following in PowerShell:
```pwsh
Install-Module -Name PowerShellGet -Force -AllowClobber
Install-Module -Name MicrosoftTeams -Force -AllowClobber
```
---
## Optional: script-assisted setup for admins
> [!CAUTION]
> **ATTENTION: Run at your own risk**
>
> I don't take responsibility of the consequences of running this PowerShell. I don't recommend running a script you don't understand.
For admins who repeat this setup across environments, a script-assisted approach can save time. **I would only recommend this if you understand what the script is doing** and have the right permissions in Microsoft Entra and Teams.
At a high level, the script can help with:
- Creating or reusing the Entra app registration
- Adding the required Microsoft Graph permissions
- Creating federated credentials for each Customer Insights environment
- Connecting to Microsoft Teams PowerShell
- Creating and assigning the required Application Access Policy
Do not run automation like this directly in production without reviewing it first. **Start in a test environment**, validate the permissions, and make sure the policy scope matches your organization’s governance model.
You can find the Script in my [GitHub Repository](https://github.com/martinplopez/CIJ-TeamsV2-Auth)
---
## Conclusion
Moving to Teams Webinar V2 **is not just a cosmetic change** in Customer Insights Journeys. It introduces a new authentication model between Customer Insights, Microsoft Teams, Microsoft Graph, and Entra ID.
That can feel like a lot, **especially for teams that only wanted to create a webinar and send invitations**
But once the setup is done correctly, the experience is much better: registrations can sync to Teams, attendees can receive their individual Join in Teams links, and Customer Insights can automatically populate attendance data after the event.
My biggest learning from implementing this is that **the process is not necessarily difficult**, but it touches several admin areas. That means **the real challenge is usually coordination**: knowing who owns Customer Insights, who owns Entra ID, and who can run the Teams PowerShell commands. (If you are that person for every area, you're not alone 😅, and it makes the setup quicker)
If you are a **functional consultant or marketer, use this article as a checklist** and involve your tenant administrator early.
If you are the **admin, validate the setup first in a non-production environment** and document which app registration, federated credentials, permissions, and access policies were created.
Once this foundation is in place, **Teams Webinar V2 becomes much easier to use** and your event registrations and attendance tracking can finally work the way users expect
*Update: after a couple of months of running this with clients, I collected the issues that kept coming back in a follow-up note — [Teams Webinar V2: First Lessons Learned](/writing/teamsv2-customer-insights-journeys-lessons/).*
---
# How to Configure Reply-To for No-Reply Senders in Customer Insights Journeys
- URL: https://martinplopez.com/writing/no-reply-customer-insights-journeys/
- Published: 2026-05-22
- Summary: Learn how to configure Reply-to addresses for no-reply senders in Dynamics 365 Customer Insights Journeys without building custom journeys, triggers, or Power Automate flows.
This week I was taking a look behind the trenches and checked out Odoo Marketing Automation capabilities, and I noticed that they can configure **an automatic reply for people who respond to the sender mailbox**. Which made me think a lot of things:
- Why were they creating this action after every email sent?
- Can't they just trigger this in a different workflow?
- What is the benefit of replying every time if the customer still does not get any real help? 😅
But most importantly, it made me question **How is this handled in Customer Insights Journeys?**
This is the journey I took to discover a simple but very useful feature in Customer Insights Journeys. 😎
---
> [!NOTE]
> **TL;DR:**
>
> In Customer Insights Journeys, you do not need to build a journey, custom trigger, or Power Automate flow just to handle replies to a no-reply sender address. You can configure a `Reply-to` address directly in the sender settings of your branding profile.
>
> When recipients hit **“Reply”** their email client uses the Reply-to header and sends the response to the configured mailbox instead of the no-reply address.
---
## My Initial Ideas
In my process of how to solve this in Dynamics Customer Insights I had 2 ideas:
### The Journey Idea
My first thought on how to solve this was: *"There must be a trigger for this and I can create a journey to handle it for sure"*

*"Well, without an out-of-the-box trigger, I cannot create a journey for this."*
So that led me to the next idea.
### The Custom Trigger with Power Automate
So I decided to try creating a **Power Automate flow triggered when an email is received in the no-reply mailbox**.
The idea was to **send that event to a custom trigger in Customer Insights Journeys** and then use a journey to send the customer an email, similar to what I saw in Odoo but **centralized inside the Microsoft ecosystem**.
This could also open more interesting scenarios, such as flagging the contact as **someone who interacted with the email but had a problem**, identifying emails that are not performing well, or even **creating a case directly in Dynamics 365 Customer Service**.
PROBLEM: I don't even have a no-reply mailbox, it's clearly a "no-reply" email address 😅
That is when I decided to look at the **sender configuration** in Customer Insights Journeys emails.
---
## How Microsoft Learn Explains It
I found the [Microsoft Learn article article](https://learn.microsoft.com/en-us/dynamics365/customer-insights/journeys/email-properties#set-sender-and-receiver-options) where the sender configuration is explained.
This configuration is currently available in the branding profile. Under the sender tab, you can select the different senders for your brand. When you open one of them, you can see the sender configuration, including the Reply-to address:

---
## My Test
So I decided to make a test email with this default sender to try it out...

### Behind the scenes
This is a protocol that is documented as the [Internet Message Format](https://www.rfc-editor.org/info/rfc5322/#section-3.6.2). So it universally works with all modern email clients (Outlook, Gmail, Apple Mail, etc.).
When you inspect the details of the message, you can see this as one of the email headers:

---
## Conclusion
Today I learned two things:
- You always learn something when you stay curious.
- Some of the best features in a tool are so well integrated that you almost do not notice them.
In this case, Customer Insights Journeys already had the answer: configure the Reply-to address in the sender settings and **let the email standard do the rest**.
---
# Lead Scoring in Dynamics 365 Customer Insights: The 5Ws and How to Start
- URL: https://martinplopez.com/writing/customer-insights-lead-scoring/
- Published: 2026-05-07
- Summary: Learn how Lead Scoring works in Dynamics 365 Customer Insights Journeys, when to use it, and how to build practical scoring models that improve lead qualification and sales focus.
Lead Scoring has always been a great unknown to me in Dynamics 365 Customer Insights - Journeys. I’ve known about it since the days of outbound marketing, but I never had the chance to see it implemented in a real customer project or help customers adopt it.
This article gets its inspiration from the session "*The chaos of Customer Insights projects - The mistakes we keep making (and pretending we don’t)*" by Vivian Voss in ColorCloud 2026. She asked the public if in their projects of Customer Insights Journeys, their clients could define what a lead is. I think only one person raised a hand. Vivian highlighted that one of **the biggest bottlenecks is that marketing and sales teams often don’t collaborate closely enough**.
This made me think, that it is probably the main cause for people to not even consider the feature lead scoring. But I wanted to explore it further because I believe **it can significantly improve conversion rates**.
---
> [!NOTE]
> **TL;DR:**
>
> Lead Scoring in Dynamics 365 Customer Insights **helps organizations identify which prospects are most likely to convert based on engagement and profile data**.
>
> By combining behavioral signals like email clicks, event registrations, and form submissions with demographic information, marketing and sales teams can focus on the **leads that matter most**.
>
> In this article, we’ll explore:
>
> - what **Lead Scoring actually is**,
> - when it makes sense to use it,
> - and **how to start building a practical scoring strategy** in Customer Insights - Journeys.
---
## What Exactly Is Lead Scoring
Lead Scoring is essentially a **prioritization system**. It allows you to give a score to a lead based on the data that we collected from the lead.
In order to score the lead you have to **give or remove points to interactions or data from the lead**. For example:
- Opened email → +2
- Clicked CTA (Call to Action) → +10
- Demo request → +50
- No interaction for 30 days → -20
This helps identify **how engaged a lead is with your product or service**. This engagement level can change over time (taking our example, if there is no interaction for 30 days, the engagement is not the same as when he clicked the Call to Action) and ultimately can give us an estimated probability of whether the client is prone to buy or not.
Negative scoring is just as important as positive scoring because **inactivity or disengagement can indicate reduced buying intent**.
Not all actions are equal, and you can adapt this scoring points over time. At least with Dynamics Customer Insights Journeys 😉
---
## Who Is Lead Scoring For
This is an important question because **Lead Scoring should be a shared effort between sales and marketing teams**. If each team works separately and specially handle leads differently, lead scoring will only be implemented halfway and it won't reach the purpose. The Marketing team will qualify the lead thanks to lead scoring and the sales team will follow up on the lead. So **they need a common definition on when a lead is qualified**.
Regarding organizations, I think it makes the most sense in **organizations with many leads**, if an organization is not having that many leads or even they don't use leads at all, lead scoring is going to be like buying a big motorbike when you are already struggling riding a bike.
> [!TIP]
> **It is always good to start somewhere, so if this is your case, this is how I would do it:**
>
> 1. Define **what a lead is** (involve the Sales and Marketing Teams in this decision)
> 2. Define the **handling process of a lead** (who will be accountable and until when) and the sources of the leads
> 3. **Adapt your processes** for leads (Marketing Forms, Journeys, Segments)
> 4. **Introduce Lead Scoring**, test different models and decide what works best for you
---
## Where Does Lead Scoring Make Sense
Lead scoring can help you best when you need to get the overview of the leads and you have very different ways of evaluating them. If you're already **setting up webinar or event campaigns** in customer insights journeys, it makes a lot of sense to introduce **lead scoring to better assess which leads are more engaged with you alongside their participation of your campaign**. Actually, Lead Scoring is most valuable when intent is gradual rather than immediate.
So, for instance, if someone downloading a brochure is not necessarily ready to buy. But **if they later attend a webinar and visit pricing pages, intent becomes clearer**.
Lead Scoring is also very useful to evaluate leads for the information you already have about them. Taking a look at **demographic or firmographic information of the lead, you can better asses if they are easier to qualify or not**.
An example of this is if you know that your services have a higher conversion rate with big enterprise companies than Small to Medium Companies, you can score the leads from the big companies higher. Or if your lead is from a CEO, it might score higher than a engineer, as it can make a purchasing decision easier than the engineer.
---
## When Should You Use Lead Scoring
There are situations in a company that beg for Lead Qualifications:
- When marketing is **generating more leads than sales can process**
- when sales complains about **lead quality**
- when **engagement data already exists**
- when customer **journeys become complex**
On the other hand, Lead Scoring might not be as helpful in early stages, as the system doesn't have many leads to handle. If you only receive a handful of leads per month, Lead Scoring may create unnecessary complexity to your processes.
---
## Why is Lead Scoring a good idea
I feel like Lead Scoring might be one of the **cornerstones of Marketing Automation**. The possibility to automate the evaluation of leads in such a level of complexity makes **scale in leads handling so much better**.
As the prioritization takes over, sales can do a faster follow-up with the leads that are qualified, which ultimately will result in higher conversion rates of the leads. This **reduces the manual qualification to the minimum** and makes a bridge between the marketing team and the sales team.
Customer Insights Journeys is designed to take all the information you're collecting from your emails, journeys, events, etc. and allows you to make your scoring models adapted to your clear necessities.
Needless to say it's a feature included in the Customer Insights Journey license and Microsoft is not going to charge you less if you don't use it 😁
---
## How to Start
Before building your first scoring model, remember that the goal is **not to create a perfect mathematical system**. The objective is to help sales teams prioritize conversations more effectively.
Start with a small number of meaningful interactions and **refine the model over time based on actual conversion results**.
To Setup Lead Scoring Models I followed this [Post by Megan V Walker](https://meganvwalker.com/create-lead-scoring-models-in-real-time-marketing) along with the [Microsoft Documentation](https://learn.microsoft.com/en-us/dynamics365/customer-insights/journeys/real-time-marketing-create-lead-scoring-model). But in short I'll put the steps I followed:
1. I activated the feature Switch for Lead Scoring
2. I created a new Model
3. I added conditions to my scoring model set up to add or substract on my conditions
4. Published my Scoring Model
5. Saw the analytics and the score of my leads

---
### My Simple Lead Scoring Strategy
I used a very simple Lead Scoring Strategy. I created a lead scoring to **identify the coffee geeks that are more willing to buy coffee beans in May**.
I setup the following conditions:
| Interaction | Score |
| ------ | ------ |
| Email received | +1 |
| Email opened | +10 |
| Barista Course Registered and Checked In | +30 |
| Email opted Out | -10 |
Here an overview of my scoring model:

After implementing the scoring model you can also **automate the conversion into marketing qualified leads** (MQL) combining the models (or not) and giving it a threshold to which they're ready to be worked on by Sales:

Different models can have different thresholds to qualify, and **models can become obsolete after a campaign or event**. There's always the possibility to stop the qualification model and modify it to fit best your new models.
---
## Conclusion
Lead Scoring is not about building the most complex scoring model possible. It’s about **helping marketing and sales teams focus on the leads that actually matter**.
The best scoring models are usually not the ones with dozens of conditions and perfectly calculated formulas. They are the ones **teams understand, trust, and consistently use in their daily processes.**
If you’re starting with Lead Scoring in Dynamics 365 Customer Insights - Journeys, keep it simple:
- define what a **qualified lead** means,
- align **marketing and sales** around that definition,
- start with a few **meaningful interactions**,
- **refine** the model over time using **real data**.
A scoring model should evolve together with your campaigns, your customer behavior, and your business goals.
Customer Insights - Journeys already provides the tools to make this possible. The real challenge is not the technology itself, it's **creating a shared understanding of what buying intent actually looks like in your organization**.
And honestly, that conversation alone may already improve your marketing process more than the scoring model itself.
---
# “Can I Bring Someone?” - Solving Plus-One Waitlisting in Customer Insights Journeys (Part 2)
- URL: https://martinplopez.com/writing/customer-insights-waitlist-2/
- Published: 2026-04-10
- Summary: In Part 2 of this series, learn how to design reactive registration journeys in Dynamics 365 Customer Insights - Journeys that automatically respond when attendees or their guests are placed on a waitlist.
Waitlists can be a **powerful tool**, but they can become difficult to manage, especially **when manual processes are involved**.
Handling waitlisted registrations can be straightforward, but once we **involve companions**, it can quickly become tricky.
Because what can we do with 2 registrations? how do we communicate the client that we have only place for one? Do we allow them to cancel one of the registrations?
In my experience, we should **give as much control to attendees as possible**. Luckily, this is achievable by **setting up journeys** and informing them via email about the status of their registration.
---
> [!NOTE]
> **TL;DR:**
>
> Handling waitlists with companions in Customer Insights Journeys can quickly become complex.
>
> This solution uses:
>
> - **2 journeys** (attendee + companion)
> - **2 emails** (waitlist start + waitlist end)
> - **1 trigger** (registration status change)
>
> Together, they allow you to:
>
> - Automatically notify users when they enter the waitlist
> - Let attendees cancel their registration directly from email
> - React when spots become available
> - Clearly communicate when the waitlist closes
>
> The result: a **fully automated, user-friendly waitlist experience**.
---
## Why This Matters
Handling waitlists becomes significantly more complex when companions are involved.
Without automation, you risk:
- Confusing communication (who got in?)
- Manual updates and errors
- Poor attendee experience
This approach ensures every scenario is handled automatically and clearly communicated.
---
## Solution Overview
In my experience, the best approach is:
- **One Email** to handle an attendee or companion entering the waitlist
- **One Email** to handle if the Event Registration has closed and we couldn't secure a place for the attendee and/or the companion
- One new **Trigger** to trigger on an Event Registration status change (So people can exit the waitlist journey)
- **Two Journeys** (one for the attendee and one for the companion) to handle the emails
This approach works well if:
- you already have a journey handling your event registrations on create
- you have normally one event at a time (If there are more, we can always create event specific journeys)
- you already struggle with handling standard waitlisting scenarios
---
## The process
### General Assumptions
This is Part 2 of the series, I **assume you already know how to handle plus one registrations** and automatically place them on the waitlist, but it is especially important that `Companion` exists as a value in the Primary role Optionset inside the event registration.
I recommend to take a look at [Part 1](/writing/customer-insights-waitlist-1).
### 1. "Waitlist - Start" Email
Create an email and use the following placeholders:

The relationships are:
- Under `trigger --> Marketing Event Registration in the Waitlist --> Reference for Marketing Event Registration`
- Primary Role
- RegistrationID
- For the EventName `trigger --> Marketing Event Registration in the Waitlist --> Reference for Marketing Event --> Event Name`
- FirstName is the default `contact --> First Name`
I like to distinguish the registration types in the **title of the email**, because if both registrations are waitlisted, the contact who signed up for the waitlist will receive 2 emails. **One for the Attendee and one for the Companion.**
This is the reason to provide the Registration ID, event name and primary role of the registration.
**Adding a cancellation button** also gives attendees more control over their registration, in case they want to **free up their spot** or they're not interested in the event if they cannot go together.
So we add the button, it should be **linked to a certain event**, for which we can use our relationships from the trigger and event registration 😉

To reference the text in the email, you can use the following:
```html
Hi {{FirstName}},
Thank you again for your interest in attending our upcoming event, we truly appreciate the enthusiasm and support we've received.
Due to an overwhelming number of registrations, we’ve had to place some attendees and/or their companions on a waitlist. While we wish we could accommodate everyone right away, this step helps us ensure the best possible experience for all participants.
This Email refers to the Event Registration: {{RegistrationID}} for our {{Eventname}} as {{Primaryrole}}
If you decide to cancel your registration or that of your companion, you can do so directly from this summary by clicking on the designated cancellation option.
The good news is that plans can change, and spots often become available. If that happens, we’ll reach out to you right away with an update.
In the meantime, thank you for your patience and understanding - it means a lot to us. We’re hopeful we’ll still be able to welcome you to the event.
Warm regards,
Martin
```
It should look like this:

---
### 2. "Waitlist - Finish" Email
Here we have to create an email that will be used **to handle attendees and companions**, but in different ways.
We'll introduce an **inline condition to personalize the message**, we'll filter the message depending if it's an attendee and we say that we're sorry they cannot attend or if they're not, they are told that they can't bring a companion:

For this Email, we'll be using the same relationships as the [previous Email](#1-waitlist---start-email).
Here the HTML of the text;
```html
Hi {{FirstName}},
Thank you very much for your interest in our event and for taking the time to register.
{{#if IsWaitlistAttendee}}Due to the high level of demand, we regret to inform you that we were unable to secure a spot for you.
As the event is now fully booked and the waitlist has officially closed, no additional places will become available.
{{else}}Due to the high level of demand, we regret to inform you that we were unable to secure a spot for your companion.
As the event is now fully booked and the waitlist has officially closed, no additional places will become available.
{{/if}}This Email refers to the Event Registration: {{RegistrationID}} for our {{Eventname}} as {{Primaryrole}}
We truly appreciate your enthusiasm and understanding. While we’re disappointed not to be able to welcome you this time, we hope to have the opportunity to host you at a future event.
If you’d like to stay informed about upcoming events, we’d be happy to keep you in the loop.
Thank you again, and we hope to connect with you soon.
Warm regards,
Martin
```
It should be kept simple, it should look like this:

---
### 3. "Event Registration Status Change" Trigger
This trigger is designed to remove contacts from the journeys if their **registrations get canceled or registered**. To set it up:
1. Name the trigger `Event Registration Status Change`
2. What action will activate the trigger? `When a record related to a customer is created or updated`
3. In which table is the record created or updated? `Event Registration`
4. Who is the audience for this trigger? `Contact`
5. Which attribute contains the audience? `Contact`
6. When is the trigger activated? `An existing record was updated`
7. Which attribute updates should activate the trigger? `Status reason`

---
### 4. "Waitlist Attendees" Journey
Create a trigger Journey and choose the `Marketing Event Registration Waitlisted` Trigger and add the condition that the Primary role is `Attendee` and allow to repeat the journey `After 1 Minutes`:

Add the [Waitlist - Start](#1-waitlist---start-email) Email that we created to the journey as the first action.
Then we add a wait condition and set it up to `Until a specified by a trigger`
As we have access to the event from the trigger, I set this up to the **date you plan to close the waitlist**, in this example 1 day before the start of the event.

This will send the [second email we created](#2-waitlist---finish-email) to the people that are in the waitlist at that point.
For those who entered the journey **but got registered automatically** if someone else unregisters, we should exit them from this journey, so we should configure the alternate exits, and that's when [our trigger](#3-event-registration-status-change-trigger) comes to play.
We would select it as an alternate exit and we will add the following conditions:
- Status Reason does not equal `Waitlisted`
- Primary Role equals `Attendee`

With this journey the waitlisted registrations for the **people in the attendee role will be handled**, additionally, we'll add a journey for the companions.
---
### 5. "Waitlist Companions" Journey
*Save as* the previous Journey and setup the start and the exit so that it is filtered for the companion instead of the attendee as a primary role:

---
### Last Tips
*This setup can cause issues if you have multiple events using these journeys for waitlisting and registration. To avoid this I recommend using these journeys as a template and create 2 Journeys filtered by event so they don't overlap.*
## Conclusion
Handling waitlists is already challenging—but when companions are involved, the complexity increases quickly.
By separating attendees and companions into **dedicated journeys**, and combining them with:
- dynamic emails
- a status change trigger
- and clear communication logic
you can turn a messy manual process into a **fully automated and user-friendly experience**.
The key idea is simple:
*Give control back to the attendee while keeping the system reactive.*
This pattern is not limited to events—you can reuse it in:
- capacity-based campaigns
- VIP access scenarios
- or any process where availability changes over time
If you haven’t read [Part 1](/writing/customer-insights-waitlist-1) yet, I highly recommend starting there to understand how the waitlisting logic is initialized.
And if you try this approach in your projects, I’d love to hear how it worked for you 😉
---
# “Can I Bring Someone?” Solving Plus-One Waitlisting in Customer Insights Journeys (Part 1)
- URL: https://martinplopez.com/writing/customer-insights-waitlist-1/
- Published: 2026-03-26
- Summary: Learn how to handle plus-one event registrations in Dynamics 365 Customer Insights Journeys using waitlists and Power Automate. Extend the out-of-the-box event logic to avoid overbooking and improve attendee management.
Events are rarely a solo experience.
In many scenarios, attendees want to bring a guest — a colleague, a partner, or a friend. But in **Dynamics 365 Customer Insights – Journeys**, handling “plus one” scenarios is not supported out-of-the-box.
This creates a real problem:
- Your event shows **1 registration**
- But **2 people actually show up**
Multiply that across dozens of attendees… and suddenly your event is over capacity.
In this article, I’ll show you how to solve this using:
- The **built-in waitlist feature**
- A **Power Automate flow** to create companion registrations
---
> [!NOTE]
> **TL;DR:**
>
> - Customer Insights Journeys includes a built-in waitlist for events
> - It works well for single attendees but not for “plus one” scenarios
> - We can extend it with Power Automate to create and manage companion registrations
---
## The Problem with Plus-One Registrations
The typical flow looks like this:
1. A contact registers and selects “I’ll bring someone”
2. A single registration is created
3. The event capacity increases by **1**
4. In reality, **2 people attend**
This leads to:
- Overbooked venues
- Poor attendee experience
- Manual corrections by organizers
---
## Possible Approaches (and Why They Fall Short)
### ❌ Disable plus-one option
Simple, but negatively impacts user experience.
### ❌ Manual registration handling
Works for small events, but not scalable.
---
## The Solution
We extend the standard process with:
- A new **“Companion” role** in Event Registration
- A **Power Automate flow** that:
- Detects plus-one selections
- Creates an additional registration
- Applies waitlisting when needed
### How does the process look like
Before diving into the implementation, here’s the idea:
1. The attendee registers normally
2. We detect if they selected “plus one”
3. We create an additional registration:
- If capacity allows → Registered
- If not → Waitlisted
4. We assign a custom role (Companion) to distinguish it
```mermaid
graph LR;
A[Registration] --> B[Flow]
B --> C[Create Companion]
C --> D[Registered]
C --> E[Waitlisted]
```
This keeps the event capacity accurate and leverages the native waitlist logic.
---
## How to make this happen
This solution involves **only one customization**:
- Adding a new option in the **"Primary Role"** of the registration

If this sounds too heavy, you can use a **ready-made solution**: [Companion Role Solution in Github](https://github.com/martinplopez/CompanionRole).
Once created, you’ll see the new option in the **registration form**.
---

---
### Setup of the Form
For setting up the form, you'll need an unmapped field in the form (I recommend of type Dropdown) with the options to come alone or with a plus one.
⚠️ **Important:** Save the **logical field name**, this will be used to fetch the response in Power Automate
Make sure:
- The field is **required**
- Default value is **“No, I’m coming alone”** (recommended)

Also ensure:
The event has **waitlist enabled** — otherwise, **everyone will be registered**

### Power Automate Flow for creating the extra registrations
Now we build the flow that will **handle plus-one registrations automatically**
### 1. Trigger
We'll set up the following:
- Change type --> **Added**
- Table Name --> **Event Registrations**
- Scope --> **Organization**
- Filter Rows --> `msevtmgt_primaryrole eq null`

---
### 2. Retrieve Field Submissions
We'll add the **List Rows** Action for the Table **Field Submissions**:

Then it's easier and more efficient to use a **fetchxml query**, you just have to modify the `[LOGICALNAME]` for the logical field name that you gave in your form [while setting it up](#setup-of-the-form)
```xml
```
---
### 3. Update Primary Registration
We'll **Update a Row** with of the entity **Event Registrations**. With this action, we'll update the current registration to give the role of the **Attendee**. In the Row ID, make sure to choose the **Event Registration** from the trigger a.k.a `@{triggerOutputs()?['body/msevtmgt_eventregistrationid']}`

---
### 4. Add a condition
We'll add a condition where we check if the **Localized Value Field** (1) or in technical name `@items('Apply_to_each')?['msdynmkt_localizedfieldvalue']` is equal to 2 (2) (which is the value os the second dropdown normally).
Be sure to use `string(2)` when you're comparing it, as the value in the localized value field will be a string.

---
### 5. Add a Registration for the plus one
Add a new Row, where the following elements are there:
- Table Name --> Event Registrations
- Event (Events) --> `/msevtmgt_events/@{triggerOutputs()?['body/_msevtmgt_eventid_value']}`
- Contact (Contacts) --> `/contacts/@{triggerOutputs()?['body/_msevtmgt_contactid_value']}`
- Registration ID --> `@null` (this is an expression)
- Primary Role --> Companion
- Status code --> **Registered**

---
### 6. Add a waitlisted registration for the plus one
Add a new Row, where the following elements are there:
- Table Name --> Event Registrations
- Event (Events) --> `/msevtmgt_events/@{triggerOutputs()?['body/_msevtmgt_eventid_value']}`
- Contact (Contacts) --> `/contacts/@{triggerOutputs()?['body/_msevtmgt_contactid_value']}`
- Registration ID --> `@null` (this is an expression)
- Primary Role --> Companion
- Status code --> **Waitlisted**
But the main point of this action, it's that it should run only when the previous action has failed (this happens due to an event being over capacity).
The steps can be found in [this Microsoft Learn Article](https://learn.microsoft.com/en-us/power-automate/guidance/coding-guidelines/error-handling#configure-run-after-settings), but basically can be summarized in:
`...` --> Configure run after --> has failed
After configuring this, you should see a different line connecting your two "Add a Row"

---
## Stay tuned for the Part 2
Great! This has been the first part of the **"Can I Bring Someone?"** Series. We'd gone over:
- How to **capture plus-one intent in forms**
- How to **extend registration roles**
- How to **automate companion registrations with Power Automate**
In Part 2, we’ll take it further:
- Notify users if their **plus one is waitlisted**
- Allow them to **cancel if needed**
- Send updates when **spots become available**
I hope this first part helps you build a cleaner and more reliable **registration process**. In the next article of the series, we’ll look at how **to communicate the outcome to attendees** and make the experience smoother when plus ones are involved.
Until then, take care 🐣
---
⚠️ **UPDATE** Part 2 is already available in [“Can I Bring Someone?” - Solving Plus-One Waitlisting in Customer Insights Journeys (Part 2)](/writing/customer-insights-waitlist-2)
---
# Guide Users in Customer Insights - Journeys with Custom Help Panes
- URL: https://martinplopez.com/writing/custom-help-page-event/
- Published: 2026-03-13
- Summary: Discover how to use Custom Help Panes in model-driven apps to guide users in Dynamics 365 Customer Insights - Journeys. Improve adoption and reduce training with contextual in-app help.
Customer Insights - Journeys offers a powerful set of features:
emails, journeys, segments, triggers, events, and forms.
But for many marketing teams, the challenge isn’t the lack of functionality - it’s navigating all those features efficiently.
New users often ask questions like:
Where do I start?
What should I configure first?
Did I miss a step?
Training sessions and documentation help, but they require users to leave the application to find answers.
What if the guidance could live directly inside the interface?
That’s exactly what **Custom Help Panes** allow you to do.
> [!NOTE]
> **TL;DR:**
>
> Customer Insights – Journeys runs on a **model-driven app**, which means you can enable **Custom Help Panes**.
>
> These panes allow you to:
>
> - Embed contextual help directly inside forms
> - Guide users through processes like email or event creation
> - Link to other pages inside the application
> - Provide checklists and best practices
>
> Instead of external documentation, marketers get **step-by-step guidance inside the product**.
---
## What Are Custom Help Panes
Custom help panes allow you to add **context-sensitive help content inside model-driven apps**. (In case you were wondering, Customer Insights - Journeys is based in a model-driven app)
They appear as a **side panel next to the form or page** and can contain:
- Rich text
- Links
- Images
- Videos
- Coach marks and help balloons
One useful behavior: the help pane **remains open while the user navigates between forms**, so you can guide users across multiple steps.
This makes it ideal for **step-by-step marketing processes!**.
## Why They Are Useful for CI Journeys
Instead of making a documentation about all the steps the marketers have to do to create an email, or a journey, you can just directly add a help pane to the Journey or the Email!

But it's better to see some real life examples 🐣
### Example 1 – Email creation
Inside the email form you can create a Email creation guidance. Here a sample of how the pane can look like
Help pane content example:
> [!TIP]
> **Example Help Page for Email**
>
> ## Purpose
>
> Use this checklist when creating marketing emails in **Customer Insights - Journeys** to ensure branding, compliance, and deliverability best
> practices are followed.
>
> ---
>
> ## 1. Use an Approved Template
>
> Start from an **approved marketing template**.
>
> Checklist:
>
> - Use the official **company email template**
> - Verify **branding elements** (logo, colors, fonts)
> - Do not remove required **footer elements**
> - Ensure layout works on **desktop and mobile**
>
> ---
>
> ## 2. Verify Sender Information
>
> Ensure the sender is recognizable and trustworthy.
>
> Checklist:
>
> - Confirm the **Sender Name**
> - Confirm the **Sender Email Address**
> - Use an approved sender domain
> - Avoid generic names like `Marketing Team` unless defined by brand
> guidelines
>
> Example:
>
> Sender Name: Martin P Lopez
> Sender Email Address: events@martinplopez.com
>
> ---
>
> ## 3. Check Personalization
>
> Validate all dynamic content.
>
> Checklist:
>
> - Review **dynamic fields**
> - Add **fallback values** where needed
> - Preview using **sample contacts**
>
> Example:
>
> `Hello {{FirstName | default:"there"}}`
>
> ---
>
> ## 4. Review the Compliance Profile
>
> Customer Insights - Journeys enforces consent and regulatory
> requirements through **Compliance Profiles**.
>
> Before using the email:
>
> - Select the **correct Compliance Profile**
> - Verify the **communication purpose** (e.g., commercial,
> informational)
> - Ensure **unsubscribe links** are present
> - Ensure the **preference center link** is included
>
> If unsure, use the **default marketing compliance profile** defined by
> your organization.
>
> ---
>
> ## 5. Test the Email
>
> Always test before using the email in a journey.
>
> Checklist:
>
> - Use **Preview and Test Send**
> - Send a test email to yourself
> - Verify:
> - Mobile rendering
> - Images
> - Links and buttons
> - Personalization fields
>
> ---
>
> ## 6. Final Validation
>
> Before activating the email or using it in a journey:
>
> - Subject line reviewed
> - Preheader text added
> - Compliance profile confirmed
> - Links tested
> - Email approved (if applicable)
>
> ---
>
> 💡 **Tip** Clear subject lines and minimal spam trigger words improve
> deliverability.
This is how it might look like:

---
### Example 2 – Event creation
Here is another example of how a checklist can help you organize your event:
> [!TIP]
> **Example Help Page for an Event**
>
> ## Purpose
>
> Use this checklist when creating **events in Customer Insights – Journeys**.
>
> ---
>
> ## 1. Define Event Details
>
> Checklist:
>
> - Event name
> - Description
> - Event type (online / hybrid / in‑person)
> - Date and time
> - Location or meeting link
>
> ---
>
> ## 2. Configure Registration
>
> Checklist:
>
> - Registration form created
> - Required fields defined
> - Capacity limits configured
> - Waitlist enabled if needed
>
> Example fields:
>
> - First Name
> - Last Name
> - Email
> - Company
>
> ---
>
> ## 3. Configure Event Communications
>
> Recommended emails:
>
> - Registration confirmation
> - Event reminder
> - Post‑event follow‑up
> - Follow-up for No-Show (optional)
> - Unregistered Confirmation (optional)
>
> Example timeline:
> ```
> Registration Confirmation → Immediately
> Reminder → 1 Day Before Event
> Reminder → 1 Hour Before Event
> ```
>
> ---
>
> ## 4. Configure Event Journeys
>
> Recommended emails:
>
> - Registration confirmation
> - Event reminder
> - Post‑event follow‑up
> - Follow-up for No-Show (optional)
> - Unregistered Confirmation (optional)
>
> Example timeline:
> ```
> Registration Confirmation → Immediately
> Reminder → 1 Day Before Event
> Reminder → 1 Hour Before Event
> ```
>
> ---
>
> ## 5. Test the Registration Process
>
> - Complete a **test registration**
> - Verify confirmation email
> - Validate reminder emails
This is how it might look like:

---
## Using help panes as process maps
But the real value comes from how you structure your Help Panes. So it can be used as a map inside your intention.
So imagine you have a normal campaign script:
```mermaid
graph LR;
A(Create your email) --> B{Is it segment-based?}
B --> C(Create/Choose your segment)
B --> D(Create/Choose your trigger)
C --> E(Configure the journey)
D --> E
```
How great would it be if you could link your next step directly in your help pane?
### Campaign Launch Guide
Creating a map of the campaign for each step, and adding it as a section to your pane helps you identify where you are in your standard process and what are the next steps:

> [!TIP]
> **Tip**
>
> Don't forget to hyperlink the map with the section you want to redirect to, that’s how you get 100% of the value from this feature.
### Event Launch Guide
To organize an event usually requires more elements as one might think, help guide them through this process
I recommend using this sequence when creating an event in Dynamics Customer Insights Journeys:
```mermaid
graph LR;
A[Emails] --> B[Journeys]
B --> C[Form]
C --> D[Event]
```
But how do I take this to the Help pane? Just like before, add a section with a checklist of the step you're in and section with a map to know which section is next or was before:

For the Checklist, I'd recommend less is more, keep it simple and put no more than 5 checks.
And if your event includes an external registration portal, you can also [build a branded event website connected to Customer Insights - Journeys](/writing/build-event-website-power-pages-dynamics-365/).
## How to enable
Microsoft documentation already explains the setup process very well: [Create guided help for your model-driven app](https://learn.microsoft.com/en-us/power-apps/maker/data-platform/create-custom-help-pages)
Here are the high-level steps:
1. Open **Power Platform Admin Center**
2. Navigate to **Environment Settings**
3. Enable **Custom Help Panes**
4. Create help pages in Dataverse (recommended inside a solution)
5. Assign security roles
Important:
- To enable this feature, you might require Power Platform Administrator or Dynamics 365 Administrator role in your tenant
- Users need permission to read the Help Page table, adapt the security role accordingly
- Authors need privileges to create and edit help pages.
More Info about Security and privileges under [Privileges required](https://learn.microsoft.com/en-us/power-apps/maker/data-platform/create-custom-help-pages#privileges-required)
## Best practices
- Help panes are linked to a specific language. If the user doesn't have a help pane for his language, he won't see anything.
- If you use balloons or checkmarks take into account that this elements are linked to the HTML element. So if the element is inside a collapsed HTML element, it will not show or not show correctly. Keep in mind that the users can be more confused than helped with this kind of things.
- Dropdown or Optionset elements don't always work with Coachmarks or ballons (for instance in Email)
- I'm more of a reader but if you have workshops in video and you can divide them into different sections, you can directly embed the videos in the help panes
- Use the links to reference other sections of Customer Insights - Journeys
## Final Thoughts
Custom Help Panes are one of the most underrated capabilities in Dynamics 365 and Power Apps model-driven applications.
Instead of relying on external documentation or training sessions, you can guide users directly inside the interface.
For teams using **Customer Insights - Journeys**, this means:
- Fewer onboarding questions
- Better process consistency
- Faster campaign execution
With just a few help pages and checklists, you can transform the application from a complex tool into a **guided marketing workspace**.
---
# Clean Up Dataverse File Columns with Power Automate
- URL: https://martinplopez.com/writing/dataverse-delete-file/
- Published: 2026-02-20
- Summary: Learn how to delete and clean up Dataverse file column data using Power Automate. This step-by-step guide shows how to safely remove file attachments and optimize storage in Microsoft Dataverse.
A few weeks after delivering a Power Automate process that generates and stores PDFs in a Dataverse file column, my client asked for something simple:
“Can we make this process run on demand?”
That’s when I hit an unexpected limitation:
You **cannot clear a Dataverse File column using "Update a row"**.
I assumed it would be straightforward. But when I tried to use the **Update a Row** action, the file column didn’t even appear 😐
> [!NOTE]
> **TL;DR:**
>
> If you need to **clear a Dataverse File column using Power Automate**, you cannot do it with **Update a row**.
>
> Instead:
>
> 1. Use **Perform an unbound action**
> 2. Select the **DeleteFile** action
> 3. Pass the **FileId** of the file stored in the column
>
> Dataverse stores file content in a separate File record. Deleting that record using `DeleteFile` properly removes the file from the column.
---
## Things I tried
First, I attempted to use **Update a Row** and set the file column to `null`.
That doesn’t work — because the File column does not appear in the action.
> ⚠️ Note: For **Image columns**, clearing with `null` *does* work using Update a Row. This limitation applies specifically to **File columns**.
Next, I tried using the **Upload a file or an image** action and passed `null` as content:

Interestingly, this creates a blank file instead of deleting it:

It works as a workaround — but it does not actually remove the file. It simply replaces it with an empty one.
Not good enough.
---
## Why This Happens
When you upload a file into a Dataverse File column:
- Dataverse creates a separate **File record** behind the scenes
- The File column does not directly store the binary content
- Instead, it stores a reference (`FileId`) to that File record
That’s why the column doesn’t appear in **Update a row** for clearing with null.
To properly remove the file, you must delete the underlying File record.
---
## The Solution
My instinct told me:
> “If this exists in Dataverse, there must be an action for it.”
In Power Automate, that usually means checking:
- **Perform a bound action**
- **Perform an unbound action**
Since File columns are generic and not tied to a specific table type, this had to be an **unbound action**.
After scrolling through the list…

I found exactly what I needed:
> **DeleteFile**
When selecting it, the action requires one parameter:
- `FileId`
And that’s the key insight.
When a file is stored in a File column, the record exposes its **FileId** — which you can retrieve dynamically.

If you pass that FileId into the `DeleteFile` action — the file is completely removed.

Voilà 😎
---
## Step-by-Step: Deleting a Dataverse File Column
1️⃣ Add **Perform an unbound action**
2️⃣ Select action: `DeleteFile`
3️⃣ In the **FileId** parameter, pass the FileId from your record
4️⃣ Save and test
Once executed, the file content is fully removed from the column.
---
## Important Notes
- This applies to **File columns**, not Image columns.
- You must have proper permissions to delete files.
- Many advanced Dataverse capabilities are exposed via unbound actions — even if they’re not available in standard connector actions.
---
## Conclusion
If you ever feel stuck because something isn’t available in a standard Dataverse connector action, check the **bound and unbound actions**.
Very often, the platform already provides a proper way to handle it — you just need to look a little deeper.
While preparing this article, I tried generating an AI image to illustrate the problem. Ironically, the AI generated something very close to the solution 😅

And I think this is important:
Even if someone has written about this before, your explanation, your scenario, and your experience will help someone in a different way.
If this article saves someone 30 minutes of frustration — it’s worth publishing.
👉 If you need help with this kind of stuff, feel free to [send me an Email](mailto:comment@martinplopez.com), . I’ll gladly try to help 😉
---
# Personalized Christmas Emails in Seconds with Power Automate + AI
- URL: https://martinplopez.com/writing/power-automate-ai-personalized-emails-dataverse/
- Published: 2025-12-18
- Summary: Learn how to use AI prompts in Power Automate to generate personalized emails using Dataverse contact data, adding context and intelligence to your automations.
When I was a kid, I remember spending hours preparing Christmas cards by hand - paper, crayons, glitter, and a lot of patience. Writing a thoughtful message, waiting for the glue to dry, finding a stamp, and finally dropping the envelope into the school mailbox was all part of the ritual.
Today, very few people send physical Christmas cards anymore. But the intention behind them hasn’t changed: letting people know we’re thinking of them and that they matter.
Companies feel the same need toward their customers. They want to communicate in a way that feels personal and genuine - but when you’re dealing with hundreds or thousands of contacts, that quickly becomes a challenge. Generic email templates don’t feel special, and complex conditional logic only goes so far.
This is where **Power Automate** and **Artificial Intelligence** come into play. By combining Dataverse data with AI-powered prompts inside a flow, it’s possible to generate highly personalized emails at scale - messages that adapt to each recipient’s context without sacrificing automation.
In this article, I’ll show how you can use **Power Automate + AI** to turn a single click into hundreds (or thousands) of truly personal emails.

> [!NOTE]
> **TL;DR:**
>
> With **Power Automate** and the **AI Prompt connector**, you can generate hundreds or thousands of highly personalized emails using Dataverse contact data - without building complex logic or templates.
>
> In this article, I’ll show how to use AI prompts inside a flow to add **context, tone, and intelligence** to your email communications, while keeping everything scalable and automated.
---
## The personalization-at-scale problem
Personalization has been a buzzword for years, but in practice it’s often reduced to little more than:
> “Hi {{FirstName}}”
The result?
Messages are sent at scale, but they don’t *feel* personal - and recipients notice.
At the same time, writing individual emails manually simply doesn’t scale. No one has the time to craft hundreds of unique messages, even if the intent is there.
What’s missing is a way to keep:
- the **efficiency of automation**
- while regaining the **human tone and context** of a personal message
That gap is exactly where AI can add real value.
If you’ve read this far, you might be thinking: *Alright Martin, but this is the third Post I read today about AI being the solution*
I can say, yes, buuuuut probably not within **Power Automate** 🙌
---
### Introducing: Power Automate + AI Prompts
Power Automate has always been excellent at orchestrating processes: triggering actions, retrieving data, and sending messages. What it traditionally lacked was the ability to *think* about the content it was sending.
That changes with the **AI Prompt connector**.
By introducing AI into your flows, you can move beyond fixed templates and rules, and instead **generate content dynamically**, based on real data and context.
In this approach:
- **Dataverse** provides structured information about each contact
(name, role, company, preferences, history, etc.)
- **Power Automate** controls *when* and *to whom* messages are sent
- **AI prompts** decide *how* the message should be written
Instead of hardcoding dozens of variations, you describe **what kind of message you want**:
- the tone (formal, friendly, festive…)
- the context (customer relationship, event, time of year)
- the key facts the AI should consider
The AI then generates a unique message for each recipient, using the data you pass into the prompt.
The result is a powerful combination:
- One flow
- One prompt
- Potentially thousands of emails
- each one different, relevant, and human-sounding.
This doesn’t replace automation. It **enhances it**, turning Power Automate from a message delivery engine into a personalization engine.
> [!WARNING]
> **Responsible use note**
>
> AI-generated text can be wrong or inappropriate. Consider adding approval for high-value customers, logging outputs, and avoiding sensitive attributes unless you have consent and a clear business need.
---
## Let’s build it
### Step 1: Create the flow
For starters, I recommend creating a manually triggered Power Automate Flow with a Dataverse “List rows” action. This will allow us to control exactly when the emails are sent and to whom.
> [!TIP]
> **Recommendations**
>
> - Rename the action to describe what it does (best practice): e.g., **List Dataverse Contacts**
> - Use **Select columns** for only the fields you’ll pass to the prompt
> - Filter out contacts without an email address to avoid failures

### Step 2: Create the AI Prompt
The next step is to select the **Run a prompt** action. This lets us generate a personalized email using the Dataverse data we just retrieved

We'll create a new prompt from scratch, so click on "+ New custom prompt", this will open a window like the following:
` Option. It works very well to give you a draft")
Here my final version of my prompt, so you can feel free to use it:
```text
You are tasked with composing personalized Christmas email based on provided contact information.
The email should be tailored to each individual using their first name, last name, birthdate, gender, and country.
Additionally, the email content must be translated appropriately to match the language or cultural context of the recipient’s country.
### Instructions:
1. **Personalization:**
- Use the first name and last name to address the recipient warmly and personally.
- Optionally, incorporate a friendly mention of their birthdate if relevant and appropriate.
2. **Cultural Sensitivity:**
- Adapt the tone and style of the Christmas greeting to suit the cultural norms of the recipient’s country.
3. **Translation:**
- Translate the entire email content into the primary language of the recipient’s country.
- Ensure the translation preserves the warmth and festive spirit of the original message.
4. **Email Content:**
- Include a warm Christmas greeting.
- Optionally, add a positive, festive message or wish for the upcoming year (2026).
5. **Output:**
- Provide the personalized and translated email text.
### Guidelines:
- Do not assume the language of the country without verification; use common language associations (e.g., Spain → Spanish, France → French).
- Keep the message respectful, warm, and festive.
- Avoid overly formal or informal language unless culturally appropriate.
- Extract all personalization details strictly from the provided contact info.
- Add a Signature of Martin P Lopez
### Output Format:
Output the personalized Christmas email in json format:
example:
{
"subject":"",
"email":""
}
Include a Subject for the Email in the subject and the email content in html in the email.
Provide the contact information here:
firstname: {{firstname}}
lastname: {{lastname}}
birthdate: {{birthdate}}
gender: {{gender}}
country: {{country}}
```
> [!TIP]
> **Notes regarding the prompt**
>
> - Feel free to edit the prompt as you need, this prompt should be used as reference.
> - I used the cultural sensitivity, translation and output instructions as I provide different Countries of reference
> - In the email content I found problems when you don't indicate the coming year, so I recommend adding it
> - In the Guidelines I indicated to add my signature, that worked very nicely for all languages
> - I find very useful to have an output in JSON so you can have the subject and body of the email as output
> - In the last part, the dynamic contents are added. They're a normal text contents, we provide the data from Dataverse afterwards.
> - My recommendation is to give a clear name of the function to the prompt as a best practice
> - The Output in HTML is to ensure the Email is formatted correctly with spacing.
> - For better results I recommend the ChatGPT 4.1 Model instead of the Mini, it will improve the result a lot.
Once we're done, we save the prompt and we can map the variables with the data from Dataverse:
?['gendercode@OData.Community.Display.V1.FormattedValue']` expression, as by default it will give you 1 or 2 as value")
Power Automate will automatically wrap the prompt and email actions in an `Apply to each` loop over the returned contacts.
### Step 3: Send the Email
Now add the Outlook action to send the emails. Map the prompt output (subject and email) to the email subject and body, and use the contact’s email address from Dataverse as the recipient. The flow should be ready!
` Action will send all the emails directly, if you prefer to review them before sending, I recommend the `Draft an email message` Action")
## Conclusion
I really like this solution, it's simple, easy to implement and gives many extension possibilities:
- Send everything dynamically uploading an Excel of contacts to a SharePoint site
- Connect and expand with Dynamics Customer Insights Journeys
- Adapt it to be more personal in your marketing strategy, for example by sending Event follow-ups, VIP customer outreach or targeted campaigns
This opens up a lot of possibilities and creates a super-powered version of Power Automate. I’m excited about what’s coming in 2026...😊
How about you? You think this combination has other possibilities, I’d love to read them! 😉 [email me](mailto:comment@martinplopez.com) or tag me on LinkedIn!
---
# Don’t Waste Your Thank-You Page: Use Dynamic Data to Drive Next-Step Actions
- URL: https://martinplopez.com/writing/dynamic-thank-you-page-dynamics-365/
- Published: 2025-11-26
- Summary: Learn how to turn your thank-you page into a high-value engagement tool using dynamic form data in Dynamics 365 Customer Insights - Journeys.
If you’ve worked with **Dynamics 365 Customer Insights Journeys**, you’ve probably noticed that the default form submission experience - a simple logo and “Thank you” message - often feels underwhelming for users.
You’re not alone 😁 Many teams want something more engaging, more branded, and more helpful than the out-of-the-box version. Luckily, Dynamics 365 allows you to redirect users to a custom thank-you page, giving you full control over design and content.
**BEFORE**:

**AFTER**:

In this article, we’ll walk through how to quickly create a personalized, dynamic thank-you page - including how to pass form submission data using `sessionStorage` so you can greet each user with relevant information right after they hit submit.
This idea (using sessionStorage) comes from an article by the great Megan V Walker, worth a read if this topic interests you 👉 [Format Session Registration Confirmation Page On Your Own Domain](https://meganvwalker.com/session-registration-confirmation-page/)
> **TL;DR:**
> Dynamics 365 CI Journeys lets you redirect form submissions to a custom thank-you page.
> By saving form data into `sessionStorage` during `onsubmit`, you can create a fully personalized thank-you experience that updates instantly - no server calls needed.
## Step 1: Build Your Custom Thank-You Page
We'll continue with the example page I created in Power Pages [in my previous article](../build-event-website-power-pages-dynamics-365/). And as we are using Power Pages, we'll keep it simple and click on **+ Page** in our Website editor.

The Result is definitely not bad 😎

Now let’s customize it.
### Add dynamic placeholders
Open **Edit Code**, then we can modify our `h1` and `h3` tags to add a `span` tag which we'll change dynamically. Here is my Page:
```html
Thank You, there!
Your registration for the event was successful.
What Happens Next?
We have sent a confirmation email with all the event details to your inbox. Please check your email and add the event to your calendar.
```
### Add script to update the placeholders
Open the `*.customjs.js` of the newly created Thank You Page and paste the following javascript that will substitute the dynamic value of page for the variables we will save in the session storage when we submit the form:
```js
document.addEventListener('DOMContentLoaded', function () {
populateThankYouPageFromSession();
});
function populateThankYouPageFromSession(options) {
const config = Object.assign(
{
storageKey: 'ciFormData',
clearAfterUse: true
},
options
);
const raw = sessionStorage.getItem(config.storageKey);
if (!raw) {
console.warn('No form data found in sessionStorage under key:', config.storageKey);
return;
}
let formData;
try {
formData = JSON.parse(raw);
} catch (error) {
console.error('Could not parse form data from sessionStorage:', error);
return;
}
const boundElements = document.querySelectorAll('[data-form-field]');
boundElements.forEach(function (el) {
const fieldName = el.getAttribute('data-form-field');
if (!fieldName) return;
const value = formData[fieldName];
if (value === undefined || value === null || value === '') {
return;
}
if (el.tagName === 'INPUT' || el.tagName === 'TEXTAREA') {
el.value = value;
} else {
el.textContent = value;
}
});
if (config.clearAfterUse) {
sessionStorage.removeItem(config.storageKey);
}
}
```
Your page should look like this:

## Step 2: Configure the Form in Customer Insights Journeys
### Enable Redirect
Open your event form in CI Journeys and set:
- **Post submission action:** Redirect
- **Redirect URL:** full URL of your new thank-you page

⚠️ If your thank-you page is on a *different domain* than your form, `sessionStorage` will not persist.
Use **localStorage** instead if needed.
### Save form values into session storage
Edit the HTML of your form and paste the script *after the closing `` tag*:
```html
```
After adding this script is the moment of the truth, cross your fingers, go to your registration page, fill it out and...

## CSP Configuration (Power Pages Only)
If the page redirects correctly **but does not show the dynamic data**, Power Pages may be blocking inline scripts.
In order to solve this, we can adjust our power pages by allowing this, I found a perfect article for this 👉 [Power Pages CSP: Starter Settings](https://michelcarlo.com/2024/11/23/power-pages-csp-starter-settings/)
Add this to your CSP settings:
```
script-src 'self' 'unsafe-eval' 'unsafe-inline' https://*.microsoft.com content.powerapps.com https://*.azureedge.net;
```

## Conclusion
Upgrading your thank-you page from the out-of-the-box message to a dynamic, branded experience is a small change with a big impact.
By capturing values in `sessionStorage` during submission and injecting them into Power Pages, you can:
- Personalize the confirmation
- Reinforce your brand
- Guide users to meaningful next steps
- Reuse this pattern across all CI Journeys forms
It’s a lightweight technique that dramatically improves user experience and makes your event flows feel more polished and intentional.
If you try it out, I’d love to hear how it worked for you, [email me](mailto:comment@martinplopez.com) or tag me on LinkedIn!
---
# Build a Branded Event Website in 20 Minutes with Power Pages and Dynamics 365 Customer Insights – Journeys
- URL: https://martinplopez.com/writing/build-event-website-power-pages-dynamics-365/
- Published: 2025-11-09
- Summary: Learn how to quickly build a live, branded event website using Microsoft Power Pages and Dynamics 365 Customer Insights – Journeys. No code, just results.
Some years ago, I was responsible for two Power Pages sites (at that time called Dynamics Portals) that were connected to Dynamics 365 Customer Insights – Journeys (back then: Dynamics 365 Marketing). At that time, adapting and branding an event portal was… painful. It took a lot of effort to get something that looked good and felt on-brand.
Fast forward to today: after a few renames and a **lot** of updates, Microsoft has evolved these tools to the point where I was able to set up a portal, brand it, and connect it to my Dynamics 365 Customer Insights – Journeys environment in as little as **20 minutes**.
The best part? After a bit of experimenting, I realized you can do this mostly by **customizing and copying a code snippet** — no deep coding skills needed.

I decided to put together this guide for you, so you can also set up your **branded event website with no code knowledge needed**. 😊
I’m basing this on Microsoft’s guide:
[Build an event registration portal with Power Pages](https://learn.microsoft.com/en-us/dynamics365/customer-insights/journeys/event-portal-template)
…but I had to fight a bit with the tools to understand what’s the **quickest and least painful way** of setting things up. This article is that shortcut.
> **TL;DR:**
> - Use the **Event portal template** in Power Pages.
> - Quickly brand the **hero, header and footer** with your logo and imagery.
> - Replace the default registration logic with the **embedded form snippet** from Customer Insights – Journeys.
> - In CIJ, add your **domain** and **Power Pages website** under Event Management.
> - Create an event, point it to your portal and publish — your branded event page is live. 🚀
---
## What will you need?
For this use case, if you want to do it in record time, we have to be prepared beforehand, so I advice you to have the following prepared:
- [X] Logo of your company (without background works even better, I'm using a png)
- [X] A cover picture that will match with your company Branding
- [X] The name of your Company and a Heading Text
- [X] A license of Powerapps (One can start with a Trial license and convert it in a production license)
- [ ] An Omega Speedmaster or Rolex Daytona to measure your speed (optional — the clock app on your computer works just as well 😁)
> ⚠️ **Important:** This guide is accurate & working at date of publishing
---
## How to build it
To achieve this in record time, we’ll divide the process into two parts:
1. **Set up Power Pages**
2. **Set up Dynamics 365 Customer Insights – Journeys**
---
## 1. Set up Power Pages
### Create the site
Start at [Power Pages Get Started](https://make.powerpages.microsoft.com/).
1. Select the correct **environment** in the top-right corner.
2. Select **Start with a template**.

3. Give your site a **name** and set the **language**.
4. Select **Done** in the bottom-right corner.
You can change the web address later to use your own domain name. If that’s interesting for you, I’ll write a separate article on how to do it.

---
### Substitute heading, cover, and logos
Now we have our website ready, but at first you might see something like this:

Give the page a refresh (Ctrl + F5 is even better) and you should see something like this:

Click **Edit background** to change the hero image. You can **Upload Image** and reuse that image later on other pages.

You can click on the **title text** in the hero area to edit it. This is where you tell visitors what this site is about:

Scroll down to the footer. There, click on the **Contoso** logo (the standard logo) and change it to your own:

If the logo is too tall (like it was for me), you can adjust the width to whatever feels appropriate — the layout will adjust automatically while maintaining the aspect ratio.

For the header, I found it easier to change the logo and site name via **Edit code**, because changing it only in the designer wasn’t reliable at the time of writing. This might be improved by Microsoft later, but here’s the workaround.
Click **Edit code**:

In Visual Studio Code, open the **content snippets** folder. There you’ll find:
- **Logo URL** (2)
- **Site name** (3)

In **Logo URL**, set the file name of the logo you uploaded (after the `\`). For example:

Save (Ctrl+S) and refresh your portal.
Now you have a **nicely branded page**!
---
### Adapt home cards to events
Back in the **Power Pages designer**, you can adapt the cards on the home page to display your event information.
You can:
- Choose a different **layout**
- Select which **event fields** to display
- Adjust the list to show only **upcoming** events

This is where you turn the template into **your** event showcase.
---
### Make the portal public
When the portal is ready to go public:
1. Go to the top of the designer and click on the **site title**.
2. Select **Manage site visibility**.

Choose **Public** and confirm. This will allow people (outside your organization) to access the site and register for your events.

---
## 2. Set up the portal in Dynamics 365 Customer Insights – Journeys
We also need a bit of setup on the Dynamics side to connect your new Power Pages site to **Events** in Customer Insights – Journeys.
### Add the domain
We need to validate our domain for external form hosting. This is required so the forms from Customer Insights – Journeys can load in a registered domain.
The good news: you don’t need to touch DNS or add records manually.
1. In Customer Insights – Journeys, go to **Settings → Domains**.
2. Create a **new domain**.
3. In the form that opens, provide your **Power Pages URL** (the site you just created).
If you’re unsure about the URL, go back to the Power Pages designer and click **Preview** in the top-right corner — copy that URL.

Click **Next**, then **Verify**, then **Done**. This should be automatic because the host of this webpage is Microsoft.
---
### Add the Power Pages website
Now we’ll link the website itself.
1. Go to **Settings** again.
2. Under **Event management**, open **Power Pages websites**.
3. Add your newly created site.

Make sure the **Registration page URL** points to your Power Pages site plus `/Register-to-Event`.
---
### Create an event and publish it to the Power Page
Now for the fun part — try creating an event.
1. Create a new **Event** in Customer Insights – Journeys.
2. When it’s ready, in **Where do you want attendees to register for this event?**, select **Event portal using Power Pages**.
3. If it doesn’t automatically select your portal, choose it manually from the lookup.

Publish the event.
You’ll now get a **registration URL** that points to your new branded Power Pages portal.
---
## Conclusion
You should now have:
- A **branded portal**
- Showing **events** you publish in Dynamics 365 Customer Insights – Journeys
- With a **working registration form** embedded directly from CIJ
All ready for real visitors and registrations.
I highly recommend you try it out in a **test event** first:
- Publish a couple of events
- Register as a test attendee
- Confirm the registration and check that the data lands correctly in Customer Insights – Journeys
While preparing this article, I timed myself. The quickest I managed to set everything up was **15 minutes and 14 seconds**. ⏱️
Do you think you can beat that?
Try to set it up yourself — I’d love to hear your times! 😁
👉 [Email me your time or any questions](mailto:comment@martinplopez.com), and feel free to tag me on LinkedIn so we can turn this into the **fastest “time-to-live” event portal** scenario in Microsoft’s ecosystem.
---
# How Solution Health Hub Prevents Downtime in Dynamics 365 Customer Insights Journeys
- URL: https://martinplopez.com/writing/solution-health-hub-customer-insights-journeys/
- Published: 2025-10-26
- Summary: Discover how the Solution Health Hub prevents downtime in Dynamics 365 Customer Insights Journeys by auto-fixing common errors before they impact campaigns.
Have you ever had a journey that suddenly stops triggering? You could swear it worked yesterday, but now it isn’t firing when it should.
Maybe you experienced trying to publish a form or a journey and it's taking forever until it gives you an error that doesn't give you much information...
I have to admit, I was once (or twice) where you were (or are) and invested time to debug what was happening. Luckily, I got help from Microsoft engineers who recommended this solution—so I’m sharing it with as many people as possible. 😊
**What it is:** an app in your Dynamics 365 environment that automatically checks the health of installed solutions—including **Customer Insights – Journeys**—and provides **targeted, one-click fixes** for common issues. In this post I’ll show you how I use it.
> **TL;DR:**
> If journeys stop triggering or publishing fails with unhelpful errors, use the **Solution Health Hub**. Create a **Marketing Rule Set** analysis job, let it run (~2 minutes), then click **Resolve** on failed checks. Re-run until all results are **Pass**.
---
## Common problems (and why they happen)
I mentioned a few scenarios in the introduction that I’ve solved with Solution Health Hub. Other common problems include:
- A real-time trigger stopped working, with no visible error.
- An upgrade fails because background processes are owned by disabled users.
- SMS/push don’t go out because a required security role/profile is missing.
- Journeys configuration records went missing after partial uninstalls/installs.
If you read until here you might be asking yourself: _Ok, I have these problems and you're gonna help me solve them, but why does that happen?_
**Why this happens:** Customer Insights – Journeys has many moving parts (plugins, background processes, application users, roles). If **one dependency is missing or misconfigured**, the whole publishing/triggering pipeline can stall. Common root causes include:
- Plugin steps or background workflows/flows disabled during imports, upgrades, or cleanups.
- Owners of critical processes are **disabled users**.
- Key configuration entities missing after partial uninstalls or failed installs.
- **MarketingServices** app user or roles lack required permissions.
- Direct customizations to OOB components conflicting with managed updates.
- Dataverse hiccups (throttling/network) leaving components partially applied.
---
## How to use the Solution Health Hub
Open the app, create a new analysis job, let it run (≈2 minutes), then review the results and click Resolve where checks failed—easy, right? 😎
I will summarize the most important points in the article, but I'm basing myself in the Documentation provided by Microsoft in this Article 👉 [Troubleshoot issues with Solution Health Hub for Customer Insights – Journeys](https://learn.microsoft.com/en-us/dynamics365/customer-insights/journeys/troubleshoot-marketing-solution-health)
### 1) Create a new analysis job
In the app, the default view is Analysis Jobs. Click New, then in the dialog select Marketing Rule Set and click OK.

Once created, it will automatically open the Analysis Job that you just created with status Running. In about 2 minutes (up to ~5 minutes on the first run) it should complete and display results.

### 2) Go Over the results and resolve failures
Once completed, scroll to the subgrid at the bottom and review the Return Status column:

- For any row with **Fail**, select it and click **Resolve** on the ribbon.
- Confirm in the dialog (**OK**).
- You’ll see **Resolution is in progress** → then **Resolution is completed**. 🎉
⚠️ **Important:** run the **analysis job again** to confirm everything turns **Pass**. I’ve had Microsoft engineers explicitly ask for a second pass to be sure.
---
### Recommendations and Tips
✅ Run Solution Health Hub **before and after upgrades**.
✅ Re-run the analysis until **all checks = Pass**.
✅ If **Resolve** isn’t available for _CheckIfProcessesOwnedByDisabledUsers_, open the analysis result → review **Failing Records** → reassign those processes to a system/admin user.
> **Permissions:** You’ll generally need **System Administrator** (or similar admin privileges) to run/resolve across environments.
> **⚠️ Customization Warning**
> If you customized **out-of-box components**, Solution Health Hub resolutions may **reapply the Microsoft baseline** to the active layer, which can overwrite your changes.
> **Recommendation:** avoid altering OOB components directly; use **custom components**.
---
## Conclusion
Downtime in Customer Insights – Journeys is not normally caused by you doing something wrong. It’s the complexity of dependencies. As we saw above, a single missing permission, disabled step, or orphaned owner can halt triggers and publishing.
**Solution Health Hub** is your built-in first-aid kit: it diagnoses, explains, and often **auto-fixes** issues before they become lost campaigns or late-night fire drills. After you use it a few times, you’ll wonder why you ever debugged this manually.
👉 **Next step:** run a **Marketing Rule Set** analysis in your environment. Even if everything is green, you’ll have the peace of mind that your system sits on a healthy baseline—and you’ll know exactly where to look if something breaks.
Have you already used the Solution Health Hub in your projects? I’d love to hear your experiences — [email me a comment](mailto:comment@martinplopez.com), tag me on LinkedIn so we can help more teams keep **Customer Insights – Journeys** running smoothly. 😉
---
# Dataverse Clone Wars: Bulk-Merging Duplicates with SSIS (KingswaySoft Guide)
- URL: https://martinplopez.com/writing/dataverse-ssis-merge-duplicates/
- Published: 2025-10-12
- Summary: How to bulk-merge duplicate Contacts and Accounts in Microsoft Dataverse using SSIS and KingswaySoft: matching, normalization, ranking, previewing, and safe execution.
A long time ago in a database far, far away… 😁
It is a time of chaos.
The mighty Dataverse is under siege by an overwhelming army of duplicate records.
Accounts and contacts have been cloned endlessly, threatening to destabilize the CRM galaxy.
Armed only with an SSIS (SQL Server Integration Services) package, a brave data engineer rises to bring balance to the system.
Their mission: to bulk merge duplicates, unify scattered data, and restore peace to the Dataverse.
At this point, you might be asking yourself:
_“Is SSIS really the only choice? I never heard of SSIS, but Power Automate always makes my dreams come true.”_
And I tell you **No, Power Automate can do it, but you might want to go for a walk while it finishes...**
---
> **TL;DR:**
> Use the ready-made **KingswaySoft SSIS package** to bulk-merge duplicates in Dataverse.
> Connect via SOAP (or OAuth), define your match logic (e.g., `emailaddress1`), adjust comparison settings, preview results with **Data Viewer**, and always **test safely in a sandbox** before going full Jedi.
---
## Why not do it in Power Automate?
Power Automate can trigger actions that can merge Records — I tried this myself, but making it efficient is a challenge I’ve yet to accept.
The main issue isn’t functionality — it’s **scale**. When you’re comparing thousands of records, Power Automate becomes more of a Padawan than a Jedi Master.
So if your duplicates are manageable in number, Power Automate might still be the right tool. I recommend this article:
👉 [Automatic Duplicate Account Merge in Dynamics 365 Sales with Power Automate](https://accessorange.com/automatic-duplicate-account-merge-in-dynamics-365-sales-with-power-automate)
But if you’re facing **50,000+ contacts or accounts**, SSIS will handle the load like a true Sith Lord of data.
---
## Requirements
To prepare your SSIS package for battle, you’ll need the following tools:
- Visual Studio 2019/2022 (Professional or Enterprise edition)
- SQL Server Data Tools (SSDT) for Visual Studio
- KingswaySoft SSIS Productivity Pack
- KingswaySoft SSIS Integration Toolkit for Microsoft Dynamics 365
If it’s your first time working with SSIS — I’m proud of you! 🫡
Start here: [How to install SSIS, KingswaySoft, and Visual Studio for Dynamics 365](https://fredrikengseth.com/install-ssis-kingswaysoft-visual-studio-dynamics-365/)
> **⚠️ Safety first**
> - Run in a **sandbox** environment first.
> - Export a CSV of candidate duplicates (source + master).
> - Keep an **audit log** of merges (`winnerid → loserid`) so you can restore data if needed.
> - Start small: test with **Top 100** records before a full-scale run.
---
## The Battle
Once we're all set, the nice guys of Kingswaysoft already offered a packaged solution to your problems, you just have to set it up 😊
[Click here to download the Kingswaysoft SSIS Package](https://www.kingswaysoft.com/downloads/samples/dynamics-365/MergeCRMDuplicates.zip)
Open Visual Studio and create a new project: **Integration Services Project**.
 for Visual Studio' package")
Ok, now unzip the KingswaySoft package and add it to your project. In the **Solution Explorer**, right-click **SSIS Packages** → _Add Existing Package_.

Choose **File System** as the Package Location, and then browse to the unzipped package path.

When you see "Succeeded in upgrading the package" means you're on the right track 😉
Next, open the **MergeCRMDuplicates.dtsx** package, and double-click the _Find and Merge CRM Duplicate Contacts_ task in the **Control Flow**. You’ll land right in the command center:

---
### Setting up the Connection
Then we can Click on "Dynamics CRM Connection Manager" and set up the Connection to our Dynamics environment:

> **Connection tip:** Even though to use the **Dynamics 365/Dataverse Connection Manager** with **OAuth (Web API)** seems more appealing than SOAP. In my experience the SOAP connection technology is more developed than the Web API with Kingswaysoft. I had cases in which the logics built in SSIS were working perfectly with SOAP but not with WebAPI.
> For reference: [Using the CRM/Dataverse/CDS Connection Manager](https://www.kingswaysoft.com/products/ssis-integration-toolkit-for-microsoft-dynamics-365/help-manual/crm/connection-manager)
---
### The Duplicate Logic
In my example, contacts are duplicated by **email address**.
The KingswaySoft package already retrieves all contacts, so I opened the _Duplicate Detection on name_ component and will rename it to _Duplicate Detection on email_ for clarity.
Here you can define:
- The **column** to match on (e.g., `emailaddress1`)
- The **match type** (Exact Match, Fuzzy Match, etc.)
- The **similarity threshold** (percentage of similarity to be considered a match)

The **real power** of this step lies in the **comparison settings**. These options normalize your column values before matching — for example:
- Ignore extra spaces
- Treat uppercase/lowercase as equal
- Trim trailing punctuation
You can also set a **Ranking Strategy** — this determines which record “wins” when multiple duplicates are found.
###### Translation: when multiple records match, the ranking defines which one becomes the master.
By default, it uses **Similarity Score**, but when you use exact matching, they’ll all have a score of 1.
I like to set a custom strategy where the **oldest record wins**, assuming it’s the original.
In other cases, I prioritize contacts that aren’t “marketing-only” — merging those into the primary one.

---
### Testing and Merging the data
Want to see what’s happening before executing the merge?
Right-click on a connector line in your control flow and select **Enable Data Viewer**.
This lets you preview the matching results, review scores, and ensure your logic works as expected.

Now, let the package run — you’ll see the number of merged contacts rising heroically as duplicates fall one by one.

---
## Final Pointers
### You merged contacts you weren't supposed to and you can't find them?
Don’t panic. They’re not gone — just **inactivated**.
You can filter by “Last Modified” to the date/time you ran the SSIS package and find them easily.

### You want to unmerge them?
Simply reactivate the contact. It won’t be linked anymore — though relationships or field values may already have been consolidated.
---
## Conclusion
You might be thinking: _“How many times did he write KingswaySoft?”_
They were **10 times**, counting this one — and no, I don’t work for them 😁
But their SSIS solution is truly **plug-and-play**. You don’t need to configure much to wipe out duplicates efficiently.
Even though SSIS isn’t the newest tool in the Power Platform arsenal, it remains a **battle-tested solution** and a reference for Dataverse data migrations and cleansing.
Did you do something similar with duplicates? Have a better approach? Or ran into issues setting this up?
I’d love to hear your story — [email me here](mailto:comment@martinplopez.com) or tag me on LinkedIn! 🚀
---
# How to Prevent Duplicate Event Registrations in Dynamics 365 Customer Insights (with Form Submission Validation)
- URL: https://martinplopez.com/writing/custom-form-submission-validation/
- Published: 2025-09-22
- Summary: Prevent one user to register twice for a Dynamics 365 Customer Insights Journeys Event. Use the Form Submission Validation logic to achieve this.
Some months ago, I got the question if there was a way to limit the Registrations for an event to **one Registration per person** in Dynamics 365 Customer Insights Journeys. I thought then, that there is no setting for this, so I decided to investigate, and after some research I stumbled into this documentation of Microsoft: [Customize form submission validation](https://learn.microsoft.com/en-us/dynamics365/customer-insights/journeys/real-time-marketing-form-customize-submission-validation)
Then I thought that if there is a way of validating a form **before the submission**, we could avoid it altogether. So I set my mind on it and after some testing and validation, I developed a **Plugin** that will validate the event registration by running on form Submission and will prevent the submission to be sent in case there is already an event registration.
_Yes, ok, but Martin, what does this mean?_
There is a way to limit the registration process by checking if this person is already registered in the Event
## HOW?
### Step 1: data-form-validation in Event Registration Form
It all starts by setting the data-form-validation to true in the form where you want to integrate this:

Without this, your plugin won’t fire, because this will allow us to trigger the plugins registered under the "msdynmkt_validateformsubmission" Message.
Here an example in code:
```html