When server-side event receivers couldn't follow us into SharePoint Online, remote event receivers (RERs) filled the gap. The idea is elegant: SharePoint calls your web service when something happens, passing the event details over the wire. In practice RERs have real limitations that pushed Microsoft — and the rest of us — towards webhooks. This article covers both, and when to choose which.

The remote event receiver model

An RER is a SOAP web service endpoint, hosted in your provider-hosted add-in, that implements IRemoteEventService. SharePoint invokes it for the same event types you knew from the server model — but across the network:

Implementing the service

The Visual Studio add-in template generates the WCF service and the RemoteEventReceiver1.svc. Your job is the ProcessEvent (synchronous) and ProcessOneWayEvent (asynchronous) methods:

public SPRemoteEventResult ProcessEvent(SPRemoteEventProperties properties)
{
    var result = new SPRemoteEventResult();

    if (properties.EventType == SPRemoteEventType.ItemAdding)
    {
        var value = properties.ItemEventProperties
                              .AfterProperties["ContractValue"];
        if (value != null && Convert.ToDecimal(value) < 0)
        {
            result.ErrorMessage = "Contract value cannot be negative.";
            result.Status = SPRemoteEventServiceStatus.CancelWithError;
        }
    }
    return result;
}

public void ProcessOneWayEvent(SPRemoteEventProperties properties)
{
    if (properties.EventType == SPRemoteEventType.ItemAdded)
    {
        using (var ctx = TokenHelper.CreateRemoteEventReceiverClientContext(properties))
        {
            if (ctx != null)
            {
                var item = properties.ItemEventProperties
                    .ListItem; // resolve via ctx as needed
                // post-commit work
                ctx.ExecuteQuery();
            }
        }
    }
}
TokenHelper.CreateRemoteEventReceiverClientContext builds an authenticated CSOM context from the token SharePoint includes in the event payload — that's how your remote code calls back into the site that raised the event.

Registering the receiver

RERs are declared in the add-in's AppManifest.xml (for app events) or attached to a list at runtime via CSOM, pointing at your service URL:

web.Lists.GetByTitle("Contracts").EventReceivers.Add(new EventReceiverDefinitionCreationInformation
{
    EventType = EventReceiverType.ItemAdded,
    ReceiverName = "ContractItemAdded",
    ReceiverUrl = "https://contoso-addin.azurewebsites.net/RemoteEventReceiver1.svc",
    SequenceNumber = 10000
});
ctx.ExecuteQuery();

Where RERs hurt

RERs work, but production taught the community several painful lessons:

Treat asynchronous RERs as best-effort. If your business process cannot tolerate a missed event, an RER alone is the wrong tool — you need a model with retries and a reconciliation sweep.

Enter webhooks

SharePoint webhooks address the reliability problem with a fundamentally different contract. Instead of sending you the event details, SharePoint sends a lightweight notification that "something changed in this list," and you call back to find out what — using the list's change log. Crucially, webhooks retry failed deliveries and your subscription has an explicit lifecycle.

Registering a webhook is a single REST call specifying the list, your notification URL, and an expiry (max 180 days):

POST .../_api/web/lists('list-guid')/subscriptions
Content-Type: application/json

{
  "resource": "https://contoso.sharepoint.com/_api/web/lists('list-guid')",
  "notificationUrl": "https://contoso-fn.azurewebsites.net/api/spwebhook",
  "expirationDateTime": "2018-03-11T00:00:00+00:00",
  "clientState": "contoso-secret"
}

Your endpoint must first answer the validation handshake: when you register, SharePoint immediately calls your URL with a validationtoken query parameter, and you must echo it back as plain text within 5 seconds. After that, change notifications arrive as small JSON payloads — and you respond fast (return HTTP 200 within seconds), queue the work, and process it asynchronously by calling GetChanges against the list.

Webhooks notify; they don't describe. Always pair a webhook with a stored change token so that when a notification arrives you call GetChanges from your last token and process exactly what changed — even if several edits batched into one notification.

Choosing between them

NeedBest fit
Cancel/validate before saveSynchronous RER (only option)
Reliable reaction after changeWebhook (retries + change log)
App install/uninstall setupApp-event RER
High-volume change processingWebhook + queue + Azure Function

I dive into the production-grade webhook architecture — subscription renewal, the storage queue, and idempotent change processing — in a dedicated later article. For now the takeaway is this: use synchronous RERs only where you truly must block a save, and reach for webhooks for everything reactive. They are the bridge from the add-in era to the Microsoft Graph change-notification model that follows.

DS

Dhaval Shah

Cloud Solution Architect — 18+ years across SharePoint, Microsoft 365, and Azure AI.