So I have been trying to create a Azure graph subscription API , the publicly accessible endpoint is
https://omstest.shiplogiq.dev/webhook/graph_listener
POST API URL:
https://graph.microsoft.com/v1.0/subscriptions
body passed as JSON is
{
"changeType" : "updated",
"notificationUrl": "https://omstest.shiplogiq.dev/webhook/graph_listener",
"resource": "users",
"expirationDateTime": "2025-03-28T09:00:07Z",
"clientState": "SecretClientStateValue"
}
along with authentication bearer
method code in codeigiter is
public function graph_listener()
{
// 1) Check GET parameter for validationToken
$validationToken = $this->input->get('validationToken');
if ($validationToken) {
header('Content-Type: text/plain');
echo $validationToken;
return; // Respond 200 with token
}
// 2) Check POST (JSON) for validationToken
$postBody = $this->input->raw_input_stream;
$postData = json_decode($postBody, true);
if (isset($postData['validationToken'])) {
header('Content-Type: text/plain');
echo $postData['validationToken'];
return; // Respond 200 with token
}
// 3) If no validation token, it's a real notification
$this->handle_graph_notifications($postData);
// 4) Return 200 OK so Graph knows we processed it
http_response_code(200);
}
These are the API permissions in Azure:
URL set in Azure
but i am getting error response
{
"error": {
"code": "ValidationError",
"message": "Subscription validation request failed. Notification endpoint must respond with 200 OK to validation request.",
"innerError": {
"date": "2025-03-26T13:18:17",
"request-id": "b008367a-60a5-4589-86a8-db95c5da9bba",
"client-request-id": "b008367a-60a5-4589-86a8-db95c5da9bba"
}
}
}
What am i doing wrong? its driving me nuts
Thanks in advance


200 OKstatus and the exactvalidationTokenin the response body as plain text. Make sure the endpoint is publicly accessible and reachable by Microsoft Graph, with the correctContent-Type: text/plainheader. Test your endpoint with a manualGETrequest containing thevalidationTokento verify it works as expected.