Forums

Articles
Create
cancel
Showing results for 
Search instead for 
Did you mean: 

Xray Cloud returning 401 in some cases

Jubin M Jacob
May 7, 2026

I am seeing a strange behavior while authenticating with the Xray Cloud API from a C# backend application.

Below is the code I initially used for authentication:

private async Task SetAuthHeaderAsync(HttpClient client, string clientId, string clientSecret)
{
    var authTokenRequest = new JObject
    {
        ["client_id"] = clientId,
        ["client_secret"] = clientSecret,
    };

    var httpResponse = await client.PostAsync(
        "authenticate",
        CreateHttpContent(authTokenRequest));

    if (httpResponse.StatusCode == HttpStatusCode.OK)
    {
        var authToken = await DeserializeResponseAsync<string>(httpResponse);

        client.DefaultRequestHeaders.Authorization =
            new AuthenticationHeaderValue(
                XRayConnectorConstants.BearerAuthScheme,
                authToken);

        return;
    }

    var message = httpResponse.StatusCode == HttpStatusCode.Unauthorized
        ? ErrorCodes.Validation_XRayConnector_Client_Id_Or_Secret_Incorrect
        : ErrorCodes.Validation_XRayConnector_Connection_Not_Reachable;

    _logger.LogError(
        "Xray authentication failed with StatusCode: {statusCode} and having Response: {response}",
        httpResponse.StatusCode,
        await httpResponse.Content.ReadAsStringAsync());

    throw new HttpClientApiException(message, httpResponse.StatusCode);
}


private HttpContent CreateHttpContent(object content) { HttpContent httpContent = null; if (content != null) { var ms = new MemoryStream(); SerializeJsonIntoStream(content, ms); ms.Seek(0, SeekOrigin.Begin); httpContent = new StreamContent(ms); httpContent.Headers.ContentType = new MediaTypeHeaderValue(MediaTypeNames.Application.Json); } return httpContent; }
 

Behavior observed

So initially I assumed this was related to Xray data residency.

However, things get interesting here:

Calling the global endpoint (https://xray.cloud.getxray.app/api/v2/) works perfectly from:

  • Postman

  • curl

And even from the same C# backend, it starts working if I change the request content creation logic to this:

private HttpContent CreateHttpContent(object content)
{
    if (content == null)
    {
        return null;
    }

    var json = JsonConvert.SerializeObject(content);

    return new StringContent(
        json,
        Encoding.UTF8,
        MediaTypeNames.Application.Json);
}

After this change, the global endpoint also returns 200 OK.

So now I am confused about what exactly is happening here:

  • Is Xray validating something specific in the request body formatting?

  • Is there some difference between StreamContent and StringContent that affects authentication?

  • Could this be related to content length, encoding, or serialization behavior?

Has anyone faced something similar with the Xray API?

0 answers

Suggest an answer

Log in or Sign up to answer