Using Webhooks in Sitecore XM and XM Cloud

,

With Sitecore 10.3 came the possibility to use Webhook Event Handlers to call external APIs when a Sitecore event is triggered. The feature is available in both XM and XM Cloud and offers an easy and no-code way of integrating with external systems via HTTP POST requests. In this post I will present a hands-on example where I integrated my Sitecore solution with the IFTTT automation platform.

Let us start with the use case: Imagine that each time an editor saves an item in Sitecore, I want to receive an email notification. Normally this would require some custom code to be included in my Sitecore solution (or setting up a Workflow), but with a Webhook Event Handler I was able to set it up without any code (except for few lines of JavaScript) in less than half an hour and without any prior experience with Sitecore Webhooks!

Setting up the Webhook Event Handler in Sitecore

I started out by creating a new Webhook Event Handler in /sitecore/System/Webhooks.

I simply called my event handler Send email.

I made sure that the event handler was Enabled and was triggered by the item_saved event:

Finally, I added a rule so that my event handler is only triggered for items underneath the Home item:

Setting up an applet in IFTTT

As an external system I used the IFTTT platform. If you do not know IFTTT (short for If-This-Then-This) it is a simple automation service. It offers a lot of build-in services (including a many IoT integrations) that you can chain together into applets in the form of:

  • If some service is triggered.
  • Then some other service is triggered.

All of this can be set up via the IFTTT’s website and is really easy – even for a non-so-technical user.

In this case, the If is a IFTTT webhook API being called and the Then is the build-in Gmail service that I use to send an email.

In between the If service and the Then service I put a filter. This is a small piece of JavaScript that enable me to filter the JSON payload from the webhook service. While not strictly needed, this allowed me to format the content of the email.

On the IFTTT website, my complete applet looks like this:

If we look at each of the services, the If webhook was super simple to set up:

I simply defined an event name (sitecore_item_saved). To get the API endpoint to trigger this event, I navigated to https://ifttt.com/maker_webhooks and click on Documentation. You will find that your webhook endpoint includes the event name and an API key (here changed to 123):

This means that we can trigger the webhook from Sitecore by calling this endpoint:

Equally simple was the Then Gmail service:

Even with this simple setup I was able to start sending emails. But as you can see the subject would simply be “An item has been saved”, and the body of the email would be the complete JsonPayload (here shorted bit):

{
   "EventName":"item:saved",
   "Item":{
      "Language":"en",
      "Version":1,
      "Id":"110d559f-dea5-42ea-9c1c-8a5df7e70ef9",
      "Name":"Home",
      "ParentId":"0de95ae4-41ab-4d01-9eb0-67441b7c2450",
      "TemplateId":"76036f5e-cbce-46d1-af0a-4143f9b557aa",
      "MasterId":"00000000-0000-0000-0000-000000000000",
      "SharedFields":[
         {
            "Id":"06d5295c-ed2f-4a54-9bf2-26228d113318",
            "Value":"Network/16x16/home.png"
         }
      ],
      "UnversionedFields":[
         {
            "Id":"9541e67d-ce8c-4225-803d-33f7f29f09ef",
            "Value":"Welcome to Sitecore.",
            "Language":"en"
         }
      ],
      "VersionedFields":[
         {
            "Id":"75577384-3c97-45da-a847-81b00500e250",
            "Value":"Sitecore Experience Platform",
            "Version":1,
            "Language":"en"
         }
         {
            "Id":"badd9cf9-53e0-4d0c-bcc0-2d784c282f6a",
            "Value":"sitecore\\Admin",
            "Version":1,
            "Language":"en"
         }
      ]
   },
   "Changes":{
      "FieldChanges":[
         {
            "FieldId":"badd9cf9-53e0-4d0c-bcc0-2d784c282f6a",
            "Value":"sitecore\\Admin",
            "OriginalValue":"sitecore\\Admin"
         }
      ],
      "PropertyChanges":[
         
      ],
      "IsUnversionedFieldChanged":false,
      "IsSharedFieldChanged":false
   },
   "WebhookItemId":"0c939ba4-2e9f-49a9-a4c7-7a4c70e9ef55",
   "WebhookItemName":"Send email"
}

To be able to extract the item name, id and author from the payload, I added this filter in IFTTT:

With these simple steps, I was now able to receive mails when content was saved:

Some considerations

As you can see above, setting up a Webhook Event Handler is really easy. I have chosen to use IFTTT because of its simplicity, but the Webhook Event Handler could potentially call any external system. But if we go into the details, I see some of the challenges in consuming the JSON payload in more complex integrations:

The payload we receive from a item_saved event in IFTTT two properties: Item and the Changes (to see a list of properties included for each Sitecore event, check the documentation here).

These are created by two data mappers in Sitecore: ItemSerializableDataMapper and ItemChangesSerializableDataMapper – both located in the Sitecore.Data.DataMappers namespace. Both classes are internal – which is also true for pretty much everything related to Webhooks. So, we are probably not be expected to override any of this.

In the ItemSerializableDataMapper we see the list of properties included in the Item object:

data.Set("Language", (object) obj.Language.Name);
data.Set("Version", (object) obj.Version.Number);
data.Set("Id", (object) resourceItem.Id);
data.Set("Name", (object) resourceItem.Name);
data.Set("ParentId", (object) resourceItem.ParentId);
data.Set("TemplateId", (object) resourceItem.TemplateId);
data.Set("MasterId", (object) resourceItem.MasterId);
data.Set("SharedFields", (object) resourceItem.SharedFields);
data.Set("UnversionedFields", (object) resourceItem.UnversionedFields);
data.Set("VersionedFields", (object) resourceItem.VersionedFields);

And I see some limitations here: The data included in the JSON is very ‘Sitecore-centric’: Having an Item ID, a Version and a Language would – internally in a Sitecore solution context – certainly be enough to generate important additional information like full path and URL – information which we would almost certainly need for more complex integration scenarios. But in an external system receiving this information we do not have these capabilities.

So, if we want to extend our IFTTT applet to include the full path of the saved item we would need a way to extend the Item data. Had the data mappers not been internal, this would have been easy to do. Along the same line, it would be cleaner to be able to extend the ItemChangesSerializableDataMapper to include the field name (so that our IFTTT Applet would not need to have the Updated By field ID hardcoded – which again is very Sitecore-centric).

If we wanted to build a more complex external service that could e.g. push a published item to a crawler we would face similiar issues as the PublisherOptions property we get from the publish:end Sitecore event only contains the Root Item ID, and not the URL of the published item.

The point is that the content for the JSON payloads is somewhat Sitecore-centric in nature as the understanding of content depend on intricate knowledge of the Sitecore solution. The JSON payload is fundamentally really hard to consume by external systems in a non-Sitecore context.

My guess is that Sitecore is already addressing these challenges (or that my expectation that the JSON payload are meant to be consumed by external systems are simply wrong). But within a Sitecore eco-system and for simple scenarios the Webhook Event Handlers are easy and pretty powerful already.