Now we’ve got the infrastructure sorted for the bot talking to Teams and getting it installed for users, we need to start letting it know when something interesting happens in D365 so the bot can send messages out to users. Enter the WebHook.
WebHooks
WebHooks are a simple way to register with D365 that some other web app is interested in an event. We can register a webhook similarly to a plugin step, so we can get D365 to send a message to our bot without writing any code.
I would normally register plugins and their steps using the Plugin Registration Tool in XrmToolBox, but it doesn’t currently support registering webhooks. Instead I had to download the official Microsoft one. Unfortunately, after installing this, I clicked “Register New Web Hook” and nothing happened. In fact, nothing happened when I clicked any of the buttons in the top ribbon. Eventually I downloaded an earlier version (9.1.0.12) from NuGet and that worked as expected.
This screen needs the URL that the details of the event are going to be sent to. In this case I’ve made up the /api/notification
endpoint within my bot domain. I haven’t written any code to actually handle this yet – that’ll come next.
The other part I need to specify is how D365 will authenticate with the endpoint. I’ve picked the simple option of WebhookKey – this will append a code
parameter to the query string and pass in the value I specify. In my code that receives the notification I can check that and reject any requests that don’t include the right value.
WebHook Steps
Now I’ve registered the webhook I can use the Register New Step option to add a step to the webhook. This is the registration step to get CDS to trigger the webhook in response to a particular event.
With this step registered, my bot will get a notification whenever someone writes a post on the timeline of a record. I’ll also repeat this for the postcomment
entity to get notifications of any replies.
That’s all I need to do in D365 / CDS – no code needed!
Bot Endpoint
CDS is now going to send some details to my /api/notification
endpoint when someone writes a post. Now I need to get my bot to handle it.
The sample code I based my bot on uses ASP.NET Core MVC, so I can easily add a handler for this endpoint as:
[Route("api/notification")] [ApiController] public class NotificationController : ControllerBase { [HttpPost] public async Task<IActionResult> PostAsync([FromQuery] string code, [FromBody] JObject requestContext) { if (code != "markcarrington.dev.notifications") return Unauthorized(); // TODO: Process notification return Ok(); } }
A few things worth pointing out from this code:
- the
code
parameter will be loaded from the query string. This should match the WebhookKey authentication value that we put into the webhook registration earlier. If we don’t get the expected value we return a401 Unauthorized
result. The documentation isn’t explicit on what error should be returned, only that the request should fail, but 401 seems a good fit. - the
requestContext
parameter is loaded from the body as aJObject
rather than being deserialized to theRemoteExecutionContext
object it represents. This is described as a best practise in the documentation. If you’re building your bot on ASP.NET Core 3.x you’ll need to install theMicrosoft.AspNetCore.Mvc.NewtonsoftJson
package and include
services.AddMvc().AddNewtonsoftJson()
That’s hopefully the last bit of wiring I need to do, and next time I’ll be able to start using this webhook to push out the proactive Teams messages we’ve already done the groundwork for.