Hello Efraim Paley,
Thanks for sharing the contexts. The 415 error happens because Microsoft Graph expects the validation token as plain text, but your endpoint is returning JSON.
When Microsoft Graph creates a subscription, it first validates your endpoint by sending a POST request with a validationToken query parameter. Your endpoint must return that exact token as plain text, not wrapped in JSON.
Most ASP.NET Core implementations use Ok() which returns JSON by default:
return Ok(validationToken); // Returns: {"validationToken":"abc123..."}
Microsoft Graph sees the JSON and rejects it with a 415 error.
My recommendations:
Return plain text instead:
return Content(validationToken, "text/plain"); // Returns: abc123...
Verify before creating your subscription:
Test your endpoint manually:
curl "https://your-endpoint/api/webhook?validationToken=test123"
Correct response: test123 (plain text)
Wrong response: {"validationToken":"test123"} (JSON)
Here's my complete controller handling both validation and notifications:
[HttpPost]
public async Task<IActionResult> HandleWebhook([FromQuery] string? validationToken)
{
// Validation phase
if (!string.IsNullOrEmpty(validationToken))
{
return Content(validationToken, "text/plain");
}
// Notification phase
using var reader = new StreamReader(Request.Body);
var requestBody = await reader.ReadToEndAsync();
var notifications = JsonSerializer.Deserialize<ChangeNotificationCollection>(
requestBody,
new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
// Process notifications...
return Accepted();
}
Here is my screenshot showing successful subscription creation.
Hope this helps!