> ## Documentation Index
> Fetch the complete documentation index at: https://docs.corti.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# C# .NET SDK - API Reference

> Complete reference for CortiClient, CustomAuthClient, and SDK types

The `Corti.Sdk` package exports two main entry points:

| Export | Namespace | Purpose |
| :- | :- | :- |
| [`CortiClient`](#corticlient) | `using Corti;` | Full API client with all resource groups, authentication, and WebSockets |
| [`CustomAuthClient`](#customauthclient) | `using Corti;` | Standalone authentication client for token management and OAuth flows |

***

## CortiClient

The main client for all Corti API operations.

### Constructors

```csharp theme={null}
// 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](/sdk/dotnet/authentication#proxy-passthrough-mode).

#### CortiRequestOptions

| Property | Type | Default | Description |
| :- | :- | :- | :- |
| `MaxRetries` | `int?` | `2` | Number of automatic retries on `408`, `429`, and `5xx` |
| `Timeout` | `TimeSpan?` | 30s | Default request timeout |
| `HttpClient` | `HttpClient?` | `null` | Custom `HttpClient` instance |
| `AdditionalHeaders` | `IEnumerable<KeyValuePair<string, string?>>?` | `null` | Headers sent with every request |
| `Analytics` | `Dictionary<string, string>?` | `null` | Extra keys on `x-corti-analytics`. Reserved keys `sdk_type` and `sdk_version` are set by the SDK. |

***

### Request options

Every HTTP method accepts an optional `RequestOptions` parameter:

```csharp title="C# .NET" theme={null}
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",
        },
    });
```

| Property | Type | Description |
| :- | :- | :- |
| `BaseUrl` | `string?` | Override the base URL for this request |
| `HttpClient` | `HttpClient?` | Override the HttpClient for this request |
| `MaxRetries` | `int?` | Override the default retry count |
| `Timeout` | `TimeSpan?` | Override the default timeout |
| `AdditionalHeaders` | `IEnumerable<KeyValuePair<string, string?>>` | Additional HTTP headers |
| `AdditionalQueryParameters` | `IEnumerable<KeyValuePair<string, string>>` | Additional query string parameters |
| `AdditionalBodyProperties` | `object?` | Additional JSON properties merged into the request body |

***

### Resource groups

The client exposes the following resource groups as properties:

| Group | Description | API endpoints |
| :- | :- | :- |
| [`client.Interactions`](#interactions) | Manage patient encounters and interaction sessions | [Interactions API](/api-reference/interactions/list-all-interactions) |
| [`client.Recordings`](#recordings) | Upload and retrieve audio recordings for interactions | [Recordings API](/api-reference/recordings/list-recordings) |
| [`client.Languages`](#languages) | List available languages and enabled endpoint details | [Languages](/stt/languages) |
| [`client.Transcripts`](#transcripts) | Create and retrieve transcriptions of recordings | [Transcripts API](/api-reference/transcripts/list-transcripts) |
| [`client.Documents`](#guided-documents) | Generate a clinical document from templates or dynamic definitions; list/get/update/delete persisted guided documents | [Documents](/api-reference/guided-documents/list-documents) |
| [`client.Documents.Templates`](#guided-templates) | Create, version, and publish guided document templates | [Guided Templates](/api-reference/guided-templates/list-templates) |
| [`client.Documents.Sections`](#guided-sections) | Create, version, and publish guided document sections | [Section Creation](/textgen/section-creation) |
| [`client.Documents.Classic`](#documents-classic) | Interaction-scoped document CRUD (deprecated) | [Documents Classic](/api-reference/documents-classic/list-documents) |
| [`client.Facts`](#facts) | Extract and manage structured clinical facts | [Facts API](/api-reference/facts/list-facts) |
| [`client.Templates`](#templates) | List and retrieve classic document generation templates | [Templates API](/api-reference/templates-classic/list-templates) |
| [`client.Codes`](#codes) | Predict medical codes from clinical data | [Codes API](/api-reference/codes/predict-codes) |
| [`client.Agents`](#agents) | Create and interact with AI agents (v1, deprecated) | [Agentic APIs v1](/agentic/agents/list-agents) |
| [`client.Agentic`](#agents-v2) | Agents API v2 (`Agentic.Agents`, Contexts, Registry) | [Agentic APIs v2](/agentic-v2/agents/list-agents) |
| [`client.Auth`](#auth) | OAuth token management and authorization URLs | [Auth API](/api-reference/admin/auth/authenticate-user-and-get-access-token) |

WebSocket APIs are created via factory methods:

| Method | Description | API endpoint |
| :- | :- | :- |
| [`CreateStreamApiAsync(interactionId, additionalQueryParameters?)`](#stream) | Real-time WebSocket streaming (transcription + facts) | [Stream API](/api-reference/streams) |
| [`CreateTranscribeApiAsync(additionalQueryParameters?)`](#transcribe) | Real-time WebSocket speech-to-text (standalone) | [Transcribe API](/api-reference/transcribe) |

***

### Interactions

Manage interaction sessions that group recordings, transcripts, documents, and facts together.

```csharp title="C# .NET" expandable theme={null}
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);
```

| Method | Parameters | Returns |
| :- | :- | :- |
| `ListAsync(request)` | Filter and pagination options | `Pager` (async enumerable) |
| `CreateAsync(request)` | Interaction definition | Created interaction |
| `GetAsync(id)` | Interaction UUID | Single interaction |
| `UpdateAsync(id, request?)` | Interaction UUID + update fields | Updated interaction |
| `DeleteAsync(id)` | Interaction UUID | `void` |

***

### Recordings

Upload and manage audio recordings within an interaction.

```csharp title="C# .NET" expandable theme={null}
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);
```

| Method | Parameters | Returns |
| :- | :- | :- |
| `UploadAsync(interactionId, file)` | Interaction UUID + file stream | Created recording |
| `ListAsync(interactionId)` | Interaction UUID | List of recordings |
| `GetAsync(interactionId, recordingId)` | Interaction UUID + recording UUID | Audio stream |
| `DeleteAsync(interactionId, recordingId)` | Interaction UUID + recording UUID | `void` |

***

### Languages

List available languages and which endpoints support each language.

| Method | Parameters | Returns |
| :- | :- | :- |
| `ListAsync(request?)` | Optional `LanguagesListRequest.Endpoint` filter | `LanguagesListResponse` |

***

### Transcripts

Create transcriptions from uploaded recordings and retrieve results.

```csharp title="C# .NET" expandable theme={null}
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);
```

| Method | Parameters | Returns |
| :- | :- | :- |
| `CreateAsync(interactionId, request)` | Interaction UUID + transcript definition | Created transcript |
| `GetStatusAsync(interactionId, transcriptId)` | Interaction UUID + transcript UUID | Transcription status |
| `ListAsync(interactionId, request?)` | Interaction UUID + optional filters | List of transcripts |
| `GetAsync(interactionId, transcriptId)` | Interaction UUID + transcript UUID | Single transcript |
| `DeleteAsync(interactionId, transcriptId)` | Interaction UUID + transcript UUID | `void` |

***

### Guided Documents

Generate structured clinical documents using guided synthesis. Accepts a template reference, an ad-hoc assembly of sections, or a fully inline dynamic template.

| Method | Parameters | Returns |
| :- | :- | :- |
| `GenerateAsync(request)` | `GuidedDocumentsGenerateRequest` — one of `templateRef`, `assemblyTemplate`, or `dynamicTemplate` | Generated document (optional `Sections`: `SectionId`, `Heading`, `Labels`) |
| `ListAsync(request)` | `GuidedDocumentsListRequest` (`TemplateId`, `InteractionId`, `Label`) | Persisted guided documents |
| `GetAsync(documentId)` | Document UUID | Persisted guided document |
| `UpdateAsync(documentId, request)` | Document UUID + `GuidedDocumentsUpdateRequest` | Updated document |
| `DeleteAsync(documentId)` | Document UUID | `void` |

<Tip>See the [Guided Synthesis guide](/textgen/documents-guided-synthesis) for the four template-supply paths and context options.</Tip>

***

### Guided Templates

Create, version, and publish document templates that define section composition and generation instructions.

| Method | Parameters | Returns |
| :- | :- | :- |
| `ListAsync(request)` | `GuidedTemplatesListRequest` — optional filters (lang, region, specialty, label, published, source) | List of templates |
| `CreateAsync(request)` | `GuidedTemplatesCreateRequest` — template definition | Created template |
| `GetAsync(templateId)` | Template UUID | Single template |
| `UpdateAsync(templateId, request)` | Template UUID + `GuidedTemplatesUpdateRequest` | Updated template |
| `DeleteAsync(templateId)` | Template UUID | `void` |

#### Template Versions

| Method | Parameters | Returns |
| :- | :- | :- |
| `Templates.Versions.ListAsync(templateId)` | Template UUID | List of versions |
| `Templates.Versions.CreateAsync(templateId, request)` | Template UUID + `GuidedTemplatesCreateVersionRequest` | Created version |
| `Templates.Versions.GetAsync(templateId, versionId)` | Template UUID + version UUID | Single version |
| `Templates.Versions.DeleteAsync(templateId, versionId)` | Template UUID + version UUID | `void` |
| `Templates.Versions.PublishAsync(templateId, versionId)` | Template UUID + version UUID | Status response |

***

### Guided Sections

Create, version, and publish document sections that define individual clinical note components with structured output schemas.

| Method | Parameters | Returns |
| :- | :- | :- |
| `ListAsync(request)` | `GuidedSectionsListRequest` — optional filters (lang, region, specialty, label, published, source) | List of sections |
| `CreateAsync(request)` | `GuidedSectionsCreateRequest` — section definition | Created section |
| `GetAsync(sectionId)` | Section UUID | Single section |
| `UpdateAsync(sectionId, request)` | Section UUID + `GuidedSectionsUpdateRequest` | Updated section |
| `DeleteAsync(sectionId)` | Section UUID | `void` |

#### Section Versions

| Method | Parameters | Returns |
| :- | :- | :- |
| `Sections.Versions.ListAsync(sectionId)` | Section UUID | List of versions |
| `Sections.Versions.CreateAsync(sectionId, request)` | Section UUID + `GuidedSectionsCreateVersionRequest` | Created version |
| `Sections.Versions.GetAsync(sectionId, versionId)` | Section UUID + version UUID | Single version |
| `Sections.Versions.DeleteAsync(sectionId, versionId)` | Section UUID + version UUID | `void` |
| `Sections.Versions.PublishAsync(sectionId, versionId)` | Section UUID + version UUID | Status response |

```csharp title="C# .NET" expandable theme={null}
using Corti;

// --- Templates ---

// List templates (with optional filters)
var templates = await client.Documents.Templates.ListAsync(
    new GuidedTemplatesListRequest { Lang = new[] { "en" }, Published = true });

// Create a template from scratch
var template = await client.Documents.Templates.CreateAsync(
    new GuidedTemplatesCreateFromScratchRequest
    {
        Name = "My Template",
        Description = "Custom SOAP note",
        Lang = "en",
        Sections = new[] { new GuidedTemplateSectionRef { SectionId = "<section-uuid>", Order = 0 } },
    });

// Get / update / delete a template
var tmpl = await client.Documents.Templates.GetAsync(templateId);
await client.Documents.Templates.UpdateAsync(templateId,
    new GuidedTemplatesUpdateRequest { Name = "Renamed" });
await client.Documents.Templates.DeleteAsync(templateId);

// --- Template Versions ---

var versions = await client.Documents.Templates.Versions.ListAsync(templateId);
var version = await client.Documents.Templates.Versions.CreateAsync(templateId,
    new GuidedTemplatesCreateVersionRequest
    {
        Sections = new[] { new GuidedTemplateSectionRef { SectionId = "<section-uuid>", Order = 0 } },
    });
await client.Documents.Templates.Versions.PublishAsync(templateId, versionId);
await client.Documents.Templates.Versions.DeleteAsync(templateId, versionId);

// --- Sections ---

var sections = await client.Documents.Sections.ListAsync(
    new GuidedSectionsListRequest { Lang = new[] { "en" } });
var section = await client.Documents.Sections.CreateAsync(
    new GuidedSectionsCreateFromScratchRequest
    {
        Name = "HPI",
        Generation = new GuidedSectionGeneration
        {
            Heading = "History of Present Illness",
            Instructions = new GuidedSectionInstructions { ContentPrompt = "Summarize the patient history" },
            OutputSchema = new GuidedStringNode(),
        },
    });
await client.Documents.Sections.UpdateAsync(sectionId,
    new GuidedSectionsUpdateRequest { Name = "Renamed" });
await client.Documents.Sections.DeleteAsync(sectionId);

// --- Section Versions ---

var sectionVersions = await client.Documents.Sections.Versions.ListAsync(sectionId);
await client.Documents.Sections.Versions.PublishAsync(sectionId, versionId);

// --- Generate ---

var result = await client.Documents.GenerateAsync(
    new GuidedDocumentsGenerateByTemplateRef
    {
        OutputLanguage = "en-US",
        InteractionId = "<interaction-uuid>",
        TemplateRef = new GuidedTemplateRef { TemplateId = "<template-uuid>" },
    });

// List / get / update / delete persisted documents
var documents = await client.Documents.ListAsync(new GuidedDocumentsListRequest());
var doc = await client.Documents.GetAsync(documentId);
await client.Documents.UpdateAsync(documentId,
    new GuidedDocumentsUpdateRequest { Name = "Renamed" });
await client.Documents.DeleteAsync(documentId);
```

***

### Documents (Classic)

<Warning>These interaction-scoped methods are **deprecated**. Use [Guided Documents](#guided-documents) with `client.Documents.GenerateAsync()` instead. Call sites are `client.Documents.Classic.*`.</Warning>

```csharp title="C# .NET" expandable theme={null}
using Corti;

// Generate a document
var document = await client.Documents.Classic.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.Classic.ListAsync(interactionId);

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

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

// Delete a document
await client.Documents.Classic.DeleteAsync(interactionId, documentId);
```

| Method | Parameters | Returns |
| :- | :- | :- |
| `Classic.CreateAsync(interactionId, request)` | Interaction UUID + document generation options | Generated document |
| `Classic.ListAsync(interactionId)` | Interaction UUID | List of documents |
| `Classic.GetAsync(interactionId, documentId)` | Interaction UUID + document UUID | Single document |
| `Classic.UpdateAsync(interactionId, documentId, request?)` | Interaction UUID + document UUID + update fields | Updated document |
| `Classic.DeleteAsync(interactionId, documentId)` | Interaction UUID + document UUID | `void` |

***

### Facts

Extract structured clinical facts from text or manage facts on an interaction.

```csharp title="C# .NET" expandable theme={null}
using Corti;

// Extract facts from text (standalone)
var extracted = await client.Facts.ExtractAsync(
    new FactsExtractRequest
    {
        Context = new[]
        {
            new CommonTextContext
            {
                Type = new CommonTextContext.TypeLiteral(),
                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();
```

| Method | Parameters | Returns |
| :- | :- | :- |
| `ExtractAsync(request)` | Extraction options (context, output language) | Extracted facts |
| `ListAsync(interactionId)` | Interaction UUID | List of facts |
| `CreateAsync(interactionId, request)` | Interaction UUID + fact definitions | Created facts |
| `BatchUpdateAsync(interactionId, request)` | Interaction UUID + fact updates | Updated facts |
| `UpdateAsync(interactionId, factId, request?)` | Interaction UUID + fact ID + update fields | Updated fact |
| `FactGroupsListAsync()` | None | Available fact group definitions |

***

### Templates

List and inspect document generation templates available to your tenant.

```csharp title="C# .NET" theme={null}
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());
```

| Method | Parameters | Returns |
| :- | :- | :- |
| `ListAsync(request)` | List filters | List of templates |
| `GetAsync(key)` | Template key string | Single template |
| `SectionListAsync(request)` | Section list filters | List of template sections |

***

### Codes

Predict medical codes from clinical data.

```csharp title="C# .NET" theme={null}
var response = await client.Codes.PredictAsync(new CodesGeneralPredictRequest
{
    System = [CommonCodingSystemEnum.Icd10CmInpatient],
    Context =
    [
        new CommonTextContext
        {
            Type = new CommonTextContext.TypeLiteral(),
            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.",
        },
    ],
});
```

| Method | Parameters | Returns |
| :- | :- | :- |
| `PredictAsync(request)` | `CodesGeneralPredictRequest` (text, code system) | Code predictions |

***

### Agents

Create and interact with AI agents. `client.Agents` is Agents API v1 (deprecated). Prefer [`client.Agentic`](#agents-v2). There is no `GetCardUrl` helper on .NET.

| Method | Parameters | Returns |
| :- | :- | :- |
| `ListAsync(request)` | List filters | List of agents |
| `CreateAsync(request)` | Agent definition | Created agent |
| `GetAsync(id)` | Agent ID | Agent details |
| `GetCardAsync(id)` | Agent ID | Agent card (A2A format) |
| `MessageSendAsync(id, request)` | Agent ID + message payload | Agent response |
| `GetTaskAsync(id, taskId, request?)` | Agent ID + task ID | Task details |
| `GetContextAsync(id, contextId, request?)` | Agent ID + context ID | Context details |
| `GetRegistryExpertsAsync(request?)` | Optional filters | Registry experts |
| `UpdateAsync(id, request?)` | Agent ID + update fields | Updated agent |
| `DeleteAsync(id)` | Agent ID | `void` |

***

### Agents v2

Agents API v2. Nested under `client.Agentic` (`Agents`, `Contexts`, `Registry`). There is no `GetCardUrl` helper on .NET.

```csharp title="C# .NET" expandable theme={null}
using Corti;
using Corti.Agentic;
using Corti.Agentic.Agents;
using Corti.Agentic.Registry;

// Replace these with your values
const string TASK_ID = "<your-task-id>";
const string CONTEXT_ID = "<your-context-id>";

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

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

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

// Get agent card (A2A)
var card = await client.Agentic.Agents.CardAsync(agent.Id);

// Send a message to an agent
var response = await client.Agentic.Agents.SendMessageAsync(
    agent.Id,
    new AgenticAgentsSendMessageRequest
    {
        Message = new CommonMessage
        {
            Role = CommonRole.RoleUser,
            MessageId = $"msg-{DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}",
            Parts = [new CommonPart { Text = "Hello" }],
        },
    });

// Get a task
var task = await client.Agentic.Agents.Tasks.GetAsync(
    agent.Id,
    TASK_ID,
    new AgenticAgentsTasksGetRequest());

// Get context (top-level resource, not agent-scoped)
var context = await client.Agentic.Contexts.GetAsync(
    CONTEXT_ID,
    new AgenticContextsGetRequest());

// List registry connectors
var connectors = await client.Agentic.Registry.Connectors.ListAsync(
    new AgenticRegistryConnectorsListRequest());

// Patch an agent (v2 uses PATCH with merge-patch semantics)
await client.Agentic.Agents.UpdateAsync(agent.Id, new AgenticAgentsPatchRequest { Description = "Updated" });

// Delete an agent
await client.Agentic.Agents.DeleteAsync(agent.Id);
```

| Method | Parameters | Returns |
| :- | :- | :- |
| `Agents.ListAsync(request)` | List filters | Paginated list of agents |
| `Agents.CreateAsync(request)` | Agent definition | Created agent |
| `Agents.GetAsync(id)` | Agent ID | Agent details |
| `Agents.UpdateAsync(id, request)` | Agent ID + patch | Updated agent |
| `Agents.DeleteAsync(id)` | Agent ID | `void` |
| `Agents.CardAsync(id)` | Agent ID | Agent card |
| `Agents.JsonRpcAsync(id, request)` | Agent ID + JSON-RPC body | JSON-RPC response |
| `Agents.SendMessageAsync(id, request)` | Agent ID + message payload | Agent response |
| `Agents.StreamMessageAsync(id, request)` | Agent ID + message payload | Stream of events |
| `Agents.UsageAsync(id, request)` | Agent ID + optional time range | Usage report |
| `Agents.Tasks.ListAsync(id, request)` | Agent ID + optional filters | Paginated tasks |
| `Agents.Tasks.GetAsync(id, taskId, request)` | Agent ID + task ID | Task details |
| `Agents.Tasks.CancelAsync(id, taskId)` | Agent ID + task ID | Cancelled task |
| `Agents.Tasks.SubscribeAsync(id, taskId)` | Agent ID + task ID | Stream of events |
| `Agents.Connectors.ListAsync(id)` | Agent ID | Connector list |
| `Agents.Connectors.CreateAsync(id, request)` | Agent ID + connector definition | Created connector |
| `Agents.Connectors.GetAsync(id, connectorId)` | Agent ID + connector ID | Connector details |
| `Agents.Connectors.UpdateAsync(id, connectorId, request)` | Agent ID + connector ID + patch | Updated connector |
| `Agents.Connectors.DeleteAsync(id, connectorId)` | Agent ID + connector ID | `void` |
| `Contexts.ListAsync(request)` | List filters | Paginated contexts |
| `Contexts.GetAsync(contextId, request)` | Context ID | Context details |
| `Contexts.DeleteAsync(contextId)` | Context ID | `void` |
| `Contexts.TraceAsync(contextId, request)` | Context ID | Paginated traces |
| `Contexts.Tasks.ListAsync(contextId, request)` | Context ID | Paginated tasks |
| `Contexts.Tasks.GetAsync(contextId, taskId)` | Context ID + task ID | Task details |
| `Contexts.Tasks.Artifacts.GetAsync(contextId, taskId, artifactId)` | Context ID + task ID + artifact ID | Artifact |
| `Contexts.Tasks.Feedback.ListAsync(contextId, taskId)` | Context ID + task ID | Feedback list |
| `Contexts.Tasks.Feedback.CreateAsync(contextId, taskId, request)` | Context ID + task ID + feedback | Created feedback |
| `Contexts.Tasks.Feedback.DeleteAsync(contextId, taskId, feedbackId)` | Context ID + task ID + feedback ID | `void` |
| `Registry.Connectors.ListAsync(request)` | List filters | Paginated registry connectors |
| `Registry.Connectors.GetAsync(connectorId)` | Connector ID | Registry connector |

***

### Stream

Real-time WebSocket connection for combined transcription, fact extraction, and more -- tied to an interaction.

```csharp title="C# .NET" expandable theme={null}
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

| Parameter | Type | Required | Description |
| :- | :- | :- | :- |
| `interactionId` | `string` | Yes | Interaction UUID to attach the stream to |
| `additionalQueryParameters` | `IEnumerable<KeyValuePair<string, string>>?` | No | Extra query string parameters on the WebSocket URL. Merged with keys from `CortiRequestOptions.Analytics` |

Returns `Task<IStreamApi>`. Query parameters are not a `ConnectAsync` argument.

#### `ConnectAsync` parameters

| Parameter | Type | Required | Description |
| :- | :- | :- | :- |
| `configuration` | `StreamConfig` | No | Stream 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 |
| `cancellationToken` | `CancellationToken` | No | Cancellation support |

#### Events

Subscribe to events using the `.Subscribe()` method on each `Event<T>` field:

| Event | Type | Description |
| :- | :- | :- |
| `Connected` | `Event<Connected>` | WebSocket connection established |
| `Closed` | `Event<Closed>` | WebSocket connection closed |
| `ExceptionOccurred` | `Event<Exception>` | WebSocket error occurred |
| `Reconnecting` | `Event<ReconnectionInfo>` | Reconnection attempt in progress |
| `StreamTranscriptMessage` | `Event<StreamTranscriptMessage>` | Transcript data received |
| `StreamFactsMessage` | `Event<StreamFactsMessage>` | Facts data received |
| `StreamFlushedMessage` | `Event<StreamFlushedMessage>` | Audio buffer flushed |
| `StreamEndedMessage` | `Event<StreamEndedMessage>` | Stream session ended |
| `StreamUsageMessage` | `Event<StreamUsageMessage>` | Usage metrics |
| `StreamDeltaUsageMessage` | `Event<StreamDeltaUsageMessage>` | Incremental usage metrics |
| `StreamErrorMessage` | `Event<StreamErrorMessage>` | Server-side error |
| `StreamConfigStatusMessage` | `Event<StreamConfigStatusMessage>` | Config accepted or denied |
| `UnknownMessage` | `Event<JsonElement>` | Unrecognised message type |

#### Methods

| Method | Description |
| :- | :- |
| `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.

```csharp title="C# .NET" theme={null}
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);
```

#### Factory method

| Parameter | Type | Required | Description |
| :- | :- | :- | :- |
| `additionalQueryParameters` | `IEnumerable<KeyValuePair<string, string>>?` | No | Extra query string parameters on the WebSocket URL. Merged with keys from `CortiRequestOptions.Analytics` |

Returns `Task<ITranscribeApi>`. Query parameters are not a `ConnectAsync` argument.

#### `ConnectAsync` parameters

| Parameter | Type | Required | Description |
| :- | :- | :- | :- |
| `configuration` | `TranscribeConfig` | No | Transcribe 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 |
| `cancellationToken` | `CancellationToken` | No | Cancellation support |

#### Events

| Event | Type | Description |
| :- | :- | :- |
| `Connected` | `Event<Connected>` | WebSocket connection established |
| `Closed` | `Event<Closed>` | WebSocket connection closed |
| `ExceptionOccurred` | `Event<Exception>` | WebSocket error occurred |
| `Reconnecting` | `Event<ReconnectionInfo>` | Reconnection attempt in progress |
| `TranscribeTranscriptMessage` | `Event<TranscribeTranscriptMessage>` | Transcript data received |
| `TranscribeCommandMessage` | `Event<TranscribeCommandMessage>` | Command detected |
| `TranscribeFlushedMessage` | `Event<TranscribeFlushedMessage>` | Audio buffer flushed |
| `TranscribeEndedMessage` | `Event<TranscribeEndedMessage>` | Session ended |
| `TranscribeUsageMessage` | `Event<TranscribeUsageMessage>` | Usage metrics |
| `TranscribeDeltaUsageMessage` | `Event<TranscribeDeltaUsageMessage>` | Incremental usage metrics |
| `TranscribeErrorMessage` | `Event<TranscribeErrorMessage>` | Server-side error |
| `TranscribeConfigStatusMessage` | `Event<TranscribeConfigStatusMessage>` | Config accepted or denied |
| `UnknownMessage` | `Event<JsonElement>` | Unrecognised message type |

#### Methods

| Method | Description |
| :- | :- |
| `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](#stream) are:

* **No interaction ID** -- Transcribe operates standalone via `CreateTranscribeApiAsync()` (no `interactionId` parameter)
* **Different message types** -- Transcribe delivers transcript, command, flushed, ended, usage, `delta_usage`, and error messages, while Stream delivers transcript, facts, flushed, ended, usage, `delta_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.

| Method | Description |
| :- | :- |
| `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](/sdk/dotnet/authentication#scoped-tokens).

<Tip>For detailed usage and end-to-end examples, see the [Authentication Guide](/sdk/dotnet/authentication). For standalone use without `CortiClient`, see [CustomAuthClient](#customauthclient) below.</Tip>

***

## 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.

```csharp title="C# .NET" theme={null}
using Corti;

// Replace these with your values
const string CLIENT_ID = "<your-client-id>";
const string CLIENT_SECRET = "<your-client-secret>";
const string ENVIRONMENT = "<eu-or-us>";
const string TENANT = "<your-tenant-name>";

var auth = CustomAuthClient.Create(
    new CortiAuthClientOptions
    {
        TenantName = TENANT,
        Environment = ENVIRONMENT,
    });

var tokenResponse = await auth.GetTokenAsync(
    new OAuthTokenRequest
    {
        ClientId = CLIENT_ID,
        ClientSecret = CLIENT_SECRET,
    });

Console.WriteLine(tokenResponse.AccessToken);
```

### Constructor options

| Property | Type | Required | Description |
| :- | :- | :- | :- |
| `TenantName` | `string` | Yes | Your Corti tenant name |
| `Environment` | `string \| CortiClientEnvironment` | Yes | API region or custom environment |
| `RequestOptions` | `CortiRequestOptions?` | No | Default request options |

### Methods

| Method | Description | Returns |
| :- | :- | :- |
| `GetTokenAsync(OAuthTokenRequest)` | Get a token via client credentials | `AuthTokenResponse` |
| `GetTokenAsync(OAuthRopcTokenRequest)` | Get a token via ROPC flow | `AuthTokenResponse` |
| `GetTokenAsync(OAuthAuthCodeTokenRequest)` | Exchange an authorization code for a token | `AuthTokenResponse` |
| `GetTokenAsync(OAuthPkceTokenRequest)` | Exchange a PKCE code for a token | `AuthTokenResponse` |
| `GetTokenAsync(OAuthRefreshTokenRequest)` | Refresh an expired token | `AuthTokenResponse` |
| `AuthorizeUrlAsync(clientId, redirectUri, codeChallenge?, scopes?)` | Generate an authorization URL | `string` |

All `GetTokenAsync` overloads also accept `*WithScopes` request variants to request [scoped tokens](/sdk/dotnet/authentication#scoped-tokens).

<Tip>For end-to-end examples of each method, see the [Authentication Guide](/sdk/dotnet/authentication).</Tip>

***

## Authentication types

The `CortiClientAuth` abstract record has the following concrete variants used with the `CortiClient` constructor:

| Variant | Parameters | Description |
| :- | :- | :- |
| `CortiClientAuth.ClientCredentials` | `clientId`, `clientSecret` | Server-to-server ([guide](/sdk/dotnet/authentication#client-credentials)) |
| `CortiClientAuth.AuthorizationCode` | `clientId`, `clientSecret`, `code`, `redirectUri` | Interactive login ([guide](/sdk/dotnet/authentication#authorization-code-flow)) |
| `CortiClientAuth.Pkce` | `clientId`, `code`, `redirectUri`, `codeVerifier` | Public client login ([guide](/sdk/dotnet/authentication#pkce-flow)) |
| `CortiClientAuth.Ropc` | `clientId`, `username`, `password` | Username/password ([guide](/sdk/dotnet/authentication#resource-owner-password-credentials)) |
| `CortiClientAuth.Bearer` | `accessToken`, `clientId?`, `refreshToken?`, `expiresIn?`, `refreshExpiresIn?`, `refreshAccessToken?` | Existing token ([guide](/sdk/dotnet/authentication#bearer-token)) |
| `CortiClientAuth.BearerCustomRefresh` | `refreshAccessToken`, `accessToken?`, `expiresIn?`, `refreshToken?`, `refreshExpiresIn?` | Custom refresh logic ([guide](/sdk/dotnet/authentication#custom-refresh)) |

***

## Error handling

The SDK throws typed exceptions you can catch and inspect:

```csharp title="C# .NET" theme={null}
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}");
}
```

| Exception | Status code | When it's thrown |
| :- | :- | :- |
| `BadRequestError` | 400 | Malformed request |
| `UnauthorizedError` | 401 | Missing or invalid authentication |
| `ForbiddenError` | 403 | Insufficient permissions |
| `NotFoundError` | 404 | Resource not found |
| `ConflictError` | 409 | Resource conflict (e.g. deleting a template with active dependents) |
| `UnprocessableEntityError` | 422 | Validation failed |
| `InternalServerError` | 500 | Server error |
| `BadGatewayError` | 502 | Gateway error |
| `GatewayTimeoutError` | 504 | Gateway timeout |

All inherit from `CortiClientApiException`, which inherits from `CortiClientException`.

***

## Utilities

Helper types for dictation transcript assembly when building a UI on `/transcribe`:

| Export | Description |
| :- | :- |
| `DictationTranscript.Apply(previous, message)` | Apply a transcribe transcript packet to a prior snapshot; returns updated committed and interim text |

***

## Environments

Use `CortiEnvironments` to create custom environments:

```csharp title="C# .NET" theme={null}
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);
```

| Method | Description |
| :- | :- |
| `FromRegion(region)` | Create an environment from a region string |
| `FromBaseUrl(baseUrl)` | Create an environment pointing at a custom proxy URL |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.