Create Form Submission and Event Registration Summaries in Dynamics 365 Marketing

Form submissions and event registrations capture important information that is often way harder to find than it should be. This post will show you how to create a flow which:

  • Creates an easy to read free text summary for each form submission and event registration

  • Creates a JSON summary that can be used to export form submissions and event registrations

NOTE: This solution is focused on Outbound Marketing Forms and Events. It is relatively easy to adjust to work with real-time marketing forms and events too (in the fullness of time).

I covered in a previous post how to export Marketing Form submissions, this is the magic behind the scenes to create the exportable JSON you need (sorry for putting the cart before the horse a little bit on that one). Future posts will show you how to put these summaries on the event registration/form submission records, create Lead/Contact timeline posts and export event registrations too!

Say Yes to storing form submissions

First things first before we go anywhere else - your marketing forms must be set to’ Store form submission’ Yes. You can update this on the marketing form Summary tab, on a per form basis. You can also define it to be Yes by default in the Landing Page settings which I highly recommend you do!

Per marketing form setting ‘Store form submission’

Landing pages setting ‘Store all form submissions’

Why combine Form Submissions and Event Registrations?

There is a lot of crossover between Marketing Forms and Events because events can be created using a Marketing Form. If an event registration is created using a marketing form, the important information is stored in the marketing form rather that the registration, so we need to tie it all back together. To avoid rebuilding things multiple times, it’s easier to create a single flow that handles all possible scenarios (Trust me, I learnt this one the hard way).

One mighty child flow

We will first create a child flow which creates the summary for either a Marketing Form Submission or an Event Registration. then we can reuse this child flow when either a Marketing Form is Submitted or an Event Registration is created.

Manually trigger a flow

The good old ‘Flow button for mobile’ trigger strikes again. Two text inputs to start with please:

  • FormSubmissionGUID

  • EventRegistrationGUID

Variables

Create the following String variables

  • ResponseStringJSON

  • ResponseString

  • EntityDefinitionsLogicalName

  • AttributesLogicalName

Is there a Marketing Form submission to summarise?

First we need to check if a Marketing Form submission GUID was provided, or was ‘N/A’ provided instead (we will control this when we call the child flows later).

Control: Condition - HasMarketingFormSubmission
FormSubmissionGUID (From flow button inputs)
is not equal to
N/A

If yes - Get the Marketing Form Field Submissions

Each field on the marketing form creates a separate ‘marketing field submission’, so we need to collect and process each of these one by one depending on the field type.

Dataverse: List rows - GetMktFormFieldSubmissions
Table Name:
Marketing field submissions
Filter rows:
(_msdyncrm_formsubmissionid_value eq '[FormSubmissionGUID (From flow button inputs)]' and msdyncrm_fieldvalue ne null and _msdyncrm_marketingformfieldid_value ne null)
Expand Query:
msdyncrm_marketingformfieldid($select=msdyncrm_lookup_target,msdyncrm_marketingformfieldtype,msdyncrm_leadmapping,msdyncrm_contactmapping,msdyncrm_prototype_attribute,msdyncrm_prototype_entity)

Process each Field Submission by field type

Apply to Each - ForEachFieldResponse
Select an output from previous steps: outputs('GetMktFormFieldSubmissions')?['body/value']

Inside the Apply to Each action add the following

Switch - FieldType
On: items('ForEachFieldResponse')?['msdyncrm_marketingformfieldid/msdyncrm_marketingformfieldtype']

Create the following Cases:

  • 11 Lookup
    Equals: 11

  • 3 Option Set
    Equals: 3

I’m going to delegate to previous blog posts for each of these bits cause if not I might give up right here (building flows is way more fun than writing them up!). Come back here just after the append action and be sure to add the append for both the JSON and the Plain Text summary into your flow

Lookups

Start with the instructions here -> https://www.ameyholden.com/articles/dynamics-365-marketing-form-submissions-lookup-guids. Finish up the Switch case with two append actions as follows:

Option Sets

Complete steps 1-4 under the title ‘Option Set Labels Please’ in this blog -> https://www.ameyholden.com/articles/dynamics-365-marketing-form-submissions-option-set-choice. You will need to change the IF in the ‘TableLogical Name’ step to use ForEachFieldResponse rather than Apply._to_each. ‘Finish up each Switch case with two append actions as follows using the formula in Step 5 (of the linked blog above):

You can also reuse a similar pattern for multi select option sets if you use these in your forms too!

Default

This case happens if the field is not a special field that needs extra handling such as numbers and text, here we simply take the values and append to the strings in the required format. We use a replace statement when appending to the JSON string to remove any double quote characters that might break the JSON.

replace(coalesce(items('ForEachFieldResponse')?['msdyncrm_fieldvalue'], '"'), '"', '')

Reset the reusable variables

After the switch statement but inside the loop. ensure you reset the reusable variables back to null

  • EntityDefinitionsLogicalName

  • AttributesLogicalName

So that is the Marketing Form Field Submissions processed - next up events that do not use marketing forms!

Is there a Event Registration without a Marketing Form?

If an Event Registration is created without an associated marketing form submission, we will need to retrieves some basic contact details for the summary which are included as a minimum in the portal event registration form. We know its just an event without a form if the ‘FormSubmissionGUID’ in set to ‘N/A’ and the ‘EventRegistrationGUID’ is not ‘N/A’.

Control: Condition - EventRegOnly
FormSubmissionGUID (From flow button inputs) is equal to ‘N/A’
EventRegistrationGUID (From flow button inputs) is not equal to ‘N/A’

If yes - Get the Contact Details

Dataverse: Get a row by ID - GetEventRegContact
Table Name:
Event Registrations
Row ID:
EventRegistrationGUID (From flow button inputs)
Select columns:
msevtmgt_eventregistrationid
Expand Query: msevtmgt_ContactId($select=firstname,lastname,emailaddress1)

Append Contact Details

For both the plain text and JSON summary, you can add additional values here if you need to!

//AppendResponseStringJSONContactDetails
"First Name":"@{outputs('GetEventRegContact')?['body/msevtmgt_contactid/firstname']}","Last Name":"@{outputs('GetEventRegContact')?['body/msevtmgt_contactid/lastname']}","Email":"@{outputs('GetEventRegContact')?['body/msevtmgt_contactid/emailaddress1']}",

//AppendResponseStringContactDetails
Name: @{outputs('GetEventRegContact')?['body/msevtmgt_contactid/firstname']} @{outputs('GetEventRegContact')?['body/msevtmgt_contactid/lastname']}
Email: @{outputs('GetEventRegContact')?['body/msevtmgt_contactid/emailaddress1']}

So that is the Formless Event Registrations processed - next up event details for all registration methods!

Is there an Event Registration?

Regardless of how the event registration was created we need to include some event details and also any custom registration field responses.

Control: Condition - HasEventReg
EventRegistrationGUID (From flow button inputs)
is not equal to
N/A

If Yes - Get the Event Details

Dataverse: Get a row by ID - GetEventRegExpand
Table Name:
Event Registrations
Row ID:
EventRegistrationGUID (From flow button inputs)
Select columns:
msevtmgt_eventregistration
Expand Query: msevtmgt_EventId($select=msevtmgt_publiceventurl,msevtmgt_eventtype,msevtmgt_eventtimezone,msevtmgt_name,msevtmgt_eventtimezonename,msevtmgt_eventstartdate)

Append Event Details

For both the plain text and JSON summary, you can add additional values here if you need to!

// JSON
"Event":"@{outputs('GetEventRegExpand')?['body/msevtmgt_eventid/msevtmgt_name']}","Start Date":"@{outputs('GetEventRegExpand')?['body/msevtmgt_eventid/msevtmgt_eventstartdate@OData.Community.Display.V1.FormattedValue']}","URL":"@{outputs('GetEventRegExpand')?['body/msevtmgt_eventid/msevtmgt_publiceventurl']}",

//Plain Text
Event: @{outputs('GetEventRegExpand')?['body/msevtmgt_eventid/msevtmgt_name']}
Start Date: @{outputs('GetEventRegExpand')?['body/msevtmgt_eventid/msevtmgt_eventstartdate@OData.Community.Display.V1.FormattedValue']}
URL: @{outputs('GetEventRegExpand')?['body/msevtmgt_eventid/msevtmgt_publiceventurl']}

Collect the Custom Registration Responses

These are the fields which are capture on the vent registration but not necessarily mapped to the contact or lead table, they are notoriously difficult to work with and often contain important information such as Dietary requirements.

Dataverse: List rows - GetCustomRegResponses
Table Name:
Registration responses
Select columns:
msevtmgt_response,_msevtmgt_customregistrationfield_value,msevtmgt_registrationresponseid
Filter rows:
_msevtmgt_eventregistration_value eq '[EventRegistrationGUID (From flow button inputs)]'
Expand Query:
msevtmgt_customregistrationfield($select=msevtmgt_type,msevtmgt_text)

Append Custom Registration Responses

All the custom registration responses are stored as simple string values so its an easy one to loop through and append the plain text and JSON summaries. We use a replace statement when appending to the JSON string to remove any double quote characters that might break the JSON.

outputs('GetCustomRegResponses')?['body/value']

replace(coalesce(items('ForEachCustomFieldResponse')?['msevtmgt_response'], '"'), '"', '')

So that is the Event Registrations processed - one final step left!

Respond with the summaries

Outside of the condition above - the final step in the magical flow is to create a response action that outputs the complete Summary in plain text and JSON format. Insert the ‘Body’ as below.

{
  "SummaryString": @{variables('ResponseString')},
  "SummaryJSON": @{concat('{',if(equals(slice(variables('ResponseStringJSON'),-1),','),substring(variables('ResponseStringJSON'),0,sub(length(variables('ResponseStringJSON')),1)),variables('ResponseStringJSON')),'}')}
}

Define the schema

Select ‘Show advanced options’ and insert the schema below

{
    "type": "object",
    "properties": {
        "SummaryString": {
            "type": "string"
        },
        "SummaryJSON": {
            "type": "string"
        }
    }
}

You can test this flow by manually inputting the Marketing Form Submission and/or Event Registration IDs (or N/A) to test this out. If it looks something like this - you have absolutely nailed it!

To be continued…

In my next post I will show you how we can trigger this awesome magical child flow to create summaries on demand and also Lead/Contact timeline posts.

In case you got lost this high level summary might help!

Previous
Previous

Show Form Submission and Event Registration Summaries in Dynamics 365 Marketing

Next
Next

Dynamic inputs for Project and Survey IDs in Customer Voice Survey actions in Power Automate