Gareth Wilson Gareth Wilson

Guide to Microsoft SharePoint Webhooks: Features and Best Practices

Published


Microsoft SharePoint webhooks notify your systems when items change in a list or document library. If you're building on SharePoint, webhooks are how you learn a list changed without polling, though you then call the API to find out what changed.

This guide covers how SharePoint webhooks work, the validation handshake (there's no signature), the thin notifications and the GetChanges pattern, subscription renewal, and the best practices for production.

What are Microsoft SharePoint webhooks?

SharePoint list webhooks send an HTTP POST to a notificationUrl you register per list. There's no HMAC or request signature. Authenticity rests on two things: a mandatory validationtoken echo when the subscription is created, and an optional clientState string echoed in every notification.

Notifications are thin, they tell you a list changed but carry no change details, so you call the list's GetChanges API with a stored change token to learn what happened.

Microsoft SharePoint webhook features

FeatureDetails
ConfigurationPOST /_api/web/lists('{list-guid}')/subscriptions
ValidationEcho the validationtoken query param as text/plain 200 within a few seconds
Per-message checkOptional clientState echoed in notifications (shared-secret sanity check)
SignatureNone
PayloadThin, batched under value[]: subscriptionId, clientState, resource (list GUID), ... no change detail
Learn what changedCall GetChanges with a stored change token
ExpiryMax 180 days; must be renewed via PATCH
Retries5x at 5-minute intervals on non-2xx
SDK@pnp/sp (manages subscriptions, not verification)

Common events

Notifications do not carry an event type. The underlying list events are:

EventFires when
ItemAddedA list item / document is added
ItemUpdatedA list item / document is updated
ItemDeletedA list item / document is deleted

Because the notification only says "this list changed", you determine the specific change by calling GetChanges.

Setting up Microsoft SharePoint webhooks

Create a subscription with POST /_api/web/lists('{list-guid}')/subscriptions, providing resource, notificationUrl, expirationDateTime (max 180 days out), and an optional clientState. SharePoint validates the endpoint with the handshake below before creating the subscription. Renew via PATCH before expiry, SharePoint doesn't track usage, so an un-renewed subscription simply lapses.

Securing Microsoft SharePoint webhooks

There's no signature, so verification is two steps.

The validation handshake

When a subscription is created (or its notificationUrl changes), SharePoint POSTs with a validationtoken query-string parameter. Echo that exact token back as an HTTP 200 text/plain body within a few seconds, or creation fails.

clientState on notifications

Set a clientState at subscription time; SharePoint echoes it in every notification's clientState field. Compare it to your stored value as a shared-secret sanity check.

const CLIENT_STATE = process.env.SP_CLIENT_STATE;

app.post("/webhook", express.json(), (req, res) => {
  // Validation handshake: echo the token as text/plain
  const validationToken = req.query.validationtoken;
  if (validationToken) {
    return res.status(200).type("text/plain").send(validationToken);
  }

  // Notifications arrive batched under value[]
  const notifications = (req.body && req.body.value) || [];
  for (const n of notifications) {
    if (CLIENT_STATE && n.clientState !== CLIENT_STATE) {
      return res.sendStatus(400); // not from your subscription
    }
  }

  res.sendStatus(200); // acknowledge fast (SharePoint retries 5x on non-2xx)
  for (const n of notifications) {
    // Enqueue: call GetChanges on n.resource with your stored change token
    processQueue.add(n);
  }
});

Because there's no cryptographic signature, set clientState, keep the notificationUrl secret, and use HTTPS.

Microsoft SharePoint webhook limitations and pain points

No signature, only clientState

The Problem: There's no HMAC to verify. The only per-message identity signal is the optional clientState. Without it, any POST to your URL looks legitimate.

Why It Happens: SharePoint's model is a validation handshake plus an optional shared-state string, not payload signing.

Workarounds:

  • Set clientState and check it, keep the URL secret, and use HTTPS.

How Hookdeck Can Help: Hookdeck gives you a dedicated ingestion URL and can enforce its own verification in front of your endpoint, adding a layer beyond clientState.

Thin notifications require GetChanges

The Problem: Notifications carry no change detail, just that a list changed. You must call GetChanges with a stored change token to learn what happened, and manage that token per list.

Why It Happens: SharePoint notifies that something changed and expects you to pull the delta.

Workarounds:

  • Store a change token per list, call GetChanges on notification, and advance the token.

How Hookdeck Can Help: Hookdeck durably queues each notification so your worker calls GetChanges at a controlled rate, without racing the 5-minute retry cadence.

Subscriptions expire at 180 days

The Problem: A subscription expires at its expirationDateTime (max 180 days) and must be renewed via PATCH. Miss the renewal and notifications stop silently.

Why It Happens: SharePoint time-boxes subscriptions and doesn't auto-renew.

Workarounds:

  • Track expirationDateTime and renew well before it, from a scheduled job.

How Hookdeck Can Help: Hookdeck's observability surfaces a drop in notification volume, so a lapsed subscription shows up as an alert rather than silent data loss.

The validation window and retries

The Problem: You must echo the validationtoken within a few seconds or creation fails, and notifications retry only 5x at 5-minute intervals, so a longer outage drops them.

Why It Happens: SharePoint validates quickly and retries sparingly.

Workarounds:

  • Echo the token immediately, acknowledge fast, and reconcile via GetChanges for anything missed.

How Hookdeck Can Help: Hookdeck can complete the handshake and durably queue notifications, so a downstream hiccup doesn't exhaust the small retry budget.

Best practices

Echo the validationtoken and check clientState

Return the validationtoken as text/plain 200 within a few seconds, and verify clientState on every notification.

Drive off GetChanges with a stored token

Treat notifications as a nudge; call GetChanges with a per-list change token to learn and process the actual changes.

Renew before 180 days

Track expirationDateTime and renew via PATCH ahead of expiry.

Acknowledge fast, process asynchronously

Return 200 quickly and defer GetChanges to a queue. See why to process webhooks asynchronously.

Make Microsoft SharePoint webhooks production-ready

Hookdeck completes the handshake, adds verification, and durably queues every list notification

Conclusion

SharePoint list webhooks have no signature: authenticity is the validationtoken handshake plus an optional clientState, and notifications are thin, so you call GetChanges with a stored change token to learn what changed. Set clientState, drive off GetChanges, renew before the 180-day expiry, and acknowledge fast.

Hookdeck completes the handshake, adds a verification layer SharePoint lacks, and durably queues every notification at the edge, so your app processes trustworthy events without losing any to a lapsed subscription.

Get started with Hookdeck for free and handle Microsoft SharePoint webhooks reliably in minutes.


Gareth Wilson

Gareth Wilson

Product Marketing

Multi-time founding marketer, Gareth is PMM at Hookdeck and author of the newsletter, Community Inc.