Skip to main content
The Corti.Sdk package exports two main entry points:
ExportNamespacePurpose
CortiClientusing Corti;Full API client with all resource groups, authentication, and WebSockets
CustomAuthClientusing Corti;Standalone authentication client for token management and OAuth flows

CortiClient

The main client for all Corti API operations.

Constructors

// 1. Tenant + environment + auth (most common)
new CortiClient(string tenantName, CortiEnvironmentInput environment, CortiClientAuth auth, CortiRequestOptions? requestOptions = null)

// 2. Bearer only — tenant and environment decoded from the JWT
new CortiClient(CortiClientBearerAuth auth, CortiRequestOptions? requestOptions = null)

// 3. Environment only — proxy / passthrough, no SDK-managed auth
new CortiClient(CortiEnvironmentInput environment, CortiRequestOptions? requestOptions = null)
CortiEnvironmentInput accepts a region string, a CortiClientEnvironment, or CortiEnvironments.FromBaseUrl(...) via implicit conversion. Overload 2 decodes tenant and environment from the access token JWT. If the token cannot be decoded, use overload 1 instead. Overload 3 sends no Authorization header. CreateStreamApiAsync / CreateTranscribeApiAsync require a tenant and will throw. See Proxy / passthrough.

CortiRequestOptions

PropertyTypeDefaultDescription
MaxRetriesint?2Number of automatic retries on 408, 429, and 5xx
TimeoutTimeSpan?30sDefault request timeout
HttpClientHttpClient?nullCustom HttpClient instance
AdditionalHeadersIEnumerable<KeyValuePair<string, string?>>?nullHeaders sent with every request

Request options

Every HTTP method accepts an optional RequestOptions parameter:
C# .NET
using System.Collections.Generic;
using Corti;

await client.Interactions.ListAsync(
    new InteractionsListRequest(),
    new RequestOptions
    {
        Timeout = TimeSpan.FromSeconds(30),
        MaxRetries = 3,
        AdditionalHeaders = new Dictionary<string, string?>
        {
            ["X-Request-Id"] = "abc123",
        },
    });
PropertyTypeDescription
BaseUrlstring?Override the base URL for this request
HttpClientHttpClient?Override the HttpClient for this request
MaxRetriesint?Override the default retry count
TimeoutTimeSpan?Override the default timeout
AdditionalHeadersIEnumerable<KeyValuePair<string, string?>>Additional HTTP headers
AdditionalQueryParametersIEnumerable<KeyValuePair<string, string>>Additional query string parameters
AdditionalBodyPropertiesobject?Additional JSON properties merged into the request body

Resource groups

The client exposes the following resource groups as properties:
GroupDescriptionAPI endpoints
client.InteractionsManage patient encounters and interaction sessionsInteractions API
client.RecordingsUpload and retrieve audio recordings for interactionsRecordings API
client.TranscriptsCreate and retrieve transcriptions of recordingsTranscripts API
client.DocumentsGenerate and manage AI-produced clinical documentsDocuments API
client.FactsExtract and manage structured clinical factsFacts API
client.TemplatesList and retrieve document generation templatesTemplates API
client.CodesPredict medical codes from clinical dataCodes API
client.AgentsCreate and interact with AI agentsAgents API
client.AuthOAuth token management and authorization URLsAuth API
WebSocket APIs are created via factory methods:
MethodDescriptionAPI endpoint
CreateStreamApiAsync(interactionId)Real-time WebSocket streaming (transcription + facts)Stream API
CreateTranscribeApiAsync()Real-time WebSocket speech-to-text (standalone)Transcribe API

Interactions

Manage interaction sessions that group recordings, transcripts, documents, and facts together.
C# .NET
using Corti;

// List interactions (pager — see Pagination)
var pager = await client.Interactions.ListAsync(new InteractionsListRequest());

// Create a new interaction
var response = await client.Interactions.CreateAsync(
    new InteractionsCreateRequest
    {
        Encounter = new InteractionsEncounterCreateRequest
        {
            Identifier = "my-encounter-id",
            Status = InteractionsEncounterStatusEnum.Planned,
            Type = InteractionsEncounterTypeEnum.FirstConsultation,
        },
    });

var interactionId = response.InteractionId;

// Get a specific interaction
var interaction = await client.Interactions.GetAsync(interactionId);

// Update an interaction
await client.Interactions.UpdateAsync(
    interactionId,
    new InteractionsUpdateRequest
    {
        Encounter = new InteractionsEncounterUpdateRequest
        {
            Status = InteractionsEncounterStatusEnum.InProgress,
        },
    });

// Delete an interaction
await client.Interactions.DeleteAsync(interactionId);
MethodParametersReturns
ListAsync(request)Filter and pagination optionsPager (async enumerable)
CreateAsync(request)Interaction definitionCreated interaction
GetAsync(id)Interaction UUIDSingle interaction
UpdateAsync(id, request?)Interaction UUID + update fieldsUpdated interaction
DeleteAsync(id)Interaction UUIDvoid

Recordings

Upload and manage audio recordings within an interaction.
C# .NET
using Corti;

// Upload a recording
var recording = await client.Recordings.UploadAsync(
    interactionId,
    File.OpenRead("audio.mp3"));

var recordingId = recording.RecordingId;

// List recordings for an interaction
var recordings = await client.Recordings.ListAsync(interactionId);

// Download a recording
var stream = await client.Recordings.GetAsync(interactionId, recordingId);

// Delete a recording
await client.Recordings.DeleteAsync(interactionId, recordingId);
MethodParametersReturns
UploadAsync(interactionId, file)Interaction UUID + file streamCreated recording
ListAsync(interactionId)Interaction UUIDList of recordings
GetAsync(interactionId, recordingId)Interaction UUID + recording UUIDAudio stream
DeleteAsync(interactionId, recordingId)Interaction UUID + recording UUIDvoid

Transcripts

Create transcriptions from uploaded recordings and retrieve results.
C# .NET
using Corti;

// Create a transcript from a recording
var transcript = await client.Transcripts.CreateAsync(
    interactionId,
    new TranscriptsCreateRequest
    {
        RecordingId = recordingId,
        PrimaryLanguage = "en",
    });

var transcriptId = transcript.Id;

// Check transcription status
var status = await client.Transcripts.GetStatusAsync(interactionId, transcriptId);

// List transcripts for an interaction
var transcripts = await client.Transcripts.ListAsync(interactionId);

// Get a specific transcript
var result = await client.Transcripts.GetAsync(interactionId, transcriptId);

// Delete a transcript
await client.Transcripts.DeleteAsync(interactionId, transcriptId);
MethodParametersReturns
CreateAsync(interactionId, request)Interaction UUID + transcript definitionCreated transcript
GetStatusAsync(interactionId, transcriptId)Interaction UUID + transcript UUIDTranscription status
ListAsync(interactionId, request?)Interaction UUID + optional filtersList of transcripts
GetAsync(interactionId, transcriptId)Interaction UUID + transcript UUIDSingle transcript
DeleteAsync(interactionId, transcriptId)Interaction UUID + transcript UUIDvoid

Documents

Generate AI-produced clinical documents (e.g. SOAP notes) from interaction data.
C# .NET
using Corti;

// Generate a document
var document = await client.Documents.CreateAsync(
    interactionId,
    new DocumentsCreateRequestWithTemplateKey
    {
        TemplateKey = "corti-soap",
        OutputLanguage = "en",
        Context = new[]
        {
            new DocumentsContextWithFacts
            {
                Type = DocumentsContextWithFactsType.Facts,
                Data = new[]
                {
                    new FactsContext { Text = "Patient reports persistent headache for 3 days", Source = CommonSourceEnum.Core },
                    new FactsContext { Text = "No history of migraines", Source = CommonSourceEnum.User },
                },
            },
        },
    });

var documentId = document.Id;

// List documents
var documents = await client.Documents.ListAsync(interactionId);

// Get a specific document
var doc = await client.Documents.GetAsync(interactionId, documentId);

// Update a document
await client.Documents.UpdateAsync(
    interactionId,
    documentId,
    new DocumentsUpdateRequest { Name = "Updated note" });

// Delete a document
await client.Documents.DeleteAsync(interactionId, documentId);
MethodParametersReturns
CreateAsync(interactionId, request)Interaction UUID + document generation optionsGenerated document
ListAsync(interactionId)Interaction UUIDList of documents
GetAsync(interactionId, documentId)Interaction UUID + document UUIDSingle document
UpdateAsync(interactionId, documentId, request?)Interaction UUID + document UUID + update fieldsUpdated document
DeleteAsync(interactionId, documentId)Interaction UUID + document UUIDvoid

Facts

Extract structured clinical facts from text or manage facts on an interaction.
C# .NET
using Corti;

// Extract facts from text (standalone)
var extracted = await client.Facts.ExtractAsync(
    new FactsExtractRequest
    {
        Context = new[]
        {
            new CommonTextContext
            {
                Type = CommonTextContextType.Text,
                Text = "Patient has a temperature of 38.5°C and reports headache.",
            },
        },
        OutputLanguage = "en",
    });

// List facts for an interaction
var facts = await client.Facts.ListAsync(interactionId);

// Create facts on an interaction
var created = await client.Facts.CreateAsync(
    interactionId,
    new FactsCreateRequest
    {
        Facts = new[]
        {
            new FactsCreateInput { Text = "Temperature 38.5°C", Group = "vitals" },
        },
    });

// Update a single fact
await client.Facts.UpdateAsync(
    interactionId,
    factId,
    new FactsUpdateRequest { Text = "Temperature 39.0°C" });

// List available fact groups
var groups = await client.Facts.FactGroupsListAsync();
MethodParametersReturns
ExtractAsync(request)Extraction options (context, output language)Extracted facts
ListAsync(interactionId)Interaction UUIDList of facts
CreateAsync(interactionId, request)Interaction UUID + fact definitionsCreated facts
BatchUpdateAsync(interactionId, request)Interaction UUID + fact updatesUpdated facts
UpdateAsync(interactionId, factId, request?)Interaction UUID + fact ID + update fieldsUpdated fact
FactGroupsListAsync()NoneAvailable fact group definitions

Templates

List and inspect document generation templates available to your tenant.
C# .NET
using Corti;

// List all templates
var templates = await client.Templates.ListAsync(new TemplatesListRequest());

// Get a specific template by key
var template = await client.Templates.GetAsync("corti-soap");

// List template sections
var sections = await client.Templates.SectionListAsync(new TemplatesSectionListRequest());
MethodParametersReturns
ListAsync(request)List filtersList of templates
GetAsync(key)Template key stringSingle template
SectionListAsync(request)Section list filtersList of template sections

Codes

Predict medical codes from clinical data.
C# .NET
var response = await client.Codes.PredictAsync(new CodesGeneralPredictRequest
{
    System = [CommonCodingSystemEnum.Icd10CmInpatient],
    Context =
    [
        new CommonTextContext
        {
            Type = CommonTextContextType.Text,
            Text = "Progress Note — Day 3: Patient continues on IV vancomycin for MRSA bacteremia. Blood cultures from yesterday still pending. Acute kidney injury improving — creatinine down to 1.8 from 2.4. Patient also has history of CHF, currently euvolemic on home dose of furosemide. Diabetes managed with insulin sliding scale, glucose well controlled.",
        },
    ],
});
MethodParametersReturns
PredictAsync(request)CodesGeneralPredictRequest (text, code system)Code predictions

Agents

Create and interact with AI agents (Agentic Framework).
C# .NET
using Corti;

// List agents
var agents = await client.Agents.ListAsync(new AgentsListRequest());

// Create an agent
var agent = await client.Agents.CreateAsync(
    new AgentsCreateAgent
    {
        Name = "My Agent",
        Description = "A helpful assistant",
    });

// Get agent details
var details = await client.Agents.GetAsync(agent.Id);

// Send a message to an agent
var response = await client.Agents.MessageSendAsync(
    agent.Id,
    new AgentsMessageSendParams
    {
        Message = new AgentsMessage
        {
            Role = AgentsMessageRole.User,
            Kind = AgentsMessageKind.Message,
            MessageId = $"msg-{DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}",
            Parts = new[]
            {
                AgentsPart.FromAgentsTextPart(
                    new AgentsTextPart { Kind = AgentsTextPartKind.Text, Text = "Hello" }),
            },
        },
    });

// Update an agent
await client.Agents.UpdateAsync(agent.Id, new AgentsUpdateAgent { Description = "Updated" });

// Delete an agent
await client.Agents.DeleteAsync(agent.Id);
MethodParametersReturns
ListAsync(request)List filtersList of agents
CreateAsync(request)Agent definitionCreated agent
GetAsync(id)Agent IDAgent details
GetCardAsync(id)Agent IDAgent card (A2A format)
MessageSendAsync(id, request)Agent ID + message payloadAgent response
GetTaskAsync(id, taskId, request?)Agent ID + task IDTask details
GetContextAsync(id, contextId, request?)Agent ID + context IDContext details
GetRegistryExpertsAsync(request?)Optional filtersRegistry experts
UpdateAsync(id, request?)Agent ID + update fieldsUpdated agent
DeleteAsync(id)Agent IDvoid

Stream

Real-time WebSocket connection for combined transcription, fact extraction, and more — tied to an interaction.
C# .NET
var stream = await client.CreateStreamApiAsync(interactionId);

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

stream.StreamFactsMessage.Subscribe(_ =>
{
    Console.WriteLine("Facts received");
});

await stream.ConnectAsync(new StreamConfig
{
    Transcription = new StreamConfigTranscription
    {
        PrimaryLanguage = "en",
        Participants = new[]
        {
            new StreamConfigParticipant { Channel = 0, Role = StreamConfigParticipantRole.Doctor },
        },
    },
    Mode = new StreamConfigMode
    {
        Type = StreamConfigModeType.Facts,
        OutputLocale = "en",
    },
});

await stream.Send(audioBytes);

Factory method

ParameterTypeRequiredDescription
interactionIdstringYesInteraction UUID to attach the stream to
Returns Task<IStreamApi>.

ConnectAsync parameters

ParameterTypeRequiredDescription
configurationStreamConfigNoStream configuration (language, participants, mode). When provided, the SDK automatically sends the config message once the socket opens and waits for CONFIG_ACCEPTED. If omitted, you must send a config message yourself
cancellationTokenCancellationTokenNoCancellation support

Events

Subscribe to events using the .Subscribe() method on each Event<T> field:
EventTypeDescription
ConnectedEvent<Connected>WebSocket connection established
ClosedEvent<Closed>WebSocket connection closed
ExceptionOccurredEvent<Exception>WebSocket error occurred
ReconnectingEvent<ReconnectionInfo>Reconnection attempt in progress
StreamTranscriptMessageEvent<StreamTranscriptMessage>Transcript data received
StreamFactsMessageEvent<StreamFactsMessage>Facts data received
StreamFlushedMessageEvent<StreamFlushedMessage>Audio buffer flushed
StreamEndedMessageEvent<StreamEndedMessage>Stream session ended
StreamUsageMessageEvent<StreamUsageMessage>Usage metrics
StreamErrorMessageEvent<StreamErrorMessage>Server-side error
StreamConfigStatusMessageEvent<StreamConfigStatusMessage>Config accepted or denied
UnknownMessageEvent<JsonElement>Unrecognised message type

Methods

MethodDescription
ConnectAsync(configuration?, cancellationToken?)Open the WebSocket connection
Send(byte[])Send binary audio data
Send(StreamFlushMessage)Request the server to flush buffered audio
Send(StreamEndMessage)Signal end of audio stream
Send(StreamConfigMessage)Send a configuration message manually
CloseAsync(cancellationToken?)Close the connection. Call Send(new StreamEndMessage()) first and wait for StreamEndedMessage
DisposeAsync()Dispose the WebSocket resources

Transcribe

Real-time WebSocket speech-to-text without an interaction context.
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);

ConnectAsync parameters

ParameterTypeRequiredDescription
configurationTranscribeConfigNoTranscribe configuration (language, punctuation, commands). When provided, the SDK automatically sends the config message once the socket opens and waits for CONFIG_ACCEPTED. If omitted, you must send a config message yourself
cancellationTokenCancellationTokenNoCancellation support

Events

EventTypeDescription
ConnectedEvent<Connected>WebSocket connection established
ClosedEvent<Closed>WebSocket connection closed
ExceptionOccurredEvent<Exception>WebSocket error occurred
ReconnectingEvent<ReconnectionInfo>Reconnection attempt in progress
TranscribeTranscriptMessageEvent<TranscribeTranscriptMessage>Transcript data received
TranscribeCommandMessageEvent<TranscribeCommandMessage>Command detected
TranscribeFlushedMessageEvent<TranscribeFlushedMessage>Audio buffer flushed
TranscribeEndedMessageEvent<TranscribeEndedMessage>Session ended
TranscribeUsageMessageEvent<TranscribeUsageMessage>Usage metrics
TranscribeErrorMessageEvent<TranscribeErrorMessage>Server-side error
TranscribeConfigStatusMessageEvent<TranscribeConfigStatusMessage>Config accepted or denied
UnknownMessageEvent<JsonElement>Unrecognised message type

Methods

MethodDescription
ConnectAsync(configuration?, cancellationToken?)Open the WebSocket connection
Send(byte[])Send binary audio data
Send(TranscribeFlushMessage)Request the server to flush buffered audio
Send(TranscribeEndMessage)Signal end of audio stream
Send(TranscribeConfigMessage)Send a configuration message manually
CloseAsync(cancellationToken?)Close the connection. Call Send(new TranscribeEndMessage()) first and wait for TranscribeEndedMessage
DisposeAsync()Dispose the WebSocket resources
The key differences from Stream are:
  • No interaction ID — Transcribe operates standalone via CreateTranscribeApiAsync() (no interactionId parameter)
  • Different message types — Transcribe delivers transcript, command, flushed, ended, usage, and error messages, while Stream delivers transcript, facts, flushed, ended, usage, and error

Auth

OAuth token management via client.Auth. In most cases you won’t call these directly — the SDK handles tokens automatically based on the auth option you pass to the constructor. These methods are useful for advanced scenarios like generating authorization URLs for browser-based flows.
MethodDescription
GetTokenAsync(OAuthTokenRequest)Get a token via client credentials
GetTokenAsync(OAuthRopcTokenRequest)Get a token via ROPC flow
GetTokenAsync(OAuthAuthCodeTokenRequest)Exchange an authorization code for a token
GetTokenAsync(OAuthPkceTokenRequest)Exchange a PKCE code for a token
GetTokenAsync(OAuthRefreshTokenRequest)Refresh an expired token
AuthorizeUrlAsync(clientId, redirectUri, ...)Generate an authorization URL
All GetTokenAsync overloads also accept *WithScopes request variants (e.g. OAuthTokenRequestWithScopes) to request scoped tokens.
For detailed usage and end-to-end examples, see the Authentication Guide. For standalone use without CortiClient, see CustomAuthClient below.

CustomAuthClient

A standalone authentication client for when you need OAuth token management without the full CortiClient. Useful for proxy servers, custom backends, or applications that handle tokens separately from API calls.
C# .NET
using Corti;

var auth = CustomAuthClient.Create(
    new CortiAuthClientOptions
    {
        TenantName = "YOUR_TENANT_NAME",
        Environment = "YOUR_ENVIRONMENT_ID",
    });

var tokenResponse = await auth.GetTokenAsync(
    new OAuthTokenRequest
    {
        ClientId = "YOUR_CLIENT_ID",
        ClientSecret = "YOUR_CLIENT_SECRET",
    });

Console.WriteLine(tokenResponse.AccessToken);

Constructor options

PropertyTypeRequiredDescription
TenantNamestringYesYour Corti tenant name
Environmentstring | CortiClientEnvironmentYesAPI region or custom environment
RequestOptionsCortiRequestOptions?NoDefault request options

Methods

MethodDescriptionReturns
GetTokenAsync(OAuthTokenRequest)Get a token via client credentialsAuthTokenResponse
GetTokenAsync(OAuthRopcTokenRequest)Get a token via ROPC flowAuthTokenResponse
GetTokenAsync(OAuthAuthCodeTokenRequest)Exchange an authorization code for a tokenAuthTokenResponse
GetTokenAsync(OAuthPkceTokenRequest)Exchange a PKCE code for a tokenAuthTokenResponse
GetTokenAsync(OAuthRefreshTokenRequest)Refresh an expired tokenAuthTokenResponse
AuthorizeUrlAsync(clientId, redirectUri, codeChallenge?, scopes?)Generate an authorization URLstring
All GetTokenAsync overloads also accept *WithScopes request variants to request scoped tokens.
For end-to-end examples of each method, see the Authentication Guide.

Authentication types

The CortiClientAuth abstract record has the following concrete variants used with the CortiClient constructor:
VariantParametersDescription
CortiClientAuth.ClientCredentialsclientId, clientSecretServer-to-server (guide)
CortiClientAuth.AuthorizationCodeclientId, clientSecret, code, redirectUriInteractive login (guide)
CortiClientAuth.PkceclientId, code, redirectUri, codeVerifierPublic client login (guide)
CortiClientAuth.RopcclientId, username, passwordUsername/password (guide)
CortiClientAuth.BeareraccessToken, clientId?, refreshToken?, expiresIn?, refreshExpiresIn?, refreshAccessToken?Existing token (guide)
CortiClientAuth.BearerCustomRefreshrefreshAccessToken, accessToken?, expiresIn?, refreshToken?, refreshExpiresIn?Custom refresh logic (guide)

Error handling

The SDK throws typed exceptions you can catch and inspect:
C# .NET
using Corti;

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}");
}
ExceptionStatus codeWhen it’s thrown
BadRequestError400Malformed request
UnauthorizedError401Missing or invalid authentication
ForbiddenError403Insufficient permissions
NotFoundError404Resource not found
UnprocessableEntityError422Validation failed (includes AgentsValidationErrorResponse body)
InternalServerError500Server error
BadGatewayError502Gateway error
GatewayTimeoutError504Gateway timeout
All inherit from CortiClientApiException, which inherits from CortiClientException.

Environments

Use CortiEnvironments to create custom environments:
C# .NET
using Corti;

// Built-in region
// var env = CortiEnvironments.FromRegion("eu");

// Custom proxy base URL
var env = CortiEnvironments.FromBaseUrl("https://your-proxy.com/api");

var client = new CortiClient(environment: env);
MethodDescription
FromRegion(region)Create an environment from a region string
FromBaseUrl(baseUrl)Create an environment pointing at a custom proxy URL