What is a Webhook?
A webhook is like a notification system between two applications.
When something happens in one system (like a new record is created), it sends a small message (HTTP POST) to another system’s URL to tell it, “Hey, something just happened!”
You don’t need to constantly check for updates — you just get notified instantly.
Example
If a new item is added to a SharePoint list, SharePoint can call your Azure Function and send the item details.
Your Azure Function can then:
Why Use Azure Functions for Webhooks?
Azure Functions are perfect for receiving webhooks because they:
Run in the cloud (no servers to manage)
Automatically scale
Are cheap — you only pay when they run
Easily integrate with other Azure services
Basically, Azure Functions are the easiest way to create a public API endpoint that reacts to webhook events.
How Webhooks Work?
Register: The source system (like SharePoint) is told where to send notifications (your Azure Function URL).
Validation: The source might send a “challenge” request to confirm your URL works. Your function must respond correctly.
Event Happens: When an event occurs, the source sends data to your URL via an HTTP POST.
Process: Your Azure Function reads the data and does something useful (save to DB, trigger another service, etc.).
Creating a Webhook Receiver in .NET 9
Let’s build a webhook receiver using Azure Functions (.NET 9, isolated worker model).
Step 1. Create a New Azure Function Project
Open Visual Studio
Launch Visual Studio → click Create a new project.
Choose Project Template
Search for “Azure Functions” in the templates list.
Select
Azure Functions → Click Next
Configure the Project
Project Name: WebhookReceiver
Location: Choose any local folder
Framework: Choose .NET 9.0 (Isolated)
Click Create
Configure Function Settings
Visual Studio will now ask for more setup options:
| Setting | Value |
|---|
| Functions Worker | .NET Isolated |
| Function Template | HTTP trigger |
| Authorization level | Function |
| Storage Account | (Storage emulator or None for now) |
Click Create
Step 2. Add Required Packages
Make sure your .csproj looks like this:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<AzureFunctionsVersion>v4</AzureFunctionsVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Azure.Functions.Worker" Version="2.*" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="2.*" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http" Version="3.*" />
</ItemGroup>
</Project>
Step 3. Program.cs (App Setup)
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
var host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults()
.ConfigureServices(services =>
{
services.AddHttpClient();
})
.ConfigureLogging(logging => logging.AddConsole())
.Build();
await host.RunAsync();
Step 4. Write the Function Code
Create a file called WebhookHandler.cs.
using System.Net;
using System.Text.Json;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Extensions.Logging;
public class WebhookHandler
{
private readonly ILogger _logger;
public WebhookHandler(ILoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<WebhookHandler>();
}
[Function("WebhookReceiver")]
public async Task<HttpResponseData> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req)
{
_logger.LogInformation("Webhook request received.");
// Step 1: Handle validation (for example, SharePoint challenge)
if (req.Url.Query.Contains("validationtoken"))
{
var query = System.Web.HttpUtility.ParseQueryString(req.Url.Query);
var token = query["validationtoken"];
var response = req.CreateResponse(HttpStatusCode.OK);
await response.WriteStringAsync(token ?? "");
return response;
}
// Step 2: Read the POST body
var requestBody = await new StreamReader(req.Body).ReadToEndAsync();
_logger.LogInformation("Payload received: {body}", requestBody);
// Step 3: Deserialize the JSON
var data = JsonSerializer.Deserialize<WebhookPayload>(requestBody);
if (data == null)
{
var badResponse = req.CreateResponse(HttpStatusCode.BadRequest);
await badResponse.WriteStringAsync("Invalid payload");
return badResponse;
}
// Step 4: Do your business logic here
_logger.LogInformation("Processing event: {event}", data.EventName);
// Step 5: Return success
var okResponse = req.CreateResponse(HttpStatusCode.OK);
await okResponse.WriteStringAsync("Webhook processed successfully");
return okResponse;
}
}
public class WebhookPayload
{
public string EventName { get; set; }
public string ResourceId { get; set; }
}
Step 5. Secure the Function
To keep it safe, use a secret header.
Add this check before processing the payload:
if (!req.Headers.TryGetValues("x-webhook-secret", out var secrets) ||
secrets.FirstOrDefault() != Environment.GetEnvironmentVariable("WEBHOOK_SECRET"))
{
_logger.LogWarning("Unauthorized request!");
var unauthorized = req.CreateResponse(HttpStatusCode.Unauthorized);
await unauthorized.WriteStringAsync("Unauthorized");
return unauthorized;
}
Add this to your local.settings.json:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated",
"WEBHOOK_SECRET": "my-super-secret-key"
}
}
Step 6. Test Locally
Run your app:
func start
Then use Postman to send:
{
"EventName": "ItemCreated",
"ResourceId": "12345"
}
You should see a success message in the console.
We will see the deployment to Azure in next article.
Conclusion
Webhooks and Azure Functions are a powerful combination.
They help you create fast, event-driven solutions that respond in real time — without servers or complex infrastructure.
With just a few lines of code in .NET 9, you can build a scalable webhook receiver that integrates with SharePoint, Power Apps, or any external service.