Event Grid Webhook Endpoints

Azure Event Grid can deliver events to any publicly accessible HTTPS endpoint. This endpoint is called a webhook. Webhooks open Event Grid to any application, platform, or service — not just Azure services. A custom web application, a third-party platform, or a service running in any cloud can receive Event Grid events through a webhook.

What Is a Webhook

A webhook is an HTTP endpoint that listens for incoming POST requests. When Event Grid delivers an event, it sends an HTTP POST request to the webhook URL with the event data in the request body.Think of a webhook like a doorbell. You register your address (webhook URL) with the post office (Event Grid). When mail (an event) arrives, the post office rings your doorbell (sends a POST request). Your application opens the door and processes the mail.
Event Occurs in Azure
         |
         | Event Grid picks it up
         v
+--------------------+
|    Event Grid      |
|    Subscription    |
|    Endpoint:       |
|    https://myapp   |
|    .com/events     |
+--------+-----------+
         |
         | HTTP POST with event JSON
         v
+--------------------+
|   Your Webhook     |
|   Endpoint         |
|   (any HTTPS URL)  |
|                    |
|   Reads event body |
|   Processes it     |
|   Returns 200 OK   |
+--------------------+

Webhook Validation: Required First Step

When you create a subscription with a webhook endpoint, Event Grid does not immediately start sending events. It first sends a validation request to confirm that you own and control the webhook URL.

Validation Flow

Step 1: You create an Event Grid subscription with webhook URL

Step 2: Event Grid sends a SubscriptionValidationEvent:
{
  "id": "abc123",
  "eventType": "Microsoft.EventGrid.SubscriptionValidationEvent",
  "data": {
    "validationCode": "512d38b6-c7b8-40c8-89fe-f46f9e9622b6",
    "validationUrl": "https://rp-eastus.eventgrid.azure.net/eventSubscriptions/..."
  }
}

Step 3 (Option A – Synchronous Validation):
  Your endpoint reads validationCode and returns it:
  HTTP 200 OK
  {
    "validationResponse": "512d38b6-c7b8-40c8-89fe-f46f9e9622b6"
  }

Step 3 (Option B – Manual Validation):
  Visit validationUrl in a browser within 5 minutes
  (for endpoints you cannot modify, like a third-party service)

Step 4: Subscription becomes Active and real events start flowing

Implementing Validation in Your Webhook

C# ASP.NET Core Controller

[HttpPost("events")]
public IActionResult ReceiveEvents([FromBody] object[] events)
{
    foreach (var eventObj in events)
    {
        var eventData = JsonDocument.Parse(eventObj.ToString());
        var root = eventData.RootElement;
        var eventType = root.GetProperty("eventType").GetString();

        // Handle validation handshake
        if (eventType == "Microsoft.EventGrid.SubscriptionValidationEvent")
        {
            var validationCode = root
                .GetProperty("data")
                .GetProperty("validationCode")
                .GetString();

            return Ok(new { validationResponse = validationCode });
        }

        // Handle real events
        if (eventType == "Microsoft.Storage.BlobCreated")
        {
            var subject = root.GetProperty("subject").GetString();
            Console.WriteLine($"New blob created: {subject}");
            // process the event
        }
    }

    return Ok();
}

Node.js Express Example

app.post('/events', (req, res) => {
  const events = req.body;

  for (const event of events) {
    // Validation handshake
    if (event.eventType === 'Microsoft.EventGrid.SubscriptionValidationEvent') {
      const validationCode = event.data.validationCode;
      return res.json({ validationResponse: validationCode });
    }

    // Real event processing
    console.log('Event type:', event.eventType);
    console.log('Subject:', event.subject);
    console.log('Data:', JSON.stringify(event.data));
  }

  res.status(200).send('OK');
});

Event Delivery to Webhooks

Event Grid sends events as an HTTP POST with a JSON array in the body. Even a single event arrives inside an array.

Sample HTTP Request from Event Grid

POST /events HTTP/1.1
Host: myapp.com
Content-Type: application/json
aeg-event-type: Notification

[
  {
    "id": "d521b1d3-9e8a-4bd0-a3c1-f78a901bc234",
    "topic": "/subscriptions/.../storageAccounts/mystorage",
    "subject": "/blobServices/default/containers/images/blobs/photo.jpg",
    "eventType": "Microsoft.Storage.BlobCreated",
    "eventTime": "2024-09-15T10:22:05.000Z",
    "data": {
      "url": "https://mystorage.blob.core.windows.net/images/photo.jpg",
      "contentType": "image/jpeg",
      "contentLength": 45678
    },
    "dataVersion": "1.0"
  }
]

HTTP Response Requirements

Your webhook must return a specific HTTP status code for Event Grid to consider delivery successful.
HTTP Status CodeEvent Grid Behavior
200 OKDelivery successful
201 CreatedDelivery successful
202 AcceptedDelivery successful
204 No ContentDelivery successful
400 Bad RequestDelivery failed — Event Grid does NOT retry
401 UnauthorizedDelivery failed — retried by Event Grid
404 Not FoundDelivery failed — retried by Event Grid
408 Request TimeoutDelivery failed — retried by Event Grid
500 Internal Server ErrorDelivery failed — retried by Event Grid
503 Service UnavailableDelivery failed — retried by Event Grid

Return 400 only if the event is genuinely malformed and retrying is pointless. Return 500 or 503 when a temporary failure occurs and a retry might succeed.

Webhook Processing Timeout

Event Grid waits 60 seconds for your webhook to respond. If your webhook takes longer than 60 seconds, Event Grid marks the delivery as failed and retries.For long-running tasks, follow this pattern:
Webhook receives event
         |
         | Accept the event immediately
         | Return 200 OK within a few seconds
         |
         v
Place event in an internal queue
(Azure Storage Queue or Service Bus)
         |
         | Return 200 OK → Event Grid satisfied
         v
Background worker reads from internal queue
         |
         | Performs the long operation
         | (could take minutes or hours)
         v
Operation completed asynchronously

Securing Your Webhook

A public webhook URL is accessible to anyone. You must verify that POST requests come from Event Grid, not from malicious actors.

Option 1 – Webhook Secret Header

Add a secret query parameter or custom header to your webhook URL. Event Grid passes it with every delivery. Your endpoint checks it.
Webhook URL: https://myapp.com/events?secret=my-secret-token-xyz

Your code:
  string incomingSecret = Request.Query["secret"];
  if (incomingSecret != "my-secret-token-xyz")
      return Unauthorized();

Option 2 – Azure AD Authentication

Configure the subscription to use Azure Active Directory to authenticate against the webhook endpoint. The endpoint validates the JWT token in the Authorization header.

Option 3 – IP Allowlisting

Azure publishes the IP ranges that Event Grid uses to deliver events. Allowlist these IP ranges on your firewall or API gateway.

Batching Events to Webhooks

By default, Event Grid sends one event per HTTP request. For high-volume scenarios, enable batch delivery to improve efficiency.
+---------------------------+
| Webhook Subscription      |
| Batching Settings:        |
|                           |
| Max Events Per Batch: 50  |
| Preferred Batch Size: 25KB|
+---------------------------+

With batching enabled:
Event Grid → one HTTP POST containing up to 50 events at once
Your webhook processes all 50 in one request
Fewer HTTP round-trips = higher throughput

Configure batching in: Event Grid Subscription → Additional Features → Enable Batching

Webhook vs Azure Service Endpoints

AspectWebhookAzure Function / Logic App
Works with non-Azure appsYesNo
Requires public HTTPS URLYesNo
Validation handshake neededYesHandled automatically
Auto-scalesDepends on your appYes
Retry on failureYes (by Event Grid)Yes

Summary

Webhook endpoints make Event Grid universal — any HTTPS service anywhere can receive events. The validation handshake confirms ownership of the endpoint before events flow. Your webhook must respond within 60 seconds with a success status code. Secure your webhook URL with a secret token, Azure AD authentication, or IP allowlisting. For long-running processing, accept the event immediately and queue it for background work. Batching improves throughput for high-volume subscriptions.