SharePoint Webhooks As Event Receivers For SharePoint Online

SharePoint Webhooks were made generally available in January 2017. It provides the mechanism of catching the Asynchronous Events like item added, item deleted etc., that happen in the SharePoint Objects. At present, SharePoint Webhooks cannot be used with Synchronous events like Adding Items, for which we will still have to rely on Remote Event Receiver solution using Provider Hosted Add-in. As of now, SharePoint webhooks are only enabled for SharePoint list items.

Working of SharePoint Webhooks

So as to implement a new SharePoint webhook, we will first have to create a service endpoint using Azure Function and then add it as a subscription to the SharePoint List. While creating the subscription, we will have to specify the below information so that the subscription can be registered with the list.

  • Resource - This would be the SharePoint List REST API URL
  • Server notification URL – This is the service endpoint URL which we will get after creating the Azure Function App. It is to this URL , SharePoint will send the notification and data payload when ever an event happens in the list.
  • Expiration date – It is the expiration date of our subscription which is by default 6 months. It cannot be more than that. 
  • Client State(Optional) – It is a string passed back to the client on all notifications. We can use this for validating notifications send from SharePoint.

Create Service Endpoint in Azure

We will make use of Azure Functions to create the Service Endpoint that will store the code logic which will be triggered in response to the event notification coming from SharePoint. We will be using the URL received after creating the Azure Function App to add as a new subscription to the List. 

To create the Azure Function App, search for ‘Function App’ in the Azure Portal and select it.

SharePoint Webhooks

Specify the App name and other details required for creating the Azure Function App.

SharePoint Webhooks

Once created, we can add a HTTP Trigger function as shown below.

SharePoint Webhooks

We will specify the Trigger Function Name and set the Authorization level to Anonymous.

SharePoint Webhooks

Upon creation, it will have a default code fragment which we will replace with our code.

SharePoint Webhooks

We will add the code at a later point. First, we need to make sure that the trigger will respond to some notification it receives. So, let’s go to the "Integrate" section and change the settings as shown below.

SharePoint Webhooks

In the run.csx file, from the test section add a new query parameter ‘validationtoken’ and set an arbitrary value.

SharePoint Webhooks

Upon running it if we get the below status in the logs we can make sure that the trigger function is setup for basic use.

SharePoint Webhooks

It will also give us a 200 OK status in the output window.

SharePoint Webhooks

After the testing get the Function URL from the run window.

SharePoint Webhooks

Copy the URL as shown below,

SharePoint Webhooks

To avoid unauthorized usage of our Azure Function we can specify a secret code which we can get from the Manage section to the end of the SharePoint webhook URL. In our case the SharePoint Webhook URL which we will use to attach to the SharePoint List will look like below:

https://sharepointwebhooks.azurewebsites.net/api/WebhookFunction?code=naSJbEvPNe5mNMVFlL4pehlkAV0HROZqnN6lKIfqrOfNwN 

SharePoint Webhooks

Add SPWebhook Subscription

Once we have got the web hook URL let's register it with the SharePoint List. In this demo we will be registering it with the list named SLA so that whenever a new item is added, we will have to inform the Service User about the new change in the List for him to process the action item before the SLA Date.

So as to add the subscription we will be making use of the below parameters

  • Resource - /_api/web/lists(‘SLA List GUID')/subscriptions
  • Server notification URL
  • Expiration date –  "2017-09-19T16:17:57+00:00"

We will be issuing a POST request with the above body. We can either use an Azure AD Application or a SharePoint Hosted Add-in to add the subscription.  As per the Microsoft Documentation, ensure that the below permission are assigned so the subscription can be added successfully,

If your application is a Microsoft Azure Active Directory (AD) application

ApplicationPermission
Office 365 SharePoint OnlineRead and write items and lists in all site collections.

If your application is a SharePoint add-in

You must grant the SharePoint add-in the following permission(s) or higher,

ScopePermission Rights
ListManage

Add SharePoint Webhook Subscription using SPWebhookAdmin

We can either create a new application to register the subscription from scratch as shown in this example. However, for this demo let’s not reinvent the wheel, we will make use of an awesome tool created by Josh that registers the SharePoint WebHook Subscription using SharePoint Framework. He has done an awesome work with SPWebHookAdmin.

You can download the entire source code from GitHub.  Lets download it to the desktop and run the SPFx solution to register SharePoint Webhook.

SharePoint Webhooks

We can download the repository from github

SharePoint Webhooks

Since this is a SharePoint Framework solution we will need to have the prerequisites required for SPFx setup,which is detailed here. Once the solution is downloaded , open Node JS command prompt and head over to the src folder location of the solution.  

SharePoint Webhooks

Build the code for the first time using the below commands as mentioned in Github.

SharePoint Webhooks

If we want to view the solution implementation we can use enter Code . to gulp so that the solution opens up in Visual Studio Code.

SharePoint Webhooks

Lets head over and run gulp serve so that the solution will be added to the local workbench.

SharePoint Webhooks

In our case we have to add the solution to SharePoint Online. We will do this by appending the below URL to our SharePoint Site Collection URL.

_layouts/15/workbench.aspx

SharePoint Webhooks

From the + Sign add the Webhook manager to the page.This will list out all the list in the site and the number of subscriptions associated with it.

SharePoint Webhooks

As we can see SLA list does not have any subscription. Add a new one clicking on the + Sign.

SharePoint Webhooks

Specify the Notification URL which is our Azure Function URL. Client State is optional as per the documentation, but this solution uses it for additional validations. You can add an arbitrary string here say: ValidationChecker. Set the Expiration date which should not exceed 6 months from the date of creation.

SharePoint Webhooks

Thus the new subscription has been added and we can see the count updated as below.

SharePoint Webhooks
On Clicking the list name we can see the newly added subscription.

SharePoint Webhooks

When the subscription was added to the SharePoint List, in the azure portal the recently created Azure Function was triggered and the we can see the validation token that was received from SharePoint in the Logs section.

SharePoint Webhooks

Add Client DLLs to Azure Function

In order to start working with the code implementation we will be using the SharePoint Client Dlls which has to be uploaded to the Azure Function App directory. Select the Azure Function App and from Platform Features, Select Advanced Tools(Kudu)

SharePoint Webhooks

Select PowerShell from Debug Console.

SharePoint Webhooks

Navigate through the folders Site -> WWWRoot->(Function Name)

SharePoint Webhooks

Create a new folder within the <Function Name> folder.

SharePoint Webhooks

Drag and drop the below client dlls from the desktop.

SharePoint Webhooks

Add the Webhook Functionality code

Once we have added the client Dlls , we can start working on the code logic that has to be triggered in response to the SharePoint Event notification.

The webhook code does the below steps,

  1. Reads the validation token and the payload content send from SharePoint
    1. foreach(var notification in notifications)  
    2.         {  
    3.             log.Info($"{notification.Resource}");  
    4.             CloudStorageAccount storageAccount = CloudStorageAccount.Parse("DefaultEndpointsProtocol=https;AccountName=spwebhookstorage;AccountKey=SVM4igqbl9/GotmaC4PGLI5nM7QCXzcJCgY+8zB1fG2D1aKgYOr+lnMv6QkDAoY1Kw6KSe/iCxM97vDu52leDw==");  
    5.                 
    6.             // Get queue... create if does not exist.  
    7.             CloudQueueClient queueClient = storageAccount.CreateCloudQueueClient();  
    8.             CloudQueue queue = queueClient.GetQueueReference("sharepointlistwebhookeventazuread");  
    9.             queue.CreateIfNotExists();  
    10.    
    11.             // add message to the queue  
    12.             string message = JsonConvert.SerializeObject(notification);  
    13.             log.Info($"Before adding a message to the queue. Message content: {message}");  
    14.             queue.AddMessage(new CloudQueueMessage(message));  
    15.             log.Info($"Message added :-)");  
    16.             listID = notification.Resource;  
    17.             subscriptionID=notification.SubscriptionId;  
    18.             
    19.               
    20.         }  
  2. Connect to SharePoint Online and create an object of the List where the Event occurred. The List ID is sent along with the SharePoint Payload. So we can make use of CSOM to create the list from this ID.
    1. SPClientContext.Credentials = spOnlineCredentials;   
    2. Web spWeb = SPClientContext.Site.RootWeb;   
    3. Guid listId = new Guid(listID);  
    4. List changedList = spWeb.Lists.GetById(listId);                
    5. SPClientContext.Load(changedList);  
    6. SPClientContext.ExecuteQuery();   
  3. Get the Changes that happened in the List – We can make use of the ChangeQuery CSOM Object to get the entire changes in the list. But in our case we need to get only the recent change. We can specify the time from which the changes has to be retrieved using the ChangeTokenStart. For this demo,to make things simpler, we are setting it to last one minute so that changes that happened in last one minute is retrieved.
    1. lastChangeToken = new ChangeToken();  
    2. lastChangeToken.StringValue = string.Format("1;3;{0};{1};-1", listID, DateTime.Now.AddMinutes(-1).ToUniversalTime().Ticks.ToString());   
    3. changeQuery.ChangeTokenStart = lastChangeToken;  

Note

However to get the very latest change we will have to set the lastChangeToken() to the time at which the webhook was last triggered which is explained in this example. It reads the last webhook run time which is stored in the database. This way we can get the list change that happened between the current webhook trigger and the previous trigger which will get us the latest list event that caused the webhook to be invoked.

  1. ChangeToken lastChangeToken = null;    
  2. lastChangeToken = new ChangeToken();    
  3. lastChangeToken.StringValue = string.Format("1;3;{0};{1};-1", listID, DateTime.Now.AddMinutes(-1).ToUniversalTime().Ticks.ToString());    
  4. ChangeQuery changeQuery = new ChangeQuery(falsetrue);    
  5. changeQuery.Item = true;    
  6. changeQuery.ChangeTokenStart = lastChangeToken;    
  7.                       
  8. var changes = changedList.GetChanges(changeQuery);    
  9. SPClientContext.Load(changes);    
  10.                              
  11. SPClientContext.ExecuteQuery();  
  1. Once the Changes are retrieved, it will contain the list item ID.We will create the list item based on the ID and send the mail to the Service User about the change in the SLA list so that he can work on the action item.
    1. foreach (Change change in changes)    
    2.                    {    
    3.                        if (change is ChangeItem)    
    4.                           {    
    5.                            ListItem changedListItem = changedList.GetItemById((change as ChangeItem).ItemId);     
    6.                            SPClientContext.Load(changedListItem);     
    7.                            SPClientContext.ExecuteQuery();    
    8.                                 
    9.                            string emailBody = "<b>New Change in the SLA List</b> </br><table style='border: 1px solid black;'>";    
    10.                            emailBody +=   "<table style='border: 1px solid black;'><tr style='color:green;'><td>ID :"+ (change as ChangeItem).ItemId +" -- </td><td><b> Action Item : "+ changedListItem["Title"]+"</b></td><td> -- SLA : "+ changedListItem["SLADate"]+"</td></tr>";    
    11.                            emailBody +=  "</table>";     
    12.      
    13.                            Microsoft.SharePoint.Client.Utilities.EmailProperties emailProperties = newMicrosoft.SharePoint.Client.Utilities.EmailProperties();    
    14.                            emailProperties.To = new string[] { "Priyaranjan@SharePointChronicle.com" };    
    15.                            emailProperties.Subject = "Attention : A new modification has occured in the SLA List";    
    16.                            emailProperties.Body = emailBody;    
    17.                            Microsoft.SharePoint.Client.Utilities.Utility.SendEmail(SPClientContext, emailProperties);    
    18.             
    19.                            SPClientContext.ExecuteQuery();    
    20.                                 log.Info($"A list item with ID : { (change as ChangeItem).ItemId} is {change.ChangeType.ToString() }" );    
    21.                           }    
    22.                    }     
SharePoint Webhooks

We can add additional logging to the code so that we can debug the code when it is run. For instance we are outputting the payload sent from SharePoint List on Event Notification as well as the list name and List ID that was added.

SharePoint Webhooks

Demo of SharePoint Webhooks

Lets see how the SharePoint Webhooks work. We will add a new item in SharePoint List which should ideally trigger the SharePoint Webhook as part of the Event Notification to Azure Function.  

SharePoint Webhooks

If we head over to the Azure Function App in the Azure Portal, we can see that the webhook function was triggered from the Monitor Section.

SharePoint Webhooks

We can also see the logs indicating a successful run of the Azure Function which has correctly outputted the payload sent from SharePoint List as well as code outputs.

SharePoint Webhooks

It has also given us the list where the event occurred, list id and the type of Event that happened in the list.

SharePoint Webhooks

As part of the code implementation, we have used SPUtility to send mail to the Service user with the details of the newly created list item so that he can work on the action item before its SLA.

SharePoint Webhooks

SharePoint Webhooks in Action

The video recording of the demo is shared.

Monitor and Manage SharePoint Webhook

Once we have created and tested the SharePoint webhook, we can monitor the events from the Monitor section of the Azure Function App.

SharePoint Webhooks

Similarly, if we don’t want the webhook function to be triggered, we can Disable the function from the Manage section.

SharePoint Webhooks

Once disabled, it will no longer respond to the SharePoint Webhook notification.

SharePoint Webhooks

If we want to completely remove the subscription association with the list as well, we can use the same SharePoint Framework solution. We can delete it using the delete button.

SharePoint Webhooks

Summary

Thus, we have seen the implementation of SharePoint webhooks and Azure Functions to simulate Event Receivers for SharePoint Online.