Azure Webhook setup guide
Azure Event Grid Webhook (CloudEvents) — Integration Guide
This guide explains how to configure your webhook endpoint so Azure Event Grid can deliver CloudEvents v1.0 to you. It includes the required validation handshake (HTTP OPTIONS) and the runtime delivery (POST) format, with example requests and responses.
Related Documentation: These webhooks publish our Event schema. For example, the Order Event will be the contents of our payload for Order Event subscriptions
Overview
- Protocol & schema: HTTP, CloudEvents v1.0 (structured JSON mode).
- Handshake: CloudEvents abuse-protection validation via HTTP
OPTIONS; you must echo specific headers. - Delivery: HTTP
POSTwithContent-Typeapplication/cloudevents+json - Retries: Event Grid retries failed deliveries with configurable max attempts and TTL (defaults: 30 attempts or 1440 minutes).
Expose Your Endpoint
- URL:
https://<your-domain>/api/events - Methods: Must support
OPTIONS(validation) andPOST(event delivery). - TLS: HTTPS required.
- Timeouts: Respond to deliveries quickly (aim < 30s) to avoid retries.
Implement the Validation Handshake (HTTP OPTIONS)
When you create/enable a subscription, Event Grid sends an HTTP OPTIONS request that includes:
WebHook-Request-Origin: <origin-host>(required)- Optionally:
WebHook-Request-Rate: <requests-per-minute>and/orWebHook-Request-Callback: <url>(some services use these).
You must respond with:
WebHook-Allowed-Origin: <same value you received>or*WebHook-Allowed-Rate: <integer or *>(required ifWebHook-Request-Ratewas sent; otherwise SHOULD be present)Allow: POST, OPTIONS- HTTP 200 OK (empty body is fine)
Microsoft’s docs: “The WebHook-Request-Origin header MUST be included in the validation request… Reply including WebHook-Allowed-Origin (and WebHook-Allowed-Rate).”
Example: Validation Request → Response
Request (from Azure):
OPTIONS /api/events HTTP/1.1
Host: your-api.example.com
WebHook-Request-Origin: eventgrid.azure.com
WebHook-Request-Rate: 120
Response (your server):
HTTP/1.1 200 OK
WebHook-Allowed-Origin: eventgrid.azure.com
WebHook-Allowed-Rate: 120
Allow: POST, OPTIONS
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, OPTIONS
Access-Control-Allow-Headers: Content-Type, WebHook-Request-Origin, WebHook-Request-Callback, WebHook-Request-Rate, Origin
Implementation Details
Accept Event Deliveries (HTTP POST)
Event Grid delivers CloudEvents using structured content mode:
Content-Type: application/cloudevents+json; charset=utf-8(single event)
CloudEvent (single) — Example
Request:
POST /api/events HTTP/1.1
Content-Type: application/cloudevents+json; charset=utf-8
{
"specversion": "1.0",
"type": "Contoso.OrderCreated",
"source": "/contoso/orders",
"id": "a4f8f7e2-6e6f-4e3e-9f6b-80d3f0f0db31",
"time": "2025-08-20T16:20:00Z",
"datacontenttype": "application/json",
"data": {
"orderId": "12345",
"status": "created"
}
}
Your response:
Reliability & Retries
Event Grid retries on non-success responses or timeouts.
- Defaults: up to 30 attempts or 1440 minutes TTL (whichever first).
- You can configure both max attempts (1–30) and event TTL (1–1440 minutes) on the subscription.
Event Grid uses an exponential backoff (with some randomization) and may adjust schedule if your endpoint appears unhealthy.
Security Options
- HTTPS only (recommended with modern TLS).
- Idempotency: Use the CloudEvent
id+sourceto deduplicate (at-least-once delivery).
CORS
If the webhook is called server-to-server (typical), CORS isn’t required. If you still need it for tooling, return:
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, OPTIONS
Access-Control-Allow-Headers: Content-Type, WebHook-Request-Origin, WebHook-Request-Callback, WebHook-Request-Rate, Origin
(These are not part of CloudEvents validation, just standard CORS.)
Reference Server Snippet (Python/Flask)
Your Flask OPTIONS handler should mirror Azure’s CloudEvents handshake:
# Handle OPTIONS requests for CloudEvent validation
@app.route("/api/events", methods=["OPTIONS"])
def handle_options():
origin = request.headers.get("WebHook-Request-Origin")
if not origin:
app.logger.info("Missing WebHook-Request-Origin header")
return "", 400
response = make_response("")
response.headers["WebHook-Allowed-Origin"] = origin
response.headers["WebHook-Allowed-Rate"] = "120" # requests per minute
response.headers["Allow"] = "POST, OPTIONS"
# Optional CORS (not required for CloudEvents itself)
response.headers["Access-Control-Allow-Origin"] = "*"
response.headers["Access-Control-Allow-Methods"] = "POST, OPTIONS"
response.headers["Access-Control-Allow-Headers"] = (
"Content-Type, WebHook-Request-Origin, WebHook-Request-Callback, WebHook-Request-Rate, Origin"
)
app.logger.info(f"CloudEvent validation successful for origin: {origin}")
return response, 200
Troubleshooting
- 400 on validation: Ensure you received
WebHook-Request-Originand echoedWebHook-Allowed-Origincorrectly. - Wrong header casing: Some hosting stacks normalize headers differently; be liberal in reading header case. Known issue noted by community.
- Unsupported media type: For deliveries, accept
application/cloudevents+json(and-batch+json) with UTF-8. - Repeated deliveries: Expect at-least-once; deduplicate by
id/source. - Persistent failures: Check Event Grid subscription retry policy / TTL and dead-letter settings.
- General validation issues: Microsoft’s “Endpoint validation with CloudEvents schema” and troubleshooting page.
Authoritative References
- Microsoft: Endpoint validation with CloudEvents schema.
- Microsoft: CloudEvents v1.0 schema with Azure Event Grid.
- Microsoft: Delivery and retry (defaults & limits).
- CloudEvents project (spec & concepts).
- Content type requirements for CloudEvents HTTP deliveries.
Copy-paste checklist for your team
OPTIONShandler echoesWebHook-Allowed-Origin(fromWebHook-Request-Origin).- Return
WebHook-Allowed-Rate(match or*). Allow: POST, OPTIONS.POSThandler acceptsapplication/cloudevents+jsonandapplication/cloudevents-batch+json.- Idempotency by
id+source. - HTTPS enforced; optional secret header check.
- Monitoring and logs around non-2xx responses.
- Set Event Grid subscription retry/TTL as needed.