July 24, 2026

Microsoft Graph Webhooks - Simply Explained

Microsoft Graph Webhooks - Simply Explained
Microsoft Graph Webhooks - Simply Explained
M365 FM Podcast
Microsoft Graph Webhooks - Simply Explained

Welcome to another episode of Knowledge Nuggets with Mirko Peters. Today we're exploring Microsoft Graph Webhooks, one of the core building blocks for creating modern, event-driven Microsoft 365 applications. Almost every developer building with Microsoft 365 eventually faces the same challenge. How do you know when something changes? Whether you're waiting for a new user to be created in Microsoft Entra ID, a document to be uploaded to SharePoint, or an email to arrive in Outlook, the traditional solution has always been polling—repeatedly asking Microsoft Graph if anything has changed. The problem is that most of those requests return nothing, wasting API calls, increasing latency, and eventually leading to Microsoft Graph throttling. Microsoft Graph Webhooks solve this by completely changing the communication model. Instead of your application constantly asking Microsoft Graph for updates, Microsoft Graph automatically sends your application an HTTP request whenever something important happens. In this episode, we'll explore how Graph Webhooks work, why they're more efficient than polling, how subscriptions and validation fit together, and why combining webhooks with Delta Query creates one of the most reliable integration patterns available for Microsoft 365.

WHY POLLING DOESN'T SCALE
Before understanding webhooks, it's important to understand the limitations of polling. Imagine checking your mailbox every five minutes throughout the day. Most of the time, there's nothing inside. Yet you continue making the same trip over and over again. Traditional applications behave exactly the same way. They repeatedly send requests asking Microsoft Graph whether anything has changed. Most requests receive exactly the same response: nothing has happened. Although each request seems harmless, thousands of applications polling Microsoft Graph every few minutes quickly generate enormous amounts of unnecessary traffic. This creates several problems. Applications consume API quotas without receiving useful information, users experience delays because changes aren't detected until the next polling cycle, and Microsoft Graph eventually begins throttling applications that generate excessive traffic. Developers often refer to this as ending up in "Graph jail." Ironically, even aggressive polling still doesn't provide true real-time updates. If a user account is created one minute after the last request, the application may not discover the change for another four minutes. Microsoft Graph Webhooks eliminate this inefficiency entirely by notifying applications only when something actually changes.

WHAT ARE MICROSOFT GRAPH WEBHOOKS?
A webhook is simply an HTTP POST request sent automatically when an event occurs. Instead of asking Microsoft Graph for updates, your application waits for Microsoft Graph to contact it. A useful analogy is a doorbell. Without a doorbell, you repeatedly open your front door to check whether someone has arrived. With a doorbell, you simply wait until someone rings. Microsoft Graph Webhooks work exactly the same way. Your application provides Microsoft Graph with a secure HTTPS endpoint. Microsoft Graph continuously monitors the resources you've subscribed to. Whenever a relevant event occurs, Microsoft Graph immediately sends an HTTP POST request containing information about that event. Microsoft refers to these webhook events as Change Notifications, but the underlying concept remains the same. Whether monitoring Outlook mailboxes, SharePoint document libraries, Microsoft Teams messages, calendars, users, groups, or Microsoft Entra ID objects, Graph Webhooks allow applications to respond almost instantly without unnecessary polling.

HOW GRAPH WEBHOOKS WORK
The complete webhook process consists of three simple building blocks. Everything starts with a subscription. Your application tells Microsoft Graph exactly which resource it wants to monitor and where notifications should be delivered. Once the subscription has been created, Microsoft Graph begins monitoring the selected resource. Whenever a matching event occurs—such as a new user being created or a SharePoint document being updated—Microsoft Graph immediately generates a notification. That notification is delivered as an HTTP POST request to your application's endpoint. Your application receives the notification, acknowledges it, and performs whatever business logic is required. Although the overall architecture appears sophisticated, the actual workflow is remarkably simple. You subscribe. Microsoft Graph watches. Microsoft Graph notifies your application whenever something changes.

CREATING A SUBSCRIPTION
Every webhook begins with creating a subscription through Microsoft Graph. When creating the subscription, four pieces of information are required. The first is the notification URL—the secure HTTPS endpoint where Microsoft Graph should deliver notifications. The second is the resource being monitored. This could be users, groups, Outlook messages, SharePoint files, Teams chats, calendars, or many other Microsoft Graph resources. Next comes the change type, allowing applications to receive notifications only for newly created items, updates, deletions, or any combination of these events. Finally, every subscription includes an expiration date. Unlike permanent registrations, Microsoft Graph subscriptions automatically expire after a predefined period. Depending on the resource, this may range from only a few days to approximately thirty days. Applications can also provide a clientState value—a secret string included with every notification. This simple mechanism allows applications to verify that notifications genuinely originated from Microsoft Graph rather than an external source attempting to send fraudulent requests. Once the subscription is successfully created, Microsoft Graph begins monitoring the selected resource automatically.

THE VALIDATION HANDSHAKE
Before Microsoft Graph trusts an endpoint, it performs a verification process known as the validation handshake. Immediately after a subscription request is received, Microsoft Graph sends a validation request containing a unique validation token. Your endpoint must return that token as plain text within a short time window. A useful analogy is signing for a package delivery. Before leaving an important package, the courier confirms someone is actually available to receive it. Microsoft Graph performs exactly the same check before delivering notifications. If the endpoint successfully returns the validation token, Microsoft Graph activates the subscription. If validation fails, the subscription is never created. One detail that frequently causes problems is the response format. The validation token must be returned as plain text, not JSON or HTML, and the endpoint must support secure HTTPS communication. Although the validation process occurs only once during subscription creation, getting it right is essential because no notifications will ever arrive if validation fails.

PROCESSING NOTIFICATIONS SECURELY
Once validation succeeds, Microsoft Graph begins sending notifications whenever monitored resources change. Each notification arrives as a JSON payload containing information about the event, including the affected resource, the type of change, timestamps, and the clientState value originally supplied during subscription creation. The very first step should always be validating the clientState. If the received value doesn't match the expected secret, the notification should be rejected immediately. Applications should also respond extremely quickly. Microsoft Graph expects a successful HTTP response within approximately three seconds. If responses become consistently slow, Microsoft Graph begins delaying future notifications. If endpoint performance deteriorates further, notifications may eventually be dropped entirely. For this reason, production applications rarely perform business logic directly inside the webhook endpoint. Instead, the endpoint immediately validates the notification, returns a successful HTTP response, and places the event into a background queue for asynchronous processing. This design keeps webhook endpoints responsive while allowing complex processing to occur separately without risking notification delivery.

KEEPING SUBSCRIPTIONS ALIVE
One of the most commonly overlooked aspects of Graph Webhooks is subscription management. Subscriptions automatically expire. If they aren't renewed before reaching their expiration date, Microsoft Graph simply stops sending notifications. Renewing a subscription is straightforward. Applications send an update request extending the expiration time before the current subscription expires. Microsoft Graph also provides Lifecycle Notifications, allowing applications to receive advance warnings whenever subscriptions are approaching expiration or authentication tokens require renewal. Rather than discovering failures after notifications stop arriving, applications can proactively renew subscriptions and maintain uninterrupted operation. Proper subscription lifecycle management is therefore just as important as creating the original subscription itself.

WEBHOOKS AND DELTA QUERY: THE PERFECT COMBINATION
Although webhooks provide immediate awareness of changes, they intentionally contain only limited information. Their purpose is simply to inform your application that something happened. To retrieve complete details, Microsoft recommends combining webhooks with Delta Query. The workflow is elegant. A webhook arrives indicating that a resource has changed.

Become a supporter of this podcast: https://www.spreaker.com/podcast/m365-fm-modern-work-security-and-productivity-with-microsoft-365--6704921/support.

🚀 Want to be part of m365.fm?

Then stop just listening… and start showing up.

👉 Connect with me on LinkedIn and let’s make something happen:

  • 🎙️ Be a podcast guest and share your story
  • 🎧 Host your own episode (yes, seriously)
  • 💡 Pitch topics the community actually wants to hear
  • 🌍 Build your personal brand in the Microsoft 365 space

This isn’t just a podcast — it’s a platform for people who take action.

🔥 Most people wait. The best ones don’t.

👉 Connect with me on LinkedIn and send me a message:
"I want in"

Let’s build something awesome 👊

1
00:00:00,000 --> 00:00:04,880
Hello everyone and welcome to another episode of Microsoft Knowledge Nuggets here on M365.

2
00:00:04,880 --> 00:00:06,360
FM, I'm your host, Mirko Peters.

3
00:00:06,360 --> 00:00:10,760
Today's topic is one that almost everyone building with Microsoft 365 has heard of,

4
00:00:10,760 --> 00:00:14,160
but very few people can explain clearly what exactly are graph webhooks.

5
00:00:14,160 --> 00:00:16,960
Why should you care about them instead of doing things the old way?

6
00:00:16,960 --> 00:00:18,760
Imagine you're expecting an important letter.

7
00:00:18,760 --> 00:00:20,480
You walk to your mailbox. It's empty.

8
00:00:20,480 --> 00:00:23,040
You walk back. 10 minutes later, you check again.

9
00:00:23,040 --> 00:00:25,560
Still nothing. You do this all day wasting time and energy.

10
00:00:25,560 --> 00:00:26,520
That's polling.

11
00:00:26,520 --> 00:00:27,840
Now imagine a different world.

12
00:00:27,840 --> 00:00:30,480
The mailman rings your doorbell the moment a letter arrives.

13
00:00:30,480 --> 00:00:33,280
You don't check. You wait for the ring. That's a webhook.

14
00:00:33,280 --> 00:00:36,760
By the end of this episode, you'll understand what graph webhooks are.

15
00:00:36,760 --> 00:00:40,280
Why they replace constant checking and how the pieces fit together.

16
00:00:40,280 --> 00:00:42,800
The polling problem. Let's break this down.

17
00:00:42,800 --> 00:00:46,440
To understand why webhooks matter, you need to understand why polling hurts.

18
00:00:46,440 --> 00:00:48,320
Here's how most applications work today.

19
00:00:48,320 --> 00:00:49,680
Your app needs an answer.

20
00:00:49,680 --> 00:00:52,320
It asks Microsoft Graph, "Hey, any new users?"

21
00:00:52,320 --> 00:00:53,960
Graph responds, "Nope."

22
00:00:53,960 --> 00:00:55,880
So your app waits a bit then asks again.

23
00:00:55,880 --> 00:00:57,240
Any new users now?

24
00:00:57,240 --> 00:01:00,320
Still no. Over and over. This process is polling.

25
00:01:00,320 --> 00:01:03,400
Think of it like constantly calling a friend to ask if they have news.

26
00:01:03,400 --> 00:01:06,400
You dial, they pick up, you check, they say no, you hang up.

27
00:01:06,400 --> 00:01:08,320
Then you call again five minutes later.

28
00:01:08,320 --> 00:01:10,440
And again, you're burning their time and your own.

29
00:01:10,440 --> 00:01:11,560
You're probably annoying them.

30
00:01:11,560 --> 00:01:15,000
Now imagine thousands of apps doing this to Microsoft Graph at the same time.

31
00:01:15,000 --> 00:01:18,400
Every app polling every few minutes, asking the same questions,

32
00:01:18,400 --> 00:01:20,560
getting the same answers. That's a lot of noise.

33
00:01:20,560 --> 00:01:23,440
Microsoft has a name for when you poll too aggressively,

34
00:01:23,440 --> 00:01:26,800
throttling, or as some developers call it, "graph jail."

35
00:01:26,800 --> 00:01:28,880
When you send too many requests, Microsoft says,

36
00:01:28,880 --> 00:01:30,680
"Okay, you're done for a while."

37
00:01:30,680 --> 00:01:32,400
They limit how many calls your app makes,

38
00:01:32,400 --> 00:01:34,240
once your throttleed everything slows down.

39
00:01:34,240 --> 00:01:37,160
The legitimate request pile up, your app stops working properly.

40
00:01:37,160 --> 00:01:38,680
You have no idea when you'll be let out.

41
00:01:38,680 --> 00:01:40,400
How many requests are we talking about?

42
00:01:40,400 --> 00:01:43,240
Say your app pulls every five minutes for user changes.

43
00:01:43,240 --> 00:01:45,440
That works out to 12 requests per hour.

44
00:01:45,440 --> 00:01:47,600
Over a full day, that's 208 requests.

45
00:01:47,600 --> 00:01:48,920
And that's just for one resource.

46
00:01:48,920 --> 00:01:52,440
If you're also polling for groups, files, messages, and calendar events,

47
00:01:52,440 --> 00:01:55,320
you rack up thousands of unnecessary requests per day.

48
00:01:55,320 --> 00:01:56,440
Most of them return nothing.

49
00:01:56,440 --> 00:01:58,440
Zero changes, but you still made the call.

50
00:01:58,440 --> 00:02:01,080
And here's the thing, even if you poll every five minutes,

51
00:02:01,080 --> 00:02:03,640
you're still not getting real-time updates.

52
00:02:03,640 --> 00:02:06,000
A change could happen one minute after your last poll.

53
00:02:06,000 --> 00:02:08,040
You won't know about it for another four minutes.

54
00:02:08,040 --> 00:02:11,000
You're wasting resources and still missing the window.

55
00:02:11,000 --> 00:02:13,040
Now, contrast that with an event-driven approach.

56
00:02:13,040 --> 00:02:15,120
Instead of calling Microsoft Graph every few minutes,

57
00:02:15,120 --> 00:02:16,720
you wait for Microsoft Graph to call you.

58
00:02:16,720 --> 00:02:18,760
You set up a listener, when something changes,

59
00:02:18,760 --> 00:02:20,400
Graph sends you a notification.

60
00:02:20,400 --> 00:02:22,840
You don't dial. You pick up when the phone rings.

61
00:02:22,840 --> 00:02:24,280
What are web hooks?

62
00:02:24,280 --> 00:02:25,920
So what exactly is a web hook?

63
00:02:25,920 --> 00:02:27,400
Here's the simplest definition.

64
00:02:27,400 --> 00:02:31,360
A web hook is an HTTP request sent automatically when something happens.

65
00:02:31,360 --> 00:02:32,600
You don't ask for it.

66
00:02:32,600 --> 00:02:34,560
The server just pushes it to you.

67
00:02:34,560 --> 00:02:35,920
Think of it like a doorbell.

68
00:02:35,920 --> 00:02:38,000
You don't stand outside checking every few minutes.

69
00:02:38,000 --> 00:02:39,120
You install a doorbell.

70
00:02:39,120 --> 00:02:40,840
When someone arrives, they ring it.

71
00:02:40,840 --> 00:02:42,160
You hear the ring and answer.

72
00:02:42,160 --> 00:02:44,280
The doorbell does the watching for you.

73
00:02:44,280 --> 00:02:46,880
Web hooks are sometimes called reverse APIs.

74
00:02:46,880 --> 00:02:50,280
With a normal API, your app makes a request and the server responds.

75
00:02:50,280 --> 00:02:51,520
Your app is in control.

76
00:02:51,520 --> 00:02:53,840
With a web hook, the server sends the request to your app.

77
00:02:53,840 --> 00:02:55,120
The server is in control.

78
00:02:55,120 --> 00:02:57,320
It pushes data instead of waiting for you to ask.

79
00:02:57,320 --> 00:02:59,560
Now Microsoft Graph uses web hooks too,

80
00:02:59,560 --> 00:03:02,240
but Microsoft calls them change notifications.

81
00:03:02,240 --> 00:03:04,080
It's the same thing, just a different name.

82
00:03:04,080 --> 00:03:06,600
When something changes in Microsoft 365,

83
00:03:06,600 --> 00:03:10,240
Graph sends an HTTP post to a URL you provide.

84
00:03:10,240 --> 00:03:12,600
That post contains information about what changed.

85
00:03:12,600 --> 00:03:15,040
There are a lot of things you can get notified about.

86
00:03:15,040 --> 00:03:18,680
A new user created in your tenant, a file updated in SharePoint,

87
00:03:18,680 --> 00:03:21,880
a message sent in Teams, a calendar event that changes,

88
00:03:21,880 --> 00:03:23,880
or a group that gets a new member.

89
00:03:23,880 --> 00:03:25,880
All of these can trigger a notification.

90
00:03:25,880 --> 00:03:28,840
Here's a real example, say you run a company with a time tracking system.

91
00:03:28,840 --> 00:03:32,120
When a new employee joins, you want to create an account automatically.

92
00:03:32,120 --> 00:03:36,560
Without web hooks, you pull the user's endpoint every few minutes checking for new people.

93
00:03:36,560 --> 00:03:39,600
With web hooks, you subscribe to user creation events.

94
00:03:39,600 --> 00:03:42,440
When someone joins, Graph sends your app a notification.

95
00:03:42,440 --> 00:03:45,640
Your app receives it, validates it, and creates the account.

96
00:03:45,640 --> 00:03:48,880
Or within seconds, no polling, no wasted requests.

97
00:03:48,880 --> 00:03:52,640
Another example, you have a SharePoint document library where receipts get uploaded.

98
00:03:52,640 --> 00:03:55,080
Without web hooks, you pull the library constantly.

99
00:03:55,080 --> 00:03:57,880
With web hooks, Graph tells you the moment a file is added.

100
00:03:57,880 --> 00:04:01,960
Your app grabs it, checks if it's a receipt, and processes it automatically.

101
00:04:01,960 --> 00:04:03,520
That's the power of web hooks.

102
00:04:03,520 --> 00:04:06,320
Instead of your app constantly asking anything new,

103
00:04:06,320 --> 00:04:08,240
you let the system tell you when something happens.

104
00:04:08,240 --> 00:04:11,720
It's more efficient, more reliable, and much faster.

105
00:04:11,720 --> 00:04:13,520
How Microsoft Graph Webhooks work.

106
00:04:13,520 --> 00:04:16,080
Now you know what web hooks are and why polling is the problem.

107
00:04:16,080 --> 00:04:18,800
Let's look at how Microsoft Graph Webhooks actually work,

108
00:04:18,800 --> 00:04:20,920
because the flow is simpler than you might think.

109
00:04:20,920 --> 00:04:26,040
It comes down to three things, a subscription, a notification, and your end point.

110
00:04:26,040 --> 00:04:28,720
First, you create a subscription, you tell Microsoft Graph,

111
00:04:28,720 --> 00:04:31,360
"Hey, I want to know when this specific thing changes."

112
00:04:31,360 --> 00:04:34,720
You give Graph the details of what to watch and where to send the news.

113
00:04:34,720 --> 00:04:36,520
Think of it like signing up for an alert.

114
00:04:36,520 --> 00:04:39,520
You tell the system what you care about and you give it your contact info.

115
00:04:39,520 --> 00:04:41,520
Second, Microsoft Graph watches that resource.

116
00:04:41,520 --> 00:04:42,520
It monitors for changes.

117
00:04:42,520 --> 00:04:46,640
When something happens, like a user created, a file updated, or a message arrives,

118
00:04:46,640 --> 00:04:47,640
Graph notices.

119
00:04:47,640 --> 00:04:49,360
Third, Graph sends a notification.

120
00:04:49,360 --> 00:04:52,080
It files off an HTTP post to the URL you provided.

121
00:04:52,080 --> 00:04:54,920
That post contains information about what changed.

122
00:04:54,920 --> 00:04:57,720
Your end point receives it and your application processes it.

123
00:04:57,720 --> 00:04:59,160
So the high level flow is simple.

124
00:04:59,160 --> 00:05:00,160
You subscribe.

125
00:05:00,160 --> 00:05:01,160
Graph watches.

126
00:05:01,160 --> 00:05:03,080
Graph sends a post when something changes.

127
00:05:03,080 --> 00:05:05,560
Let's break down those three building blocks a bit more.

128
00:05:05,560 --> 00:05:08,000
The subscription is your request to be alerted.

129
00:05:08,000 --> 00:05:12,040
It's like saying, "I want to know whenever a new user is added to my tenant."

130
00:05:12,040 --> 00:05:14,680
You send that request to Microsoft Graph and Graph stores it.

131
00:05:14,680 --> 00:05:17,360
From that point on, Graph is watching on your behalf.

132
00:05:17,360 --> 00:05:21,680
The notification is the alert itself, an HTTP post that Graph sends to your endpoint.

133
00:05:21,680 --> 00:05:25,760
The post body contains details about the change, like what resource changed, what type of change

134
00:05:25,760 --> 00:05:27,240
happened, and when it happened.

135
00:05:27,240 --> 00:05:29,240
Your endpoint is the door you leave open.

136
00:05:29,240 --> 00:05:32,600
It's a URL on your server, configured to receive these post requests.

137
00:05:32,600 --> 00:05:34,280
It just needs to listen and respond fast.

138
00:05:34,280 --> 00:05:35,280
That's it.

139
00:05:35,280 --> 00:05:37,560
No complex logic required at the receiving end.

140
00:05:37,560 --> 00:05:40,120
Just catch the notification, acknowledge it, and process it.

141
00:05:40,120 --> 00:05:41,520
Now compare this to polling.

142
00:05:41,520 --> 00:05:46,000
With polling, your app sends hundreds of get requests, each asking anything new.

143
00:05:46,000 --> 00:05:47,560
Most of the time the answer is no.

144
00:05:47,560 --> 00:05:49,280
With webhooks, there's just one post.

145
00:05:49,280 --> 00:05:50,280
The notification.

146
00:05:50,280 --> 00:05:51,280
That's it.

147
00:05:51,280 --> 00:05:52,280
One message instead of hundreds.

148
00:05:52,280 --> 00:05:56,560
The efficiency difference is enormous, and your endpoint doesn't need to be complicated.

149
00:05:56,560 --> 00:06:00,560
It just needs to be publicly accessible, speak HTTPS, and respond quickly.

150
00:06:00,560 --> 00:06:02,800
We'll talk about what quickly means in a bit.

151
00:06:02,800 --> 00:06:04,440
But the core idea is simple.

152
00:06:04,440 --> 00:06:09,560
Your endpoint listens, Graph calls, you respond, and done.

153
00:06:09,560 --> 00:06:10,560
Creating a subscription.

154
00:06:10,560 --> 00:06:12,720
So how do you actually set one of these up?

155
00:06:12,720 --> 00:06:15,280
Let's walk through creating a subscription step by step.

156
00:06:15,280 --> 00:06:19,640
You start by sending an HTTP post request to a specific URL on Microsoft Graph.

157
00:06:19,640 --> 00:06:21,440
That URL is HTTPS, Graph.

158
00:06:21,440 --> 00:06:24,400
Microsoft.com v1.0, subscriptions.

159
00:06:24,400 --> 00:06:26,880
You're posting straight to the subscriptions endpoint.

160
00:06:26,880 --> 00:06:30,720
Inside that request body, you include the details of what you want to subscribe to.

161
00:06:30,720 --> 00:06:32,560
There are four key fields you need to provide.

162
00:06:32,560 --> 00:06:34,240
First is notification URL.

163
00:06:34,240 --> 00:06:35,960
This is your endpoint's URL.

164
00:06:35,960 --> 00:06:38,560
The place where Microsoft Graph will send the notifications.

165
00:06:38,560 --> 00:06:41,800
It has to be HTTPS and it has to be publicly accessible because Graph needs to reach it

166
00:06:41,800 --> 00:06:42,800
from the internet.

167
00:06:42,800 --> 00:06:44,000
Second is resource.

168
00:06:44,000 --> 00:06:45,760
This tells Graph what you want to watch.

169
00:06:45,760 --> 00:06:47,920
You specify the Microsoft Graph resource path.

170
00:06:47,920 --> 00:06:53,040
For example, users if you want to watch for user changes or me, male folders, inbox messages

171
00:06:53,040 --> 00:06:55,400
if you want to watch for new emails in your inbox.

172
00:06:55,400 --> 00:06:57,680
The resource tells Graph exactly what you care about.

173
00:06:57,680 --> 00:06:59,400
Third is change type.

174
00:06:59,400 --> 00:07:02,440
This tells Graph what kind of changes to notify you about.

175
00:07:02,440 --> 00:07:05,880
You can specify created, updated, deleted, or any combination.

176
00:07:05,880 --> 00:07:09,640
So if you only care about new users being created, you set change type to create it.

177
00:07:09,640 --> 00:07:12,640
If you care about updates too, you set it to create it, updated.

178
00:07:12,640 --> 00:07:14,640
Fourth is expiration date time.

179
00:07:14,640 --> 00:07:18,000
This is when the subscription expires because subscriptions don't last forever.

180
00:07:18,000 --> 00:07:21,920
You set a date and time and Graph stops sending notifications after that point.

181
00:07:21,920 --> 00:07:24,640
The maximum duration depends on the resource you're watching.

182
00:07:24,640 --> 00:07:27,200
For users and groups, the max is about three days.

183
00:07:27,200 --> 00:07:30,760
For drive items in SharePoint or OneDrive, it can be up to 30 days.

184
00:07:30,760 --> 00:07:33,640
You need to check the documentation for each resource type.

185
00:07:33,640 --> 00:07:35,760
There's also an optional field worth mentioning.

186
00:07:35,760 --> 00:07:36,920
Client state.

187
00:07:36,920 --> 00:07:38,400
This is a secret string you create.

188
00:07:38,400 --> 00:07:39,960
It can be anything you want.

189
00:07:39,960 --> 00:07:42,320
A random string, a good, whatever you choose.

190
00:07:42,320 --> 00:07:46,560
Microsoft Graph includes this value in every notification it sends to your endpoint.

191
00:07:46,560 --> 00:07:50,560
And you use it to verify that the notification actually came from your subscription.

192
00:07:50,560 --> 00:07:54,400
If someone tries to send fake notifications to your endpoint, the client state won't match

193
00:07:54,400 --> 00:07:55,920
and you can reject it immediately.

194
00:07:55,920 --> 00:07:58,400
It's a simple security measure, but it's important.

195
00:07:58,400 --> 00:08:01,880
Creating a subscription is like signing up for a newspaper delivery.

196
00:08:01,880 --> 00:08:03,560
You give the newspaper your address.

197
00:08:03,560 --> 00:08:05,560
That's your notification URL.

198
00:08:05,560 --> 00:08:09,120
You tell them what sections you want, that's your resource and change type, and you tell

199
00:08:09,120 --> 00:08:11,320
them how long you want the delivery to continue.

200
00:08:11,320 --> 00:08:12,840
That's your expiration date time.

201
00:08:12,840 --> 00:08:14,320
The newspaper handles the rest.

202
00:08:14,320 --> 00:08:16,520
But here's the thing, subscriptions expire.

203
00:08:16,520 --> 00:08:19,000
You can't create one and forget about it.

204
00:08:19,000 --> 00:08:23,240
After the expiration date time passes, Graph stops sending notifications and your subscription

205
00:08:23,240 --> 00:08:24,240
is gone.

206
00:08:24,240 --> 00:08:27,400
If you want to keep receiving notifications, you need to renew the subscription before it

207
00:08:27,400 --> 00:08:31,120
expires by sending a patch request to the subscription endpoint with a new expiration

208
00:08:31,120 --> 00:08:32,120
date time.

209
00:08:32,120 --> 00:08:34,680
So creating a subscription is the first step, but it's not the last.

210
00:08:34,680 --> 00:08:36,080
You need to manage the life cycle.

211
00:08:36,080 --> 00:08:37,840
We'll talk more about that later.

212
00:08:37,840 --> 00:08:39,480
The validation handshake.

213
00:08:39,480 --> 00:08:42,800
Before your subscription goes live, there's one step you can't skip.

214
00:08:42,800 --> 00:08:46,800
Microsoft Graph doesn't just accept your subscription request and start sending notifications.

215
00:08:46,800 --> 00:08:50,360
It first needs to confirm your endpoint is real and ready to receive them.

216
00:08:50,360 --> 00:08:51,360
Here's how it works.

217
00:08:51,360 --> 00:08:55,120
When you send your post request to create a subscription, Microsoft Graph immediately fires

218
00:08:55,120 --> 00:08:57,960
off a test post to your notification URL.

219
00:08:57,960 --> 00:09:01,280
That test post includes a query parameter called validation token.

220
00:09:01,280 --> 00:09:02,600
It's just a random string.

221
00:09:02,600 --> 00:09:06,360
Your endpoint needs to grab that token and send it back as a plain text response and you

222
00:09:06,360 --> 00:09:08,160
have to do it within 10 seconds.

223
00:09:08,160 --> 00:09:10,240
Think of it like a courier delivering a package.

224
00:09:10,240 --> 00:09:12,840
They show up at your door and ask, is this the right address?

225
00:09:12,840 --> 00:09:13,840
Can you confirm?

226
00:09:13,840 --> 00:09:14,840
You have to sign for it.

227
00:09:14,840 --> 00:09:16,240
If you don't, they walk away.

228
00:09:16,240 --> 00:09:17,880
The courier doesn't leave the package.

229
00:09:17,880 --> 00:09:18,880
They just leave.

230
00:09:18,880 --> 00:09:20,600
But that's exactly what's happening here.

231
00:09:20,600 --> 00:09:24,240
Microsoft Graph is saying, I'm about to send you important notifications.

232
00:09:24,240 --> 00:09:27,880
Prove you're the right person at the right address.

233
00:09:27,880 --> 00:09:32,240
Your endpoint responds by echoing back the validation token, which tells Graph, yes,

234
00:09:32,240 --> 00:09:33,240
I'm here.

235
00:09:33,240 --> 00:09:34,240
I'm listening.

236
00:09:34,240 --> 00:09:35,800
Send me the notifications.

237
00:09:35,800 --> 00:09:38,160
Now here's where a lot of developers trip up.

238
00:09:38,160 --> 00:09:42,960
The validation token must be returned as plain text, not JSON, not HTML, plain text.

239
00:09:42,960 --> 00:09:46,400
And the response must have a content type header set to text plain.

240
00:09:46,400 --> 00:09:50,480
If you send it back wrapped in JSON or encoded in anyway, the validation fails.

241
00:09:50,480 --> 00:09:51,920
Your subscription won't be created.

242
00:09:51,920 --> 00:09:55,240
Microsoft Graph returns an error and you're stuck wondering what went wrong.

243
00:09:55,240 --> 00:09:57,040
There's another technical detail worth knowing.

244
00:09:57,040 --> 00:10:01,000
Microsoft Graph's validation service doesn't reliably support TLS 1.3.

245
00:10:01,000 --> 00:10:02,840
It negotiates TLS 1.2.

246
00:10:02,840 --> 00:10:07,560
So if your endpoint only accepts TLS 1.3 connections, the validation handshake fails before your

247
00:10:07,560 --> 00:10:09,360
application code even runs.

248
00:10:09,360 --> 00:10:14,760
You need to make sure your endpoint supports TLS 1.2, at least for the validation step.

249
00:10:14,760 --> 00:10:18,680
After that, you can enforce whatever TLS version you want for the actual notifications.

250
00:10:18,680 --> 00:10:21,840
The good news is this validation handshake only happens once per subscription.

251
00:10:21,840 --> 00:10:25,440
It's not something your endpoint has to handle every time a notification arrives, just the

252
00:10:25,440 --> 00:10:26,640
first time.

253
00:10:26,640 --> 00:10:31,200
Once Graph confirms your endpoint is real, it trusts it for the life of that subscription.

254
00:10:31,200 --> 00:10:33,960
So you only need to get this right once.

255
00:10:33,960 --> 00:10:35,760
Handling notifications and security.

256
00:10:35,760 --> 00:10:39,440
Once the validation handshake goes through and your subscription goes live, Microsoft Graph

257
00:10:39,440 --> 00:10:41,480
starts sending those notifications.

258
00:10:41,480 --> 00:10:43,880
So what do those actually look like in practice?

259
00:10:43,880 --> 00:10:48,720
When something changes, Graph sends an HTTP post to your endpoint with a JSON object in

260
00:10:48,720 --> 00:10:49,720
the body.

261
00:10:49,720 --> 00:10:53,480
That object contains the resource that changed the type of change that created, updated,

262
00:10:53,480 --> 00:10:55,480
or deleted, and a timestamp.

263
00:10:55,480 --> 00:10:59,360
It also includes the client state value you set when you created the subscription.

264
00:10:59,360 --> 00:11:02,240
That's your secret string your way of knowing the message is real.

265
00:11:02,240 --> 00:11:05,240
So here's the first thing to do when a notification arrives.

266
00:11:05,240 --> 00:11:06,560
Validate that client state.

267
00:11:06,560 --> 00:11:09,600
Compare the value in the notification against the secret you stored when you created the

268
00:11:09,600 --> 00:11:10,600
subscription.

269
00:11:10,600 --> 00:11:12,640
If they don't match, discard it immediately.

270
00:11:12,640 --> 00:11:14,000
Because it could be a fake.

271
00:11:14,000 --> 00:11:16,760
Someone trying to trick your system into processing bad data.

272
00:11:16,760 --> 00:11:19,480
This matters because your endpoint lives on the public internet.

273
00:11:19,480 --> 00:11:22,160
Anyone who knows your URL could send a post to it.

274
00:11:22,160 --> 00:11:25,480
Without client state validation, you'd have no way to tell the difference between a real

275
00:11:25,480 --> 00:11:28,280
Microsoft Graph notification and a fake one.

276
00:11:28,280 --> 00:11:30,640
It's a simple check, but it's your first line of defense.

277
00:11:30,640 --> 00:11:34,760
Once you've confirmed the notification is legit, you need to respond fast.

278
00:11:34,760 --> 00:11:38,440
Microsoft Graph expects a 200 level HTTP response within three seconds.

279
00:11:38,440 --> 00:11:42,720
If you take longer graph notices, and if it happens too often, graph starts treating you

280
00:11:42,720 --> 00:11:43,720
differently.

281
00:11:43,720 --> 00:11:44,720
Here's how the throttling works.

282
00:11:44,720 --> 00:11:49,720
If more than 10% of your responses exceed the three second window in any 10 minute period,

283
00:11:49,720 --> 00:11:51,960
Microsoft Graph marks your endpoint as slow.

284
00:11:51,960 --> 00:11:54,920
When that happens, new notifications get delayed by 10 minutes.

285
00:11:54,920 --> 00:11:58,120
They still arrive eventually, but you lose real time delivery.

286
00:11:58,120 --> 00:12:03,160
If things get worse, with more than 15% of your responses exceeding 10 seconds, graph marks

287
00:12:03,160 --> 00:12:04,680
your endpoint as drop.

288
00:12:04,680 --> 00:12:07,760
In that state, notifications are dropped entirely for 10 minutes.

289
00:12:07,760 --> 00:12:08,760
Here's the scary part.

290
00:12:08,760 --> 00:12:10,640
Drop notifications cannot be recovered.

291
00:12:10,640 --> 00:12:12,640
They're gone, and you'll never get them back.

292
00:12:12,640 --> 00:12:14,280
Think of it like a busy phone line.

293
00:12:14,280 --> 00:12:16,840
If you don't pick up quickly, the caller tries again later.

294
00:12:16,840 --> 00:12:19,800
But if you never pick up, eventually the caller gives up.

295
00:12:19,800 --> 00:12:23,120
Microsoft Graph is the caller, and your endpoint is the phone.

296
00:12:23,120 --> 00:12:25,120
You can't pick up fast or risk losing the message.

297
00:12:25,120 --> 00:12:26,920
So what's the best practice here?

298
00:12:26,920 --> 00:12:29,080
Acknowledge immediately and process asynchronously.

299
00:12:29,080 --> 00:12:34,080
Your endpoint should validate the client state, accept the notification, and return it 200 or

300
00:12:34,080 --> 00:12:36,160
202 responses as fast as possible.

301
00:12:36,160 --> 00:12:39,360
Then push the notification into a queue for background processing.

302
00:12:39,360 --> 00:12:42,480
And Azure queue, a service bus, even a simple database table.

303
00:12:42,480 --> 00:12:46,720
The point is to get out of the response cycle quickly and let your background workers handle

304
00:12:46,720 --> 00:12:47,720
the heavy lifting.

305
00:12:47,720 --> 00:12:50,520
And one more security reminder, your endpoint must be HTTPS.

306
00:12:50,520 --> 00:12:55,120
No exceptions, Microsoft Graph will not send notifications to an HTTP endpoint.

307
00:12:55,120 --> 00:12:58,480
And your endpoint must be publicly accessible if it's behind a firewall or a VPN Graph

308
00:12:58,480 --> 00:12:59,480
can't reach it.

309
00:12:59,480 --> 00:13:03,320
For development, you can use tools like en-groc to create a public tunnel to your local

310
00:13:03,320 --> 00:13:04,320
machine.

311
00:13:04,320 --> 00:13:08,840
But in production, your endpoint needs to live somewhere, Graph can find it.

312
00:13:08,840 --> 00:13:11,040
Keeping notifications alive, life cycle.

313
00:13:11,040 --> 00:13:15,000
So you've got your subscription setup, notifications are flowing, and everything looks good.

314
00:13:15,000 --> 00:13:17,280
But here's something that catches a lot of people off guard.

315
00:13:17,280 --> 00:13:19,000
Your subscription has an expiration date.

316
00:13:19,000 --> 00:13:21,880
It doesn't last forever, depending on what resource you're watching.

317
00:13:21,880 --> 00:13:25,200
The maximum subscription duration is usually between three and seven days.

318
00:13:25,200 --> 00:13:28,920
For some resources like drive items and SharePoint, you can get up to 30 days.

319
00:13:28,920 --> 00:13:30,640
But the point is, it's not permanent.

320
00:13:30,640 --> 00:13:33,280
You cannot create a subscription and forget about it.

321
00:13:33,280 --> 00:13:35,200
Think of it like a magazine subscription.

322
00:13:35,200 --> 00:13:38,040
You sign up, you start receiving issues and everything's great.

323
00:13:38,040 --> 00:13:41,320
But if you don't renew before the subscription runs out, the issue stops coming.

324
00:13:41,320 --> 00:13:42,320
Same thing here.

325
00:13:42,320 --> 00:13:45,560
If your subscription expires, Microsoft Graph stops sending notifications.

326
00:13:45,560 --> 00:13:48,480
And you might not realize it until you wonder why nothing's happening.

327
00:13:48,480 --> 00:13:51,040
So you need a renewal strategy, the way you renew is simple.

328
00:13:51,040 --> 00:13:55,320
Send a patch request to the subscription endpoint using the subscription ID and update the

329
00:13:55,320 --> 00:13:57,520
expiration date time to a new future date.

330
00:13:57,520 --> 00:13:59,560
That extends the life of the subscription.

331
00:13:59,560 --> 00:14:03,160
You can do this as many times as you want, as long as you do it before the current expiration

332
00:14:03,160 --> 00:14:04,160
passes.

333
00:14:04,160 --> 00:14:08,040
Now, Microsoft Graph actually helps you hear with something called life cycle notifications.

334
00:14:08,040 --> 00:14:12,200
When you create a subscription, you can provide a separate URL called the life cycle notification

335
00:14:12,200 --> 00:14:13,200
URL.

336
00:14:13,200 --> 00:14:17,080
Microsoft Graph sends alerts to that URL when certain things happen.

337
00:14:17,080 --> 00:14:20,960
Like when your subscription is about to expire, when your access token is about to run out,

338
00:14:20,960 --> 00:14:22,960
or when the permissions on your app change.

339
00:14:22,960 --> 00:14:25,080
These life cycle notifications give you a heads up.

340
00:14:25,080 --> 00:14:27,440
You don't have to guess when your subscription is going to die.

341
00:14:27,440 --> 00:14:30,500
Graph tells you, hey, your subscription expires in a few hours.

342
00:14:30,500 --> 00:14:32,000
You might want to renew.

343
00:14:32,000 --> 00:14:35,400
It's like getting a reminder from your magazine publisher that your subscription is about

344
00:14:35,400 --> 00:14:36,400
to end.

345
00:14:36,400 --> 00:14:38,000
Very helpful if you're paying attention.

346
00:14:38,000 --> 00:14:40,160
But what happens if things go wrong anyway?

347
00:14:40,160 --> 00:14:43,600
Maybe your endpoint goes down for a while or notifications get dropped because you were too

348
00:14:43,600 --> 00:14:44,600
slow.

349
00:14:44,600 --> 00:14:45,600
What then?

350
00:14:45,600 --> 00:14:46,600
That's where Delta query comes in.

351
00:14:46,600 --> 00:14:50,560
Delta query is a separate feature of Microsoft Graph that lets you ask, what changed since the

352
00:14:50,560 --> 00:14:51,960
last time I checked?

353
00:14:51,960 --> 00:14:54,200
And it returns only the changes, nothing else.

354
00:14:54,200 --> 00:14:55,200
Here's how it works.

355
00:14:55,200 --> 00:14:59,000
The first time you run a Delta query on a resource, say, users, graph returns all the users

356
00:14:59,000 --> 00:15:00,000
in your tenant.

357
00:15:00,000 --> 00:15:03,200
But it also returns something extra, a special link called a Delta link.

358
00:15:03,200 --> 00:15:04,200
You save that link.

359
00:15:04,200 --> 00:15:06,960
The next time you run the same query, you use that Delta link.

360
00:15:06,960 --> 00:15:10,880
And instead of getting all users again, you get only the ones that changed since your last

361
00:15:10,880 --> 00:15:11,880
request.

362
00:15:11,880 --> 00:15:14,160
New users, updated users, deleted users.

363
00:15:14,160 --> 00:15:15,640
That's it.

364
00:15:15,640 --> 00:15:19,360
Think of it like reading a what's new list instead of reading the entire encyclopedia every

365
00:15:19,360 --> 00:15:21,280
time, much faster, much less data.

366
00:15:21,280 --> 00:15:22,800
Now here's the smart pattern.

367
00:15:22,800 --> 00:15:24,680
Combine web hooks with Delta query.

368
00:15:24,680 --> 00:15:26,920
Use web hooks to tell you when something changed.

369
00:15:26,920 --> 00:15:29,480
Then use Delta query to find out exactly what changed.

370
00:15:29,480 --> 00:15:30,880
Let me explain why that matters.

371
00:15:30,880 --> 00:15:32,880
Web hooks are fast, but they're not perfect.

372
00:15:32,880 --> 00:15:33,880
Notifications can get lost.

373
00:15:33,880 --> 00:15:36,080
Subscriptions can expire and your endpoint can go down.

374
00:15:36,080 --> 00:15:37,320
But Delta query is reliable.

375
00:15:37,320 --> 00:15:38,680
You can run it any time.

376
00:15:38,680 --> 00:15:41,360
And it gives you a complete list of changes since your last check.

377
00:15:41,360 --> 00:15:44,520
So even if you miss a notification, you can catch up.

378
00:15:44,520 --> 00:15:45,920
And here's the typical flow.

379
00:15:45,920 --> 00:15:48,440
Set up a web hook subscription for user changes.

380
00:15:48,440 --> 00:15:52,040
Run an initial Delta query to get the baseline state and save the Delta link.

381
00:15:52,040 --> 00:15:55,880
When a web hook arrives telling you a user changed, run a Delta query using your saved

382
00:15:55,880 --> 00:15:56,880
Delta link.

383
00:15:56,880 --> 00:15:58,640
That gives you the exact change.

384
00:15:58,640 --> 00:15:59,640
Process it.

385
00:15:59,640 --> 00:16:01,280
Then save the new Delta link from that response.

386
00:16:01,280 --> 00:16:03,760
Next time a web hook comes in, repeat the process.

387
00:16:03,760 --> 00:16:08,040
And if you ever miss a notification or if your subscription expires, just run the Delta

388
00:16:08,040 --> 00:16:09,040
query again.

389
00:16:09,040 --> 00:16:10,360
You'll get everything you missed.

390
00:16:10,360 --> 00:16:11,360
It's a safety net.

391
00:16:11,360 --> 00:16:14,440
Web hooks give you speed and Delta query gives you reliability.

392
00:16:14,440 --> 00:16:17,080
Together, they're a powerful combination.

393
00:16:17,080 --> 00:16:19,160
Real world example and next steps.

394
00:16:19,160 --> 00:16:21,720
Imagine a new employee joins your company tomorrow.

395
00:16:21,720 --> 00:16:26,920
Someone in HR creates their account in Microsoft Entra ID and that single action kicks off

396
00:16:26,920 --> 00:16:28,120
a chain reaction.

397
00:16:28,120 --> 00:16:31,960
If you've set up a web hook subscription for user changes, Microsoft Graph notices instantly

398
00:16:31,960 --> 00:16:34,040
and fires a post to your endpoint.

399
00:16:34,040 --> 00:16:35,080
Here's what happens next.

400
00:16:35,080 --> 00:16:39,880
Your endpoint receives the notification, validates the client state, and if it matches your secret,

401
00:16:39,880 --> 00:16:41,160
you know it's legit.

402
00:16:41,160 --> 00:16:46,320
To return a 202 accepted response within three seconds, acknowledging the notification,

403
00:16:46,320 --> 00:16:48,040
and then your background worker picks it up.

404
00:16:48,040 --> 00:16:51,680
The worker Caesar user was created, but the notification doesn't include all the details,

405
00:16:51,680 --> 00:16:54,440
so it runs a Delta query using your saved Delta link.

406
00:16:54,440 --> 00:16:59,160
That returns the new user's full information, name, email, department, job title, manager,

407
00:16:59,160 --> 00:17:00,280
everything you need.

408
00:17:00,280 --> 00:17:03,880
Now your worker takes that information and creates an account for the new employee in

409
00:17:03,880 --> 00:17:05,680
your third party time tracking system.

410
00:17:05,680 --> 00:17:06,760
It sends a welcome email.

411
00:17:06,760 --> 00:17:09,360
It adds them to the appropriate distribution lists.

412
00:17:09,360 --> 00:17:14,040
All of this happens within seconds of HR creating the account in Entra ID, no polling,

413
00:17:14,040 --> 00:17:16,640
no manual steps, no delays.

414
00:17:16,640 --> 00:17:17,960
Think about the alternative.

415
00:17:17,960 --> 00:17:21,840
Without web hooks, you'd have to poll the user's endpoint every few minutes, hundreds of requests

416
00:17:21,840 --> 00:17:25,600
per day, and even then you might not catch the new user for several minutes.

417
00:17:25,600 --> 00:17:27,040
With web hooks, it's instant.

418
00:17:27,040 --> 00:17:29,280
One notification, one Delta query done.

419
00:17:29,280 --> 00:17:30,280
That's the real value.

420
00:17:30,280 --> 00:17:34,280
Your system becomes faster, more efficient, and less likely to hit throttling limits.

421
00:17:34,280 --> 00:17:38,200
Instead of constantly asking Graph for updates, you let Graph tell you when something happens.

422
00:17:38,200 --> 00:17:40,840
It's a fundamental shift in how you build integrations.

423
00:17:40,840 --> 00:17:42,200
So here's what I want you to try.

424
00:17:42,200 --> 00:17:44,760
Think about one process you're currently polling for.

425
00:17:44,760 --> 00:17:47,120
Maybe you're checking for new emails in a shared mailbox.

426
00:17:47,120 --> 00:17:50,160
Maybe you're watching for file changes in a SharePoint library.

427
00:17:50,160 --> 00:17:54,320
Maybe your monitoring user updates for your HR system could a web hook replace that polling?

428
00:17:54,320 --> 00:17:56,240
In most cases, the answer is yes.

429
00:17:56,240 --> 00:17:57,920
And you don't need to build everything from scratch.

430
00:17:57,920 --> 00:18:01,680
Tools like Azure Functions can host your web hook endpoint with minimal code.

431
00:18:01,680 --> 00:18:05,760
Power Automate can handle subscriptions and notifications without writing a single line.

432
00:18:05,760 --> 00:18:07,720
Even logic apps can process web hook payloads.

433
00:18:07,720 --> 00:18:08,800
The infrastructure is there.

434
00:18:08,800 --> 00:18:10,640
You just need to use it.

435
00:18:10,640 --> 00:18:12,520
So here's the core idea in plain English.

436
00:18:12,520 --> 00:18:13,520
Stop polling.

437
00:18:13,520 --> 00:18:14,520
Start listening.

438
00:18:14,520 --> 00:18:18,640
Web hooks let you move from constantly asking Microsoft Graph for updates to getting notified

439
00:18:18,640 --> 00:18:20,680
instantly when something changes.

440
00:18:20,680 --> 00:18:25,000
That shift transforms your system from one that wastes, calls, and risks throttling to one

441
00:18:25,000 --> 00:18:27,080
that's fast, efficient, and reliable.

442
00:18:27,080 --> 00:18:31,840
Instead of thousands of unnecessary requests per day, you get one notification per change.

443
00:18:31,840 --> 00:18:34,680
Instead of waiting minutes to find out about an update, you know, within seconds.

444
00:18:34,680 --> 00:18:38,840
Instead of worrying about hitting GraphJail, you work within the system the way it was designed.

445
00:18:38,840 --> 00:18:43,400
So if you're building integrations with Microsoft 365, web hooks are the smarter path.

446
00:18:43,400 --> 00:18:44,800
They make your system faster.

447
00:18:44,800 --> 00:18:47,040
They make it less likely to be throttled.

448
00:18:47,040 --> 00:18:49,880
And they give you real time awareness of what's happening in your tenant.

449
00:18:49,880 --> 00:18:52,520
That's it for this episode of Microsoft Knowledge Nuggets.

450
00:18:52,520 --> 00:18:56,160
Subscribe on your favorite podcast platform and share this with someone starting their journey

451
00:18:56,160 --> 00:18:57,360
with Microsoft Graph.

452
00:18:57,360 --> 00:18:58,360
I'm Mirko Peters.