Automatic sending of webinar recordings

A fully automated journey for the accompanying communication for all events is by now a standard feature and much talked about. But what does it look like when the webinar recording of a Teams webinar (or meeting) is to be sent automatically to the participants? There are several reasons why this requirement sounds easier than it actually is. In this blog article, I’ll show you what the pitfalls are and how you can solve the problem!

The idea and where the standard tools fall short

A webinar or meeting recording in Microsoft Teams is automatically saved in the event owner’s OneDrive Business account. You can react to this process with Power Automate, create a sharing link there and then save it in the event. All contacts registered with the event can then be sent the link via Journey. This can be done fully automatically.

Sounds pretty straight forward, but the devil is in the detail. Firstly, the OneDrive Connector in Power Automate only ever has access to one personal area, namely the one whose connection it uses. The recording must then be mapped to the event from Customer Insights using the name. This also works in principle, as Teams always saves the meeting with the public name of the event in the file name. However, OneDrive (like Sharepoint) has some restrictions on the use of special characters. For example, a ‘?’ would be replaced by a ‘_’. This means that a simple string comparison does not work. And last but not least, a trigger is required that is the starting point for sending the link. A Dataverse trigger is unsuitable as there is no direct relationship to contacts. This means that a custom trigger must be used and this must be triggered somehow, preferably by another Power Automate Flow.

Low code vs. pro code approach

In this blog, I will show you an approach that works entirely with low-code tools. It will work great for small and medium-sized environments. For a larger scope, you might want to consider a “pro code” approach. One possible way to react to new recordings – tenant-wide – is with so-called MS Graph subscriptions. Once set up, such a subscription sends a notification to a previously defined webhook for every new recording in the entire tenant. You can find more information about this approach here. However, there is a lot to consider and maintain here: the data is encrypted, the subscription only runs for a few days, an extension must then also be implemented in the code, an SSL certificate is required for encryption and decryption, an app registration must be created and provided with the correct authorisations, and so on. In addition, Power Automate cannot decrypt data, which is why Azure Functions or local environments are required.

For all these reasons, I am focussing on the low-code approach here 🙂

Part one: Get the link into the event

This is the really complex part of the process. Firstly, we need to create a field in which the recording link is saved. Create a new URL field (string) in your solution with a few more characters if possible, I have chosen 300 characters as my upper limit, which I think should always be sufficient, even for very long links. Place the field on the event form and consider configuring it read-only to put the entire process in the hands of automation.

Redundant flows required – working with Childflow

As the OneDrive Connector can only ever access the personal area of the account stored in the connection, it is first necessary to identify who in the company creates Customer Insights events. This is because the user who creates the events in Dynamics 365 Customer Insights – Journeys also becomes the owner of the Teams Webinar or Teams Meetings. This means that the recording is stored in the associated OneDrive in the ‘Recordings’ folder, which is located in the root directory of the personal OneDrive for Business account.

To avoid having to duplicate the entire logic of the flow for each user, we will work with a child flow and only create the really necessary part with the OneDrive actions redundantly for each user. This means that the flow logic visible below must be created for every user who creates webinars via Customer Insights.

Connection references

Firstly, we should create all the required connections for all relevant users. To do this, create a new ‘Connection reference’ within your solution. For example, name it ‘OneDrive Johannes’ and select the ‘OneDrive for Business’ connector. A new connection must now be created under ‘Connection’. A login field opens and a login must be made via the account of the relevant user.

Important note: By adding this connection, it is possible to access the content of the user’s corresponding OneDrive via Power Automate Flows. This should be carefully considered and agreed with those responsible and the user.

After the connection has been created, return to the creation of the connection reference, click on “Refresh” and select the new connection.

Create the parent flow per user

Now we can create the first flow and select the OneDrive for Business trigger “When a file is created” as the entry point. The connection reference of the user for whom this flow is being created must be stored in the trigger and all subsequent OneDrive for Business actions. Select the “Recordings” folder in the trigger.

Note that the recording folder needs to be reselected later when the flow is copied for other users! Behind this is the Path ID, which is of course different each time, even if the folder is always called ‘Recordings’.

We then need the metadata of the file in order to be able to process the media type and file name. To do this, select the “Get file metadata” action and select the “File identifier” from the trigger as a parameter.

We now transfer the entire logic for the mapping to the event to a child flow. This means that this logic remains centrally managed. Before we can select ‘Run a Child Flow’, we must first create it. We can return here later. We define the child flow so that it either returns an event GUID together with the status 200 or a 404 status.

Respond to the result from the childflow

If an event GUID is identified, we can then create a sharing link and save it in the event. We therefore check the response from the child flow in a condition

body('Run_a_Child_Flow')?['status'] eq string(200)

If the check returns “true”, we can use the “Create share link” action to create a public view link. Finally, we save this link in the event in the field that we created at the beginning.

This parent flow must now be copied for each relevant user (i. in other words, each user who creates Dynamics Customer Insights web events in the company and is therefore the owner of the corresponding Teams events) using the ‘Save as’ function. After copying, the connection references must be replaced in all OneDrive actions. In addition, the recording path must be reselected in the trigger so that it points to the correct path id.

Child flow for identifying an event

For the child flow, we select the trigger “Manually trigger a flow” and create two input parameters of type text: “mediaType” and “fileName”. We then pass these input parameters from the parent flow when we call the child flow.

If the mediaType is not “video/mp4”, the flow is aborted to prevent unforeseen behaviour.

Check the structure of the file name

Event recordings are saved in the format [name of event]-[date][time]-Meeting Recording.mp4 (example: ‘An event of another user-20250526_223801-Meeting Recording.mp4’). There is a twist here, as some special characters are not permitted in file names within OneDrive or Sharepoint. These characters are automatically replaced by ‘’. This is important if we want to find the event name in the CRM later.

To be able to extract the event name, we use the ‘Find text position’ action and search for the substring with today’s date in the fileName parameter, e.g. ‘-20250527’. Everything in front of it will be the name of the event.

Format for the substring to be searched for

-@{formatDateTime(utcNow(), 'yyyyMMdd')}

If no substring of this type is found, we should also cancel the flow at this point, as we will then not be able to evaluate the format correctly.

Extract the event name and list all events in the CRM

If a corresponding substring was found, we can take everything before it and use it as our event name. To do this, use the “Substring” action, select the fileName parameter as text input, start at position 0 and go up to the identified position from the previous action @{body('Find_current_day_in_filename')}.

We need a variable for the event ID, as we now need to list all relevant events in the CRM in the next step and check them individually. Due to the forbidden characters, it may very well be that the event name in the CRM does not clearly match our extracted value from the recording. Therefore, we will list all events that are currently active in the CRM and that are not in the future and we will take each individual event name, adjust it and then compare the adjusted name with our extracted value.

Values for “List all active events in the past

Select columnsmsevtmgt_name,msevtmgt_eventid
Filter rowsstatecode eq 0 and msevtmgt_eventenddate le @{utcNow()}

Check and compare the event names

Now that we have retrieved all the event names, we need to check each one. In order to compare the event name, we need to replace forbidden characters in the event name with ‘_’. To do this, we use the replace() function in a compose action.

Forbidden characters in the file name

SignDescription
/Slash
\Backslash
:Colon
*Asterisc
?Questionmark
Quotation marks
<Smaller than
>Larger than
|Vertical line

The easiest way to replace is to nest replace() inside each other, replacing all forbidden characters with “_” one after the other. This looks terrible, but can simply be copied from here.

replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(
items('Apply_to_each_active_event')?['msevtmgt_name'], '"', '_'), '*', '_'), ':', '_'), '<', '_'), '>', '_'), '?', '_'), '/', '_'), '\', '_'), '|', '_'), '#', '_')

Now we can compare the customised name with our extracted name in a condition:

outputs('Replace_all_forbidden_characters_from_eventname') eq body('Extract_event_name')

If the check returns “true”, we set the guid of the current event in our variable. As we only expect one event, we can increase the concurrency of the Apply to each loop despite using the variable so that it does not run forever.

By doing this, we will always identify exactly one event. If (for whatever reason) there are several events with the same name, the result would not be unique.

Responding to the parent flow

If our comparison has identified an event, we pass the GUID to the parent flow, otherwise we pass a 404.

length(variables('Event ID')) gt 0

If the result of this comparison is “true”, we use the “Respond to a Power App or flow” action to transfer the GUID from the variable to the parent flow.

Part two: Send the link to the participants

From here on, it’s pretty straight forward. We need to create a custom trigger that we can react to later in a journey. This can either be an existing event journey (e.g. with the starting point “When an event registration gets created”) by waiting for our custom trigger to send the recording link for a certain period of time after the event has expired, or a completely independent journey that contains our new trigger as a starting point and then simply sends an email.

Create custom trigger

It would be so nice and easy if we could use a dataverse trigger, because it would simply react to changes in the ‘Recording Link’ field. However, there is no direct relationship between events and contacts, but this is a mandatory requirement for a dataverse trigger. We will therefore use a custom trigger, which we will activate via Power Automate Flow.

Create a new trigger, select ‘Custom Trigger’ and give the trigger a second parameter ‘Event’ in addition to the ‘Contact’ target group and use the ‘Entity reference’ type for the ‘Event’ table.

I have named the trigger ‘New recording link for event‘ and added the following to the description: ‘This trigger is triggered for all registered contacts of an event when a new recording link is added to the event‘. so that it can also be understood by others.

Journey for sending the link

For demonstration purposes, I have decided to set up a separate journey just for sending the recording link. When creating the journey, you should define in which cases such a link should be sent (e.g. only for certain event types). I have kept the journey very simple here and simply send a corresponding email whenever the trigger for a contact is triggered.

The email must then contain the link from the ‘Recording Link’ field on the event, e.g. in a button. Important: The correct trigger must be selected depending on the context in which the email is to be sent. An email can only ever use the trigger for personalisation that is set as the starting point of the journey in which the email is used. So if we set up our own journey that reacts to our custom trigger as the starting point, the event link should be selected via the ‘New recording link is created’ trigger. However, if this is to be part of a larger event communication journey that has been running since registration, the recording link would then have to be pulled from the ‘When an event registration is created’ trigger.

Activate custom trigger via Power Automate

In Power Automate, we create a new flow with the trigger ‘When a row is added, modified, or deleted’ and configure it so that it reacts to changes in the ‘Recording link’ field for events. We then retrieve the event once in a separate action, actually only to avoid accidentally deleting all references to the event in the subsequent actions if changes are made to the trigger at a later date. In this way, we can delete the trigger, create a new one and still retain the flow logic in full.

We use the ‘Filter rows’ line to specify that the flow only starts if there is actually a value in the field (and the change was not made by deleting the value).

Select columnsnew_recording_link
Filter rowsnew_recording_link ne null

Auflisten der Kontakte

Now we will look for all contacts who are registered for the event. Alternatively, you could also only search for those for whom there is a check-in. To do this, we use a FetchXML filter (which we have e.g. previously created in a contact view in CRM and then exported). It is important that we include the event ID dynamically (marked in bold). Here is my FetchXML:

<fetch version="1.0" output-format="xml-platform" mapping="logical" no-lock="false" distinct="true">
  <entity name="contact">
    <filter type="and">
      <condition attribute="statecode" operator="eq" value="0"/>
    </filter>
    <link-entity name="msevtmgt_eventregistration" alias="aa" link-type="inner" from="msevtmgt_contactid" to="contactid">
      <link-entity name="msevtmgt_event" alias="ab" link-type="inner" from="msevtmgt_eventid" to="msevtmgt_eventid">
        <filter type="and">
          <condition attribute="msevtmgt_eventid" operator="eq" value="@{outputs('Get_event')?['body/msevtmgt_eventid']}"/>
        </filter>
      </link-entity>
    </link-entity>
  </entity>
</fetch>

Activating the trigger

We then only need to activate the trigger for each contact found. We achieve this with the ‘Perform an unbound action’ action. We can find our custom trigger here under the logical name that was created in the background of Dynamics 365. The schema for this is ‘msdynmkt_[name of the trigger without spaces and lowercase]_id’. So if we search for ‘recording’, for example, we will be able to find the trigger.

Due to the added entity reference, this action has lots of optional event-related parameters. Only the following are actually important:

msevtmgt_event msevmgt_eventid@{outputs(‘Get_event’)?[‘body/msevtmgt_eventid’]}
msdynmkt_signalingestiontimestamp@{utcNow()}
msdynmkt_signaltimestamp@{utcNow()}
msdynmkt_signaluserauthid@{items(‘Apply_to_each_contact’)?[‘contactid’]}
msdynmkt_profileid@{items(‘Apply_to_each_contact’)?[‘contactid’]}

We’ve done it! With this flow, we have completed the last step in the automation. As soon as a link is stored in the event, the flow searches for all relevant contacts and triggers ‘New recording link created’ and the journey sends the recording link to the participants!


Stay informed

Never miss a new post and simply subscribe to the newsletter.

Leave a Comment

Your email address will not be published. Required fields are marked *