Microsoft Graph Webhook (Shared Mailbox)
Microsoft Graph Webhook for a Shared Mailbox
Subscribe to message events in a shared mailbox so your service is notified when mail arrives, then process it with Semantic Kernel (SK).
When to use
- Ingest emails for classification, extraction, triage, or automation via SK.
- Need nearβrealβtime notifications without polling.
Prerequisites
- App registration with Application permissions:
Mail.ReadBasic.All(subjects/senders) orMail.Read(full content)- Admin consent granted
- Public HTTPS endpoint to receive notifications and handle validation
- Access to the shared mailbox with app permissions
- Scope access using Exchange Online Application Access Policies (recommended for least privilege)
For highly regulated data, consider
includeResourceDatato receive encrypted payloads and verify signatures. Otherwise, fetch the message after you get a notification.
Create the subscription (C# Graph SDK)
using Azure.Identity;
using Microsoft.Graph;
using Microsoft.Graph.Models;
var credential = new ClientSecretCredential(tenantId, clientId, clientSecret);
var graph = new GraphServiceClient(credential, new[] { "https://graph.microsoft.com/.default" });
var sharedMailbox = "shared-mailbox@contoso.com"; // UPN or objectId
var sub = new Subscription
{
ChangeType = "created",
Resource = $"/users/{sharedMailbox}/mailFolders('inbox')/messages",
NotificationUrl = "https://yourapp.example.com/graph/webhook",
ClientState = Guid.NewGuid().ToString("N"),
ExpirationDateTime = DateTimeOffset.UtcNow.AddHours(12) // renew before expiry
};
var created = await graph.Subscriptions.PostAsync(sub);
Console.WriteLine($"Subscription: {created?.Id}, Expires: {created?.ExpirationDateTime}");
- Resource options:
/users/{id}/messagesor a folder path, e.g., Inbox. - Typical renewal window for Outlook resources is limited; renew well before expiry.
Webhook validation and notifications (ASP.NET Core minimal API)
using Microsoft.AspNetCore.Mvc;
using Microsoft.Graph.Models;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
// 1) Validation (Graph sends GET with validationToken)
app.MapGet("/graph/webhook", ([FromQuery] string validationToken) =>
{
return Results.Content(validationToken, "text/plain");
});
// 2) Notifications (POST)
app.MapPost("/graph/webhook", async ([FromBody] ChangeNotificationCollection body, ILogger<Program> logger) =>
{
foreach (var n in body.Value)
{
// Verify clientState
if (n.ClientState != YourStore.LoadClientStateFor(n.SubscriptionId))
return Results.Unauthorized();
// Fetch the message (resourceData contains IDs)
var (userId, messageId) = (n.ResourceData?.AdditionalData?["userId"]?.ToString(),
n.ResourceData?.Id);
await YourQueue.EnqueueAsync(new FetchRequest(userId!, messageId!));
}
return Results.Ok();
});
app.Run();
- Respond to validation within 10s, echoing
validationTokenastext/plain. - For non-
includeResourceDatasubscriptions, Graph may not populate IDs inresourceData; if so, parse fromn.Resourceor switch to resource data mode. Prefer queueing and processing asynchronously.
Renewals
var updated = await graph.Subscriptions[created!.Id].PatchAsync(new Subscription
{
ExpirationDateTime = DateTimeOffset.UtcNow.AddHours(12)
});
- Run a timer/background job (see Timer Trigger) to renew before expiry.
- Store subscription IDs securely; handle re-creation on errors.
Security and resilience
- Least privilege: use
Mail.ReadBasic.Allif sufficient; otherwiseMail.Read. - Scope mailbox access with Exchange Application Access Policies.
- Validate
clientStateand use HTTPS; avoid relying solely on IP allow-lists. - Idempotency: dedupe notifications by
subscriptionId+resource+changeType+ time. - Backpressure: immediately ack POST and process via a queue with limited concurrency.
Integrating with SK
- After fetching the message with Graph, call an SK function/plugin to classify, route, or extract.
- Combine with the Webhook Trigger pattern and Timer Trigger for renewals.
Pros / Cons
- Pros: Near real-time, efficient, integrates cleanly with SK pipelines.
- Cons: Subscription lifecycle/renewals; validation and security hardening required.