CXone API Authentication: client_credentials grant returning 401 in Azure Function

Configuration is broken for some reason…

I am trying to implement a service-to-service authentication flow for CXone APIs using the client_credentials grant type from a .NET 6 Azure Function. The goal is to fetch data without user context, but I consistently receive a 401 Unauthorized response when exchanging the client ID and secret for an access token.

Here is my current implementation using HttpClient:

var client = new HttpClient();
var content = new FormUrlEncodedContent(new[]
{
 new KeyValuePair<string, string>("grant_type", "client_credentials"),
 new KeyValuePair<string, string>("client_id", _configuration["CxoneClientId"]),
 new KeyValuePair<string, string>("client_secret", _configuration["CxoneClientSecret"])
});

var response = await client.PostAsync("https://api.custhelp.com/oauth2/token", content);
var responseBody = await response.Content.ReadAsStringAsync();

Steps to reproduce:

  1. Define CxoneClientId and CxoneClientSecret in Azure Key Vault (accessible via Managed Identity).
  2. Trigger the Azure Function.
  3. The function executes the PostAsync call to the CXone OAuth endpoint.
  4. The response status code is 401 Unauthorized.
  5. The response body contains {"error": "invalid_client", "error_description": "Client authentication failed"}.

I have verified the following:

  • The Client ID and Secret are correct (copied directly from the CXone Developer Console).
  • The endpoint URL is correct (https://api.custhelp.com/oauth2/token).
  • The content type header is automatically set to application/x-www-form-urlencoded by FormUrlEncodedContent.

Is there a specific header requirement for CXone that differs from standard OAuth2? Or is the client_credentials grant restricted in certain CXone environments? I am used to Genesys Cloud where this flow works seamlessly with similar code. Any insights on CXone-specific auth quirks would be appreciated.