André Lynum
02/19/2026, 9:31 AMMarvin
02/19/2026, 9:31 AMMarvin
02/19/2026, 9:36 AMAuthorization: Bearer <PREFECT_API_KEY>
- In the Azure Portal, add this as an Additional/Delivery header on the Event Subscription (it must be present on the validation request too).
- If you see 401/403 in the Event Grid “last attempt” details, it’s an auth issue.
3) Delivery schema
- Event Grid Schema and CloudEvents v1.0 are both supported by Prefect Cloud webhooks.
- If validation keeps failing, try switching the subscription to CloudEvents v1.0 and re-validate.
4) Sanity check with curl
- If no auth on the webhook:
curl -i -X POST "<your-prefect-webhook-url>"
Expect 204 No Content.
- If the webhook requires auth:
curl -i -X POST "<your-prefect-webhook-url>" -H "Authorization: Bearer <PREFECT_API_KEY>"
Expect 204 No Content.
- 401/403 = add the Authorization header to the Event Grid subscription. 404 = wrong URL or webhook deleted.
5) Check Event Grid diagnostics
- In the Event Subscription blade, look at the validation attempt status code/response:
- 401/403 → missing/invalid Authorization header
- 404 → wrong endpoint
- 5xx → temporary issue; retry
- TLS/cert errors → ensure your environment allows outbound to Prefect Cloud
Minimal Prefect webhook templates for Azure payloads (after validation succeeds):
- If you selected CloudEvents v1.0 in Event Grid:
{{ body|from_cloud_event(headers) }}
- If you selected Event Grid Schema (Azure posts an array):
{% set ev = body[0] if body is iterable else body %}
{
"event": "azure.eventgrid",
"resource": {
"prefect.resource.id": "azure.{{ ev.eventType }}",
"azure.subject": "{{ ev.subject }}"
},
"payload": {{ ev.data|tojson }}
}
Docs for reference:
- Prefect webhooks overview and Azure Event Grid notes: Webhooks (Prefect 3.x)
- How to create and use a webhook: Create a webhook (Prefect Cloud)
- Azure endpoint validation (what Event Grid expects): Azure: Endpoint validation
Could you share:
- Which delivery schema you chose (Event Grid schema vs CloudEvents v1.0)?
- Whether your Prefect webhook requires auth (and if you added the Authorization header to the Azure subscription)?
- The status code/response shown in the Event Grid validation error details?
- A quick curl test result against your webhook (redact IDs/keys)?
With those details I can pinpoint the exact cause and get you unblocked quickly.André Lynum
02/19/2026, 9:59 AMMarvin
02/19/2026, 10:00 AMMarvin
02/19/2026, 10:05 AMcurl -i -X OPTIONS "<your-prefect-webhook-url>" -H "WebHook-Request-Origin: <http://eventgrid.azure.net|eventgrid.azure.net>"
Expected: 200 with a WebHook-Allowed-Origin header. If you get a 5xx here, that explains the CloudEvents failure.
- Test the Event Grid schema validation POST:
curl -i -X POST "<your-prefect-webhook-url>" \
-H "Content-Type: application/json" \
-d '[{
"id":"1",
"eventType":"Microsoft.EventGrid.SubscriptionValidationEvent",
"subject":"test",
"data":{"validationCode":"abc123","validationUrl":"<https://example>"},
"eventTime":"2024-01-01T00:00:00Z",
"metadataVersion":"1",
"dataVersion":"1",
"topic":"/subscriptions/.../resourceGroups/.../providers/Microsoft.EventGrid/topics/..."
}]'
Expected: a 200 with a JSON body like {"validationResponse":"abc123"}. If you get a 204/empty or anything else, the Event Grid schema validation will fail.
If either of those tests fails, that points to the webhook endpoint not handling Azure’s validation handshake as expected. We can dig in on our side if you share:
- Webhook ID (the last path segment of the URL)
- Approximate timestamps (with timezone) of the failing attempts
- The Azure region of your Event Grid topic
- Whether you’re using Event Grid system topic vs. custom topic, and which source service (e.g., Storage)
Immediate workaround (reliable and commonly used):
Put a tiny Azure Function/Logic App in front that:
- Responds to CloudEvents handshake (OPTIONS) by echoing WebHook-Allowed-Origin
- Responds to SubscriptionValidationEvent by echoing the validationResponse
- For all other events, forwards the request to your Prefect webhook URL
Example Azure Function (Python, minimal):
import json
import os
import requests
PREFECT_WEBHOOK_URL = os.environ["PREFECT_WEBHOOK_URL"]
def main(req):
# Handle CloudEvents webhook handshake (OPTIONS)
if req.method == "OPTIONS":
origin = req.headers.get("WebHook-Request-Origin", "*")
return {
"status_code": 200,
"headers": {"WebHook-Allowed-Origin": origin},
"body": ""
}
# Parse JSON body (Event Grid schema can be an array)
try:
body = req.get_json()
except Exception:
body = None
# Handle Event Grid schema validation event (POST)
if isinstance(body, list) and body and body[0].get("eventType") == "Microsoft.EventGrid.SubscriptionValidationEvent":
code = body[0]["data"]["validationCode"]
return {
"status_code": 200,
"headers": {"Content-Type": "application/json"},
"body": json.dumps({"validationResponse": code})
}
# Forward everything else to Prefect
headers = {k: v for k, v in req.headers.items() if k.lower() != "host"}
resp = requests.request(req.method, PREFECT_WEBHOOK_URL, headers=headers, data=req.get_body())
return {
"status_code": resp.status_code,
"headers": dict(resp.headers),
"body": resp.text
}
Then point Event Grid at the Function URL, and set PREFECT_WEBHOOK_URL to your Prefect webhook. This lets Azure complete validation and still delivers events to Prefect.
Docs for context:
- Prefect webhooks overview: Webhooks (Azure Event Grid notes)Marvin
02/19/2026, 10:05 AM