# C# SDK for REST APIs

> For the complete documentation index, see [llms.txt](https://developers.thoughtspot.com/docs/llms.txt)

Source: https://developers.thoughtspot.com/docs/rest-api-sdk-csharp

# C# SDK for REST APIs

The [REST API C# SDK](https://github.com/thoughtspot/rest-api-sdk/tree/release/sdks/csharp) provides a client library to interact with ThoughtSpot REST API v2 endpoints from `.NET` applications. The SDK targets `net8.0` and ships both synchronous and asynchronous variants of every API method.

The SDK package is available on [NuGet](https://www.nuget.org/packages/ThoughtSpot.RestApi.Sdk).

## Before you begin

Before you begin, check the following prerequisites:

-   Your environment targets .NET 8 (`net8.0`) or later.
    
-   You have access to a ThoughtSpot instance and the following information:
    
    -   The URL of your ThoughtSpot instance
        
    -   User credentials (username and password, or a secret key for trusted authentication)
        
    
-   You have user privileges and object permissions to view, edit, or create ThoughtSpot objects and resources.
    

## Import the SDK

Install the package:

Using the .NET CLI

```bash
dotnet add package ThoughtSpot.RestApi.Sdk --version 2.27.0
```

Using the NuGet Package Manager console

```bash
Install-Package ThoughtSpot.RestApi.Sdk -Version 2.27.0
```

## API client configuration

All SDK clients are configured with an `ApiClientConfiguration` record. Provide your ThoughtSpot instance URL and one authentication option, then build your client using `CreateAsync`.

`CreateAsync` is the recommended entry point. It is required for server-sent event (SSE) streaming methods and for automatic token refresh when using `Username`+`Password`, `Username`+`SecretKey`, or `TokenProvider`.

```csharp
using ThoughtSpot.RestApi.Sdk;
using ThoughtSpot.RestApi.Sdk.Api;
using ThoughtSpot.RestApi.Sdk.Model;

var config = new ApiClientConfiguration
{
    Host     = "https://your-thoughtspot-instance.thoughtspot.cloud",
    Username = "your-username",
    Password = "your-password",
};

var api = await ThoughtSpotRestApi.CreateAsync(config);
```

### Configuration options

  
| Option | Default | Description |
| --- | --- | --- |
| 
`Host`

 | 

—

 | 

Required. Base URL of your ThoughtSpot instance, for example, `https://my-cluster.thoughtspot.cloud`.

 |
| 

`Username` / `Password`

 | 

`null`

 | 

Credentials for password-based authentication. The SDK fetches and refreshes a bearer token automatically.

 |
| 

`Username` / `SecretKey`

 | 

`null`

 | 

Credentials for trusted authentication. Use when Trusted authentication is enabled on your instance.

 |
| 

`TokenProvider`

 | 

`null`

 | 

An async callback (`Func<CancellationToken, Task<string>>`) invoked before every request. You own caching and refresh logic inside this function.

 |
| 

`BearerToken`

 | 

`null`

 | 

Static bearer token. Does not refresh. Requests fail with 401 after the token expires. Use `TokenProvider` or `CreateAsync` for automatic refresh instead.

 |
| 

`TokenValiditySeconds`

 | 

`300`

 | 

How long (in seconds) a fetched token is considered valid before the SDK refreshes it. The value is sent to the server and used client-side.

 |
| 

`ConnectTimeout`

 | 

60 seconds

 | 

TCP connection establishment timeout. Matches the Java SDK’s `connectTimeoutMillis` default.

 |
| 

`ReadTimeout`

 | 

300 seconds

 | 

Time allowed to read a response after the connection is established. Matches the Java SDK’s `readTimeoutMillis` default.

 |
| 

`WriteTimeout`

 | 

300 seconds

 | 

Time allowed to send a request body. Matches the Java SDK’s `writeTimeoutMillis` default.

 |
| 

`IgnoreSslErrors`

 | 

`false`

 | 

Disables SSL certificate validation. Use only for development or test environments with self-signed certificates.

 |
| 

`EnableRetries`

 | 

`false`

 | 

Set to `true` to enable the built-in Polly retry pipeline.

 |
| 

`RetryPipeline`

 | 

`null`

 | 

A custom Polly `ResiliencePipeline<HttpResponseMessage>`. Used only when `EnableRetries` is `true`. Falls back to `RetryConfiguration.Default` when `null`.

 |
| 

`DefaultHeaders`

 | 

Empty

 | 

Headers added to every outgoing request.

 |

## Authentication

The SDK supports the following authentication modes. These modes use automatic token management and require `CreateAsync`.

-   [Username and password]({{navprefix}}/{{rest-api-csharp-sdk}}#username-and-password)
    
-   [Username and secret key]({{navprefix}}/{{rest-api-csharp-sdk}}#username-and-secret-key)
    
-   [Token provider]({{navprefix}}/{{rest-api-csharp-sdk}}#token-provider)
    

> **NOTE:** The SDK also accepts a static BearerToken. However, a static token does not refresh and when it expires, all requests fail with HTTP 401. Use the Username and Password, Username and SecretKey, or TokenProvider modes instead.

### Username and password

The SDK calls the `fullAccessToken` API internally with the provided credentials to obtain a bearer token on startup. The token is cached and refreshed automatically 30 seconds before it expires. No token management is required in your application code.

```csharp
var config = new ApiClientConfiguration
{
    Host     = "https://your-thoughtspot-instance.thoughtspot.cloud",
    Username = "your-username",
    Password = "your-password",
};

var api = await ThoughtSpotRestApi.CreateAsync(config);
var me = await api.GetCurrentUserInfoAsync();
Console.WriteLine($"Logged in as: {me.Name}");
```

### Username and secret key (trusted authentication)

Use this mode when Trusted authentication is enabled on your ThoughtSpot instance. The SDK calls the `fullAccessToken` API internally with the provided username and [secret key]({{navprefix}}/{{trusted-auth-secret-key}}) to obtain a bearer token on startup. The token is cached and refreshed automatically 30 seconds before it expires. No token management is required in your application code.

```csharp
var config = new ApiClientConfiguration
{
    Host      = "https://your-thoughtspot-instance.thoughtspot.cloud",
    Username  = "your-username",
    SecretKey = "your-secret-key",
};

var api = await ThoughtSpotRestApi.CreateAsync(config);
```

### Token provider

Use this mode when you manage tokens externally, for example, through an identity provider or a secrets vault. The `TokenProvider` delegate is invoked before every request. Implement your own caching and refresh logic inside the delegate.

```csharp
var config = new ApiClientConfiguration
{
    Host          = "https://your-thoughtspot-instance.thoughtspot.cloud",
    TokenProvider = async cancellationToken =>
        await myIdentityProvider.FetchBearerAsync(cancellationToken),
};

var api = await ThoughtSpotRestApi.CreateAsync(config);
```

## Per-tag API classes and the aggregate client

The SDK exposes the ThoughtSpot REST API surface through two complementary access styles:

-   **28 per-tag API classes**: Each covers one functional area. Use a focused class when you only need a narrow surface (for example, `UsersApi`, `MetadataApi`, or `AIApi`).
    
-   **`ThoughtSpotRestApi`**: Aggregates all 28 API classes behind a single object. Use this when your application calls endpoints across multiple areas.
    

Both styles are created with `CreateAsync` and take the same `ApiClientConfiguration`:

```csharp
// Using the aggregate client
var api = await ThoughtSpotRestApi.CreateAsync(config);
var users = await api.SearchUsersAsync(new SearchUsersRequest());

// Using a focused per-tag class
var usersApi = await UsersApi.CreateAsync(config);
var users    = await usersApi.SearchUsersAsync(new SearchUsersRequest());
```

## Synchronous and asynchronous usage

Every method has both an asynchronous variant (`XxxAsync`) and a blocking synchronous variant (the same name without the `Async` suffix). Use the synchronous variant from code that cannot use `await`.

```csharp
// Async (recommended)
var me = await api.GetCurrentUserInfoAsync();

// Synchronous (blocking)
var me = api.GetCurrentUserInfo();
Console.WriteLine(me.Name);
```

## Access response status and headers

Every method has a `WithHttpInfo` / `WithHttpInfoAsync` variant that returns an `ApiResponse<T>` wrapping the HTTP status code, response headers, and deserialized data.

```csharp
var response = await api.GetCurrentUserInfoWithHttpInfoAsync();
Console.WriteLine(response.StatusCode);  // e.g. 200
Console.WriteLine(response.Data.Name);
```

## Streaming (SSE)

Endpoints that return server-sent events (SSE) expose a `XxxStreamAsync` method returning `IAsyncEnumerable<string>`. This enables real-time streaming of AI responses from Spotter endpoints. Streaming requires the API class to be built with `CreateAsync`.

```csharp
var aiApi = await AIApi.CreateAsync(config);

await foreach (var chunk in aiApi.SendAgentConversationMessageStreamingStreamAsync(
    conversationIdentifier: conversationId,
    sendAgentConversationMessageStreamingRequest: new SendAgentConversationMessageStreamingRequest
    {
        Messages = new List<string> { "What is the total revenue by region?" },
    }))
{
    Console.Write(chunk);
}
```

## File uploads

Multipart endpoints, for example, dbt and Style Customization, accept a `FileParameter` built from a `Stream` with an optional filename and content type.

```csharp
var dbtApi = await DbtApi.CreateAsync(config);

await using var stream = File.OpenRead("project.zip");
await dbtApi.DbtConnectionAsync(
    connectionName: "my-connection",
    databaseName:   "MY_DB",
    importType:     "ZIP_FILE",
    fileContent:    new FileParameter("project.zip", stream));
```

> **NOTE:** The SDK automatically rewinds seekable upload streams before each retry attempt. If a stream is not seekable, the SDK aborts with a non-retryable error rather than sending incomplete data.

## File downloads

Export endpoints return a `FileParameter` wrapping the response stream, filename, and content type.

```csharp
var reportsApi = await ReportsApi.CreateAsync(config);

var file = await reportsApi.ExportLiveboardReportAsync(
    new ExportLiveboardReportRequest
    {
        MetadataIdentifier = liveboardId,
        FileFormat         = "PDF",
    });

await using var output = File.Create("report.pdf");
await file.Content.CopyToAsync(output);
```

## Error handling

Failed calls throw `ThoughtSpot.RestApi.Sdk.Client.ApiException`. The exception exposes:

-   `ErrorCode`: the HTTP status code.
    
-   `Message`: a human-readable error message.
    
-   `ErrorContent`: the deserialized error response body.
    
-   `Headers`: the HTTP response headers.
    

```csharp
try
{
    await api.SearchUsersAsync(new SearchUsersRequest());
}
catch (ThoughtSpot.RestApi.Sdk.Client.ApiException ex)
{
    Console.WriteLine($"{ex.ErrorCode}: {ex.Message}");
    Console.WriteLine(ex.ErrorContent);
}
```

## Retries

Retries are disabled by default. Set `EnableRetries = true` on `ApiClientConfiguration` to enable the built-in Polly pipeline, up to three attempts with exponential backoff (1 s / 2 s / 4 s) and up to 500 ms of random jitter, applied on network errors and `429`, `500`, `502`, and `503` responses.

```csharp
var config = new ApiClientConfiguration
{
    Host          = "https://your-thoughtspot-instance.thoughtspot.cloud",
    Username      = "your-username",
    Password      = "your-password",
    EnableRetries = true,
};
```

To apply a custom pipeline to a single client instance, set `RetryPipeline`:

```csharp
var config = new ApiClientConfiguration
{
    Host          = "https://your-thoughtspot-instance.thoughtspot.cloud",
    Username      = "your-username",
    Password      = "your-password",
    EnableRetries = true,
    RetryPipeline = new ResiliencePipelineBuilder<HttpResponseMessage>()
        .AddRetry(new RetryStrategyOptions<HttpResponseMessage> { MaxRetryAttempts = 5 })
        .Build(),
};
```

To set a global fallback pipeline used by all instances that do not supply a `RetryPipeline`:

```csharp
RetryConfiguration.Default = new ResiliencePipelineBuilder<HttpResponseMessage>()
    .AddRetry(new RetryStrategyOptions<HttpResponseMessage> { MaxRetryAttempts = 5 })
    .Build();
```

## Runtime reconfiguration

You can swap the host, credentials, or timeouts at runtime without restarting your application. Call `ApplyConfigurationAsync` on any API client with a new `ApiClientConfiguration`.

The swap is atomic. In-flight requests complete against the old configuration before the underlying resources are disposed.

```csharp
var newConfig = config with
{
    Host     = "https://new-cluster.thoughtspot.cloud",
    Username = "new-username",
    Password = "new-password",
};

await api.ApplyConfigurationAsync(newConfig);
```

## Supported versions

 
| ThoughtSpot release | Recommended SDK version |
| --- | --- |
| 
ThoughtSpot Cloud 26.8.0.cl

 | 

v2.27.1 or later

 |

## Additional resources

-   [ThoughtSpot.RestApi.Sdk on NuGet](https://www.nuget.org/packages/ThoughtSpot.RestApi.Sdk)
    
-   [SDK source on GitHub](https://github.com/thoughtspot/rest-api-sdk/tree/release/sdks/csharp)
    
-   [REST API v2 authentication]({{navprefix}}/{{authentication}})
    
-   [REST API v2 Playground]({{navprefix}}/restV2-playground)
    
-   [REST API v2 reference]({{navprefix}}/{{rest-api-v2-reference}})
    
-   [REST API v2 changelog]({{navprefix}}/{{rest-apiv2-changelog}})