Welcome to the fourteenth post in my series focused on providing a set of revision notes for the MB-400: Microsoft Power Apps + Dynamics 365 Developer exam. Last time around, we explored some of the capabilities offered by the Dynamics 365 Web API, that can be particularly useful when you are building integrations targeting Dynamics 365 / the Common Data Service. This post finished up our discussion on the Extend the platform area of the exam, which has a 15-20% total weighting. We now move into the final exam area, Develop Integrations and our first subject area concerning events:

Publish and consume events

  • publish an event by using the API
  • publish an event by using the Plug-in Registration Tool
  • register a webhook
  • create an Azure event listener application

Although this area of the exam has a somewhat low weighting (10-15%) when compared with the other subjects we’ve looked at, I would urge longstanding Dynamics CRM on-premise developers to pay particular attention here. Events are just one way in which you can integrate your Dynamics 365 Online / Common Data Service instance alongside Microsoft Azure. Developing modern, cloud applications involving core Microsoft products invariably means you must have a general awareness of the capabilities within Azure, so this exam area provides an excellent opportunity to increase your knowledge in this area as well. Let’s dive in now to see what events are all about and how they relate to Azure!

As with all posts in this series, the aim is to provide a broad outline of the core areas to keep in mind when tackling the exam, linked to appropriate resources for more focused study. Your revision should, ideally, involve a high degree of hands-on testing and familiarity in working with the platform if you want to do well in this exam. It would help if you also have some understanding working with plug-ins and the Plug-in Registration Tool as well, which we’ve covered already in the series.

What are Events?

You may be worried at this stage that events are a whole new concept that will take considerable time to understand. Fortunately, that’s not the case at all and, if you have good familiarity working with the IExecutionContext Interface from within a plug-in, you will find yourself right at home. Events encapsulate all of the data points that we would typically work with in our execution context – whether that be shared variables, output parameters, Business Unit ID or more. An example of how an event can look, when passed out as a JSON object, can be seen below:

{
	"BusinessUnitId": "f64c9d1f-d076-ea11-a812-000d3a86a586",
	"CorrelationId": "dfe79039-5a08-4d0a-b4ee-9534625c8654",
	"Depth": 1,
	"InitiatingUserAzureActiveDirectoryObjectId": "00000000-0000-0000-0000-000000000000",
	"InitiatingUserId": "c164a5aa-765e-4181-8771-537e8b1ebf3b",
	"InputParameters": [
		{
			"key": "Target",
			"value": {
				"__type": "Entity:http://schemas.microsoft.com/xrm/2011/Contracts",
				"Attributes": [
					{
						"key": "territorycode",
						"value": {
							"__type": "OptionSetValue:http://schemas.microsoft.com/xrm/2011/Contracts",
							"Value": 1
						}
					},
					{
						"key": "address2_freighttermscode",
						"value": {
							"__type": "OptionSetValue:http://schemas.microsoft.com/xrm/2011/Contracts",
							"Value": 1
						}
					},
					{
						"key": "address2_shippingmethodcode",
						"value": {
							"__type": "OptionSetValue:http://schemas.microsoft.com/xrm/2011/Contracts",
							"Value": 1
						}
					},
					{
						"key": "isprivate",
						"value": false
					},
					{
						"key": "followemail",
						"value": true
					},
					{
						"key": "donotbulkemail",
						"value": false
					},
					{
						"key": "donotsendmm",
						"value": false
					},
					{
						"key": "emailaddress1",
						"value": "[email protected]"
					},
					{
						"key": "jobtitle",
						"value": "Manager"
					},
					{
						"key": "customertypecode",
						"value": {
							"__type": "OptionSetValue:http://schemas.microsoft.com/xrm/2011/Contracts",
							"Value": 1
						}
					},
					{
						"key": "fullname",
						"value": "John Doe"
					},
					{
						"key": "isautocreate",
						"value": false
					},
					{
						"key": "ownerid",
						"value": {
							"__type": "EntityReference:http://schemas.microsoft.com/xrm/2011/Contracts",
							"Id": "c164a5aa-765e-4181-8771-537e8b1ebf3b",
							"KeyAttributes": [],
							"LogicalName": "systemuser",
							"Name": null,
							"RowVersion": null
						}
					},
					{
						"key": "isbackofficecustomer",
						"value": false
					},
					{
						"key": "donotbulkpostalmail",
						"value": false
					},
					{
						"key": "donotpostalmail",
						"value": false
					},
					{
						"key": "donotemail",
						"value": false
					},
					{
						"key": "statecode",
						"value": {
							"__type": "OptionSetValue:http://schemas.microsoft.com/xrm/2011/Contracts",
							"Value": 0
						}
					},
					{
						"key": "address2_addresstypecode",
						"value": {
							"__type": "OptionSetValue:http://schemas.microsoft.com/xrm/2011/Contracts",
							"Value": 1
						}
					},
					{
						"key": "donotphone",
						"value": false
					},
					{
						"key": "createdon",
						"value": "/Date(1598771723000)/"
					},
					{
						"key": "transactioncurrencyid",
						"value": {
							"__type": "EntityReference:http://schemas.microsoft.com/xrm/2011/Contracts",
							"Id": "785af3f0-d976-ea11-a812-000d3a86a586",
							"KeyAttributes": [],
							"LogicalName": "transactioncurrency",
							"Name": null,
							"RowVersion": null
						}
					},
					{
						"key": "contactid",
						"value": "a2b1d88b-90ea-ea11-a815-000d3a86a3ce"
					},
					{
						"key": "haschildrencode",
						"value": {
							"__type": "OptionSetValue:http://schemas.microsoft.com/xrm/2011/Contracts",
							"Value": 1
						}
					},
					{
						"key": "modifiedby",
						"value": {
							"__type": "EntityReference:http://schemas.microsoft.com/xrm/2011/Contracts",
							"Id": "c164a5aa-765e-4181-8771-537e8b1ebf3b",
							"KeyAttributes": [],
							"LogicalName": "systemuser",
							"Name": null,
							"RowVersion": null
						}
					},
					{
						"key": "leadsourcecode",
						"value": {
							"__type": "OptionSetValue:http://schemas.microsoft.com/xrm/2011/Contracts",
							"Value": 1
						}
					},
					{
						"key": "statuscode",
						"value": {
							"__type": "OptionSetValue:http://schemas.microsoft.com/xrm/2011/Contracts",
							"Value": 1
						}
					},
					{
						"key": "modifiedonbehalfby",
						"value": null
					},
					{
						"key": "preferredcontactmethodcode",
						"value": {
							"__type": "OptionSetValue:http://schemas.microsoft.com/xrm/2011/Contracts",
							"Value": 1
						}
					},
					{
						"key": "lastname",
						"value": "Doe"
					},
					{
						"key": "firstname",
						"value": "John"
					},
					{
						"key": "createdby",
						"value": {
							"__type": "EntityReference:http://schemas.microsoft.com/xrm/2011/Contracts",
							"Id": "c164a5aa-765e-4181-8771-537e8b1ebf3b",
							"KeyAttributes": [],
							"LogicalName": "systemuser",
							"Name": null,
							"RowVersion": null
						}
					},
					{
						"key": "educationcode",
						"value": {
							"__type": "OptionSetValue:http://schemas.microsoft.com/xrm/2011/Contracts",
							"Value": 1
						}
					},
					{
						"key": "yomifullname",
						"value": "John Doe"
					},
					{
						"key": "donotfax",
						"value": false
					},
					{
						"key": "merged",
						"value": false
					},
					{
						"key": "customersizecode",
						"value": {
							"__type": "OptionSetValue:http://schemas.microsoft.com/xrm/2011/Contracts",
							"Value": 1
						}
					},
					{
						"key": "marketingonly",
						"value": false
					},
					{
						"key": "owningbusinessunit",
						"value": {
							"__type": "EntityReference:http://schemas.microsoft.com/xrm/2011/Contracts",
							"Id": "f64c9d1f-d076-ea11-a812-000d3a86a586",
							"KeyAttributes": [],
							"LogicalName": "businessunit",
							"Name": null,
							"RowVersion": null
						}
					},
					{
						"key": "shippingmethodcode",
						"value": {
							"__type": "OptionSetValue:http://schemas.microsoft.com/xrm/2011/Contracts",
							"Value": 1
						}
					},
					{
						"key": "creditonhold",
						"value": false
					},
					{
						"key": "modifiedon",
						"value": "/Date(1598771723000)/"
					},
					{
						"key": "participatesinworkflow",
						"value": false
					},
					{
						"key": "preferredappointmenttimecode",
						"value": {
							"__type": "OptionSetValue:http://schemas.microsoft.com/xrm/2011/Contracts",
							"Value": 1
						}
					},
					{
						"key": "exchangerate",
						"value": 1.0
					}
				],
				"EntityState": null,
				"FormattedValues": [
					{
						"key": "territorycode",
						"value": "Default Value"
					},
					{
						"key": "address2_freighttermscode",
						"value": "Default Value"
					},
					{
						"key": "address2_shippingmethodcode",
						"value": "Default Value"
					},
					{
						"key": "isprivate",
						"value": "No"
					},
					{
						"key": "followemail",
						"value": "Allow"
					},
					{
						"key": "donotbulkemail",
						"value": "Allow"
					},
					{
						"key": "donotsendmm",
						"value": "Send"
					},
					{
						"key": "customertypecode",
						"value": "Default Value"
					},
					{
						"key": "isautocreate",
						"value": "No"
					},
					{
						"key": "isbackofficecustomer",
						"value": "No"
					},
					{
						"key": "donotbulkpostalmail",
						"value": "No"
					},
					{
						"key": "donotpostalmail",
						"value": "Allow"
					},
					{
						"key": "donotemail",
						"value": "Allow"
					},
					{
						"key": "statecode",
						"value": "Active"
					},
					{
						"key": "address2_addresstypecode",
						"value": "Default Value"
					},
					{
						"key": "donotphone",
						"value": "Allow"
					},
					{
						"key": "createdon",
						"value": "2020-08-30T07:15:23-00:00"
					},
					{
						"key": "haschildrencode",
						"value": "Default Value"
					},
					{
						"key": "leadsourcecode",
						"value": "Default Value"
					},
					{
						"key": "statuscode",
						"value": "Active"
					},
					{
						"key": "preferredcontactmethodcode",
						"value": "Any"
					},
					{
						"key": "educationcode",
						"value": "Default Value"
					},
					{
						"key": "donotfax",
						"value": "Allow"
					},
					{
						"key": "merged",
						"value": "No"
					},
					{
						"key": "customersizecode",
						"value": "Default Value"
					},
					{
						"key": "marketingonly",
						"value": "No"
					},
					{
						"key": "shippingmethodcode",
						"value": "Default Value"
					},
					{
						"key": "creditonhold",
						"value": "No"
					},
					{
						"key": "modifiedon",
						"value": "2020-08-30T07:15:23-00:00"
					},
					{
						"key": "participatesinworkflow",
						"value": "No"
					},
					{
						"key": "preferredappointmenttimecode",
						"value": "Morning"
					}
				],
				"Id": "a2b1d88b-90ea-ea11-a815-000d3a86a3ce",
				"KeyAttributes": [],
				"LogicalName": "contact",
				"RelatedEntities": [],
				"RowVersion": "4198516"
			}
		}
	],
	"IsExecutingOffline": false,
	"IsInTransaction": false,
	"IsOfflinePlayback": false,
	"IsolationMode": 1,
	"MessageName": "Create",
	"Mode": 1,
	"OperationCreatedOn": "/Date(1598771723000+0000)/",
	"OperationId": "b1b1d88b-90ea-ea11-a815-000d3a86a3ce",
	"OrganizationId": "9c5d5db0-47c7-4741-aa25-08eb4cdf59a3",
	"OrganizationName": "orgad623a9e",
	"OutputParameters": [
		{
			"key": "id",
			"value": "a2b1d88b-90ea-ea11-a815-000d3a86a3ce"
		}
	],
	"OwningExtension": {
		"Id": "aa45df79-90ea-ea11-a815-000d3a86a3ce",
		"KeyAttributes": [],
		"LogicalName": "sdkmessageprocessingstep",
		"Name": null,
		"RowVersion": null
	},
	"ParentContext": {
		"BusinessUnitId": "f64c9d1f-d076-ea11-a812-000d3a86a586",
		"CorrelationId": "dfe79039-5a08-4d0a-b4ee-9534625c8654",
		"Depth": 1,
		"InitiatingUserAzureActiveDirectoryObjectId": "00000000-0000-0000-0000-000000000000",
		"InitiatingUserId": "c164a5aa-765e-4181-8771-537e8b1ebf3b",
		"InputParameters": [
			{
				"key": "Target",
				"value": {
					"__type": "Entity:http://schemas.microsoft.com/xrm/2011/Contracts",
					"Attributes": [
						{
							"key": "emailaddress1",
							"value": "[email protected]"
						},
						{
							"key": "jobtitle",
							"value": "Manager"
						},
						{
							"key": "lastname",
							"value": "Doe"
						},
						{
							"key": "firstname",
							"value": "John"
						},
						{
							"key": "creditonhold",
							"value": false
						},
						{
							"key": "donotpostalmail",
							"value": false
						},
						{
							"key": "donotfax",
							"value": false
						},
						{
							"key": "donotphone",
							"value": false
						},
						{
							"key": "donotbulkemail",
							"value": false
						},
						{
							"key": "followemail",
							"value": true
						},
						{
							"key": "donotemail",
							"value": false
						},
						{
							"key": "preferredcontactmethodcode",
							"value": {
								"__type": "OptionSetValue:http://schemas.microsoft.com/xrm/2011/Contracts",
								"Value": 1
							}
						},
						{
							"key": "statuscode",
							"value": {
								"__type": "OptionSetValue:http://schemas.microsoft.com/xrm/2011/Contracts",
								"Value": 1
							}
						},
						{
							"key": "donotbulkpostalmail",
							"value": false
						},
						{
							"key": "transactioncurrencyid",
							"value": {
								"__type": "EntityReference:http://schemas.microsoft.com/xrm/2011/Contracts",
								"Id": "785af3f0-d976-ea11-a812-000d3a86a586",
								"KeyAttributes": [],
								"LogicalName": "transactioncurrency",
								"Name": null,
								"RowVersion": null
							}
						},
						{
							"key": "ownerid",
							"value": {
								"__type": "EntityReference:http://schemas.microsoft.com/xrm/2011/Contracts",
								"Id": "c164a5aa-765e-4181-8771-537e8b1ebf3b",
								"KeyAttributes": [],
								"LogicalName": "systemuser",
								"Name": null,
								"RowVersion": null
							}
						},
						{
							"key": "preferredappointmenttimecode",
							"value": {
								"__type": "OptionSetValue:http://schemas.microsoft.com/xrm/2011/Contracts",
								"Value": 1
							}
						},
						{
							"key": "customersizecode",
							"value": {
								"__type": "OptionSetValue:http://schemas.microsoft.com/xrm/2011/Contracts",
								"Value": 1
							}
						},
						{
							"key": "address2_shippingmethodcode",
							"value": {
								"__type": "OptionSetValue:http://schemas.microsoft.com/xrm/2011/Contracts",
								"Value": 1
							}
						},
						{
							"key": "address2_freighttermscode",
							"value": {
								"__type": "OptionSetValue:http://schemas.microsoft.com/xrm/2011/Contracts",
								"Value": 1
							}
						},
						{
							"key": "educationcode",
							"value": {
								"__type": "OptionSetValue:http://schemas.microsoft.com/xrm/2011/Contracts",
								"Value": 1
							}
						},
						{
							"key": "isautocreate",
							"value": false
						},
						{
							"key": "leadsourcecode",
							"value": {
								"__type": "OptionSetValue:http://schemas.microsoft.com/xrm/2011/Contracts",
								"Value": 1
							}
						},
						{
							"key": "shippingmethodcode",
							"value": {
								"__type": "OptionSetValue:http://schemas.microsoft.com/xrm/2011/Contracts",
								"Value": 1
							}
						},
						{
							"key": "participatesinworkflow",
							"value": false
						},
						{
							"key": "marketingonly",
							"value": false
						},
						{
							"key": "territorycode",
							"value": {
								"__type": "OptionSetValue:http://schemas.microsoft.com/xrm/2011/Contracts",
								"Value": 1
							}
						},
						{
							"key": "isprivate",
							"value": false
						},
						{
							"key": "isbackofficecustomer",
							"value": false
						},
						{
							"key": "merged",
							"value": false
						},
						{
							"key": "donotsendmm",
							"value": false
						},
						{
							"key": "address2_addresstypecode",
							"value": {
								"__type": "OptionSetValue:http://schemas.microsoft.com/xrm/2011/Contracts",
								"Value": 1
							}
						},
						{
							"key": "customertypecode",
							"value": {
								"__type": "OptionSetValue:http://schemas.microsoft.com/xrm/2011/Contracts",
								"Value": 1
							}
						},
						{
							"key": "haschildrencode",
							"value": {
								"__type": "OptionSetValue:http://schemas.microsoft.com/xrm/2011/Contracts",
								"Value": 1
							}
						},
						{
							"key": "contactid",
							"value": "a2b1d88b-90ea-ea11-a815-000d3a86a3ce"
						}
					],
					"EntityState": null,
					"FormattedValues": [],
					"Id": "a2b1d88b-90ea-ea11-a815-000d3a86a3ce",
					"KeyAttributes": [],
					"LogicalName": "contact",
					"RelatedEntities": [],
					"RowVersion": null
				}
			},
			{
				"key": "SuppressDuplicateDetection",
				"value": false
			}
		],
		"IsExecutingOffline": false,
		"IsInTransaction": false,
		"IsOfflinePlayback": false,
		"IsolationMode": 1,
		"MessageName": "Create",
		"Mode": 1,
		"OperationCreatedOn": "/Date(1598771723000+0000)/",
		"OperationId": "b1b1d88b-90ea-ea11-a815-000d3a86a3ce",
		"OrganizationId": "9c5d5db0-47c7-4741-aa25-08eb4cdf59a3",
		"OrganizationName": "orgad623a9e",
		"OutputParameters": [],
		"OwningExtension": {
			"Id": "aa45df79-90ea-ea11-a815-000d3a86a3ce",
			"KeyAttributes": [],
			"LogicalName": "sdkmessageprocessingstep",
			"Name": null,
			"RowVersion": null
		},
		"ParentContext": null,
		"PostEntityImages": [],
		"PreEntityImages": [],
		"PrimaryEntityId": "a2b1d88b-90ea-ea11-a815-000d3a86a3ce",
		"PrimaryEntityName": "contact",
		"RequestId": "bda24fe5-1f23-4c96-a4ab-34739a2f5628",
		"SecondaryEntityName": "none",
		"SharedVariables": [
			{
				"key": "IsAutoTransact",
				"value": true
			},
			{
				"key": "DefaultsAddedFlag",
				"value": true
			},
			{
				"key": "ChangedEntityTypes",
				"value": [
					{
						"__type": "KeyValuePairOfstringstring:#System.Collections.Generic",
						"key": "contact",
						"value": "Update"
					}
				]
			}
		],
		"Stage": 30,
		"UserAzureActiveDirectoryObjectId": "00000000-0000-0000-0000-000000000000",
		"UserId": "c164a5aa-765e-4181-8771-537e8b1ebf3b"
	},
	"PostEntityImages": [
		{
			"key": "AsynchronousStepPrimaryName",
			"value": {
				"Attributes": [
					{
						"key": "fullname",
						"value": "John Doe"
					},
					{
						"key": "contactid",
						"value": "a2b1d88b-90ea-ea11-a815-000d3a86a3ce"
					}
				],
				"EntityState": null,
				"FormattedValues": [],
				"Id": "a2b1d88b-90ea-ea11-a815-000d3a86a3ce",
				"KeyAttributes": [],
				"LogicalName": "contact",
				"RelatedEntities": [],
				"RowVersion": null
			}
		}
	],
	"PreEntityImages": [],
	"PrimaryEntityId": "a2b1d88b-90ea-ea11-a815-000d3a86a3ce",
	"PrimaryEntityName": "contact",
	"RequestId": "bda24fe5-1f23-4c96-a4ab-34739a2f5628",
	"SecondaryEntityName": "none",
	"SharedVariables": [
		{
			"key": "IsAutoTransact",
			"value": true
		},
		{
			"key": "DefaultsAddedFlag",
			"value": true
		}
	],
	"Stage": 40,
	"UserAzureActiveDirectoryObjectId": "00000000-0000-0000-0000-000000000000",
	"UserId": "c164a5aa-765e-4181-8771-537e8b1ebf3b"
}

The main difference with events is how we consume them; that is, outside of the application, as opposed to inside. There are a variety of reasons why it may be desirable to do this:

  • As we saw when discussing plug-ins, there are some particular limitations that sandbox processing can impose on your custom code – such as the restricted use of third party libraries and the 2-minute maximum execution time. By processing these operations external from Dynamics 365 / the Common Data Service, developers can circumvent these restrictions, while still benefitting from working with the same type of information typically available as part of a standard plug-in.
  • For situations where your instance is processing hundreds or even thousands of record changes each hour, that then need to be processed asynchronously, events provide the most streamlined mechanism for achieving this. Also, it helps to reduce the reliance on the platform and the applications Asynchronous service in performing complex processing of these requests; instead, they can be straightforwardly “passed on” to a dedicated service responsible for this.
  • It may be necessary to immediately trigger an external endpoint or API as soon as a user creates, updates or deletes a record. By using events alongside Webhooks (more on this shortly), developers have a streamlined mechanism to meet this requirement.
  • With the recent changes announced around API request and service protection limits at the platform level, developers may start to struggle when using the traditional plug-in assembly route to process complex operations. As such, we can realise benefits by moving this processing outside of the application and, as part of this, avoid hitting any nasty error messages resulting from an overage in the number of processed platform requests.

In most cases, you will typically use one of several different Microsoft Azure services when processing events received from the application. However, there are options available to integrate alongside other cloud platforms or systems. For the exam, focusing and having an awareness of the Azure options will be essential.

Understanding Service Endpoints & the Azure Service Bus

There are two core concepts to understand alongside events – service endpoints and the Azure Service Bus:

  • Service Endpoints: This defines the location where you wish to write your events out into, for further processing. In pretty much all cases, you will want to use the Plug-in Registration Tool to create your service endpoint, but you can also deploy one programmatically via the web API. You can also use a service endpoint to receive incoming requests back into Dynamics 365 for processing. Once defined, you must then register the appropriate steps that will trigger your endpoint request, much in the same way as defining a plug-in step (e.g. Update on Contact, Delete on Lead, etc.). An advantage with this is that we can include pre/post entity images as part of each event payload. The critical thing to remember, though, is that we must specify these as asynchronous operations; synchronous calls are not allowed. In most cases, your service endpoint will target an Azure Service Bus queue, but you can also configure an Azure Event Hub endpoint as well.
  • Azure Service Bus: Depending on the type of integration you are attempting to perform, the synchronous (i.e. all at once) processing of information may not be needed or desirable. This could be down to the single fact that the number of potential requests to process will be too high. For when this is relevant, Azure Service Bus comes into the equation, by offering a decoupled, asynchronous mechanism to receive, analyse and route multiple requests through to an intended destination – whether this is a database, another endpoint or a storage location. The service bus will process all events it receives in order, holding onto each request for as long as is necessary before handing it off. As well as allowing for a predictable flow of information, it can also provide assurances from a failure standpoint; if, for example, the backend endpoint goes offline for whatever reason, messages will remain in the queue and resume processing as soon as the endpoint comes back online. Developers have flexibility over the type of Azure Service Bus listener service to implement, which Microsoft outlines in this article, but the most common scenario is to use a queue. We can also define the format of events that get written out the Azure Service Bus listener but, in most cases, outputting this as JSON will be the recommended option.

Demo: Posting Dynamics 365 Events to Azure Service Bus

In this video, we’ll walk through how to deploy out an Azure Service Bus resource and configure a service endpoint to receive requests from Dynamics 365:

Webhooks Overview

A webhook is a lightweight mechanism for integrating multiple Web API’s. It operates on a publish and subscribe model; namely, we publish an event out, and an external endpoint subscribes to each event, processing it as it sees fit. As semi-automated, standard HTTP requests in their simplest form, they conform broadly to this pattern and – if they are a new concept – you should experience little difficulty in understanding them if you’ve previously worked with RESTful Web API’s. Webhooks are commonplace these days, both in the Microsoft world and across other vendors as well. For example, Azure supports the ability to write out any log alert event as a webhook to an endpoint of your choosing.

From a Dynamics 365 / Common Data Service perspective, developers can register custom webhooks to any endpoint of their choosing. We can handle authentication into these endpoints in one of four ways:

  • HttpHeader: Here, we must declare the appropriate header key/value pair values that will allow us to authenticate into the endpoint. HttpHeader is the option you will need to go for if you are using Basic or OAuth 2.0 authentication via an authorization bearer token.
  • WebhookKey: For this option, the platform will append a query string parameter called code onto the URL, whose value then allows you to authenticate into the API. If your endpoint is an Azure Function, then this is a possible candidate option for you to consider using, as all requests targeting your function will expect this by default.
  • HttpQueryString: Best suited for endpoints that support shared access signature (SAS) authentication, developers specify the appropriate key/value pairs for this, in the same manner as the HttpHeader option. The main difference here is that the platform will instead add these values onto the endpoint URL as query parameters.
  • Anonymous: Finally, it is possible to call any endpoint that does not enforce authentication. To do this, ensure you select the HttpHeader authentication type and supply a single key/value pair containing anything you like; otherwise, you may get errors when registering the Webhook.

Then, similar to working with service endpoints, we define the appropriate steps and Pre/Post images that will trigger the webhook call. The resulting operation will then initiate an HTTP POST request to your endpoint, containing the event data illustrated in the example earlier.

An important question arises around the usage of Webhooks in comparison to the Azure Service Bus. Certainly, Webhooks are the most attractive option to consider if you are integrating alongside non-Azure based services, your expected volume of API calls are low, or you need your request to execute synchronously. However, they will ultimately only be as reliable as the endpoint that you are contacting. Also, for high volume requests where we can tolerate a delay in processing, Webhooks will likely fall over pretty quickly. When this occurs, the Service Endpoint and Azure Service Bus option represent your optimal choice instead.

For more information on how to work with Webhooks, consult this Microsoft Docs article, which also provides some examples for you to work through.

Demo: Registring & Consuming a Dynamics 365 Webhook

To see an example of how to configure and mock-up a basic webhook, check out the below video, which uses the convenient Request Bin tool:

 

We’re on the home stretch now, with only one more topic to look at before we wrap up the series. Next time around, we’ll look at how you can enable some specific capabilities on your Dynamics 365 / Common Data Service entities to support data synchronisation to an Azure SQL Database.

Share This