Skip to main content
The Corti.Sdk NuGet package is the official C# .NET SDK for the Corti API. It provides full coverage of every REST endpoint and WebSocket connection, with built-in authentication, automatic token refresh, async/await support, and typed exceptions.
Package: Corti.Sdk on NuGet | Examples: corti-examples

Prerequisites

  • .NET 8 or later (also supports .NET Framework 4.6.2+ and .NET Standard 2.0)
  • A Corti API client from the API Console

Installation

dotnet add package Corti.Sdk

Initialization

The simplest way to get started is with client credentials (server-side):
C# .NET
using Corti;

var client = new CortiClient(
    tenantName: "YOUR_TENANT_NAME",
    environment: "YOUR_ENVIRONMENT_ID",
    auth: CortiClientAuth.ClientCredentials(
        clientId: "YOUR_CLIENT_ID",
        clientSecret: "YOUR_CLIENT_SECRET")
);
Client credentials authentication is intended for backend / server-side applications only. Never expose your client secret in client-side code. For frontend scenarios, use bearer tokens or pass tokens from your backend.
Create a single CortiClient instance and reuse it throughout your application. The SDK handles token refresh automatically — there is no need to re-initialize for each request.

Authentication

The SDK supports six authentication methods to cover different deployment scenarios:
MethodEnvironmentUse case
Client Credentials (recommended)Backend onlyServer-to-server integrations
Authorization CodeBackendInteractive user login (confidential clients)
PKCEFrontend & BackendInteractive user login (public clients)
ROPCBackend onlyUsername/password for trusted apps
Bearer TokenFrontend & BackendWhen you already have a token from another source
Custom RefreshFrontend & BackendYou manage token refresh yourself
Client Credentials is the recommended authentication method. Your backend should handle all data access checks and never expose the service-account token or client secret to the browser. The other flows (Authorization Code, PKCE, ROPC) are primarily intended for Embedded Assistant use cases where end-user login is required.
Each method is covered in detail in the Authentication Guide.

Usage examples

Create an interaction

C# .NET
var now = DateTime.UtcNow;

var interaction = await client.Interactions.CreateAsync(new InteractionsCreateRequest
{
    AssignedUserId = "GUID OF YOUR CHOOSING",
    Encounter = new InteractionsEncounterCreateRequest
    {
        Identifier = "GUID OF YOUR CHOOSING",
        Status = InteractionsEncounterStatusEnum.Planned,
        Type = InteractionsEncounterTypeEnum.FirstConsultation,
        Period = new InteractionsEncounterPeriod
        {
            StartedAt = now,
            EndedAt = now,
        },
        Title = "Consultation",
    },
    Patient = new InteractionsPatient
    {
        Identifier = "<string>",
        Name = "<string>",
        Gender = InteractionsGenderEnum.Male,
        BirthDate = now,
        Pronouns = "<string>",
    },
});
See the full request specification in the API Reference.

Real-time transcription (WebSocket)

C# .NET
var transcribe = await client.CreateTranscribeApiAsync();

transcribe.TranscribeTranscriptMessage.Subscribe(message =>
{
    Console.WriteLine($"Transcript: {message.Data.Text}");
});

await transcribe.ConnectAsync(new TranscribeConfig
{
    PrimaryLanguage = "en",
    AutomaticPunctuation = true,
});

// Send audio data (e.g. from a microphone stream)
await transcribe.Send(audioBytes);

Text generation

C# .NET
var document = await client.Documents.CreateAsync(
    interactionId,
    new DocumentsCreateRequestWithTemplateKey
    {
        Context =
        [
            DocumentsContext.FromDocumentsContextWithTranscript(
                new DocumentsContextWithTranscript
                {
                    Type = DocumentsContextWithTranscriptType.Transcript,
                    Data = new CommonTranscriptRequest
                    {
                        Text =
                            "tell me a bit about what's been going on? When did you first start feeling unwell?\n\n- Erm, I think it was probably about a week ago.\n\n- Right, okay. And when you say achy, was it more like muscle aches, or joint pain?",
                    },
                }
            ),
        ],
        TemplateKey = "corti-brief-clinical-note",
        Name = "test brief note from transcript",
        OutputLanguage = "en",
    }
);

Error handling

The SDK throws typed exceptions you can catch and inspect:
try
{
    await client.Interactions.GetAsync("non-existent-id");
}
catch (CortiClientApiException ex)
{
    Console.WriteLine($"Status: {ex.StatusCode}");
    Console.WriteLine($"Message: {ex.Message}");
    Console.WriteLine($"Body: {ex.Body}");
}

Advanced configuration

Retries

The SDK retries failed requests with exponential backoff when the response is retryable and the attempt count is below the limit (default: 2). A response is retryable when the status code is 408, 429, or any 5xx. Override per request:
await client.Interactions.CreateAsync(
    new InteractionsCreateRequest { /* ... */ },
    new RequestOptions { MaxRetries = 0 }
);
Set defaults for all requests via CortiRequestOptions on the client:
var client = new CortiClient(
    tenantName: "YOUR_TENANT",
    environment: "YOUR_ENVIRONMENT_ID",
    auth: CortiClientAuth.ClientCredentials(
        clientId: "YOUR_CLIENT_ID",
        clientSecret: "YOUR_CLIENT_SECRET"
    ),
    requestOptions: new CortiRequestOptions
    {
        MaxRetries = 3,
        Timeout = TimeSpan.FromMinutes(2),
    }
);

Timeouts

The default request timeout is 30 seconds. Override per request:
await client.Interactions.CreateAsync(
    new InteractionsCreateRequest { /* ... */ },
    new RequestOptions { Timeout = TimeSpan.FromSeconds(120) }
);
Or set a default timeout together with other options using CortiRequestOptions — see the example above.

Raw responses

Methods that return WithRawResponseTask<T> let you await either the parsed model or parsed data plus HTTP metadata. Call .WithRawResponse() on the task to get status code, URL, and headers alongside Data:
var result = await client.Interactions.GetAsync("YOUR_INTERACTION_ID").WithRawResponse();

var interaction = result.Data;

Console.WriteLine(result.RawResponse.StatusCode);
Console.WriteLine(result.RawResponse.Url);

if (result.RawResponse.Headers.TryGetValue("X-Request-Id", out var requestId))
{
    Console.WriteLine($"Request ID: {requestId}");
}
Paginated ListAsync helpers return a Pager and do not expose .WithRawResponse(). Use a non-paginated endpoint method that returns WithRawResponseTask<T> when you need raw HTTP metadata.

Pagination

List endpoints are paginated. The SDK returns a pager you can iterate with await foreach:
var pager = await client.Interactions.ListAsync(new InteractionsListRequest());

await foreach (var interaction in pager)
{
    Console.WriteLine(interaction.InteractionId);
}

Additional headers

await client.Interactions.CreateAsync(
    new InteractionsCreateRequest { /* ... */ },
    new RequestOptions
    {
        AdditionalHeaders = new Dictionary<string, string?>
        {
            ["X-Custom-Header"] = "custom-value",
        },
    });

Additional query parameters

await client.Interactions.ListAsync(
    new InteractionsListRequest(),
    new RequestOptions
    {
        AdditionalQueryParameters = new Dictionary<string, string>
        {
            ["custom_param"] = "custom-value",
        },
    });

Forward-compatible enums

Enum-like types in the SDK accept unknown API values without failing deserialization. You can use built-in values, custom string values from the API, and inspect .Value in a switch:
var sort = InteractionsListRequestSort.Id;
var custom = InteractionsListRequestSort.FromCustom("future-sort-key");

switch (sort.Value)
{
    case InteractionsListRequestSort.Values.Id:
        Console.WriteLine("Id");
        break;

    default:
        Console.WriteLine($"Unknown or forward-compatible: {sort.Value}");
        break;
}

string asString = (string)InteractionsListRequestSort.Id;
InteractionsListRequestSort fromString = (InteractionsListRequestSort)"id";

Custom HttpClient

Supply your own HttpClient for advanced networking (defaults, handlers, proxies):
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Add("X-Custom", "value");

var client = new CortiClient(
    tenantName: "YOUR_TENANT",
    environment: "YOUR_ENVIRONMENT_ID",
    auth: CortiClientAuth.ClientCredentials(
        clientId: "YOUR_CLIENT_ID",
        clientSecret: "YOUR_CLIENT_SECRET"
    ),
    requestOptions: new CortiRequestOptions
    {
        HttpClient = httpClient,
    });

Proxy / passthrough mode

For scenarios where your server proxies requests to Corti and handles auth externally, you can create a client with only an environment (no credentials):
var env = CortiEnvironments.FromBaseUrl("https://your-proxy.com/api");

var client = new CortiClient(environment: env);
No Authorization header is sent by default. Add your own headers per request using RequestOptions.AdditionalHeaders. See the Authentication Guide for details.

Resources


For support or questions, reach out through help.corti.app