# Introduction to the Administration API Source: https://docs.corti.ai/about/admin-api Programmatic access to manage your Corti Console account ## What is the Admin API? The `Admin API` lets you manage your Corti Console programmatically. It is built for administrators who want to automate account operations. This `Admin API` is separate from the `Corti API` used for speech to text, text generation, and agentic workflows: * Authentication and scope for the `Admin API` uses email-and-password to obtain a bearer token via `/auth/token`. This token is only used for API administration. * The `Admin API` endpoints `/customers` and `/users` manage Embedded Assistant end-users. Please [contact us](mailto:help@corti.ai) if you have questions. ### Use Cases The following functionality is currently supported by the `Admin API`: | Feature | Functionality | Scope | | :------------------- | :----------------------------------------------------------------------- | :----------- | | **Authentication** | Authenticate user and get access token | All projects | | **Manage Customers** | Create, update, list, and delete customer accounts within your project | All projects | | **Manage Users** | Create, update, list, and delete users associated with customer accounts | All projects | Permissions mirror the Corti Console - only project admins or owners can create, update, or delete resources. ## Quickstart * Sign up or log in at [console.corti.app](https://console.corti.app/) * Ensure your account has a password set Best practice: use a dedicated service account for Admin API automation. Assign only the minimal required role and rotate credentials regularly. Call `/auth/token` with your Console email and password to obtain a JWT access token. See API Reference: [Authenticate user and get access token](/api-reference/admin/auth/authenticate-user-and-get-access-token) ```bash theme={null} curl -X POST https://api.console.corti.app/functions/v1/public/auth/token \ -H "Content-Type: application/json" \ -d '{ "email": "your-email@example.com", "password": "your-password" }' ``` Example response: ```json theme={null} { "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "tokenType": "bearer", "expiresIn": 3600 } ``` Include the token in the Authorization header for subsequent requests: ```bash theme={null} curl -X GET https://api.console.corti.app/functions/v1/public/projects/{projectId}/customers \ -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." ``` Tokens expire after `expiresIn` seconds. Once expired, call the `auth/token` endpoint again to obtain a new token. *** ## Top Pages Obtain an access token Create a new customer in a project Create a new user within a customer
Please [contact us](mailto:help@corti.ai) for support or more information # Compliance & Trust Source: https://docs.corti.ai/about/compliance # Help Center Source: https://docs.corti.ai/about/help # Public Roadmap Source: https://docs.corti.ai/about/roadmap # A2A JSON-RPC binding Source: https://docs.corti.ai/agentic-v2/a2a/a2a-json-rpc-binding /agentic/auto-generated-openapi-v2.yml post /v2/agentic/agents/{agentId}/a2a The `JSONRPC` protocol binding for A2A v1.0. Accepts a single JSON-RPC 2.0 request whose `method` is one of `SendMessage`, `SendStreamingMessage`, `GetTask`, `ListTasks`, `CancelTask`, or `SubscribeToTask`. Streaming methods (`SendStreamingMessage`, `SubscribeToTask`) respond with `text/event-stream`; all others respond with a single JSON-RPC response. # Cancel a task (A2A REST) Source: https://docs.corti.ai/agentic-v2/a2a/cancel-a-task-a2a-rest /agentic/auto-generated-openapi-v2.yml post /v2/agentic/agents/{agentId}/a2a/tasks/{taskId}:cancel # Get a task (A2A REST) Source: https://docs.corti.ai/agentic-v2/a2a/get-a-task-a2a-rest /agentic/auto-generated-openapi-v2.yml get /v2/agentic/agents/{agentId}/a2a/tasks/{taskId} # List tasks (A2A REST) Source: https://docs.corti.ai/agentic-v2/a2a/list-tasks-a2a-rest /agentic/auto-generated-openapi-v2.yml get /v2/agentic/agents/{agentId}/a2a/tasks # Send a message (A2A REST) Source: https://docs.corti.ai/agentic-v2/a2a/send-a-message-a2a-rest /agentic/auto-generated-openapi-v2.yml post /v2/agentic/agents/{agentId}/a2a/message:send The `HTTP+JSON` binding of A2A `SendMessage`. # Stream a message (A2A REST) Source: https://docs.corti.ai/agentic-v2/a2a/stream-a-message-a2a-rest /agentic/auto-generated-openapi-v2.yml post /v2/agentic/agents/{agentId}/a2a/message:stream The `HTTP+JSON` binding of A2A `SendStreamingMessage`. Responds with a `text/event-stream` of `Task`, `statusUpdate`, and `artifactUpdate` events. # Subscribe to task events (A2A REST) Source: https://docs.corti.ai/agentic-v2/a2a/subscribe-to-task-events-a2a-rest /agentic/auto-generated-openapi-v2.yml post /v2/agentic/agents/{agentId}/a2a/tasks/{taskId}:subscribe Resubscribe to an in-flight task's event stream over SSE. # Get the A2A agent card Source: https://docs.corti.ai/agentic-v2/agent-card/get-the-a2a-agent-card /agentic/auto-generated-openapi-v2.yml get /v2/agentic/agents/{agentId}/.well-known/agent-card.json Returns the A2A v1.0 agent card describing the agent's capabilities, skills, and supported protocol interfaces. Served at the standard `.well-known` location for agent discovery. # Create an agent Source: https://docs.corti.ai/agentic-v2/agents/create-an-agent /agentic/auto-generated-openapi-v2.yml post /v2/agentic/agents Creates a new agent. The server assigns the UUIDv7 `id`. # Delete an agent Source: https://docs.corti.ai/agentic-v2/agents/delete-an-agent /agentic/auto-generated-openapi-v2.yml delete /v2/agentic/agents/{agentId} Deletes a `persistent` agent. `ephemeral` agents are expired in place. Idempotent: deleting an already-deleted agent returns `204`. # Get an agent Source: https://docs.corti.ai/agentic-v2/agents/get-an-agent /agentic/auto-generated-openapi-v2.yml get /v2/agentic/agents/{agentId} # List agents Source: https://docs.corti.ai/agentic-v2/agents/list-agents /agentic/auto-generated-openapi-v2.yml get /v2/agentic/agents Lists agents visible to the caller. `private` agents are visible only to their creator/service principal; `unlisted` agents are omitted (fetch by ID instead); `public` agents are listed tenant-wide. The `visibility`, `lifecycle`, `label`, and `q` filter parameters are accepted but not yet honored by the server; the response is unfiltered. # Update an agent (partial) Source: https://docs.corti.ai/agentic-v2/agents/update-an-agent-partial /agentic/auto-generated-openapi-v2.yml patch /v2/agentic/agents/{agentId} Partially updates an agent using JSON Merge Patch (RFC 7386). Omitted fields are unchanged; `null` clears a field; arrays replace. # Get an artifact Source: https://docs.corti.ai/agentic-v2/artifacts/get-an-artifact /agentic/auto-generated-openapi-v2.yml get /v2/agentic/contexts/{contextId}/tasks/{taskId}/artifacts/{artifactId} Returns an artifact produced by a task within a context. File parts may carry inline `bytes` or a `uri` to fetch the content out of band. # Attach a connector Source: https://docs.corti.ai/agentic-v2/connectors/attach-a-connector /agentic/auto-generated-openapi-v2.yml post /v2/agentic/agents/{agentId}/connectors # Get an agent-scoped connector Source: https://docs.corti.ai/agentic-v2/connectors/get-an-agent-scoped-connector /agentic/auto-generated-openapi-v2.yml get /v2/agentic/agents/{agentId}/connectors/{agentConnectorId} # List an agent's connectors Source: https://docs.corti.ai/agentic-v2/connectors/list-an-agents-connectors /agentic/auto-generated-openapi-v2.yml get /v2/agentic/agents/{agentId}/connectors # Remove a connector Source: https://docs.corti.ai/agentic-v2/connectors/remove-a-connector /agentic/auto-generated-openapi-v2.yml delete /v2/agentic/agents/{agentId}/connectors/{agentConnectorId} # Update an agent-scoped connector (partial) Source: https://docs.corti.ai/agentic-v2/connectors/update-an-agent-scoped-connector-partial /agentic/auto-generated-openapi-v2.yml patch /v2/agentic/agents/{agentId}/connectors/{agentConnectorId} Partially updates an agent-scoped connector using JSON Merge Patch (RFC 7386). `type` is immutable. **Future scope**: not yet implemented; the server returns `501`. # Delete or expire a context Source: https://docs.corti.ai/agentic-v2/contexts/delete-or-expire-a-context /agentic/auto-generated-openapi-v2.yml delete /v2/agentic/contexts/{contextId} # Export the observability trace for a context Source: https://docs.corti.ai/agentic-v2/contexts/export-the-observability-trace-for-a-context /agentic/auto-generated-openapi-v2.yml get /v2/agentic/contexts/{contextId}/trace Returns the execution traces for the context — LLM calls, tool executions, and token usage — in OpenInference format. Traces are ordered newest-first and paginated; each page returns up to `pageSize` traces with their spans inlined. # Get a context Source: https://docs.corti.ai/agentic-v2/contexts/get-a-context /agentic/auto-generated-openapi-v2.yml get /v2/agentic/contexts/{contextId} Returns the context's metadata together with its `tasks`, oldest first. Each task carries its full message `history`; the user's prompt for a task is the `ROLE_USER` message within that task's history (there is no separate top-level message list). # Get a task within a context Source: https://docs.corti.ai/agentic-v2/contexts/get-a-task-within-a-context /agentic/auto-generated-openapi-v2.yml get /v2/agentic/contexts/{contextId}/tasks/{taskId} # List contexts Source: https://docs.corti.ai/agentic-v2/contexts/list-contexts /agentic/auto-generated-openapi-v2.yml get /v2/agentic/contexts Lists contexts matching the filters. **Future scope**: not yet implemented; the server currently returns an empty page and ignores all parameters. # List tasks within a context Source: https://docs.corti.ai/agentic-v2/contexts/list-tasks-within-a-context /agentic/auto-generated-openapi-v2.yml get /v2/agentic/contexts/{contextId}/tasks # Delete all feedback for a task Source: https://docs.corti.ai/agentic-v2/feedback/delete-all-feedback-for-a-task /agentic/auto-generated-openapi-v2.yml delete /v2/agentic/contexts/{contextId}/tasks/{taskId}/feedback Soft-deletes every feedback resource the authenticated user submitted for the task. The task must exist, belong to the supplied context, and belong to the authenticated customer. Idempotent: deleting when there is no feedback returns `204`. # List feedback for a task Source: https://docs.corti.ai/agentic-v2/feedback/list-feedback-for-a-task /agentic/auto-generated-openapi-v2.yml get /v2/agentic/contexts/{contextId}/tasks/{taskId}/feedback Returns all feedback resources submitted for the task by the authenticated user, newest-first. The task must exist, belong to the supplied context, and belong to the authenticated customer. Feedback is scoped to the calling user via row-level security, so the response contains only that user's feedback. # Submit feedback for a task Source: https://docs.corti.ai/agentic-v2/feedback/submit-feedback-for-a-task /agentic/auto-generated-openapi-v2.yml post /v2/agentic/contexts/{contextId}/tasks/{taskId}/feedback Submits feedback about a task as a whole or about a specific user-visible message within the task. The task must exist, belong to the supplied context, and belong to the authenticated customer. Multiple feedback resources may be submitted for the same task or message. # Get registry connector details and configuration schema Source: https://docs.corti.ai/agentic-v2/registry/get-registry-connector-details-and-configuration-schema /agentic/auto-generated-openapi-v2.yml get /v2/agentic/registry/connectors/{connectorId} # List available registry connectors Source: https://docs.corti.ai/agentic-v2/registry/list-available-registry-connectors /agentic/auto-generated-openapi-v2.yml get /v2/agentic/registry/connectors # Get an agent's usage Source: https://docs.corti.ai/agentic-v2/usage/get-an-agents-usage /agentic/auto-generated-openapi-v2.yml get /v2/agentic/agents/{agentId}/usage Returns invocation metrics for the agent over the half-open `[from, to)` time range (UTC), bucketed at the requested `granularity`. The response echoes the resolved range and granularity, a `totals` summary across the whole range, and one `buckets` entry per period that had activity (the array is empty when there was none). When `from`/`to` are omitted, the range defaults to the last 30 days. # A2A protocol Source: https://docs.corti.ai/agentic/a2a-protocol Learn about the Agent-to-Agent protocol in the Agentic Framework: v1.0 only, the two protocol bindings, and how A2A relates to MCP. The **Agent-to-Agent (A2A)** protocol is an open standard that enables secure, framework-agnostic communication between autonomous AI agents. Instead of building bespoke integrations whenever you want agents to collaborate, A2A gives Corti and other systems a common language agents can use to discover, talk to, and delegate work to one another. For the full technical specification, see the official A2A project docs at [a2a-protocol.org](https://a2a-protocol.org/latest/). ## Why Corti uses A2A We chose A2A because it: * **Standardizes agent communication**: Agents can talk to each other without siloed, point-to-point integrations. That makes composite workflows easier to build and maintain. * **Supports real workflows**: A2A includes discovery, task negotiation, and streaming updates, so agents can coordinate long-running or multi-step jobs. * **Preserves security and opacity**: Agents exchange structured messages without sharing internal memory or tools. That protects intellectual property and keeps interactions predictable. * **Leverages open tooling**: There are open source SDKs in multiple languages and example implementations you can reuse. ## A2A v1.0 in Corti The Agentic Framework supports **A2A v1.0 only**. The previous v0.3 protocol version is no longer supported. All requests and responses follow the v1.0 specification. ### A2A-Version header The server returns an `A2A-Version: 1.0` header on every A2A response, indicating the protocol version used to handle the request. Client SDKs may also send this header on requests. ## Protocol bindings A2A v1.0 defines two protocol bindings. Corti supports both, and they share the same base URL: ``` /v2/agentic/agents/{agentId}/a2a ``` ### JSON-RPC binding The JSON-RPC binding sends a JSON-RPC 2.0 envelope to the base URL. The `method` field determines the operation: | JSON-RPC method | Description | | ---------------------- | ---------------------------------------------- | | `SendMessage` | Send a message (blocking or non-blocking) | | `SendStreamingMessage` | Send a message and stream the response via SSE | | `GetTask` | Get a task by ID | | `ListTasks` | List tasks for the agent | | `CancelTask` | Cancel a task | | `SubscribeToTask` | Subscribe to task events via SSE | ```json theme={null} { "jsonrpc": "2.0", "id": "msg-001", "method": "SendMessage", "params": { "message": { "role": "ROLE_USER", "parts": [{ "text": "What is the ICD-10 code for asthma?" }] } } } ``` ### HTTP+JSON binding The HTTP+JSON binding uses path-suffixed endpoints. Each JSON-RPC method maps to a specific HTTP endpoint: | JSON-RPC method | HTTP endpoint | | ---------------------- | ------------------------------------ | | `SendMessage` | `POST /a2a/message:send` | | `SendStreamingMessage` | `POST /a2a/message:stream` | | `ListTasks` | `GET /a2a/tasks` | | `GetTask` | `GET /a2a/tasks/{taskId}` | | `CancelTask` | `POST /a2a/tasks/{taskId}:cancel` | | `SubscribeToTask` | `POST /a2a/tasks/{taskId}:subscribe` | The HTTP+JSON binding is simpler for most use cases. Use the JSON-RPC binding if your client or tooling expects a JSON-RPC envelope. ### REST query parameters The HTTP+JSON binding supports query parameters on certain endpoints: | Parameter | Endpoints | Description | | --------------- | --------------------------------------------------- | ----------------------------------------------------- | | `historyLength` | `GET /a2a/tasks/{taskId}`, `POST /a2a/message:send` | Cap the number of history messages returned per task | | `pageSize` | `GET /a2a/tasks` | Page size for list responses | | `pageToken` | `GET /a2a/tasks` | Opaque cursor from a prior response's `nextPageToken` | | `contextId` | `GET /a2a/tasks` | Filter tasks by context | ## Agent discovery A2A enables agent discovery through [agent cards](/agentic/agent-cards). Every agent exposes its card at the `.well-known/agent-card.json` location, describing its capabilities, skills, and supported protocol bindings. You can retrieve a card to inspect an agent before integrating. The agent card endpoint requires the same authentication (bearer token and tenant header) as all other v2 endpoints. ## Relationship to MCP MCP (Model Context Protocol) and A2A serve different purposes in the Agentic Framework: * **A2A** is the conversational surface: how clients talk to agents, send messages, receive tasks, and stream responses * **MCP** is a connector type: how agents call external tool servers to retrieve data or perform actions In v2, MCP servers are attached as [connectors](/agentic/connectors) (type `mcp`) to an agent. The agent runtime calls the MCP server when it needs data or actions from that tool. A2A is the protocol the client uses to talk to the agent itself. The v1 MCP protocol and authentication pages are preserved in the [v1 archived documentation](/agentic/v1/mcp-protocol). In v2, MCP concepts are covered by the [connectors](/agentic/connectors) and [connector auth](/agentic/guides/connector-auth) pages. ## Open source SDKs and tooling For links to Corti's official SDK and the official A2A project SDKs (Python, JavaScript/TypeScript, Java, Go, and .NET), see [SDKs and integrations](/agentic/sdks-integrations). ## Next steps * Learn about [agent cards](/agentic/agent-cards) for A2A discovery * Read about [connectors](/agentic/connectors) and how MCP fits as a connector type * Follow the [quickstart](/agentic/quickstart) to send your first A2A message # Agent cards Source: https://docs.corti.ai/agentic/agent-cards Learn how A2A agent cards enable discovery and describe an agent's capabilities, skills, and supported protocol bindings. An agent card is a JSON document that describes an agent's identity, capabilities, skills, and supported protocol bindings. It serves as a machine-readable business card that lets clients discover agents and determine how to communicate with them. Agent cards follow the A2A v1.0 specification and are served at the standard `.well-known` location. ## Retrieve an agent card Every agent exposes its card at: ``` GET /v2/agentic/agents/{agentId}/.well-known/agent-card.json ``` This endpoint requires authentication (bearer token and tenant header), same as all other v2 API endpoints. The response includes an `A2A-Version: 1.0` header indicating the protocol version the server uses. ## Card structure | Field | Type | Description | | --------------------- | --------- | --------------------------------------------------------------------------- | | `name` | string | Agent display name | | `description` | string | What the agent does | | `version` | string | Agent card version (SemVer) | | `capabilities` | object | Capability flags: `streaming`, `pushNotifications` | | `defaultInputModes` | string\[] | Default input media types (e.g. `text/plain`) | | `defaultOutputModes` | string\[] | Default output media types | | `provider` | object | Publishing organization and URL | | `skills` | object\[] | Skills the agent exposes, each with `id`, `name`, `description`, and `tags` | | `supportedInterfaces` | object\[] | A2A protocol bindings the agent supports | | `documentationUrl` | string | URL providing additional documentation about the agent | | `iconUrl` | string | Optional URL to an icon for the agent | ### Capabilities The `capabilities` object advertises what the agent can do: * **`streaming`**: Whether the agent supports streaming responses via SSE. If `true`, you can use `message:stream` and `tasks:subscribe`. * **`pushNotifications`**: Whether the agent can push task updates to a client-supplied webhook. `pushNotifications` is future scope. The `tasks/pushNotificationConfig/*` management endpoints are not yet implemented. Expect this field to be `false` until they ship. ### Supported interfaces Each entry in `supportedInterfaces` declares one A2A protocol binding: | Property | Description | | ----------------- | ------------------------------------------- | | `protocolBinding` | The binding type: `JSONRPC` or `HTTP+JSON` | | `protocolVersion` | Always `1.0` (Corti supports A2A v1.0 only) | | `url` | The endpoint URL for this binding | Both bindings share the same base URL (`/v2/agentic/agents/{agentId}/a2a`). The `JSONRPC` binding sends a JSON-RPC envelope; the `HTTP+JSON` binding uses path-suffixed endpoints like `message:send` and `message:stream`. See [A2A protocol](/agentic/a2a-protocol) for details. ### Skills Each skill in the `skills` array represents a capability the agent exposes through its connectors. The `id` is a bare UUID (the connector's external ID). Skills help clients understand what an agent can do before sending a message. ## Using cards for discovery Agent cards enable a discovery workflow: 1. A client fetches the card from a known agent URL. 2. The client inspects `supportedInterfaces` to determine which protocol binding to use. 3. The client checks `capabilities.streaming` to decide whether to use blocking or streaming calls. 4. The client reviews `skills` to understand what the agent can do. 5. The client sends a message using the appropriate binding. Retrieving an agent card requires the same authentication as any other v2 API request. You need a valid bearer token and tenant header. ## Card example The following is a typical agent card for a coding agent: ```json theme={null} { "name": "coder", "description": "Returns ICD-10 codes for a clinical encounter.", "version": "0.1.0", "capabilities": { "streaming": true, "pushNotifications": false }, "defaultInputModes": ["text/plain"], "defaultOutputModes": ["text/plain"], "provider": { "organization": "Corti", "url": "https://corti.ai" }, "skills": [ { "id": "con.0192f4c8-7baf-7083-a46f-81d2bd70cf95", "name": "coding-expert", "description": "ICD-10 coding.", "tags": ["expert"] } ], "supportedInterfaces": [ { "protocolBinding": "JSONRPC", "protocolVersion": "1.0", "url": "https://api.eu.corti.app/v2/agentic/agents/agt.0192f4c8-2c5a-7b3e-9f1a-3c8d6e2b7a40/a2a" }, { "protocolBinding": "HTTP+JSON", "protocolVersion": "1.0", "url": "https://api.eu.corti.app/v2/agentic/agents/agt.0192f4c8-2c5a-7b3e-9f1a-3c8d6e2b7a40/a2a" } ] } ``` ## Next steps * Learn about [connectors](/agentic/connectors), which power the skills advertised in the card * Read the [A2A protocol](/agentic/a2a-protocol) page for details on the two protocol bindings * Follow the [quickstart](/agentic/quickstart) to create an agent and retrieve its card # Agent SDK API reference Source: https://docs.corti.ai/agentic/agent-sdk/api-reference Full method and type reference for the Corti Agent SDK in TypeScript and Python. The Corti Agent SDK is in **alpha v2 private preview**. The API may change between releases. Contact [help@corti.ai](mailto:help@corti.ai) to request access. This page is a complete reference for every public class, method, and type exported by the SDK. ## AgentsClient and AgentsResource In TypeScript, agent CRUD lives on `client.agents` (`AgentsResource`). In Python, it lives on `AgentsClient(client)`. | Method | Returns | Description | | --------------------------------------- | ------------------------------------------------------- | ------------------------------------------ | | `create(body)` / `create(...)` | `Agent` (TS) / `AgentHandle` (Python) | Create a new agent | | `get(agentId)` / `get(agent_id)` | `Agent` (TS) / `AgentHandle` (Python) | Fetch an existing agent by ID | | `list(params?)` / `list()` | `AgentListResponse` (TS) / `list[AgentHandle]` (Python) | List agents in the tenant | | `update(agentId, body)` / `update(...)` | `Agent` (TS) / `AgentHandle` (Python) | Partial update via JSON Merge Patch | | `delete(agentId)` / `delete()` | `void` | Delete an agent | | `wrap(agent)` (Python only) | `AgentHandle` | Wrap a raw API dict without a network call | In TypeScript, `create()` and `get()` return raw `Agent` objects. Wrap them with `new AgentHandle(agent, client)` to get conversation helpers. ## AgentHandle | Member | Type | Description | | ------------------------------------------------ | ---------------------------------- | --------------------------------------------------- | | `id` | `string` | Agent ID (server-assigned) | | `name` | `string` | Agent name | | `description` | `string \| undefined` | Agent description | | `systemPrompt` / `system_prompt` | `string \| undefined` | System prompt | | `model` | `string \| undefined` (TS only) | Model the agent uses | | `visibility` | `string` | `private`, `unlisted`, or `public` | | `lifecycle` | `string` | `ephemeral` or `persistent` | | `connectors` | `Connector[]` | Attached connectors | | `labels` | `Record` (TS only) | Free-form labels | | `raw` | `Agent` / `Dict` | Underlying API response object | | `createContext(opts?)` / `create_context(...)` | `AgentContext` | Open a new conversation thread (lazy) | | `getContext(id, opts?)` / `get_context(id, ...)` | `AgentContext` | Resume an existing thread by ID | | `run(input, opts?)` | `Promise` | One-shot invoke: create context, send, return reply | | `stream(input, opts?)` (TS only) | `AsyncGenerator` | One-shot streaming invoke | | `update(patch)` / `update(...)` | `Promise` | Partially update the agent, returns a new handle | | `refresh()` | `Promise` | Re-fetch the agent from the API | | `delete()` | `Promise` | Delete the agent | ## AgentContext | Member | Type | Description | | ------------------------------------------------------------------- | -------------------------------- | -------------------------------------------------------------- | | `id` | `string \| undefined` | Thread ID, `undefined` / `None` until the first turn completes | | `sendText(text, opts?)` / `send_text(text, ...)` | `Promise` | Send plain text | | `sendMessage(parts, opts?)` / `send_message(parts, ...)` | `Promise` | Send arbitrary `Part[]` (text, data, file) | | `streamMessage(parts, opts?)` / `stream_message(parts)` | `AsyncGenerator` | Stream incremental events for the reply | | `getTask(taskId, opts?)` / `get_task(task_id, ...)` (TS only) | `Promise` | Fetch a task by ID | | `cancelTask(taskId, opts?)` / `cancel_task(task_id, ...)` (TS only) | `Promise` | Cancel a running task | ### SendMessageOptions | Field | Type | Description | | ----------------------------------------- | ------------------------- | ----------------------------------------- | | `historyLength` | `number` | Number of history messages to include | | `blocking` | `boolean` | Wait for task completion before returning | | `acceptedOutputModes` | `string[]` | Output modes the client accepts | | `metadata` | `Record` | Free-form metadata | | `timeoutInSeconds` / `timeout_in_seconds` | `number` | Per-request timeout override (default 60) | | `abortSignal` (TS only) | `AbortSignal` | Abort the request | | `credentials` (Python only) | `CredentialStore` | MCP auth credentials | ## MessageResponse | Member | Type | Description | | -------------------------- | ------------------------------ | --------------------------------------------------------------------------------------- | | `text` | `string \| null` | All text parts from the agent reply, joined | | `status` | `string` | Task state: `completed`, `failed`, `working`, `submitted`, `canceled`, `input-required` | | `state` (TS only) | `TaskState` | Raw task state enum value | | `message` (TS only) | `Message \| undefined` | The agent's reply message | | `task` (TS only) | `Task \| undefined` | The full A2A task object | | `taskId` / `task_id` | `string \| undefined` | Task ID for this invocation | | `contextId` / `context_id` | `string \| undefined` | Thread ID, set after the first turn | | `artifacts` | `Artifact[]` | Structured outputs, deduplicated by parts content | | `raw` | `SendMessageResponse` / `Dict` | Full, unmodified API response | ## Composition primitives | Function | Returns | Description | | -------------------------------------------------------- | --------------------------- | --------------------------------------------------------- | | `workflow(steps)` | `Workflow` | Build a deterministic pipeline | | `Workflow.run(input)` | `Promise` | Execute the pipeline | | `parallel(steps)` | `Parallel` | Build a fan-out block | | `Parallel.run(input)` | `Promise` | Run all steps concurrently | | `stateGraph()` / `stateGraph()` | `StateGraph` | Build a graph with cycles and typed shared state | | `StateGraph.addNode(name, nodeFn)` / `add_node(...)` | `StateGraph` | Register a node. Use `agentNode()` for agent-backed nodes | | `StateGraph.addEdge(from, to)` / `add_edge(...)` | `StateGraph` | `to` may be a node name, `END`, or an `EdgeRouter` | | `StateGraph.run(start, state, opts?)` | `Promise` | Execute. `opts.maxIterations` bounds cycles (default 25) | | `agentNode(agent, inputFn, mergeFn)` / `agent_node(...)` | `NodeFn` | Wrap an agent as a graph node | | `END` | `EndType` | Sentinel to terminate a `StateGraph` run | ## Connector helpers ### TypeScript | Helper | Returns | Description | | ------------------------------------------ | ------------------------- | ----------------------------------------------- | | `connectors.registry(name, opts?)` | `RegistryConnectorCreate` | Use a published Corti registry connector | | `connectors.mcp({ name, url, auth? })` | `McpConnectorCreate` | Attach an MCP server | | `connectors.agent(agentId, opts?)` | `AgentConnectorCreate` | Wire another Corti agent as a sub-agent | | `connectors.a2a(url, opts?)` | `A2AConnectorCreate` | Connect to a remote A2A agent | | `connectors.schema({ name, schema, ... })` | `SchemaConnectorCreate` | Define a JSON Schema tool for structured output | ### Python | Helper | Returns | Description | | ------------------------------------------------------------------- | --------------------- | -------------------------------------------- | | `connectors.registry(name, *, system_prompt?)` | `RegistryConnector` | Use a named expert from the registry | | `connectors.mcp(mcp_url, *, name?, transport?, auth_type?, token?)` | `McpConnector` | Attach an MCP server | | `connectors.from_agent(agent_id)` | `CortiAgentConnector` | Reference another Corti agent as a sub-agent | | `connectors.a2a(a2a_url)` | `A2AConnector` | A2A protocol (reserved, not yet supported) | ### Auth helpers (TypeScript only) | Helper | Returns | Description | | --------------------------------------------- | --------------- | ------------------------------------------------------- | | `auth.none()` | `ConnectorAuth` | No auth required | | `auth.bearer()` | `ConnectorAuth` | Bearer token auth | | `auth.apiKey(ref?)` | `ConnectorAuth` | API key auth, optionally with a stored secret reference | | `auth.oauth2({ scope?, redirectUrl?, ref? })` | `ConnectorAuth` | OAuth2 auth | ## Types ### Parts | Type | Shape | Notes | | ---------- | ---------------------------------------------- | ------------------------------ | | `Part` | `TextPart \| DataPart \| FilePart` | Message content | | `TextPart` | `{ text: string }` | Plain text | | `DataPart` | `{ data: unknown }` | Structured payload | | `FilePart` | `{ file: { name?, mimeType?, bytes?, uri? } }` | Send `bytes` (base64) or `uri` | ### Tasks and messages | Type | Shape | Notes | | ----------- | --------------------------------------------------------------------------- | ---------------------------------------------------------- | | `Task` | Full A2A task object | Exposed via `MessageResponse.task` / `MessageResponse.raw` | | `Message` | `{ role, parts, messageId, contextId?, taskId? }` | An A2A message | | `Artifact` | `{ artifactId, name?, parts }` | Structured output from the agent | | `TaskState` | `submitted \| working \| input-required \| completed \| canceled \| failed` | Returned in `MessageResponse.status` | ### Composition types | Type | Shape | Notes | | -------------------------- | -------------------------------------------------------------------------------- | ------------------------------------------------------- | | `Runnable` | `{ run(input): Promise }` | What `workflow`/`parallel` accept besides `AgentHandle` | | `WorkflowStep` | `AgentHandle \| Parallel \| { agent, when?, transform?, retries?, retryDelay? }` | A workflow step | | `WorkflowResult` | `{ output, steps, stoppedEarly }` | Result of `Workflow.run()` | | `ParallelStep` | `AgentHandle \| { agent, input?, credentials? }` | A parallel step | | `ParallelResult` | `{ results, fulfilled, rejected }` | Result of `Parallel.run()` | | `EdgeRouter` | `(state: S) => string \| END` | Function form of `addEdge` | | `NodeFn` | `(state: S) => Promise> \| Partial` | Custom graph node function | | `StateGraphStep` | `{ node: string, delta: Partial }` | Per-step trace entry | | `StateGraphResult` | `{ state, steps, iterations, terminatedBy }` | Result of `StateGraph.run()` | ### Error types | Type | Description | | ----------------- | ------------------------------------------------------------------------- | | `CortiError` | Base class for all SDK errors | | `ManagementError` | Management-plane error (agent CRUD, connectors, etc.) | | `A2AError` | A2A-plane error (message send, stream, task operations) | | `HttpError` | HTTP error when the response body cannot be parsed as a known error shape | ## TypeScript vs Python naming The wire format (A2A JSON-RPC) is identical: both SDKs talk to the same API. The naming conventions differ: | TypeScript | Python | | --------------------------- | ---------------------------------------------------- | | `new CortiClient({...})` | `async with CortiClient(...) as client:` | | `sendText()` | `await send_text()` | | `sendMessage()` | `await send_message()` | | `streamMessage()` | `stream_message()` | | `createContext()` | `create_context()` | | `getContext(id)` | `get_context(id)` | | `systemPrompt` | `system_prompt` | | `timeoutInSeconds` | `timeout_in_seconds=` (keyword-only) | | `connectors.agent(agentId)` | `connectors.from_agent(agent_id)` | | `connectors.mcp({ url })` | `connectors.mcp(mcp_url=...)` | | `agentNode()` | `agent_node()` | | `stateGraph()` | `stateGraph()` (same: Python also exports camelCase) | | `terminatedBy` | `terminated_by` | | `stoppedEarly` | `stopped_early` | | `retryDelay` | `retry_delay` | # Common pitfalls Source: https://docs.corti.ai/agentic/agent-sdk/common-pitfalls Avoid the most frequent mistakes when using the Corti Agent SDK. The Corti Agent SDK is in **alpha v2 private preview**. The API may change between releases. Contact [help@corti.ai](mailto:help@corti.ai) to request access. ## Timeouts **Orchestrators time out at 60 seconds by default.** Anything that fans out to sub-agents or MCP connectors should pass `timeoutInSeconds: 180` (or higher) to `run()` or `sendText()`. The default 60-second timeout is fine for single-agent calls but will cut off orchestrators that delegate to multiple connectors. ```ts title="JavaScript" theme={null} // Bad: default 60s timeout may be too short for orchestrators const reply = await handle.run("Complex clinical question..."); // Good: raise the timeout for orchestrators const reply = await handle.run("Complex clinical question...", { timeoutInSeconds: 180 }); ``` ```python title="Python" theme={null} # Bad: default 60s timeout may be too short for orchestrators reply = await agent.run("Complex clinical question...") # Good: raise the timeout for orchestrators reply = await agent.run("Complex clinical question...", timeout_in_seconds=180) ``` ## Context IDs **You do not need to manage context IDs.** Keep the `AgentContext` object in memory across turns. The SDK tracks the thread automatically. Only use `handle.getContext(id)` / `agent.get_context(id)` when resuming after a process restart. **`ctx.id` is `undefined` / `None` until the first turn completes.** Do not persist it before then. **`createContext()` does not accept a context ID.** Passing `{ contextId }` will be silently ignored. Use `handle.getContext(contextId)` / `agent.get_context(context_id)` to resume an existing thread. ## Workflow vs stateGraph **`workflow` is linear, `stateGraph` has cycles.** Do not use `workflow` for branching that revisits earlier agents. If you need loops, conditional routing based on accumulated state, or a reviewer-approval cycle, use `stateGraph` instead. ## Parallel failures **`parallel` swallows individual failures.** Inspect `rejected` if you need fail-fast semantics. When used inside a `workflow`, if all parallel steps fail, the workflow throws. ```ts title="JavaScript" theme={null} const result = await parallel([a, b, c]).run(input); if (result.rejected.length > 0) { console.warn(`${result.rejected.length} steps failed`); for (const err of result.rejected) { console.error(err); } } ``` ```python title="Python" theme={null} result = await parallel([a, b, c]).run(input) if result.rejected: print(f"{len(result.rejected)} steps failed") for err in result.rejected: print(err) ``` ## Persistent agents **Persistent agents accumulate.** The default is `ephemeral`; only use `persistent` if you truly need the agent to survive restarts, and always delete what you no longer need. ```ts title="JavaScript" theme={null} const agent = await client.agents.create({ name: "my-bot", description: "...", lifecycle: "persistent" }); // ... use it across sessions ... // Clean up when done const handle = new AgentHandle(agent, client); await handle.delete(); ``` ```python title="Python" theme={null} agent = await agents.create(name="my-bot", description="...", lifecycle="persistent") # ... use it across sessions ... # Clean up when done await agent.delete() ``` ## ESM only (TypeScript) The TypeScript package is ESM-only (`"type": "module"`). Import with `import`, not `require`. For CommonJS projects, use dynamic `import()` or set your project to ESM. ## Self-sufficient prompts **Sub-agents that ask for clarification will stall an orchestrator.** Prompt worker agents with instructions like "Never ask for clarification" so they always produce output. An orchestrator that delegates to a sub-agent which asks a question will hang waiting for user input that never comes. ## Credential keys must match connector names (Python only) In the Python SDK (v1 architecture), the credential key in the `CredentialStore` must match the connector's `name`: ```python theme={null} # The connector name "my-mcp" must match the credential key agent = await agents.create( name="auth-demo", description="...", connectors=[connectors.mcp(mcp_url=MCP_URL, name="my-mcp", auth_type="bearer")], ) ctx = agent.create_context(credentials={ "my-mcp": {"type": "token", "token": MCP_TOKEN}, # key matches connector name }) ``` In the TypeScript v2 SDK, auth is configured on the connector itself at creation time, so this pattern does not apply. ## Streaming field names **Use `event.message`, not `event.msg`.** The streaming event field for message updates is `message`. The field for artifact updates is `artifactUpdate`, not `artifact`. ```ts title="JavaScript" theme={null} // Correct if (event.message) { /* ... */ } if (event.artifactUpdate) { /* ... */ } // Wrong: these fields do not exist if (event.msg) { /* ... */ } if (event.artifact) { /* ... */ } ``` # Multi-agent composition Source: https://docs.corti.ai/agentic/agent-sdk/composition Build deterministic workflows, fan out in parallel, and route with state graphs using the Corti Agent SDK. The Corti Agent SDK is in **alpha v2 private preview**. The API may change between releases. Contact [help@corti.ai](mailto:help@corti.ai) to request access. The Agent SDK provides three composition primitives for coordinating multiple agents. Start with `workflow`; only reach for `stateGraph` when you need cycles or branching that depends on accumulated state. ## At a glance The three patterns differ in topology: ```mermaid theme={null} flowchart LR Input --> A[Agent A
summarize] --> B[Agent B
classify] --> C[Agent C
escalate] --> Output B -.->|when: not urgent| Skip[skipped] Skip --> Output ``` Each step runs in order. A `when` predicate can skip a step, and `transform` can rewrite the input to the next step.
```mermaid theme={null} flowchart TD Input --> A[Agent A
differential] Input --> B[Agent B
red flags] Input --> C[Agent C
workup] A --> Join[Join fulfilled results
with newlines] B --> Join C --> Join Join --> Output ``` All agents run at the same time on the same input. Fulfilled results are collected; rejected steps are isolated.
```mermaid theme={null} flowchart TD Start --> Triage Triage -->|urgent| Coder Triage -->|routine| End[END] Coder --> Reviewer Reviewer -->|approved| End Reviewer -->|rejected| Coder ``` Nodes share a typed state object. Routing functions inspect the state after each node and pick the next node, `END`, or loop back. `maxIterations` bounds cycles.
## Workflow A `Workflow` is a fixed list of steps. Each step receives the previous step's response (or a transform of it) and returns a new response. ```mermaid theme={null} flowchart LR Input --> A[Agent A
summarize] --> B[Agent B
classify] --> C{when?} C -->|yes| D[Agent C
escalate] --> Output C -->|no| Output D -.->|failed| Stop[stoppedEarly] Stop --> Output ``` ```ts title="JavaScript" theme={null} import { workflow } from "@corti/agent-sdk"; const result = await workflow([ summarizer, // step 1: bare agent classifier, // step 2: bare agent { // step 3: full config agent: escalator, when: (prev) => (prev.text ?? "").toLowerCase().includes("urgent"), transform: () => note, retries: 2, retryDelay: 500, }, ]).run(note); console.log(result.output.text); // final step's text console.log(result.steps); // per-step responses console.log(result.stoppedEarly); // true if a step failed ``` ```python title="Python" theme={null} from corti_agent_sdk import workflow result = await workflow([ summarizer, # step 1: bare agent classifier, # step 2: bare agent { # step 3: full config "agent": escalator, "when": lambda prev: "urgent" in (prev.text or ""), "transform": lambda _: note, "retries": 2, "retry_delay": 0.5, }, ]).run(note) print(result.output.text) # final step's text print(result.steps) # per-step responses print(result.stopped_early) # True if a step failed ``` ### Step options Each step can be a bare `AgentHandle`, a `Parallel` group (auto-wrapped), or a configuration object: | Field | Description | | ---------------------------- | -------------------------------------------------------------------------------------------------------------------- | | `agent` | Any object with a `run(input)` method: an `AgentHandle`, a `Parallel` group, or a custom runnable | | `when` | Predicate on the previous response. If false, the step is skipped and the previous response passes forward unchanged | | `transform` | Map the previous response to a new input for this step. Default: `prev.text` | | `retries` | Additional attempts when a step returns `status: "failed"` (default 0) | | `retryDelay` / `retry_delay` | Delay between retries in milliseconds (TypeScript) or seconds (Python), default 1000 ms / 1.0 s | ### WorkflowResult | Field | Type | Description | | -------------------------------- | ------------------- | ----------------------------------------------------------- | | `output` | `MessageResponse` | The last executed response | | `steps` | `MessageResponse[]` | Responses from every executed step (skipped steps excluded) | | `stoppedEarly` / `stopped_early` | `boolean` | True when a step failed and stopped execution early | ## Parallel `Parallel` runs multiple agents concurrently on the same input. Use it standalone or drop it into a workflow step list. ```mermaid theme={null} flowchart TD Input --> A[Agent A] Input --> B[Agent B] Input --> C[Agent C] A -->|fulfilled| Join[Join with newlines] B -->|fulfilled| Join C -->|rejected| Rejected[rejected array] Join --> Output ``` ```ts title="JavaScript" theme={null} import { parallel, workflow } from "@corti/agent-sdk"; // Standalone const fanout = await parallel([differential, redFlags, workup]).run(presentation); fanout.fulfilled; // MessageResponse[]: successful results fanout.rejected; // unknown[]: exceptions from failed steps // Inside a workflow: fulfilled outputs are joined with newlines const { output } = await workflow([ parallel([differential, redFlags, workup]), synthesizer, ]).run(presentation); ``` ```python title="Python" theme={null} from corti_agent_sdk import parallel, workflow # Standalone fanout = await parallel([differential, red_flags, workup]).run(presentation) fanout.fulfilled # List[MessageResponse]: successful results fanout.rejected # List: exceptions from failed steps # Inside a workflow: fulfilled outputs are joined with newlines result = await workflow([ parallel([differential, red_flags, workup]), synthesizer, ]).run(presentation) ``` ### Per-step overrides Pass per-step input or credentials when branches need different data: ```ts title="JavaScript" theme={null} parallel([ { agent: differential, input: "specialised prompt for differential" }, { agent: redFlags, input: "focus on red flags only" }, workup, // bare handle: uses shared input ]); ``` ```python title="Python" theme={null} parallel([ {"agent": differential, "input": "specialised prompt for differential"}, {"agent": red_flags, "input": "focus on red flags only"}, workup, # bare handle: uses shared input ]) ``` ### ParallelResult | Field | Type | Description | | ----------- | --------------------------------------- | -------------------------------------------------------- | | `results` | `PromiseSettledResult[]` / `List[Dict]` | One entry per step with `status` and `value` or `reason` | | `fulfilled` | `MessageResponse[]` | Responses from steps that completed without raising | | `rejected` | `unknown[]` | Exceptions from steps that raised | `Parallel` swallows individual failures. Inspect `rejected` if you need fail-fast semantics. When used inside a `Workflow`, if all parallel steps fail, the workflow throws. ## StateGraph When you need **cycles** or branching that depends on accumulated state, use `StateGraph`. Each node mutates a typed state object; edges route to the next node, including loops bounded by `maxIterations`. ```mermaid theme={null} flowchart TD Triage -->|severity: urgent| Coder Triage -->|severity: routine| End[END] Coder --> Reviewer Reviewer -->|approved| End Reviewer -->|rejected| Coder Coder -.->|maxIterations| End ``` ### Concepts * **State**: a plain typed object that accumulates across every node execution. Each node returns a `Partial` that is shallow-merged in. * **Nodes**: async functions `(state: S) => Promise>`. Use `agentNode()` to wrap an `AgentHandle`. * **Edges**: a static node name, `END`, or a routing function `(state: S) => string | END` that runs after the node updates the state. * **`END`**: sentinel that stops execution. A node with no registered edge also terminates the run. ### Minimal example ```ts title="JavaScript" theme={null} import { stateGraph, agentNode, END } from "@corti/agent-sdk"; interface TriageState { note: string; severity: string; codes: string; approved: boolean; } const graph = stateGraph() .addNode("triage", agentNode( triageAgent, (s) => s.note, (r) => ({ severity: r.text ?? "" }), )) .addNode("coder", agentNode( coderAgent, (s) => s.note, (r) => ({ codes: r.text ?? "" }), )) .addNode("reviewer", agentNode( reviewerAgent, (s) => `Note: ${s.note}\n\nProposed codes: ${s.codes}`, (r) => ({ approved: (r.text ?? "").toLowerCase().startsWith("approved") }), )) .addEdge("triage", (s) => s.severity.toLowerCase().includes("urgent") ? "coder" : END, ) .addEdge("coder", "reviewer") .addEdge("reviewer", (s) => (s.approved ? END : "coder")); const result = await graph.run("triage", { note, severity: "", codes: "", approved: false }, { maxIterations: 10 }); result.state; // final TriageState result.steps; // per-node deltas result.iterations; // number of node executions result.terminatedBy; // "end" | "maxIterations" | "noEdge" ``` ```python title="Python" theme={null} from corti_agent_sdk import stateGraph, agent_node, END class TriageState(TypedDict): note: str severity: str codes: str approved: bool graph = ( stateGraph() .add_node("triage", agent_node( triage_agent, input_fn=lambda s: s["note"], merge_fn=lambda r: {"severity": r.text or ""}, )) .add_node("coder", agent_node( coder_agent, input_fn=lambda s: s["note"], merge_fn=lambda r: {"codes": r.text or ""}, )) .add_node("reviewer", agent_node( reviewer_agent, input_fn=lambda s: f"Note: {s['note']}\n\nProposed codes: {s['codes']}", merge_fn=lambda r: {"approved": (r.text or "").lower().startswith("approved")}, )) .add_edge("triage", lambda s: "coder" if "urgent" in s["severity"].lower() else END) .add_edge("coder", "reviewer") .add_edge("reviewer", lambda s: END if s["approved"] else "coder") ) result = await graph.run("triage", {"note": note, "severity": "", "codes": "", "approved": False}, max_iterations=10) result.state # final state dict result.steps # per-node deltas result.iterations # number of node executions result.terminated_by # "end" | "maxIterations" | "noEdge" ``` ### agentNode Wraps an `AgentHandle` as a node function. Provide two callbacks: one to extract the agent's input from state, and one to merge the response back. ```ts title="JavaScript" theme={null} agentNode( myAgent, (state) => state.input, // extract input from state (response) => ({ output: response.text ?? "" }), // merge response into state ) ``` ```python title="Python" theme={null} agent_node( my_agent, input_fn=lambda s: s["input"], merge_fn=lambda r: {"output": r.text or ""}, ) ``` ### Custom nodes You can add non-agent nodes that transform state directly: ```ts title="JavaScript" theme={null} graph.addNode("normalize", (s) => ({ note: s.note.trim() })); ``` ```python title="Python" theme={null} graph.add_node("normalize", lambda s: {"note": s["note"].strip()}) ``` ### Result shape | Field | Type | Description | | -------------------------------- | -------------------------------------- | ---------------------------------------------------------- | | `state` | `S` | Final accumulated state after all nodes ran | | `steps` | `StateGraphStep[]` | Per-node history: `node` name, `delta`, post-delta `state` | | `iterations` | `number` | Total node executions (including repeated nodes in cycles) | | `terminatedBy` / `terminated_by` | `"end" \| "maxIterations" \| "noEdge"` | Why the graph stopped | ### Choosing between workflow and stateGraph | | `workflow()` | `stateGraph()` | | ------------ | ------------------------------ | ------------------------------------------------ | | Shape | Linear list of steps | Named nodes with explicit edges | | Shared state | Previous response text only | Typed object, accumulated | | Branching | `when` predicate (skip or run) | Routing function (pick any node) | | Cycles | None | Supported, bounded by `maxIterations` | | Best for | Known fixed pipelines | Conditional flows, review loops, dynamic routing | ## Decision guide | You want to... | Use | | -------------------------------------------------------------- | ------------ | | Run a fixed pipeline of agents (A to B to C) | `workflow` | | Run agents concurrently on the same input | `parallel` | | Conditionally branch, loop, or share typed state across agents | `stateGraph` | # Agent SDK core concepts Source: https://docs.corti.ai/agentic/agent-sdk/concepts Learn the building blocks of the Corti Agent SDK: client, agents, contexts, connectors, message responses, streaming, and credentials. The Corti Agent SDK is in **alpha v2 private preview**. The API may change between releases. Contact [help@corti.ai](mailto:help@corti.ai) to request access. This page covers the core building blocks you use in every Agent SDK application. ## CortiClient The root client. You create one instance and pass it to every resource client. It handles authentication, base URL resolution, and the `Tenant-Name` and `A2A-Version` headers automatically. ```ts title="JavaScript" theme={null} import { CortiClient } from "@corti/agent-sdk"; // Replace these with your values const TENANT = ""; const TOKEN = ""; const client = new CortiClient({ token: TOKEN, tenant: TENANT }); ``` ```python title="Python" theme={null} from corti_agent_sdk import CortiClient # Replace these with your values CLIENT_ID = "" CLIENT_SECRET = "" ENVIRONMENT = "" TENANT = "" client = CortiClient( tenant_name=TENANT, environment=ENVIRONMENT, auth={"client_id": CLIENT_ID, "client_secret": CLIENT_SECRET}, ) ``` The TypeScript client accepts a `tokenProvider` function instead of a static `token` for OAuth flows where the token expires. The client calls it before every request and uses the return value. The client exposes typed resource clients: | Property | Covers | | ------------------ | --------------------------------------------------------------------- | | `client.agents` | Agent CRUD (`POST`, `GET`, `PATCH`, `DELETE` on `/v2/agentic/agents`) | | `client.contexts` | Contexts, tasks, and traces | | `client.registry` | Browse pre-built registry connectors | | `client.usage` | Agent usage metrics | | `client.feedback` | Submit task feedback | | `client.agentCard` | A2A agent card | | `client.models` | List available models | The resource clients above are available in the TypeScript SDK. The Python SDK currently provides `AgentsClient` only; additional resource clients will be added in the v2 rebuild. The TypeScript package also exposes a `client.raw` property: the underlying `openapi-fetch` client for direct API access when you need an endpoint the SDK does not wrap yet. ## Agents An agent is a reusable unit of behaviour: a name, a description, a system prompt, and a set of tools (connectors). You create agents via the agents resource client, which returns typed `AgentHandle` objects. ### Create an agent ```ts title="JavaScript" theme={null} import { AgentHandle, connectors } from "@corti/agent-sdk"; // Replace these with your values const REGISTRY_NAME = "@corti/medical-coding"; const TENANT = ""; const TOKEN = ""; const client = new CortiClient({ token: TOKEN, tenant: TENANT }); const agent = await client.agents.create({ name: "my-agent", description: "Handles medical coding queries", systemPrompt: "You are a medical coding assistant.", lifecycle: "persistent", connectors: [connectors.registry(REGISTRY_NAME)], }); const handle = new AgentHandle(agent, client); ``` ```python title="Python" theme={null} from corti_agent_sdk import CortiClient, AgentsClient, connectors # Replace these with your values CLIENT_ID = "" CLIENT_SECRET = "" ENVIRONMENT = "" REGISTRY_NAME = "@corti/medical-coding" TENANT = "" async with CortiClient( tenant_name=TENANT, environment=ENVIRONMENT, auth={"client_id": CLIENT_ID, "client_secret": CLIENT_SECRET}, ) as client: agents = AgentsClient(client) agent = await agents.create( name="my-agent", description="Handles medical coding queries", system_prompt="You are a medical coding assistant.", lifecycle="persistent", connectors=[connectors.registry(name=REGISTRY_NAME)], ) ``` ### Fetching and listing ```ts title="JavaScript" theme={null} const handle = new AgentHandle(await client.agents.get(""), client); const list = await client.agents.list(); ``` ```python title="Python" theme={null} agent = await agents.get("") all_agents = await agents.list() ``` ### Updating Only the fields you pass are sent. Passing `connectors` **replaces** the full set. ```ts title="JavaScript" theme={null} const updated = await handle.update({ systemPrompt: "Be more concise." }); ``` ```python title="Python" theme={null} updated = await agent.update(system_prompt="Be more concise.") ``` ### AgentHandle An `AgentHandle` wraps an agent response and provides conversation helpers. In TypeScript, `client.agents.create()` returns a raw `Agent` object; you wrap it explicitly. In Python, `AgentsClient.create()` returns an `AgentHandle` directly. | Member | Description | | ------------------------------------------------------- | ------------------------------------------------------------ | | `id` | Server-assigned agent ID | | `name`, `description`, `systemPrompt` / `system_prompt` | Agent metadata | | `raw` | The underlying API response object | | `createContext()` / `create_context()` | Open a new conversation thread | | `getContext(id)` / `get_context(id)` | Resume an existing thread by ID | | `run(input, opts?)` | One-shot: create a context, send a message, return the reply | | `stream(input, opts?)` | One-shot streaming (TypeScript only) | | `update(patch)` | Partially update the agent, returns a new handle | | `refresh()` | Re-fetch the agent from the API | | `delete()` | Delete the agent | ### Lifecycle: ephemeral vs persistent Agents default to `ephemeral`, which means the server cleans them up automatically. Use `persistent` only when the agent must survive process restarts. | | `ephemeral` (default) | `persistent` | | -------------------------- | ----------------------------------------------- | ------------------------------------------------------------------------------------- | | Cleaned up by server | Yes, automatically | No, you must call `delete()` | | Visible in `agents.list()` | No | Yes | | Survives process restarts | No | Yes, store `agent.id` | | Best for | Scripts, request handlers, tests, one-off tasks | Long-lived bots or services where recreating the agent on every deploy is undesirable | Lifecycle controls the agent definition (system prompt, connectors). Conversation threads (`AgentContext`) are always managed automatically regardless of lifecycle. You never need to create or delete threads manually. Use ephemeral unless you have a specific reason not to. Persistent agents accumulate in your tenant if you forget to delete them. ## Contexts and conversations An `AgentContext` represents a single conversation thread. Contexts are lazy: no network call is made until the first message is sent, at which point the server creates the thread and returns a `contextId` that the SDK tracks automatically. ### Multi-turn conversation ```ts title="JavaScript" theme={null} const ctx = handle.createContext(); const reply1 = await ctx.sendText("What is the ICD-10 code for hypertension?"); console.log(reply1.text); // "The ICD-10 code is I10." // Follow-ups on the same context remember prior turns const reply2 = await ctx.sendText("What about type 2 diabetes?"); console.log(reply2.text); // "The ICD-10 code is E11.9." ``` ```python title="Python" theme={null} ctx = agent.create_context() reply1 = await ctx.send_text("What is the ICD-10 code for hypertension?") print(reply1.text) # "The ICD-10 code is I10." # Follow-ups on the same context remember prior turns reply2 = await ctx.send_text("What about type 2 diabetes?") print(reply2.text) # "The ICD-10 code is E11.9." ``` You do not need to manage context IDs yourself. Keep the context object in memory across turns. Only persist `ctx.id` if you need to resume the exact same thread after a process restart, and use `handle.getContext(id)` to do so. ### One-shot helper No context object needed for single-shot invocations: ```ts title="JavaScript" theme={null} const reply = await handle.run("What is the ICD-10 code for asthma?"); ``` ```python title="Python" theme={null} reply = await agent.run("What is the ICD-10 code for asthma?") ``` ### Resuming a thread across sessions If you need to continue a thread after a process restart, persist `ctx.id` (only available after the first turn) and use `handle.getContext(id)` / `agent.get_context(id)` next time. ```ts title="JavaScript" theme={null} // Session 1 const ctx = handle.createContext(); await ctx.sendText("Hello"); const savedId = ctx.id!; // persist this // Session 2 const ctx2 = handle.getContext(savedId); await ctx2.sendText("What did I say last time?"); ``` ```python title="Python" theme={null} # Session 1 ctx = agent.create_context() await ctx.send_text("Hello") saved_id = ctx.id # persist this # Session 2 ctx2 = agent.get_context(saved_id) await ctx2.send_text("What did I say last time?") ``` This is the only valid way to pass a context ID. Do not construct `AgentContext` yourself or pass IDs to `createContext()`. ### sendText vs sendMessage vs streamMessage All three live on `AgentContext` and send to the same thread (the `contextId` is shared). The only differences are the input shape and whether the response is buffered or streamed. | Method | Input | Output | Use when | | ------------------------------------------------ | --------------------------- | ----------------------------- | ------------------------------------------------- | | `sendText(text)` / `send_text(text)` | `string` | `Promise` | You only need to send plain text | | `sendMessage(parts)` / `send_message(parts)` | `Part[]` (text, data, file) | `Promise` | You need to attach data, files, or mix part kinds | | `streamMessage(parts)` / `stream_message(parts)` | `Part[]` | `AsyncGenerator` | You want incremental tokens as they arrive | `sendText` is a convenience wrapper for `sendMessage`: ```ts title="JavaScript" theme={null} ctx.sendText("hello"); // is equivalent to ctx.sendMessage([{ text: "hello" }]); ``` ```python title="Python" theme={null} await ctx.send_text("hello") # is equivalent to await ctx.send_message([{"text": "hello"}]) ``` When to reach for `sendMessage` over `sendText`: ```ts title="JavaScript" theme={null} // Mix text + structured data in one turn await ctx.sendMessage([ { text: "Use the attached patient record:" }, { data: { patientId: "abc", age: 62 } }, ]); // Send a file await ctx.sendMessage([ { file: { name: "scan.pdf", mimeType: "application/pdf", uri: "https://..." } }, ]); ``` ```python title="Python" theme={null} # Mix text + structured data in one turn await ctx.send_message([ {"text": "Use the attached patient record:"}, {"data": {"patientId": "abc", "age": 62}}, ]) # Send a file await ctx.send_message([ {"file": {"name": "scan.pdf", "mimeType": "application/pdf", "uri": "https://..."}} ]) ``` `streamMessage` returns events incrementally; `sendMessage` waits for `completed` or `failed` and returns the final aggregate. Streaming has no buffered `MessageResponse`: assemble the text yourself by concatenating `event.message.parts`. ## Connectors Connector factories build typed connector definitions. You declare connectors at agent creation time in the `connectors` array. The agent can call them autonomously when its prompt suggests doing so. ```ts title="JavaScript" theme={null} import { connectors } from "@corti/agent-sdk"; connectors.registry("@corti/medical-coding"); connectors.mcp({ name: "my-mcp", url: "https://mcp.example.com" }); connectors.agent(""); connectors.a2a("https://remote-agent.example.com"); connectors.schema({ name: "structured-output", schema: { type: "object" } }); ``` ```python title="Python" theme={null} from corti_agent_sdk import connectors connectors.registry(name="@corti/medical-coding") connectors.mcp(mcp_url="https://mcp.example.com") connectors.from_agent(agent_id="") connectors.a2a(a2a_url="https://remote-agent.example.com") ``` | Factory | Type | Required fields | Description | | ----------------------------------------- | ---------- | ---------------------- | ----------------------------------------------------------------- | | `registry(name)` | `registry` | `name` | Use a published Corti registry connector | | `mcp({ name, url })` / `mcp(mcp_url=...)` | `mcp` | `url` / `mcp_url` | Attach an MCP server | | `agent(agentId)` / `from_agent(agent_id)` | `agent` | `agentId` / `agent_id` | Wire another Corti agent in as a sub-agent | | `a2a(url)` / `a2a(a2a_url)` | `a2a` | `url` / `a2a_url` | Connect to a remote A2A agent | | `schema({ name, schema })` | `schema` | `name`, `schema` | Define a JSON Schema tool for structured output (TypeScript only) | The `schema` connector type is new in v2 and currently available in the TypeScript package only. The Python package will add it in the v2 rebuild. ### Registry connectors Typical registry connectors include: | Name | What it does | | ------------------- | -------------------------------------------------------------- | | `web-search-expert` | Live web search, good for recent guidelines and drug approvals | | `coding-expert` | ICD-10 and medical coding, translates clinical terms to codes | | `pubmed-expert` | PubMed literature search, finds relevant clinical studies | To see what connectors are available in your tenant, browse the [registry connector reference](/agentic/registry/overview). ### Building a clinical orchestrator Use multiple connectors together to build a well-rounded clinical orchestrator: ```ts title="JavaScript" theme={null} import { CortiClient, AgentHandle, connectors } from "@corti/agent-sdk"; // Replace these with your values const TENANT = ""; const TOKEN = ""; const client = new CortiClient({ token: TOKEN, tenant: TENANT }); // A small worker agent const symptomExtractor = await client.agents.create({ name: "symptom-extractor", description: "Extracts symptoms from a clinical note.", systemPrompt: "You are a symptom extractor. Reply with ONLY a comma-separated list of symptoms. Never ask for clarification.", }); const extractorHandle = new AgentHandle(symptomExtractor, client); // An orchestrator that wires sub-agent + registry connectors const orchestrator = await client.agents.create({ name: "triage-orchestrator", description: "Triages a clinical note.", systemPrompt: "Pass the note to symptom-extractor, then write a one-paragraph triage recommendation.", connectors: [ connectors.agent(extractorHandle.id), connectors.registry("web-search-expert"), connectors.registry("coding-expert"), ], }); const orchestratorHandle = new AgentHandle(orchestrator, client); // Orchestrators fan out: raise the timeout const reply = await orchestratorHandle.run( "62yo with sudden severe headache, photophobia, neck stiffness.", { timeoutInSeconds: 180 }, ); console.log(reply.text); ``` ```python title="Python" theme={null} import asyncio from corti_agent_sdk import CortiClient, AgentsClient, connectors # Replace these with your values CLIENT_ID = "" CLIENT_SECRET = "" ENVIRONMENT = "" TENANT = "" async def main(): async with CortiClient( tenant_name=TENANT, environment=ENVIRONMENT, auth={"client_id": CLIENT_ID, "client_secret": CLIENT_SECRET}, ) as client: agents = AgentsClient(client) symptom_extractor = await agents.create( name="symptom-extractor", description="Extracts symptoms from a clinical note.", system_prompt="You are a symptom extractor. Reply with ONLY a comma-separated list of symptoms.", ) orchestrator = await agents.create( name="triage-orchestrator", description="Triages a clinical note.", system_prompt="Pass the note to symptom-extractor, then write a one-paragraph triage recommendation.", connectors=[ connectors.from_agent(agent_id=symptom_extractor.id), connectors.registry(name="web-search-expert"), connectors.registry(name="coding-expert"), ], ) # Orchestrators fan out: raise the timeout reply = await orchestrator.run( "62yo with sudden severe headache, photophobia, neck stiffness.", timeout_in_seconds=180, ) print(reply.text) asyncio.run(main()) ``` ## MessageResponse Every non-streaming call returns a `MessageResponse` that promotes the fields you most often need to the top level. | Member | Type | Description | | -------------------------- | --------------------- | --------------------------------------------------------------------------------------- | | `text` | `string \| null` | All text parts from the agent reply, joined | | `status` | `string` | Task state: `completed`, `failed`, `working`, `submitted`, `canceled`, `input-required` | | `artifacts` | `Artifact[]` | Structured outputs the agent attached, deduplicated | | `contextId` / `context_id` | `string \| undefined` | The thread ID, set after the first turn | | `taskId` / `task_id` | `string \| undefined` | The task ID for this invocation | | `raw` | `object` | The full, unmodified API response | ## Streaming Streaming responses arrive as an async generator of `StreamEvent` objects. Each event is one of `task`, `message`, `statusUpdate`, or `artifactUpdate`. ```ts title="JavaScript" theme={null} for await (const event of ctx.streamMessage([{ text: "Explain photosynthesis." }])) { // Status transitions: "submitted" -> "working" -> "completed" | "failed" if (event.statusUpdate) { console.log(`[${event.statusUpdate.status.state}]`); } // Incremental message parts if (event.message) { for (const part of event.message.parts) { if (part.text) process.stdout.write(part.text); } } // event.artifactUpdate: emitted when the agent attaches structured outputs } ``` ```python title="Python" theme={null} async for event in ctx.stream_message([{"text": "Explain photosynthesis."}]): if "statusUpdate" in event: print(f"[{event['statusUpdate']['status']['state']}]") if "message" in event: for part in event["message"]["parts"]: if "text" in part: print(part["text"], end="") ``` `streamMessage` tracks the same `contextId` as `sendMessage`. You can freely mix streaming and non-streaming calls on a single context. ## Connector auth In the TypeScript v2 SDK, connector auth is configured on the connector itself at agent creation time, not forwarded as credentials at call time. Use the `auth` factory to specify the auth type: ```ts title="JavaScript" theme={null} import { connectors, auth } from "@corti/agent-sdk"; // Replace these with your values const MCP_URL = "https://mcp.example.com"; connectors.mcp({ name: "my-mcp", url: MCP_URL, auth: auth.bearer(), }); connectors.mcp({ name: "secure-mcp", url: MCP_URL, auth: auth.apiKey(""), }); connectors.mcp({ name: "oauth-mcp", url: MCP_URL, auth: auth.oauth2({ scope: "read", redirectUrl: "https://app.example.com/callback" }), }); ``` The Python SDK (still on v1 architecture) uses a `CredentialStore` passed to `create_context()`. The credential key must match the connector's `name`: ```python title="Python" theme={null} # Replace these with your values MCP_TOKEN = "" MCP_URL = "https://mcp.example.com" agent = await agents.create( name="auth-demo", description="Calls an auth-protected MCP server.", connectors=[connectors.mcp(mcp_url=MCP_URL, name="my-mcp", auth_type="bearer")], ) ctx = agent.create_context(credentials={ "my-mcp": {"type": "token", "token": MCP_TOKEN}, }) reply = await ctx.send_text("List the tools you have access to.") print(reply.status) # "completed" ``` When an MCP connector requires auth, the agent may reply with status `auth-required` until you provide credentials. The Python SDK forwards credentials transparently and re-sends them if the agent asks again. # Agent SDK overview Source: https://docs.corti.ai/agentic/agent-sdk/overview Get started with the Corti Agent SDK for TypeScript and Python, in alpha v2 private preview. The Corti Agent SDK is in **alpha v2 private preview**. The API surface may change between releases, and the Python package is still being rebuilt for v2 parity. Do not use it in production without a dedicated support arrangement. Contact [help@corti.ai](mailto:help@corti.ai) to request access. The Corti Agent SDK (`@corti/agent-sdk` for TypeScript, `corti-agent-sdk` for Python) is a developer-friendly wrapper for building multi-agent systems on the [Agentic Framework](/agentic/overview) v2 API. The TypeScript package is the v2 reference implementation. The Python package is being rebuilt for v2 parity; until then, the two may differ in available features. The TypeScript package is built on [openapi-ts](https://openapi-ts.dev/) with `openapi-fetch` as its only runtime dependency. The Python package uses [httpx](https://www.python-httpx.org/) with async/await. ## What it provides * **Agent lifecycle**: create, list, get, update, and delete agents via typed resource clients * **A2A messaging**: send messages, stream responses, and manage conversation contexts with automatic context ID tracking * **Connector factories**: build `registry`, `mcp`, `agent`, `a2a`, and `schema` (TypeScript only) connectors with typed factory helpers * **Multi-agent composition**: deterministic `workflow` pipelines, `parallel` fan-out, and `stateGraph` routing with cycles and typed shared state * **Structured errors**: typed error classes (`CortiError`, `ManagementError`, `HttpError`, `A2AError`) for both management and A2A planes * **Extended resources**: contexts, registry, usage, feedback, agent cards, and models ## Prerequisites * A Corti API tenant with credentials * **TypeScript**: Node.js 18 or later (uses native `fetch` and `AsyncIterable`) * **Python**: Python 3.9 or later The TypeScript package is ESM-only. For CommonJS projects, use dynamic `import()` or set your project to ESM. ## Install ```bash title="JavaScript" theme={null} npm install @corti/agent-sdk ``` ```bash title="Python" theme={null} pip install corti-agent-sdk ``` ## Quick example ```ts title="JavaScript" theme={null} import { CortiClient, AgentHandle, connectors } from "@corti/agent-sdk"; // Replace these with your values const REGISTRY_NAME = "@corti/medical-coding"; const TENANT = ""; const TOKEN = ""; const client = new CortiClient({ token: TOKEN, tenant: TENANT }); const agent = await client.agents.create({ name: "my-agent", description: "Handles medical coding queries", connectors: [connectors.registry(REGISTRY_NAME)], }); const handle = new AgentHandle(agent, client); const ctx = handle.createContext(); const reply = await ctx.sendText("ICD-10 code for hypertension?"); console.log(reply.text); // "The ICD-10 code is I10." ``` ```python title="Python" theme={null} import asyncio from corti_agent_sdk import CortiClient, AgentsClient, connectors # Replace these with your values CLIENT_ID = "" CLIENT_SECRET = "" ENVIRONMENT = "" REGISTRY_NAME = "@corti/medical-coding" TENANT = "" async def main(): async with CortiClient( tenant_name=TENANT, environment=ENVIRONMENT, auth={"client_id": CLIENT_ID, "client_secret": CLIENT_SECRET}, ) as client: agents = AgentsClient(client) agent = await agents.create( name="my-agent", description="Handles medical coding queries", connectors=[connectors.registry(name=REGISTRY_NAME)], ) ctx = agent.create_context() reply = await ctx.send_text("ICD-10 code for hypertension?") print(reply.text) # "The ICD-10 code is I10." asyncio.run(main()) ``` ## Decision guide | You want to... | Use | | -------------------------------------------------------------- | ------------------------------------ | | Send one message and get one reply | `agent.run(text)` or `ctx.sendText` | | Hold a multi-turn conversation | `agent.createContext()` + `sendText` | | Resume a thread from a previous process or session | `agent.getContext(contextId)` | | Stream tokens as the agent writes them | `ctx.streamMessage([...])` | | Run a fixed pipeline of agents (A to B to C) | `workflow([...])` | | Run agents concurrently on the same input | `parallel([...])` | | Conditionally branch, loop, or share typed state across agents | `stateGraph()` | | Attach an MCP server, sub-agent, or registry connector | `connectors.*` in `create({...})` | **Rule of thumb:** start with `workflow`. Only reach for `stateGraph` when you need cycles or branching that depends on accumulated state. ## Next steps Learn about the client, agents, contexts, connectors, message responses, and streaming. Build workflows, fan out in parallel, and route with state graphs. Full method and type reference for both languages. Avoid the most frequent mistakes when using the SDK. # Create Agent Source: https://docs.corti.ai/agentic/agents/create-agent /agentic/auto-generated-openapi.yml post /agents This endpoint allows the creation of a new agent that can be utilized in the `POST /agents/{id}/v1/message:send` endpoint. # Delete Agent by ID Source: https://docs.corti.ai/agentic/agents/delete-agent-by-id /agentic/auto-generated-openapi.yml delete /agents/{id} This endpoint deletes an agent by its identifier. Once deleted, the agent can no longer be used in threads. # Delete Context by ID Source: https://docs.corti.ai/agentic/agents/delete-context-by-id /agentic/auto-generated-openapi.yml delete /agents/{id}/v1/contexts/{contextId} This endpoint deletes a context (thread) and scrubs all associated data including messages, memories, and memory chunks for the given agent. Thread and task metadata is soft-deleted for audit purposes, while content columns are irreversibly overwritten. # Get Agent by ID Source: https://docs.corti.ai/agentic/agents/get-agent-by-id /agentic/auto-generated-openapi.yml get /agents/{id} This endpoint retrieves an agent by its identifier. The agent contains information about its capabilities and the experts it can call. # Get Agent Card Source: https://docs.corti.ai/agentic/agents/get-agent-card /agentic/auto-generated-openapi.yml get /agents/{id}/agent-card.json This endpoint retrieves the agent card in JSON format, which provides metadata about the agent, including its name, description, and the experts it can call. # Get Context by ID Source: https://docs.corti.ai/agentic/agents/get-context-by-id /agentic/auto-generated-openapi.yml get /agents/{id}/v1/contexts/{contextId} This endpoint retrieves all tasks and top-level messages associated with a specific context for the given agent. # Get Task by ID Source: https://docs.corti.ai/agentic/agents/get-task-by-id /agentic/auto-generated-openapi.yml get /agents/{id}/v1/tasks/{taskId} This endpoint retrieves the status and details of a specific task associated with the given agent. It provides information about the task's current state, history, and any artifacts produced during its execution. # List Agents Source: https://docs.corti.ai/agentic/agents/list-agents /agentic/auto-generated-openapi.yml get /agents This endpoint retrieves a list of all agents that can be called by the Corti Agent Framework. # List Registry Experts Source: https://docs.corti.ai/agentic/agents/list-registry-experts /agentic/auto-generated-openapi.yml get /agents/registry/experts This endpoint retrieves the experts registry, which contains information about all available experts that can be referenced when creating agents through the AgentsCreateExpertReference schema. # Send Message to Agent Source: https://docs.corti.ai/agentic/agents/send-message-to-agent /agentic/auto-generated-openapi.yml post /agents/{id}/v1/message:send This endpoint sends a message to the specified agent to start or continue a task. The agent processes the message and returns a response. If the message contains a task ID that matches an ongoing task, the agent will continue that task; otherwise, it will start a new task. # Update Agent by ID Source: https://docs.corti.ai/agentic/agents/update-agent-by-id /agentic/auto-generated-openapi.yml patch /agents/{id} This endpoint updates an existing agent. Only the fields provided in the request body will be updated; other fields will remain unchanged. # System architecture Source: https://docs.corti.ai/agentic/architecture Learn how the Agentic Framework's agent runtime receives requests, routes through connectors, and returns results. The Agentic Framework uses an agent runtime architecture to power AI solutions. Instead of a monolithic LLM, the runtime coordinates between connectors, contexts, and the reasoning layer to deliver specialized, safe, and auditable results. ## Architecture overview Diagram illustrating the Agentic Framework architecture, showing the agent runtime, connectors, and contexts and how they interact. The architecture consists of three core components: * **Agent runtime**: The central reasoning layer that receives user requests, plans steps, selects and invokes [connectors](/agentic/connectors), and generates responses * **Connectors**: Typed integrations that provide tools and data sources to the agent. Connectors replace v1's experts, MCP servers, and sub-agents with a unified model * **[Contexts](/agentic/context-memory)**: Maintain persistent conversation state, enabling the agent to make informed decisions and ensuring continuity across messages Together, this architecture enables complex workflows through protocol-based composition while maintaining strict data isolation and stateless reasoning. ## Request flow When you send a message to an agent, the following flow occurs: 1. **Message received**: The A2A server receives the message via the HTTP+JSON or JSON-RPC binding 2. **Context resolution**: The server resolves or creates a [context](/agentic/context-memory) to scope the conversation 3. **Reasoning and planning**: The agent runtime analyzes the request and determines which steps to take 4. **Connector selection**: The runtime decides which connectors to call, in what order, and with what data 5. **Connector invocation**: Selected connectors are invoked to retrieve information or perform actions 6. **Response generation**: The runtime aggregates results from connectors and generates the final response 7. **Response returned**: The client receives either a `Task` (for long-running work) or a `Message` (for quick responses) ## What the agent runtime does The agent runtime is the central intelligence layer. Its core responsibilities include: * **Reasoning and planning**: Analyzes user requests and determines the necessary steps to complete them * **Connector selection**: Decides which connectors to call, in what order, and with what data * **Task decomposition**: Breaks complex requests into discrete steps that can be handled by individual connectors * **Response generation**: Aggregates results from connectors and generates the final response * **Context management**: Has access to the [context](/agentic/context-memory), ensuring continuity across the conversation * **Safety enforcement**: Enforces guardrails, type validation, and policy-driven constraints to ensure safe operation in production environments The runtime does not perform specialized work itself. Instead, it delegates to appropriate connectors and coordinates their activities to accomplish complex workflows. ## Interaction mechanisms The A2A protocol supports two interaction patterns: * **Request/response**: You send a message and wait for the response. For long-running tasks, you can poll the task endpoint for status and results. Use `message:send` with `configuration.returnImmediately: false` (the default) for this pattern. * **Streaming with Server-Sent Events (SSE)**: You open an SSE stream to receive incremental updates in real time. Use `message:stream` for streaming a new message, or `tasks/{id}:subscribe` to stream updates for an existing task. The server writes event IDs but does not yet read the `Last-Event-ID` header, so resumption without gaps is not yet implemented. See [Stream responses](/agentic/guides/stream-responses) for details on SSE streaming. ## Observability The Agentic Framework provides two observability surfaces: * **[Traces](/agentic/guides/export-traces)**: OpenInference-format traces for each context, showing spans for LLM calls, connector invocations, and tool usage. Useful for debugging and performance analysis. * **[Usage](/agentic/guides/view-usage)**: Aggregated usage metrics per agent, including invocations, unique contexts, and token consumption. Useful for cost tracking and capacity planning. ## What changed from v1 In v1, the architecture was described as an "Orchestrator + Experts + Memory" triad. The orchestrator was a separate concept that delegated to specialized experts and MCP servers. In v2, the orchestrator is the agent runtime, and experts, MCP servers, and sub-agents are all unified under the [connectors](/agentic/connectors) model. The memory concept is formalized as [contexts](/agentic/context-memory) with their own API endpoints. | v1 concept | v2 equivalent | | ------------ | -------------------------------- | | Orchestrator | Agent runtime | | Experts | `registry` connectors | | MCP servers | `mcp` connectors | | Sub-agents | `agent` connectors | | Memory | Contexts (first-class resources) | The v1 Orchestrator page is preserved in the [v1 archived documentation](/agentic/v1/orchestrator). ## Next steps * Learn about [connectors](/agentic/connectors) and the five connector types * Read about [contexts and memory](/agentic/context-memory) * Understand the [A2A protocol](/agentic/a2a-protocol) and its two bindings # Connectors Source: https://docs.corti.ai/agentic/connectors Learn about the unified connector model in the Agentic Framework: the five connector types, the type discriminator, connector auth, and how connectors replace v1 experts. Connectors are the tools and data sources an agent uses to perform its work. In v2, the Agentic Framework unifies all external integrations under a single connector model. Whether you are attaching a pre-built registry connector, a remote MCP server, another agent, or a custom schema tool, you use the same `connectors` array on the agent. ## What is a connector A connector is a typed integration attached to an agent. When the agent reasons about a user's message, it selects and invokes connectors to retrieve information, call external tools, or delegate work to other agents. Every connector shares a common base: | Property | Type | Description | | --------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `id` | string | Server-generated connector identifier (prefixed UUIDv7, e.g. `con.0192f4c8-...`) | | `type` | string | The connector discriminator (see below) | | `enabled` | boolean | Whether the connector is active for invocations (default: `true`). Only `schema` connectors return this field today; `mcp`, `registry`, `agent`, and `a2a` connectors omit it (treat as enabled). | Additional properties depend on the connector `type`. ## The type discriminator Every connector has a `type` field that determines its shape and behavior. The v2 API ships five connector types: | Type | Description | | ---------- | ------------------------------------------------------------------- | | `registry` | A pre-built connector from the Corti registry (replaces v1 experts) | | `mcp` | A remote MCP server you bring yourself | | `agent` | Another Corti agent in the same tenant | | `a2a` | A remote A2A agent discovered by endpoint URL | | `schema` | A custom tool defined by a JSON Schema | The `type` field is immutable: you cannot change a connector's type after creation. To switch types, remove the connector and attach a new one. ## Connector types ### Registry connectors Registry connectors are pre-built integrations maintained by Corti and partners. They are the simplest to attach: you reference the connector by its namespaced `name`. ```json theme={null} { "type": "registry", "name": "coding-expert", "config": { "codingSystem": "icd-10" } } ``` | Property | Required | Description | | -------- | -------- | ----------------------------------------------------------------------- | | `type` | yes | Always `registry` | | `name` | yes | Registry connector name (e.g. `coding-expert`) | | `config` | no | Connector-specific configuration, validated against the registry schema | The `config` field is accepted on create but not yet persisted by the server — it is silently dropped. Configuration will be honored once the feature is fully implemented. Browse available registry connectors via the [registry API](/agentic/guides/use-registry) or the [registry connector catalog](/agentic/registry/overview). ### MCP connectors MCP connectors connect to remote Model Context Protocol servers you operate yourself. You provide the server URL and optional authentication. ```json theme={null} { "type": "mcp", "name": "policybot", "url": "https://mcp.example.com", "auth": { "type": "bearer" } } ``` | Property | Required | Description | | -------- | -------- | -------------------------------------------------------------------- | | `type` | yes | Always `mcp` | | `name` | yes | Display name for the MCP connector | | `url` | yes | MCP server endpoint URL | | `auth` | no | Authentication configuration (see [Connector auth](#connector-auth)) | The MCP server must implement the Model Context Protocol. See [MCP protocol](/agentic/v1/mcp-protocol) in the v1 archived docs for protocol details. ### Agent connectors Agent connectors delegate to another Corti agent in the same tenant. This enables multi-agent composition: one agent can call another as a tool. ```json theme={null} { "type": "agent", "agentId": "agt.0192f4c8-2c5a-7b3e-9f1a-3c8d6e2b7a40" } ``` | Property | Required | Description | | --------- | -------- | --------------------- | | `type` | yes | Always `agent` | | `agentId` | yes | The target agent's ID | ### A2A connectors A2A connectors connect to remote A2A agents outside your Corti tenant. You provide the agent's A2A endpoint URL (typically a `.well-known/agent-card.json` location). ```json theme={null} { "type": "a2a", "name": "external-research-agent", "url": "https://marginalia.polycode.co.uk/.well-known/agent-card.json" } ``` | Property | Required | Description | | -------- | -------- | ------------------------------------------ | | `type` | yes | Always `a2a` | | `url` | yes | Remote agent A2A endpoint URL | | `name` | no | Optional display name for the remote agent | ### Schema connectors Schema connectors define a custom tool by providing a JSON Schema. The LLM uses the schema's `name` and `description` to decide when to call the tool, and the schema validates the tool's output shape. ```json theme={null} { "type": "schema", "name": "submit_code", "description": "Submit the final ICD-10 code with a confidence score.", "transition": "complete", "schema": { "type": "object", "properties": { "code": { "type": "string" }, "confidence": { "type": "number", "minimum": 0, "maximum": 1 } }, "required": ["code"] } } ``` | Property | Required | Description | | ------------- | -------- | --------------------------------------------------------------------------------------------------- | | `type` | yes | Always `schema` | | `name` | yes | Tool name the LLM calls | | `schema` | yes | JSON Schema defining the tool's output shape | | `description` | no | What the tool does; read by the LLM to decide when to call it | | `transition` | no | If set to `complete` or `input_required`, calling this tool terminates the agent loop in that state | | `enabled` | no | Whether the connector is active (default: `true`) | The `transition` field lets you build structured-output agents. When set to `complete`, the agent stops reasoning after the tool is called, validates the output against the schema, and returns the result. No further LLM call is made. ## Connector auth Connectors that call external services can authenticate using the `auth` field. Only MCP connectors support `auth` configuration; A2A connectors automatically forward the caller's bearer token. The `ConnectorAuth` schema supports three types: | Type | Description | | -------- | ------------------------------------------------------------- | | `none` | No authentication (default if `auth` is omitted) | | `bearer` | Bearer token authentication | | `oauth2` | OAuth2 authentication with optional `scope` and `redirectUrl` | For `bearer` and `oauth2`, credentials are provided at call time via the `authorizationData` mechanism. ```json theme={null} { "type": "oauth2", "scope": "read:policies", "redirectUrl": "https://app.corti.ai/oauth/callback" } ``` See [Configure connector authentication](/agentic/guides/connector-auth) for a detailed guide. ## Managing connectors You can manage connectors in two ways: 1. **Via the agent's `connectors` array**: When you create or PATCH an agent, you provide the full `connectors` array. PATCH replaces the entire array wholesale. 2. **Via the connector sub-resource**: You can attach, update, remove, or list individual connectors without rewriting the full agent. The sub-resource endpoints use JSON Merge Patch for updates. | Operation | Method and path | | ------------------ | -------------------------------------------------------------- | | List connectors | `GET /v2/agentic/agents/{agentId}/connectors` | | Attach a connector | `POST /v2/agentic/agents/{agentId}/connectors` | | Get a connector | `GET /v2/agentic/agents/{agentId}/connectors/{connectorId}` | | Update a connector | `PATCH /v2/agentic/agents/{agentId}/connectors/{connectorId}` | | Remove a connector | `DELETE /v2/agentic/agents/{agentId}/connectors/{connectorId}` | The connector PATCH (update) endpoint is in private preview and returns HTTP 501. To change a connector, remove it and attach a new one. The other sub-resource operations (list, attach, get, remove) are fully functional. See [Manage connectors](/agentic/guides/manage-connectors) for a detailed guide. ## What replaced v1 experts In v1, the Agentic Framework used three separate concepts: Experts (pre-built specialized agents), MCP servers (remote tool servers), and sub-agents (other Corti agents). v2 unifies all three under the connector model: | v1 concept | v2 connector type | | ---------- | --------------------------------------------- | | Expert | `registry` connector | | MCP server | `mcp` connector | | Sub-agent | `agent` connector | | (new) | `a2a` connector (remote A2A agents) | | (new) | `schema` connector (custom JSON Schema tools) | If you are migrating from v1, see the [migration guide](/agentic/guides/migrate-v1-to-v2) for a detailed mapping. ## Relationship to the registry The registry is a catalog of pre-built connectors maintained by Corti and partners. Registry connectors are listed and discoverable via the [registry API](/agentic/guides/use-registry). Each registry entry includes: * A stable `id` (e.g. `coding-expert`) * A `type` indicating what connector kind it provisions * A `configSchema` describing accepted configuration * Capabilities, tags, and documentation links When you attach a `registry` connector to an agent, you reference it by its `name` (which matches the registry entry's `id`). The platform provisions the connector and validates any `config` you provide against the registry's `configSchema`. ## Next steps * Follow the [quickstart](/agentic/quickstart) to create an agent with connectors * Learn how to [manage connectors](/agentic/guides/manage-connectors) via the API * Browse the [registry connector catalog](/agentic/registry/overview) * Read about [connector authentication](/agentic/guides/connector-auth) # Context and memory Source: https://docs.corti.ai/agentic/context-memory Learn how contexts work in the Agentic Framework: first-class resources with their own endpoints, task grouping, data isolation, and trace export. A **context** in the Agentic Framework makes use of memory from previous text and data in the conversation so far. Think of it as a thread that maintains conversation history. Understanding how context works is essential for building effective integrations that maintain continuity across multiple messages. Diagram showing context and memory flow in the Agentic Framework ## What is a context A context (identified by a server-generated `contextId`) is a logical grouping of related messages, tasks, and artifacts, providing context across a multi-turn conversation. It enables you to associate multiple tasks with a single patient encounter, call, or workflow, ensuring continuity and proper scoping of shared knowledge. The `contextId` is **always created on the server**. You never generate it client-side. This ensures proper state management and prevents conflicts. In v2, contexts are first-class resources with their own API endpoints. You can inspect and delete contexts independently of agents. Contexts are created implicitly when you send a message; there is no create or update endpoint. Context listing (`GET /v2/agentic/contexts`) is in private preview — use `GET /v2/agentic/contexts/{contextId}` to retrieve a specific context. ## Context API endpoints | Operation | Method and path | | ----------------------- | ----------------------------------------------------- | | List contexts | `GET /v2/agentic/contexts` | | Get a context | `GET /v2/agentic/contexts/{contextId}` | | Delete a context | `DELETE /v2/agentic/contexts/{contextId}` | | Export traces | `GET /v2/agentic/contexts/{contextId}/trace` | | List tasks in a context | `GET /v2/agentic/contexts/{contextId}/tasks` | | Get a task in a context | `GET /v2/agentic/contexts/{contextId}/tasks/{taskId}` | ### Listing contexts The `GET /v2/agentic/contexts` listing endpoint is in private preview — it returns an empty list. Use `GET /v2/agentic/contexts/{contextId}` to retrieve a specific context. `GET /v2/agentic/contexts` returns a paginated list of contexts. You can filter by: * `agentId`: Restrict to contexts owned by a specific agent * `from`: Inclusive lower bound on `createdAt` (RFC 3339) * `to`: Exclusive upper bound on `createdAt` (RFC 3339) ### Getting a context `GET /v2/agentic/contexts/{contextId}` returns the context's metadata together with its tasks, oldest first. Each task carries its full message `history`; the user's prompt for a task is the `ROLE_USER` message within that task's history. Use the `historyLength` query parameter to cap the number of history messages returned per task. ### Deleting a context `DELETE /v2/agentic/contexts/{contextId}` deletes the context and its associated data. This is irreversible. See [Work with contexts](/agentic/guides/work-with-contexts) for a detailed guide. ## Data isolation and scoping Contexts provide strict data isolation. Data can **never** leak across contexts. Each `contextId` creates a completely isolated conversation scope. Messages, tasks, artifacts, and any data within one context are completely inaccessible to agents working in a different context. This ensures: * **Privacy and security**: Patient data from one encounter cannot accidentally be exposed to another encounter * **Data integrity**: Information from different workflows remains properly separated * **Compliance**: You can confidently scope sensitive data to specific contexts without risk of cross-contamination When you need to share information across contexts, you must explicitly pass it via data parts in your messages. There is no automatic data sharing between contexts. ## Using context for automatic memory management The simplest way to use context is to let the framework automatically manage conversation memory: 1. **First message**: Send your message without a `contextId`. The server creates a new context automatically. 2. **Response**: The server's response includes the newly created `contextId` in the task or message object. 3. **Subsequent messages**: Include that `contextId` in your requests. Memory from previous messages in that context is automatically managed and available to the agent. When you include a `contextId` in your request, the agent has access to all previous messages, artifacts, and state within **that specific context only**. Data from other contexts is completely isolated and inaccessible. ### Standalone requests If you don't want automatic memory management, always send messages without a `contextId`. Each message is treated as a standalone request without access to prior conversation history. This is useful for one-off queries, testing, and scenarios where you want explicit control over what context is included. ## Passing additional context with each request In addition to automatic memory management via `contextId`, you can pass additional context in each request by including data parts in your message. This is useful when you want to provide specific structured data, summaries, or other context that should be considered for that particular request. ```json theme={null} { "message": { "role": "ROLE_USER", "parts": [ { "text": "Generate a summary of this patient encounter" }, { "data": { "patientId": "pat_12345", "encounterDate": "2026-05-19", "chiefComplaint": "Chest pain", "vitalSigns": { "bloodPressure": "120/80", "heartRate": 72, "temperature": 98.6 } } } ] } } ``` This approach allows you to: * Provide structured data (patient records, clinical facts, etc.) alongside text * Include summaries or distilled information from external sources * Pass metadata or configuration that should be considered for this specific request * Combine automatic memory (via `contextId`) with explicit context (via data parts) ## How memory works The Agentic Framework uses an intelligent memory system that automatically indexes content within a context. Semantic retrieval requires a memory connector to be attached to the agent. Automatic semantic retrieval (just-in-time prompt injection) is in private preview. The framework automatically indexes all text and data parts, but retrieval requires a memory connector and an explicit `search_memory` tool call by the LLM — it is not injected automatically. ### Automatic indexing Every text part and data part you send in messages is automatically indexed and stored in the context's memory. This includes text content from user and agent messages, structured data from data part objects, artifacts generated by tasks, and any other content that flows through the context. ### Semantic retrieval The memory system operates like a RAG (Retrieval Augmented Generation) pipeline. When a memory connector is attached to the agent and the agent decides to call the `search_memory` tool: 1. **Semantic search**: The tool performs semantic search across all indexed content in the context's memory 2. **Relevant retrieval**: It retrieves the most semantically relevant information based on the current query or task 3. **Agent integration**: The retrieved content is returned to the LLM as tool output, making it available for reasoning The agent must choose to call the `search_memory` tool; the framework does not automatically inject retrieved context into prompts. ## Context vs. reference task IDs The framework provides two mechanisms for linking related work: * **`contextId`**: Groups multiple related messages, tasks, and artifacts together. This provides automatic memory management and is sufficient for most use cases. * **`referenceTaskIds`**: An optional list of specific past task IDs within the same context that should be treated as explicit inputs or background. These are scoped to a context; they reference tasks within the same `contextId`. In most situations, you can ignore `referenceTaskIds` since the automatic memory provided by `contextId` is sufficient. Use `referenceTaskIds` only when you need to explicitly direct the agent to pay attention to specific tasks or artifacts within the context. ## Context TTL and expiration The `expiresAt` field is in private preview — not yet persisted by the server. Contexts persist until explicitly deleted. Contexts can have an expiration time (`expiresAt`). When a context expires, it is automatically cleaned up. A `null` `expiresAt` means the context does not expire. Ephemeral agents (with `lifecycle: "ephemeral"`) may have shorter context TTLs. Persistent agents (with `lifecycle: "persistent"`) typically have longer-lived contexts. Check the `expiresAt` field on context responses to understand the lifetime of a specific context. ## Trace export You can export OpenInference-format traces for a context to inspect the agent's reasoning, connector calls, and tool usage: ``` GET /v2/agentic/contexts/{contextId}/trace ``` Traces contain spans for LLM calls, connector invocations, and tool usage, with attributes like `llm.token_count.total`, `tool.name`, and `input.value`. This is useful for debugging, performance analysis, and compliance auditing. See [Export traces](/agentic/guides/export-traces) for a detailed guide. ## Context and interaction IDs If you are using contexts alongside Corti's internal interaction representation (for example, when integrating with other Corti products that use `interactionId`), note that these two concepts are currently not linked: * `contextId` (from the Agentic Framework) and `interactionId` (from Corti's internal systems) are separate concepts that you need to map yourself in your application * There is no automatic association between a Corti `interactionId` and an Agentic Framework `contextId` **Recommended approach**: Use a fresh context per interaction. Store the mapping between your `interactionId` and `contextId`(s) in your own application state. If you need to share data across multiple contexts within the same interaction, explicitly pass it via data part objects. ## Next steps * Learn how to [work with contexts](/agentic/guides/work-with-contexts) via the API * Read about [exporting traces](/agentic/guides/export-traces) for observability * Understand [core concepts](/agentic/core-concepts) for the full vocabulary # Core concepts Source: https://docs.corti.ai/agentic/core-concepts Learn the fundamental building blocks of the Agentic Framework: agents, connectors, tasks, messages, parts, artifacts, and contexts. This page adds Corti-specific detail on top of the core A2A concepts. For the canonical definition of these concepts, see the A2A documentation on [Core Concepts and Components](https://a2a-protocol.org/latest/topics/key-concepts). The Agentic Framework uses a set of core concepts that define how agents, connectors, and external systems interact. Understanding these building blocks is essential for developing on the Corti platform and for integrating your own systems using the A2A protocol. ## Core actors At Corti, these actors map to concrete products and integrations: * **User**: A clinician, contact-center agent, knowledge worker, or an automated service in your environment. The user initiates a request that requires assistance from one or more Corti-powered agents. * **A2A Client (Client Agent)**: The application that calls Corti. This is your application or server. The client initiates communication using the A2A protocol and orchestrates how results are used in your product. * **A2A Server (Remote Agent)**: A Corti agent that exposes an HTTP endpoint implementing the A2A protocol. It receives requests from clients, processes tasks, and returns results or status updates. ## Prefixed UUIDs All resource identifiers in v2 use type-prefixed UUIDv7 format. The prefix indicates the resource type, making IDs self-documenting and easy to distinguish in logs and traces: | Prefix | Resource | Example | | ------- | ---------------- | ------------------------------------------- | | `agt.` | Agent | `agt.0192f4c8-2c5a-7b3e-9f1a-3c8d6e2b7a40` | | `con.` | Connector | `con.0192f4c8-7baf-7083-a46f-81d2bd70cf95` | | `ctx.` | Context | `ctx.0192f4c8-3d6b-7c4f-a02b-4d9e7f3c8b51` | | `task.` | Task | `task.0192f4c8-4e7c-7d50-b13c-5eaf8a4d9c62` | | `msg.` | Message | `msg.0192f4c8-5f8d-7e61-924d-6fb09b5ead73` | | `art.` | Artifact | `art.0192f4c8-6a9e-7f72-a35e-70c1ac6fbe84` | | `fb.` | Feedback | `fb.0192f4c8-7e2a-7b3c-9d4e-5f6a7b8c9d01` | | `usr.` | User / principal | `usr.0192f4c8-8bc0-7194-8570-92e3ce81d0a6` | On input, you can send either a prefixed or bare UUIDv7. The server always returns prefixed IDs in responses. ## Fundamental communication elements The following elements are fundamental to A2A communication and how Corti uses them: A JSON metadata document describing an agent's identity, capabilities, endpoint, skills, and supported protocol bindings. Served at the standard `.well-known/agent-card.json` location without authentication. **Key purpose**: Enables discovery and understanding of how to call an agent securely and effectively. See [Agent cards](/agentic/agent-cards). A stateful unit of work initiated by a message, with a unique ID and defined lifecycle. Tasks have states (submitted, working, completed, failed, canceled, input-required, auth-required, rejected), a history of messages, and can produce artifacts. **Key purpose**: Powers long-running operations in Corti (for example, document generation or multi-step workflows) and enables tracking and collaboration. See [Task lifecycle](/agentic/task-lifecycle). A single turn of communication between a client and an agent, containing content and a role (`ROLE_USER` or `ROLE_AGENT`). Messages have a `messageId` (prefixed UUIDv7), ordered `parts`, optional `referenceTaskIds`, and `metadata`. **Key purpose**: Carries instructions, clinical context, user questions, and agent responses between your application and agents. The fundamental content container used within messages and artifacts. A part has `text`, `file`, or `data` properties. Unlike v1, v2 parts do not use a `kind` discriminator. **Key purpose**: Lets Corti exchange text, structured JSON, and files in a consistent way across agents and tools. A named output generated by an agent during a task (for example, a document, coding result, or structured data). An artifact has an `artifactId`, an optional `name`, and one or more parts. **Key purpose**: Represents concrete results such as SOAP notes, call summaries, coding suggestions, or other structured outputs. A server-generated identifier (`contextId`) that logically groups related tasks, messages, and artifacts, providing context across a series of interactions. **Key purpose**: Enables you to associate multiple tasks with a single patient encounter, call, or workflow, ensuring continuity and proper scoping of shared knowledge. See [Context and memory](/agentic/context-memory). A typed integration attached to an agent that provides tools and data sources. Connectors replace v1's experts, MCP servers, and sub-agents with a unified model. **Key purpose**: Lets agents retrieve information, call external tools, and delegate work. See [Connectors](/agentic/connectors). ## Agent metadata Agents in v2 carry first-class metadata that controls visibility, lifecycle, model selection, and organization: | Field | Type | Default | Description | | -------------- | ------ | -------------- | --------------------------------------------------------------------------------------------------------------------------------- | | `description` | string | none | Human-readable description of the agent | | `systemPrompt` | string | none | System prompt that guides the agent's reasoning and behavior | | `visibility` | enum | `private` | Who can use the agent: `private` (creator only), `unlisted` (usable by ID, hidden from lists), `public` (listed tenant-wide) | | `lifecycle` | enum | `ephemeral` | How long the agent persists: `ephemeral` (short-lived, expired automatically) or `persistent` (retained until explicitly deleted) | | `model` | string | tenant default | Model identifier for the agent | | `labels` | object | none | Free-form `string` to `string` metadata for filtering and organization (max 64 keys) | The `visibility` and `labels` fields are in private preview — accepted by the API but not yet persisted. All agents are returned as `visibility: "private"` and `labels` are never returned. The `model` field is supported in the v2 API. Model precedence over connector-level models is being finalized. See the [migration guide](/agentic/guides/migrate-v1-to-v2) if you are migrating from v1 where model was configured per-expert. ## Messages and parts A message represents a single turn of communication between a client and an agent. It includes: * `messageId`: A prefixed UUIDv7 (e.g. `msg.0192f4c8-...`) * `role`: Either `ROLE_USER` (sent by the client) or `ROLE_AGENT` (sent by the agent) * `parts`: An ordered list of content parts * `referenceTaskIds`: Optional list of task IDs this message references * `extensions`: Optional URIs of A2A extensions that contributed to this message * `metadata`: Free-form metadata, including Corti's `$timestamp` (RFC 3339) for timing ### Part types A part is the fundamental content container. In v2, parts use property-based discrimination (no `kind` field): * **Text part**: Contains plain text in the `text` property * **File part**: Contains a file in the `file` property with `name`, `mimeType`, `uri`, or inline `bytes` (base64) * **Data part**: Contains structured JSON in the `data` property ```json theme={null} { "text": "Generate a summary of this patient encounter" } ``` ```json theme={null} { "data": { "patientId": "pat_12345", "encounterDate": "2026-05-19", "chiefComplaint": "Chest pain" } } ``` ## Artifacts An artifact represents a tangible output or concrete result generated by an agent during task processing. Unlike general messages, artifacts are the actual deliverables. An artifact has: * `artifactId`: A prefixed UUIDv7 (e.g. `art.0192f4c8-...`) * `name`: An optional human-readable name * `parts`: Content parts containing the artifact data In Corti, artifacts typically correspond to business outputs such as clinical notes, extracted facts, coding suggestions, or generated documents. ## Agent response: Task or message The agent response can be a new `Task` (when the agent needs to perform a long-running operation) or a `Message` (when the agent can respond immediately): * For quick operations (for example, a short completion or a classification), the agent responds with a `Message` * For longer workflows (for example, generating a full clinical document, coordinating multiple connectors, or waiting on downstream systems), the agent responds with a `Task` that you can monitor and retrieve artifacts from See [Task lifecycle](/agentic/task-lifecycle) for details on task states and streaming. ## Next steps * Read about [connectors](/agentic/connectors), the unified integration model * Learn about [agent cards](/agentic/agent-cards) for A2A discovery * Follow the [quickstart](/agentic/quickstart) to create your first agent # FAQ Source: https://docs.corti.ai/agentic/faq Frequently asked questions about the Agentic Framework. Common questions and answers to help you get the most out of the Agentic Framework and the underlying A2A-based APIs. The **agent runtime** is the central reasoning layer of the Agentic Framework. It receives user requests, reasons about what needs to be done, and delegates work to connectors. The runtime does not perform specialized work itself. Instead, it plans, selects appropriate connectors, and coordinates their activities to accomplish complex workflows. A **connector** is a typed integration that provides tools and data sources to the agent. Connectors replace v1's experts, MCP servers, and sub-agents with a unified model. The runtime composes complex workflows by chaining multiple connectors together. In summary: the agent runtime coordinates and delegates; connectors execute specialized work. For more details, see [Architecture](/agentic/architecture) and [Connectors](/agentic/connectors). **A2A (Agent-to-Agent)** is the protocol your application uses to communicate with Corti agents. It handles agent-to-agent communication: sending messages, receiving tasks, streaming responses, and managing the agent lifecycle. A2A is the conversational surface. **MCP (Model Context Protocol)** is one of five connector types in v2. When you attach an MCP connector to an agent, the agent can call the MCP server's tools at runtime. MCP handles agent-to-tool interactions. In the Agentic Framework: A2A is how clients talk to agents; MCP (as a connector type) is how agents talk to external tool servers. The two protocols complement each other and serve different layers. For more information, see [A2A protocol](/agentic/a2a-protocol) and [Connectors](/agentic/connectors). The agent response can be a new `Task` (for long-running work) or a `Message` (for quick responses). A Task represents a stateful unit of work with a unique ID and defined lifecycle. Tasks are used for: * Long-running operations (for example, generating a full clinical document) * Multi-step workflows that coordinate multiple connectors * Operations that may need to wait on downstream systems * Any work that benefits from tracking and monitoring Messages (with immediate responses) are used for very quick operations like simple classifications or completions that can be resolved immediately without asynchronous processing. For more details, see [Task lifecycle](/agentic/task-lifecycle) and [Core concepts](/agentic/core-concepts). Use **text parts** for messages that will be directly available to the agent runtime and the LLM. Text part content is immediately available for reasoning and response generation. Use **data parts** for structured JSON data that will be stored in memory and accessed through semantic retrieval. Data part content is automatically indexed and stored in the context's memory, enabling retrieval when needed. Data parts are useful for structured data like patient records, clinical facts, workflow parameters, or EHR identifiers. You can combine both in a single message: use a text part for the primary instruction or question, and a data part to provide structured context. For more details, see [Core concepts](/agentic/core-concepts) and [Context and memory](/agentic/context-memory). Both `Message` and `Artifact` use the same underlying part primitives, but they serve different roles: * **Message** (with `role: "ROLE_AGENT"`): Represents a single turn of communication from the agent. Best for conversational output, intermediate reasoning, clarifications, or status updates. * **Artifact**: Represents a tangible, durable output of a task (for example, a SOAP note, coding suggestions, or a generated document). Has its own `artifactId`, name, and parts. A useful mental model: **Messages are how agents talk; Artifacts are what they produce.** For more details, see [Task lifecycle](/agentic/task-lifecycle). The Agentic Framework provides automatic memory management through contexts. The `contextId` is always created on the server. Send your first message without a `contextId`, and the server returns one in the response. Include that `contextId` in subsequent messages to maintain conversation history automatically. You can also pass additional context in each request using data parts to include structured data alongside the automatic memory. For comprehensive guidance, see [Context and memory](/agentic/context-memory). No. Contexts provide strict data isolation. Data can never leak across contexts. Each `contextId` creates a completely isolated conversation scope where messages, tasks, artifacts, and any data within one context are completely inaccessible to agents working in a different context. If you need to share information across contexts, you must explicitly pass it via data parts in your messages. For more details, see [Context and memory](/agentic/context-memory). The agent runtime analyzes incoming requests and uses reasoning to determine which connectors are needed. It considers the nature of the request, the available connectors, and their capabilities. You can influence connector selection by writing system prompts in the agent configuration. System prompts guide how the runtime reasons about task decomposition and connector selection. The runtime can compose multiple connectors, calling them in sequence or parallel as needed. For more information, see [Architecture](/agentic/architecture) and [Connectors](/agentic/connectors). Agents have three visibility levels: * **`private`** (default): Only the creator can use the agent. * **`unlisted`**: Usable by anyone who knows the agent ID, but hidden from list results. * **`public`**: Listed tenant-wide, visible to all users in the tenant. For more details, see [Create an agent](/agentic/guides/create-agent). * **`ephemeral`** (default): Short-lived agents that expire automatically. Good for one-off tasks and testing. * **`persistent`**: Retained until explicitly deleted. Good for production agents. For more details, see [Create an agent](/agentic/guides/create-agent). v2 uses JSON Merge Patch (RFC 7386) for agent and connector updates. Omitted fields are unchanged (not removed), `null` clears a field, and the `connectors` array is replaced wholesale. For example, to rename an agent and clear its system prompt: ```json theme={null} { "name": "coder-v2", "systemPrompt": null } ``` This leaves all other fields unchanged. See [Create an agent](/agentic/guides/create-agent) for details. All resource identifiers in v2 use type-prefixed UUIDv7 format. The prefix indicates the resource type, making IDs self-documenting in logs and traces: * `agt.` for agents * `con.` for connectors * `ctx.` for contexts * `task.` for tasks * `msg.` for messages * `art.` for artifacts For more details, see [Core concepts](/agentic/core-concepts). Key changes in v2 include: * Experts, MCP servers, and sub-agents are unified under the connector model * API paths move from `/agents` to `/v2/agentic/agents` * A2A protocol is upgraded to v1.0 (v0.3 not carried forward to v2, still available on v1) * Agents gain `visibility`, `lifecycle`, `model`, and `labels` metadata * Contexts, connectors, registry, usage, feedback, traces, and agent cards all have dedicated API endpoints * All IDs use prefixed UUIDv7 format * PATCH uses JSON Merge Patch semantics See the [v1-to-v2 migration guide](/agentic/guides/migrate-v1-to-v2) for a full mapping. # Configure connector authentication Source: https://docs.corti.ai/agentic/guides/connector-auth Learn how to configure authentication for outbound connectors using the ConnectorAuth schema. This guide shows you how to configure authentication for connectors that call external services (MCP connectors only). You will learn about the three auth types and how to configure them at connector creation time. ## Prerequisites * An existing agent (see [Create an agent](/agentic/guides/create-agent)) * Understanding of [connectors](/agentic/connectors) and [connector management](/agentic/guides/manage-connectors) ## Auth types The `ConnectorAuth` schema supports three authentication mechanisms: | Type | Description | | -------- | ------------------------------------------------------------- | | `none` | No authentication (default if `auth` is omitted) | | `bearer` | Bearer token authentication | | `oauth2` | OAuth2 authentication with optional `scope` and `redirectUrl` | ## Configuring auth on connector create ### Bearer auth ```bash theme={null} # Replace these with your values AGENT_ID="" ENVIRONMENT="" TENANT="" TOKEN="" curl -X POST "https://api.${ENVIRONMENT}.corti.app/v2/agentic/agents/${AGENT_ID}/connectors" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Tenant-Name: ${TENANT}" \ -H "Content-Type: application/json" \ -d '{ "type": "mcp", "name": "policybot", "url": "https://mcp.example.com", "auth": {"type": "bearer"} }' ``` ### OAuth2 auth ```bash theme={null} curl -X POST "https://api.${ENVIRONMENT}.corti.app/v2/agentic/agents/${AGENT_ID}/connectors" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Tenant-Name: ${TENANT}" \ -H "Content-Type: application/json" \ -d '{ "type": "mcp", "name": "ehr-connector", "url": "https://mcp.example.com", "auth": { "type": "oauth2", "scope": "read:policies", "redirectUrl": "https://app.corti.ai/oauth/callback" } }' ``` ## v1 migration In v1, MCP connector authentication used an `authorizationType` field. In v2, this is replaced by `ConnectorAuth.type`: | v1 `authorizationType` | v2 `ConnectorAuth.type` | | ---------------------- | ----------------------------------------------- | | `none` | `none` (or omit `auth`) | | `bearer` | `bearer` | | `inherit` | `none` (caller's token forwarded automatically) | | `oauth2.0` | `oauth2` | See the [migration guide](/agentic/guides/migrate-v1-to-v2) for the full v1-to-v2 mapping. ## Next steps * Learn how to [manage connectors](/agentic/guides/manage-connectors) via the API * Read about [connectors](/agentic/connectors) for the full type reference * Browse [registry connectors](/agentic/guides/use-registry) for pre-built options # Create and configure an agent Source: https://docs.corti.ai/agentic/guides/create-agent Learn how to create, list, get, patch, and delete agents using the v2 Agentic API. This guide shows you how to create and configure an agent with connectors, metadata, and labels. You will learn the full agent CRUD lifecycle using the v2 Agentic API. ## Prerequisites * An access token or client credentials * Your tenant name * Basic understanding of [connectors](/agentic/connectors) and [core concepts](/agentic/core-concepts) ## Create a minimal agent The simplest agent has just a name. All other fields are optional: ```bash theme={null} # Replace these with your values ENVIRONMENT="" TENANT="" TOKEN="" curl -X POST "https://api.${ENVIRONMENT}.corti.app/v2/agentic/agents" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Tenant-Name: ${TENANT}" \ -H "Content-Type: application/json" \ -d '{ "name": "my-agent" }' ``` The response includes the agent's `id` (a prefixed UUIDv7), `visibility` (default: `private`), `lifecycle` (default: `ephemeral`), and an empty `connectors` array. ## Create an agent with connectors To make an agent useful, attach connectors at creation time: ```bash theme={null} curl -X POST "https://api.${ENVIRONMENT}.corti.app/v2/agentic/agents" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Tenant-Name: ${TENANT}" \ -H "Content-Type: application/json" \ -d '{ "name": "coder", "description": "Returns ICD-10 codes for a clinical encounter.", "systemPrompt": "Respond with only the ICD-10 code.", "model": "corti-default", "visibility": "private", "lifecycle": "persistent", "connectors": [ {"type": "registry", "name": "coding-expert"}, {"type": "mcp", "name": "policybot", "url": "https://mcp.example.com"}, { "type": "schema", "name": "submit_code", "description": "Submit the final ICD-10 code with a confidence score.", "transition": "complete", "schema": { "type": "object", "properties": { "code": {"type": "string"}, "confidence": {"type": "number", "minimum": 0, "maximum": 1} }, "required": ["code"] } } ], "labels": {"team": "coding", "env": "prod"} }' ``` The `visibility` and `labels` fields are in private preview — accepted by the API but not yet persisted. All agents are returned as `visibility: "private"` and `labels` are silently dropped. The `model` field is supported in the v2 API. In the current implementation, a model is configured per connector rather than per agent; the precedence of an agent-level `model` over connector-level models is being finalized. ## List agents Filter agents by visibility, lifecycle, labels, or free-text search: The `visibility`, `lifecycle`, `label`, and `q` listing filters are in private preview — accepted by the API but silently ignored. Only `pageSize` and `pageToken` are functional. ```bash theme={null} # All public agents curl "https://api.${ENVIRONMENT}.corti.app/v2/agentic/agents?visibility=public" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Tenant-Name: ${TENANT}" # Persistent agents with label team=coding curl "https://api.${ENVIRONMENT}.corti.app/v2/agentic/agents?lifecycle=persistent&label=team%3Dcoding" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Tenant-Name: ${TENANT}" # Free-text search curl "https://api.${ENVIRONMENT}.corti.app/v2/agentic/agents?q=coder" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Tenant-Name: ${TENANT}" ``` ## Get a single agent ```bash theme={null} AGENT_ID="" curl "https://api.${ENVIRONMENT}.corti.app/v2/agentic/agents/${AGENT_ID}" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Tenant-Name: ${TENANT}" ``` ## Patch an agent Use JSON Merge Patch to update an agent. Omitted fields are unchanged; `null` clears; `connectors` is replaced wholesale: ```bash theme={null} curl -X PATCH "https://api.${ENVIRONMENT}.corti.app/v2/agentic/agents/${AGENT_ID}" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Tenant-Name: ${TENANT}" \ -H "Content-Type: application/merge-patch+json" \ -d '{ "name": "coder-v2", "systemPrompt": "Respond with only the SNOMED CT code.", "connectors": [ {"type": "registry", "name": "coding-expert"} ] }' ``` The `connectors` array in a PATCH replaces the entire list. To modify individual connectors without replacing the whole array, use the [connector sub-resource endpoints](/agentic/guides/manage-connectors). The server currently honors only `name`, `description`, `systemPrompt`, `model`, and `connectors` in PATCH requests. The `visibility`, `lifecycle`, and `labels` fields are accepted but not yet persisted. ## Delete an agent ```bash theme={null} curl -X DELETE "https://api.${ENVIRONMENT}.corti.app/v2/agentic/agents/${AGENT_ID}" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Tenant-Name: ${TENANT}" ``` Deleting a persistent agent is irreversible. Ephemeral agents expire automatically. ## Common patterns ### Ephemeral vs. persistent * **Ephemeral** (`lifecycle: "ephemeral"`): Short-lived agents that expire automatically. Good for one-off tasks and testing. * **Persistent** (`lifecycle: "persistent"`): Retained until explicitly deleted. Good for production agents. ### Private vs. public * **Private** (`visibility: "private"`): Only the creator can use the agent. * **Unlisted** (`visibility: "unlisted"`): Usable by anyone who knows the ID, but hidden from list results. * **Public** (`visibility: "public"`): Listed tenant-wide. ## Next steps * Learn how to [send a message](/agentic/guides/send-message) to your agent * Learn how to [manage connectors](/agentic/guides/manage-connectors) individually * Read about [connector auth](/agentic/guides/connector-auth) for outbound connectors # Export OpenInference traces Source: https://docs.corti.ai/agentic/guides/export-traces Learn how to export OpenInference traces for a context for observability and debugging. This guide shows you how to export OpenInference-format traces for a context. Traces provide detailed observability into the agent's reasoning, connector calls, and tool usage. ## Prerequisites * An existing context with at least one task (see [Send a message](/agentic/guides/send-message)) * An access token or client credentials ## What are OpenInference traces OpenInference is an open standard for tracing LLM applications. Each trace contains spans that represent individual operations: LLM calls, connector invocations, tool usage, and more. Each span has attributes like `llm.token_count.total`, `tool.name`, and `input.value`. ## Export traces ```bash theme={null} # Replace these with your values CONTEXT_ID="" ENVIRONMENT="" TENANT="" TOKEN="" curl "https://api.${ENVIRONMENT}.corti.app/v2/agentic/contexts/${CONTEXT_ID}/trace" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Tenant-Name: ${TENANT}" ``` ### Pagination The trace endpoint supports pagination with `pageSize` (default: 50, max: 200) and `pageToken` query parameters. The response includes `nextPageToken` (opaque cursor for the next page, or `null` if there are no more pages) and `totalSize` (not currently populated by the server): ```bash theme={null} curl "https://api.${ENVIRONMENT}.corti.app/v2/agentic/contexts/${CONTEXT_ID}/trace?pageSize=100&pageToken=${PAGE_TOKEN}" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Tenant-Name: ${TENANT}" ``` ## Trace structure The response contains a `traces` array, ordered newest first. Each trace has: | Field | Description | | ------------------ | --------------------------------------------- | | `trace.id` | Trace identifier (OTel trace ID, 32-char hex) | | `trace.name` | Human-readable trace name | | `trace.start_time` | When the trace started | | `trace.thread_id` | Context ID associated with the trace | | `spans` | Array of OpenInference spans in this trace | Each span has: | Field | Description | | ---------------- | ----------------------------------------------------------- | | `name` | Human-readable span name | | `span_id` | Unique span identifier | | `parent_span_id` | Parent span ID (omitted for root span) | | `start_time` | When the span started | | `attributes` | Span attributes (e.g. `llm.token_count.total`, `tool.name`) | ## Using traces for debugging Traces help you understand: * **Which connectors were called**: Look for spans with `tool.name` attributes * **How many tokens were consumed**: Look for `llm.token_count.total` attributes * **What inputs were sent to connectors**: Look for `input.value` attributes * **How long each step took**: Compare `start_time` across spans * **The reasoning chain**: Follow `parent_span_id` to reconstruct the call tree ## Next steps * Learn how to [work with contexts](/agentic/guides/work-with-contexts) via the API * Read about [viewing usage](/agentic/guides/view-usage) for aggregated metrics * Understand the [architecture](/agentic/architecture) for how traces fit into the system # Manage connectors Source: https://docs.corti.ai/agentic/guides/manage-connectors Learn how to list, attach, update, and remove connectors using the connector sub-resource endpoints. This guide shows you how to manage connectors attached to an agent using the connector sub-resource endpoints. You will learn about the full connector CRUD lifecycle and JSON Merge Patch semantics. ## Prerequisites * An existing agent (see [Create an agent](/agentic/guides/create-agent)) * Understanding of [connectors](/agentic/connectors) and the five connector types ## Two ways to manage connectors You can manage connectors in two ways: 1. **Via the agent's `connectors` array**: When you PATCH an agent, the `connectors` array is replaced wholesale. This is good for bulk changes. 2. **Via the connector sub-resource**: Attach, update, remove, or list individual connectors without rewriting the full agent. This is good for incremental changes. This guide focuses on the sub-resource approach. ## List connectors ```bash theme={null} # Replace these with your values AGENT_ID="" ENVIRONMENT="" TENANT="" TOKEN="" curl "https://api.${ENVIRONMENT}.corti.app/v2/agentic/agents/${AGENT_ID}/connectors" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Tenant-Name: ${TENANT}" ``` The response is a JSON object with a `connectors` array, each discriminated by `type`: ```json theme={null} { "connectors": [ {"type": "registry", "id": "con.0192f4c8-...", "name": "coding-expert"}, {"type": "mcp", "id": "con.0192f4c8-...", "name": "policybot", "url": "https://mcp.example.com"} ] } ``` ## Attach a connector ```bash theme={null} curl -X POST "https://api.${ENVIRONMENT}.corti.app/v2/agentic/agents/${AGENT_ID}/connectors" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Tenant-Name: ${TENANT}" \ -H "Content-Type: application/json" \ -d '{ "type": "registry", "name": "coding-expert", "config": {"codingSystem": "icd-10"} }' ``` Each connector type has different required fields. See [Connectors](/agentic/connectors) for the full schema of each type. ### Attach an MCP connector ```bash theme={null} curl -X POST "https://api.${ENVIRONMENT}.corti.app/v2/agentic/agents/${AGENT_ID}/connectors" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Tenant-Name: ${TENANT}" \ -H "Content-Type: application/json" \ -d '{ "type": "mcp", "name": "policybot", "url": "https://mcp.example.com", "auth": {"type": "bearer"} }' ``` ### Attach a schema connector ```bash theme={null} curl -X POST "https://api.${ENVIRONMENT}.corti.app/v2/agentic/agents/${AGENT_ID}/connectors" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Tenant-Name: ${TENANT}" \ -H "Content-Type: application/json" \ -d '{ "type": "schema", "name": "submit_code", "description": "Submit the final ICD-10 code.", "transition": "complete", "schema": { "type": "object", "properties": {"code": {"type": "string"}}, "required": ["code"] } }' ``` ## Get a connector ```bash theme={null} CONNECTOR_ID="" curl "https://api.${ENVIRONMENT}.corti.app/v2/agentic/agents/${AGENT_ID}/connectors/${CONNECTOR_ID}" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Tenant-Name: ${TENANT}" ``` ## Update a connector The connector PATCH endpoint is in private preview and returns HTTP 501. To change a connector, remove it and attach a new one. Use JSON Merge Patch to update a connector. The `type` field is immutable: ```bash theme={null} curl -X PATCH "https://api.${ENVIRONMENT}.corti.app/v2/agentic/agents/${AGENT_ID}/connectors/${CONNECTOR_ID}" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Tenant-Name: ${TENANT}" \ -H "Content-Type: application/merge-patch+json" \ -d '{ "enabled": false, "name": "policybot-v2", "url": "https://mcp.example.com/v2" }' ``` | Field | Patch behavior | | --------- | ----------------------------------------------------- | | `enabled` | Omit to keep; set to toggle | | `name` | Omit to keep; `null` has no effect (name is required) | | `url` | Omit to keep; `null` clears it | | `config` | Omit to keep; `null` clears it | | `auth` | Omit to keep; `null` clears it | ## Remove a connector ```bash theme={null} curl -X DELETE "https://api.${ENVIRONMENT}.corti.app/v2/agentic/agents/${AGENT_ID}/connectors/${CONNECTOR_ID}" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Tenant-Name: ${TENANT}" ``` ## Next steps * Read about [connector auth](/agentic/guides/connector-auth) for outbound connectors * Browse [registry connectors](/agentic/guides/use-registry) for pre-built options * Learn about the [connectors concept](/agentic/connectors) for the full type reference # Migrate from v1 to v2 Source: https://docs.corti.ai/agentic/guides/migrate-v1-to-v2 Learn the path changes, experts-to-connectors mapping, PATCH semantics, and A2A protocol changes for migrating from v1 to v2. This guide helps you migrate from the v1 Agentic Framework API to the v2 API. It covers path changes, the experts-to-connectors mapping, PATCH semantics, A2A protocol changes, and new features. The v1 API reference remains available in the API Reference tab as "Agentic APIs v1 (deprecated)". The v1 documentation is preserved in the [v1 archived docs](/agentic/v1/overview). ## API path changes All v2 endpoints move under `/v2/agentic/`: | v1 path | v2 path | | ------------------------------------ | --------------------------------------------------------- | | `POST /agents` | `POST /v2/agentic/agents` | | `GET /agents` | `GET /v2/agentic/agents` | | `GET /agents/{id}` | `GET /v2/agentic/agents/{id}` | | `PATCH /agents/{id}` | `PATCH /v2/agentic/agents/{id}` | | `DELETE /agents/{id}` | `DELETE /v2/agentic/agents/{id}` | | `GET /agents/{id}/agent-card.json` | `GET /v2/agentic/agents/{id}/.well-known/agent-card.json` | | `POST /agents/{id}/v1/message:send` | `POST /v2/agentic/agents/{id}/a2a/message:send` | | `GET /agents/{id}/v1/tasks/{taskId}` | `GET /v2/agentic/agents/{id}/a2a/tasks/{taskId}` | | `GET /agents/registry/experts` | `GET /v2/agentic/registry/connectors` | ## Experts to connectors In v1, the Agentic Framework used three separate concepts: Experts, MCP servers, and sub-agents. v2 unifies all three under the connector model. | v1 concept | v2 connector type | Example | | ---------- | ----------------- | ------------------------------------------------------------ | | Expert | `registry` | `{"type": "registry", "name": "coding-expert"}` | | MCP server | `mcp` | `{"type": "mcp", "name": "policybot", "url": "https://..."}` | | Sub-agent | `agent` | `{"type": "agent", "agentId": "agt.0192f4c8-..."}` | ### v1 agent creation (with experts) ```json theme={null} { "name": "coder", "experts": [{"name": "coding-expert"}] } ``` ### v2 agent creation (with connectors) ```json theme={null} { "name": "coder", "connectors": [ {"type": "registry", "name": "coding-expert"} ] } ``` ## PATCH semantics v1 already used partial-update PATCH semantics: omitted fields were unchanged, and arrays (`experts`, `mcpServers`) were replaced wholesale when provided. v2 adopts **JSON Merge Patch** (RFC 7386), which adds the ability to explicitly clear fields with `null`: * **Omitted fields**: Unchanged (same as v1) * **`null`**: Clears the field (new in v2 — in v1, `null` decoded to a nil Go pointer and was treated as "unchanged") * **`connectors` array**: Replaced wholesale (same as v1's `experts` array) ```json theme={null} { "name": "coder-v2", "systemPrompt": null, "connectors": [ {"type": "registry", "name": "coding-expert"} ] } ``` This PATCH renames the agent, clears the system prompt, and replaces the entire connectors array with a single registry connector. All other fields remain unchanged. The `Content-Type` header for PATCH requests is `application/merge-patch+json` in v2. ## A2A v0.3 to v1.0 v2 supports A2A v1.0 only. v0.3 is not carried forward to v2 (still available on the v1 API surface). Key changes: | v1 (A2A v0.3) | v2 (A2A v1.0) | | ------------------------------------- | ------------------------------------------------------------------------ | | `role: "user"` | `role: "ROLE_USER"` | | `kind: "message"` on message envelope | Removed (no `kind` field) | | `kind: "text"` on parts | Removed (use `text` property directly) | | `kind: "data"` on parts | Removed (use `data` property directly) | | No `A2A-Version` header | `A2A-Version: 1.0` header on A2A endpoints (optional, defaults to `1.0`) | | Single binding (HTTP+JSON) | Two bindings: JSON-RPC and HTTP+JSON | ### v1 message format ```json theme={null} { "message": { "role": "user", "parts": [{"kind": "text", "text": "Hello"}], "messageId": "msg-001", "kind": "message" } } ``` ### v2 message format ```json theme={null} { "message": { "role": "ROLE_USER", "parts": [{"text": "Hello"}], "messageId": "msg.0192f4c8-5f8d-7e61-924d-6fb09b5ead73" } } ``` ## Removed features | v1 feature | v2 replacement | | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | | `?ephemeral=true` query param on agent create | `lifecycle: "ephemeral"` in request body | | `experts` array on agent create/patch | `connectors` array with `type: "registry"` | | `authorizationType` on MCP connectors | `auth.type` in `ConnectorAuth` object | | v1 agent card path (`/agent-card.json`) | `.well-known/agent-card.json` | | `agentType` field (expert, orchestrator, interviewing-expert) | Removed — all agents use the same model; specialization comes from connectors and system prompts | | `transportType` on MCP connectors (stdio/streamable\_http/sse) | Removed — MCP connectors use streamable HTTP | | Inline expert creation (`type: "new"` with system prompt and MCP servers) | Removed — use `registry` connectors referencing pre-built experts | | `pushNotificationConfig` on message send | Removed (future scope) | | `kind: "task"` and `kind: "file"` on parts | Removed (use `text`, `file`, or `data` properties directly) | | `description` required on agent create | `description` is now optional (only `name` is required) | | Artifact `description`, `metadata`, `extensions` fields | Removed (artifacts now have `artifactId`, `name`, `parts` only) | | Task states `submitted`, `working` | Renamed to `TASK_STATE_SUBMITTED`, `TASK_STATE_WORKING` (PascalCase enum) | ## New features in v2 v2 introduces several new capabilities: * **Agent metadata**: `visibility` (private, unlisted, public), `lifecycle` (ephemeral, persistent), `model`, `labels` * **Connector sub-resources**: Individual connector CRUD via `/agents/{id}/connectors` endpoints * **Contexts API**: List, get, and delete contexts via `/v2/agentic/contexts` endpoints * **Registry API**: Browse and inspect pre-built connectors via `/v2/agentic/registry/connectors` * **Usage API**: Agent usage metrics via `/agents/{id}/usage` * **Feedback API**: Submit, list, and delete feedback via `/contexts/{id}/tasks/{id}/feedback` * **Trace export**: OpenInference traces via `/contexts/{id}/trace` * **Agent cards**: Standard `.well-known/agent-card.json` location (requires authentication) * **Streaming**: `message:stream` and `tasks:subscribe` SSE endpoints * **Prefixed UUIDv7 IDs**: All resource IDs use type-prefixed format (e.g. `agt.`, `ctx.`, `task.`) * **JSON-RPC binding**: Alternative to HTTP+JSON for A2A communication ## Next steps * Read the [v2 overview](/agentic/overview) for the full v2 feature set * Follow the [quickstart](/agentic/quickstart) to create a v2 agent * Browse the [v1 archived documentation](/agentic/v1/overview) for reference * Use the [v1 API reference](/api-reference) (labeled "Agentic APIs v1 (deprecated)") for v1 endpoint details # Send a message to an agent Source: https://docs.corti.ai/agentic/guides/send-message Learn how to send a message using the A2A HTTP+JSON binding, handle task and message responses, and manage context. This guide shows you how to send a message to an agent and handle the response. You will learn about blocking vs. non-blocking requests, context management, and error handling. ## Prerequisites * An existing agent (see [Create an agent](/agentic/guides/create-agent)) * An access token or client credentials * Your tenant name ## Send a message Use the HTTP+JSON binding to send a message: ```bash theme={null} # Replace these with your values AGENT_ID="" ENVIRONMENT="" TENANT="" TOKEN="" curl -X POST "https://api.${ENVIRONMENT}.corti.app/v2/agentic/agents/${AGENT_ID}/a2a/message:send" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Tenant-Name: ${TENANT}" \ -H "Content-Type: application/json" \ -H "A2A-Version: 1.0" \ -d '{ "message": { "role": "ROLE_USER", "parts": [{ "text": "What is the ICD-10 code for asthma?" }] } }' ``` ## Message structure A message contains: | Field | Required | Description | | ------------------ | -------- | ---------------------------------------------------------- | | `role` | yes | `ROLE_USER` or `ROLE_AGENT` | | `parts` | yes | Ordered content parts (text, file, or data) | | `messageId` | no | Message identifier (UUID); server generates one if omitted | | `contextId` | no | Context ID for conversation continuity | | `referenceTaskIds` | no | Task IDs this message references | | `extensions` | no | URIs of A2A extensions | | `metadata` | no | Free-form metadata | The request body also accepts an optional `configuration` object: | Field | Default | Description | | --------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------- | | `returnImmediately` | `false` | If `true`, return as soon as the task is submitted. If `false` (default), wait until the task reaches a terminal or interrupted state. | | `historyLength` | unset | Cap the number of history messages returned per task | | `acceptedOutputModes` | unset | Output media types the client accepts | ## Blocking vs. non-blocking By default, `message:send` is blocking: the server waits for the task to complete before returning. Set `configuration.returnImmediately` to `true` to return as soon as the task is submitted: ```bash theme={null} curl -X POST "https://api.${ENVIRONMENT}.corti.app/v2/agentic/agents/${AGENT_ID}/a2a/message:send" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Tenant-Name: ${TENANT}" \ -H "Content-Type: application/json" \ -H "A2A-Version: 1.0" \ -d '{ "message": { "role": "ROLE_USER", "parts": [{ "text": "Generate a full clinical note for this encounter." }] }, "configuration": { "returnImmediately": true } }' ``` With `returnImmediately: true`, the response returns immediately with the task in `TASK_STATE_SUBMITTED` or `TASK_STATE_WORKING` state. Poll the task endpoint or [subscribe via SSE](/agentic/guides/stream-responses) to get updates. ## Handling the response The response contains either a `task` or a `message` object: * **`task`**: The agent is performing long-running work. Check `task.status.state` for the current state. When `TASK_STATE_COMPLETED`, the agent's response is in `task.history` (the last `ROLE_AGENT` message). Artifacts are in `task.artifacts`. * **`message`**: The agent responded directly with a message. The text is in `message.parts[0].text`. ```json theme={null} { "task": { "id": "task.0192f4c8-...", "contextId": "ctx.0192f4c8-...", "status": { "state": "TASK_STATE_COMPLETED", "timestamp": "2026-05-19T12:00:01Z" }, "history": [ {"role": "ROLE_USER", "messageId": "msg.0192f4c8-...", "parts": [{"text": "What is the ICD-10 code for asthma?"}]}, {"role": "ROLE_AGENT", "messageId": "msg.0192f4c8-...", "parts": [{"text": "J45.909"}]} ] } } ``` ## Context management The server automatically creates a context if you don't provide a `contextId`. The response includes the `contextId` in the task object. To continue a conversation, include that `contextId` in subsequent messages: ```bash theme={null} CONTEXT_ID="ctx.0192f4c8-..." curl -X POST "https://api.${ENVIRONMENT}.corti.app/v2/agentic/agents/${AGENT_ID}/a2a/message:send" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Tenant-Name: ${TENANT}" \ -H "Content-Type: application/json" \ -H "A2A-Version: 1.0" \ -d '{ "message": { "role": "ROLE_USER", "parts": [{ "text": "What about the code for acute exacerbation?" }], "contextId": "'"$CONTEXT_ID"'" } }' ``` See [Context and memory](/agentic/context-memory) for details on how context works. ## Using data parts for structured context Pass structured data alongside text using data parts: ```bash theme={null} curl -X POST "https://api.${ENVIRONMENT}.corti.app/v2/agentic/agents/${AGENT_ID}/a2a/message:send" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Tenant-Name: ${TENANT}" \ -H "Content-Type: application/json" \ -H "A2A-Version: 1.0" \ -d '{ "message": { "role": "ROLE_USER", "parts": [ { "text": "Code this encounter:" }, { "data": { "chiefComplaint": "Acute asthma exacerbation", "patientAge": 45, "vitalSigns": {"heartRate": 110, "respiratoryRate": 28} } } ] } }' ``` ## Error handling Errors follow the A2A `google.rpc.Status` error format: ```json theme={null} { "error": { "code": 400, "status": "INVALID_ARGUMENT", "message": "Message must include at least one part.", "details": [ { "@type": "type.googleapis.com/google.rpc.ErrorInfo", "reason": "INVALID_REQUEST", "domain": "a2a-protocol.org", "metadata": { "timestamp": "2026-05-19T12:00:00Z" } } ] } } ``` Common error codes: | HTTP status | `status` | Description | | ----------- | ------------------ | --------------------------------------- | | 400 | `INVALID_ARGUMENT` | Invalid request (malformed message) | | 401 | `UNAUTHENTICATED` | Unauthorized (missing or invalid token) | | 404 | `NOT_FOUND` | Agent not found | ## Next steps * Learn how to [stream responses](/agentic/guides/stream-responses) via SSE * Understand the [task lifecycle](/agentic/task-lifecycle) for long-running work * Read about [contexts](/agentic/guides/work-with-contexts) for conversation management # Stream agent responses Source: https://docs.corti.ai/agentic/guides/stream-responses Learn how to stream agent responses via SSE using message:stream and tasks:subscribe, including resumption with Last-Event-ID. This guide shows you how to stream agent responses in real time using Server-Sent Events (SSE). You will learn about the two streaming endpoints, the SSE event format, and how to resume interrupted streams. ## Prerequisites * An existing agent (see [Create an agent](/agentic/guides/create-agent)) * An access token or client credentials * Understanding of the [task lifecycle](/agentic/task-lifecycle) and [A2A protocol](/agentic/a2a-protocol) ## When to stream Use streaming when you need real-time updates as the agent processes a message: * Showing incremental progress to users * Displaying partial results as they arrive * Monitoring long-running tasks For simple request/response interactions, use [message:send](/agentic/guides/send-message) instead. ## Stream a new message Use `message:stream` to send a message and receive the response as an SSE stream: ```bash theme={null} # Replace these with your values AGENT_ID="" ENVIRONMENT="" TENANT="" TOKEN="" curl -N -X POST "https://api.${ENVIRONMENT}.corti.app/v2/agentic/agents/${AGENT_ID}/a2a/message:stream" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Tenant-Name: ${TENANT}" \ -H "Content-Type: application/json" \ -H "A2A-Version: 1.0" \ -d '{ "message": { "role": "ROLE_USER", "parts": [{ "text": "Generate a clinical note for this encounter." }] } }' ``` The `-N` flag disables cURL's output buffering, which is necessary for receiving SSE events in real time. ## SSE event format Each event follows the W3C SSE wire format. The server currently sends only `id` and `data` lines. The `event` and `retry` fields are declared for forward compatibility but are not sent: ``` data: {"task":{"id":"task.0192f4c8-...","contextId":"ctx.0192f4c8-...","status":{"state":"TASK_STATE_WORKING","timestamp":"2026-05-19T12:00:00Z"}}} data: {"artifactUpdate":{"taskId":"task.0192f4c8-...","contextId":"ctx.0192f4c8-...","artifact":{"artifactId":"art.0192f4c8-...","name":"clinical-note","parts":[{"text":"SOAP Note..."}]},"lastChunk":true}} data: {"statusUpdate":{"taskId":"task.0192f4c8-...","contextId":"ctx.0192f4c8-...","status":{"state":"TASK_STATE_COMPLETED","timestamp":"2026-05-19T12:00:01Z"}}} ``` ### Event types Each SSE event carries an `A2AStreamResponse` with exactly one field: | Field | Description | | ---------------- | --------------------------------------------------------------------------------------- | | `task` | The full task object, sent when the task is created or state changes | | `message` | A direct message response (no task lifecycle) | | `statusUpdate` | Incremental status change (no `final` flag; clients infer finality from the task state) | | `artifactUpdate` | Incremental artifact update with `lastChunk` flag | ## Subscribe to an existing task If you have a task ID (from a non-blocking `message:send` or after a stream dropped), use `tasks:subscribe` to stream updates: ```bash theme={null} TASK_ID="" curl -N -X POST "https://api.${ENVIRONMENT}.corti.app/v2/agentic/agents/${AGENT_ID}/a2a/tasks/${TASK_ID}:subscribe" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Tenant-Name: ${TENANT}" \ -H "A2A-Version: 1.0" ``` ## Resumption with Last-Event-ID The server writes event IDs but does not yet read the `Last-Event-ID` request header, so resumption without gaps is not implemented. The section below describes the intended design for when this feature ships. If an SSE stream drops, the intended resumption mechanism is to send the `Last-Event-ID` header with the most recent event ID you received: ```bash theme={null} LAST_EVENT_ID="event-123" curl -N -X POST "https://api.${ENVIRONMENT}.corti.app/v2/agentic/agents/${AGENT_ID}/a2a/message:stream" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Tenant-Name: ${TENANT}" \ -H "Content-Type: application/json" \ -H "A2A-Version: 1.0" \ -H "Last-Event-ID: ${LAST_EVENT_ID}" \ -d '{ "message": { "role": "ROLE_USER", "parts": [{ "text": "Continue generating the note." }] } }' ``` The server will replay events from that point forward once the feature is implemented. This will work with both `message:stream` and `tasks:subscribe`. ## Cancel a task Cancel an in-flight task: ```bash theme={null} curl -X POST "https://api.${ENVIRONMENT}.corti.app/v2/agentic/agents/${AGENT_ID}/a2a/tasks/${TASK_ID}:cancel" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Tenant-Name: ${TENANT}" \ -H "A2A-Version: 1.0" \ -H "Content-Type: application/json" \ -d '{}' ``` The task transitions to `TASK_STATE_CANCELED`. ## JSON-RPC streaming binding You can also stream via the JSON-RPC binding by sending a `message/stream` method to the base A2A endpoint: ```bash theme={null} curl -N -X POST "https://api.${ENVIRONMENT}.corti.app/v2/agentic/agents/${AGENT_ID}/a2a" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Tenant-Name: ${TENANT}" \ -H "Content-Type: application/json" \ -H "A2A-Version: 1.0" \ -d '{ "jsonrpc": "2.0", "id": "msg-001", "method": "SendStreamingMessage", "params": { "message": { "role": "ROLE_USER", "parts": [{ "text": "Generate a clinical note." }] } } }' ``` The SSE events carry JSON-RPC response envelopes instead of raw `A2AStreamResponse` objects. ## Next steps * Read about the [task lifecycle](/agentic/task-lifecycle) for state transitions * Learn how to [send a message](/agentic/guides/send-message) without streaming * Understand the [A2A protocol](/agentic/a2a-protocol) bindings # Submit feedback on tasks Source: https://docs.corti.ai/agentic/guides/submit-feedback Learn how to submit, list, and delete feedback for tasks and messages using the feedback API. This guide shows you how to submit, list, and delete feedback for tasks and messages. Feedback is useful for building thumbs-up/down UIs, case review workflows, and automated evaluation pipelines. ## Prerequisites * An existing task (see [Send a message](/agentic/guides/send-message)) * An access token or client credentials * A context ID and task ID ## Submit feedback ```bash theme={null} # Replace these with your values CONTEXT_ID="" TASK_ID="" ENVIRONMENT="" TENANT="" TOKEN="" curl -X POST "https://api.${ENVIRONMENT}.corti.app/v2/agentic/contexts/${CONTEXT_ID}/tasks/${TASK_ID}/feedback" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Tenant-Name: ${TENANT}" \ -H "Content-Type: application/json" \ -d '{ "rating": { "scale": "binary", "value": 1 }, "labels": ["correct", "helpful"], "reason": "The response accurately identified the ICD-10 code." }' ``` ## Rating scales | Scale | Values | Description | | -------------- | ---------------------------- | --------------------------- | | `binary` | 0 (negative) or 1 (positive) | Available now | | `likert5` | 1-5 | Planned (not yet available) | | `continuous01` | 0.0-1.0 | Planned (not yet available) | ## Labels Labels provide structured observations about the result. Positive and negative labels can be combined to represent mixed feedback: **Positive labels**: `correct`, `complete`, `helpful`, `wellPresented`, `efficient` **Negative labels**: `incorrect`, `missingInformation`, `irrelevant`, `misunderstoodRequest`, `unsupportedClaim`, `unsafeOrInappropriate`, `poorlyPresented`, `tooVerbose` **Neutral**: `other` (requires a `reason`) A maximum of five labels may be submitted per feedback entry. Duplicate labels are rejected. The `other` label requires a `reason` field. ## Targeting a specific message By default, feedback applies to the task as a whole. To target a specific message in the task's history, include the `target` field with the message ID: ```bash theme={null} curl -X POST "https://api.${ENVIRONMENT}.corti.app/v2/agentic/contexts/${CONTEXT_ID}/tasks/${TASK_ID}/feedback" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Tenant-Name: ${TENANT}" \ -H "Content-Type: application/json" \ -d '{ "rating": {"scale": "binary", "value": 0}, "labels": ["incorrect"], "reason": "The ICD-10 code was wrong for this specific response.", "target": {"messageId": "msg.0192f4c8-5f8d-7e61-924d-6fb09b5ead73"} }' ``` ## Metadata Include provenance and correlation information using the `metadata` field: ```json theme={null} { "metadata": { "collectionMethod": "thumbs", "clientReference": "session-abc-123", "actor": {"externalId": "user-pseudonymous-id"} } } ``` | Field | Description | | ------------------ | -------------------------------------------------------- | | `collectionMethod` | How the feedback was collected (e.g. `thumbs`, `survey`) | | `clientReference` | Customer-defined reference for correlation | | `actor.externalId` | Pseudonymous identifier for the submitter | The `actor.externalId` must not contain names, emails, national identifiers, or medical record numbers. Use a pseudonymous identifier. ## List feedback ```bash theme={null} curl "https://api.${ENVIRONMENT}.corti.app/v2/agentic/contexts/${CONTEXT_ID}/tasks/${TASK_ID}/feedback" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Tenant-Name: ${TENANT}" ``` The response includes all feedback entries for the task, scoped to the authenticated user via row-level security, newest first. The response is wrapped in a `feedbacks` array: ```json theme={null} { "feedbacks": [ { "id": "fb.0192f4c8-...", "taskId": "task.0192f4c8-...", "rating": {"scale": "binary", "value": 1}, "normalizedScore": 1.0, "labels": ["correct", "helpful"], "reason": "The response accurately identified the ICD-10 code.", "createdAt": "2026-05-19T12:00:00Z" } ] } ``` Feedback is scoped to the authenticated user. The response contains only the caller's feedback, not all feedback for the task. ## Delete feedback Delete all feedback you submitted for a task. The DELETE endpoint targets the task as a whole — there is no per-entry feedback ID in the path: ```bash theme={null} curl -X DELETE "https://api.${ENVIRONMENT}.corti.app/v2/agentic/contexts/${CONTEXT_ID}/tasks/${TASK_ID}/feedback" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Tenant-Name: ${TENANT}" ``` DELETE removes **all** feedback you submitted for the task, not a single entry. The operation is idempotent: deleting when there is no feedback returns `204`. ## Use cases * **Thumbs UI**: Collect binary feedback after each task response * **Case review**: Use labels like `correct`, `incorrect`, `missingInformation` for structured review * **Automated evaluation**: Submit feedback programmatically with metadata for tracking ## Next steps * Learn how to [work with contexts](/agentic/guides/work-with-contexts) to find task IDs * Read about the [task lifecycle](/agentic/task-lifecycle) for task states * Learn how to [export traces](/agentic/guides/export-traces) for debugging # Browse the connector registry Source: https://docs.corti.ai/agentic/guides/use-registry Learn how to list and inspect pre-built registry connectors and get their configuration schemas. This guide shows you how to browse the registry of pre-built connectors maintained by Corti and partners. You will learn how to search for connectors, inspect their details, and use them in your agents. ## Prerequisites * An access token or client credentials * Understanding of [connectors](/agentic/connectors) ## List registry connectors ```bash theme={null} # Replace these with your values ENVIRONMENT="" TENANT="" TOKEN="" curl "https://api.${ENVIRONMENT}.corti.app/v2/agentic/registry/connectors" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Tenant-Name: ${TENANT}" ``` ### Searching The `q` free-text search parameter is in private preview — accepted by the API but silently ignored. Use the `q` parameter for free-text search over connector names and descriptions: ```bash theme={null} QUERY="coding" curl "https://api.${ENVIRONMENT}.corti.app/v2/agentic/registry/connectors?q=${QUERY}" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Tenant-Name: ${TENANT}" ``` ### Pagination The registry list endpoint supports `pageSize` and `pageToken` query parameters. The response includes `nextPageToken` (opaque cursor for the next page) and `totalSize` (not currently populated by the server — treat as absent). ```bash theme={null} curl "https://api.${ENVIRONMENT}.corti.app/v2/agentic/registry/connectors?pageSize=20" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Tenant-Name: ${TENANT}" ``` ## Get a registry connector ```bash theme={null} CONNECTOR_ID="coding-expert" curl "https://api.${ENVIRONMENT}.corti.app/v2/agentic/registry/connectors/${CONNECTOR_ID}" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Tenant-Name: ${TENANT}" ``` The response includes: The `version`, `provider`, `capabilities`, `tags`, and `documentationUrl` fields are in private preview — declared in the schema but not yet populated. Only `id`, `type`, `name`, `title`, `description`, and `configSchema` are returned. | Field | Description | | ------------------ | ---------------------------------------------------------------- | | `id` | Stable, namespaced identifier (use as the `name` when attaching) | | `type` | Connector kind this entry provisions | | `name` | Programmatic name | | `title` | Human-readable display name | | `description` | Description (may contain CommonMark) | | `version` | Latest published version | | `provider` | Publishing organization | | `capabilities` | Connector capabilities | | `tags` | Keywords for search and filtering | | `configSchema` | JSON Schema describing accepted `config` | | `documentationUrl` | Documentation URL | | `icons` | Icon URLs for the connector (e.g. logo) | | `websiteUrl` | Website URL for the connector provider | ## Using a registry connector When you find a registry connector you want to use, attach it to an agent by referencing its `id` as the `name` field: ```bash theme={null} AGENT_ID="" curl -X POST "https://api.${ENVIRONMENT}.corti.app/v2/agentic/agents/${AGENT_ID}/connectors" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Tenant-Name: ${TENANT}" \ -H "Content-Type: application/json" \ -d '{ "type": "registry", "name": "coding-expert", "config": {"codingSystem": "icd-10"} }' ``` The `config` object is validated against the registry connector's `configSchema`. If your config doesn't match the schema, the request will be rejected. ## Available registry connectors For a catalog of available registry connectors with detailed documentation, see the [Registry Connectors](/agentic/registry/overview) section. ## Next steps * Learn how to [manage connectors](/agentic/guides/manage-connectors) on your agents * Read about [connector auth](/agentic/guides/connector-auth) for outbound connectors * Browse the [registry connector catalog](/agentic/registry/overview) # View agent usage Source: https://docs.corti.ai/agentic/guides/view-usage Learn how to retrieve usage metrics for an agent, including granularity, date ranges, and bucket structure. This guide shows you how to retrieve usage metrics for an agent using the v2 Agentic API. You will learn about granularity, date ranges, and how to interpret the response. ## Prerequisites * An existing agent with at least one invocation * An access token or client credentials ## Get usage ```bash theme={null} # Replace these with your values AGENT_ID="" ENVIRONMENT="" TENANT="" TOKEN="" curl "https://api.${ENVIRONMENT}.corti.app/v2/agentic/agents/${AGENT_ID}/usage" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Tenant-Name: ${TENANT}" ``` ## Query parameters | Parameter | Type | Description | | ------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `from` | date-time | Inclusive start of the range (UTC). Defaults to 30 days ago. | | `to` | date-time | Exclusive end of the range (UTC). Defaults to now. | | `granularity` | enum | Bucket size: `minute`, `hour`, `day` (default), or `week`. Only `day` is currently honored; other values are accepted but produce daily buckets (the server always returns `day`). | ```bash theme={null} curl "https://api.${ENVIRONMENT}.corti.app/v2/agentic/agents/${AGENT_ID}/usage?from=2026-05-19T00:00:00Z&to=2026-05-21T00:00:00Z&granularity=day" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Tenant-Name: ${TENANT}" ``` ## Response structure The response contains range-wide `totals` and an array of `buckets`: ```json theme={null} { "granularity": "day", "from": "2026-05-19T00:00:00Z", "to": "2026-05-21T00:00:00Z", "totals": { "invocations": 15, "uniqueContexts": 6 }, "buckets": [ { "periodStart": "2026-05-19T00:00:00Z", "periodEnd": "2026-05-20T00:00:00Z", "invocations": 12, "uniqueContexts": 5 }, { "periodStart": "2026-05-20T00:00:00Z", "periodEnd": "2026-05-21T00:00:00Z", "invocations": 3, "uniqueContexts": 2 } ] } ``` ## Interpreting metrics | Metric | Description | | ---------------- | ------------------------------------------------- | | `invocations` | Number of agent invocations in the period | | `uniqueContexts` | Number of distinct contexts invoked in the period | For per-task token and credit accounting, see the `$usage` field in the [task metadata](/agentic/task-lifecycle#task-metadata-and-usage). ## Next steps * Learn how to [export traces](/agentic/guides/export-traces) for detailed observability * Read about the [task lifecycle](/agentic/task-lifecycle) for per-task usage data * Learn how to [submit feedback](/agentic/guides/submit-feedback) on task results # Work with contexts Source: https://docs.corti.ai/agentic/guides/work-with-contexts Learn how to list, inspect, and delete contexts, and how to retrieve tasks within a context. This guide shows you how to manage contexts using the v2 Agentic API. Contexts are first-class resources with their own endpoints for listing, inspection, and deletion. ## Prerequisites * An existing agent with at least one sent message (see [Send a message](/agentic/guides/send-message)) * Understanding of [context and memory](/agentic/context-memory) ## List contexts The `GET /v2/agentic/contexts` listing endpoint is in private preview — it returns an empty list and ignores all filters. Use `GET /v2/agentic/contexts/{contextId}` to retrieve a specific context. ```bash theme={null} # Replace these with your values ENVIRONMENT="" TENANT="" TOKEN="" curl "https://api.${ENVIRONMENT}.corti.app/v2/agentic/contexts" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Tenant-Name: ${TENANT}" ``` ### Filtering | Parameter | Description | | --------- | ----------------------------------------------- | | `agentId` | Restrict to contexts owned by a specific agent | | `from` | Inclusive lower bound on `createdAt` (RFC 3339) | | `to` | Exclusive upper bound on `createdAt` (RFC 3339) | ```bash theme={null} AGENT_ID="" curl "https://api.${ENVIRONMENT}.corti.app/v2/agentic/contexts?agentId=${AGENT_ID}&from=2026-05-19T00:00:00Z" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Tenant-Name: ${TENANT}" ``` The response is paginated with `contexts`, `nextPageToken`, and `totalSize` fields. ## Get a context ```bash theme={null} CONTEXT_ID="" curl "https://api.${ENVIRONMENT}.corti.app/v2/agentic/contexts/${CONTEXT_ID}" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Tenant-Name: ${TENANT}" ``` Use the `historyLength` query parameter to cap the number of history messages returned per task: ```bash theme={null} curl "https://api.${ENVIRONMENT}.corti.app/v2/agentic/contexts/${CONTEXT_ID}?historyLength=10" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Tenant-Name: ${TENANT}" ``` The response includes the context's metadata and its `tasks` array (oldest first). Each task carries its full message `history`. ```json theme={null} { "id": "ctx.0192f4c8-...", "agentId": "agt.0192f4c8-...", "taskCount": 1, "createdAt": "2026-05-19T12:00:00Z", "updatedAt": "2026-05-19T12:00:01Z", "expiresAt": null, "tasks": [ { "id": "task.0192f4c8-...", "contextId": "ctx.0192f4c8-...", "status": {"state": "TASK_STATE_COMPLETED", "timestamp": "2026-05-19T12:00:01Z"}, "history": [ {"role": "ROLE_USER", "messageId": "msg-...", "parts": [{"text": "Code this encounter."}]}, {"role": "ROLE_AGENT", "messageId": "msg-...", "parts": [{"text": "J45.901"}]} ] } ] } ``` ## Delete a context ```bash theme={null} curl -X DELETE "https://api.${ENVIRONMENT}.corti.app/v2/agentic/contexts/${CONTEXT_ID}" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Tenant-Name: ${TENANT}" ``` Deleting a context is irreversible. All associated tasks, messages, and artifacts are permanently removed. ## List tasks in a context ```bash theme={null} curl "https://api.${ENVIRONMENT}.corti.app/v2/agentic/contexts/${CONTEXT_ID}/tasks" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Tenant-Name: ${TENANT}" ``` ## Get a specific task in a context ```bash theme={null} TASK_ID="" curl "https://api.${ENVIRONMENT}.corti.app/v2/agentic/contexts/${CONTEXT_ID}/tasks/${TASK_ID}" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Tenant-Name: ${TENANT}" ``` ## Get an artifact Retrieve a specific artifact produced by a task: ```bash theme={null} ARTIFACT_ID="" curl "https://api.${ENVIRONMENT}.corti.app/v2/agentic/contexts/${CONTEXT_ID}/tasks/${TASK_ID}/artifacts/${ARTIFACT_ID}" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Tenant-Name: ${TENANT}" ``` ## Next steps * Learn how to [export traces](/agentic/guides/export-traces) for observability * Read about [context and memory](/agentic/context-memory) concepts * Learn how to [submit feedback](/agentic/guides/submit-feedback) on task results # Overview of the Corti Agentic Framework Source: https://docs.corti.ai/agentic/overview Learn what the Agentic Framework is, what problems it solves, and what you can build with it. The Corti Agentic Framework is a modular AI system for building advanced agents that perform complex tasks without months of complex architecture work. An LLM agent is not a chatbot answering everything from internal knowledge. The language model is used for reasoning and planning: understanding a request, breaking it down, and deciding which tools and data sources are best suited to handle each part. The Agentic Framework supports use cases across the industry spectrum, from chat-based assistants to automating data entry and powering decision support workflows. ## What problems it solves Modern LLMs are powerful, but on their own they are insufficient and unsafe for production use. The Agentic Framework addresses two fundamental gaps: ### LLMs do not have reliable access to domain data LLMs cannot be trusted to rely on internal knowledge alone. In production, responses must be grounded in validated reference sources, real-time system data, and customer-owned systems and APIs. Without access to these sources at runtime, models are forced to infer or guess, which is unacceptable in specialised settings. The Agentic Framework addresses this by enabling agents to retrieve information directly from trusted external tools through [connectors](/agentic/connectors). Instead of hallucinating answers, agents look things up, verify context, and base their outputs on authoritative data. ### LLMs cannot safely act on the world Production workflows require more than generating text. They involve interacting with real systems: querying databases, drafting and updating documentation, preparing structured outputs, and triggering downstream processes. The framework provides a controlled execution layer that allows agents to plan actions, invoke tools, and coordinate multi-step workflows while remaining within clearly defined safety boundaries. Where necessary, agents can pause execution, request human approval, and resume only once explicit consent is given. ## What you can build with it Using the Agentic Framework, teams can build: * **User-facing assistants**: documentation editing, guideline and reference lookup, coding and administrative support * **Programmatic agent endpoints**: embedded into existing software, triggered by events, APIs, or workflows * **Customer-embedded agents**: customers bring their own tools and systems; agents combine Corti, third-party, and customer capabilities All of these share the same underlying agent runtime, safety model, and connector layer. ## Built for industry by design Industry applications are not general-purpose, and the framework reflects that reality. Typed inputs and outputs, explicit tool schemas, and guardrails around action-taking ensure safe operation in production environments. Every decision and tool call is observable with replayable traces and structured logs for transparency, compliance, and quality assurance. Fine-tuned reasoning layers optimized for industry language, workflows, and compliance needs. A unified connector model enables agents to call registry tools, MCP servers, other agents, remote A2A agents, and custom schema tools. Maintain persistent, context-aware conversations and manage multiple active contexts without losing information throughout the session. Access a library of pre-built connectors maintained by Corti and partners: specialized tools that connect to data sources and services to execute complex tasks. Plug directly into EHRs, decision support systems, and knowledge bases with minimal setup. Pass relevant context with each query, including structured data formats, enabling connectors to work with rich, domain-specific information. ## v2 at a glance The v2 API introduces several key changes: * **Unified connectors**: Experts, MCP servers, and sub-agents are all replaced by a single [connectors](/agentic/connectors) model with five types: `registry`, `mcp`, `agent`, `a2a`, and `schema` * **Clean CRUD verbs**: Consistent REST patterns for agents, connectors, contexts, and feedback * **A2A v1.0 only**: The [A2A protocol](/agentic/a2a-protocol) is upgraded to v1.0; v0.3 is deprecated but still available on the v1 API surface * **First-class metadata**: Agents have `visibility` (private, unlisted, public), `lifecycle` (ephemeral, persistent), `model`, and `labels` * **New endpoints**: Contexts, connectors, registry, usage, artifacts, feedback, traces, and agent cards all have dedicated API surfaces * **Prefixed UUIDv7 IDs**: All resource IDs use type-prefixed UUIDv7 format (e.g. `agt.`, `ctx.`, `task.`, `msg.`, `con.`) Migrating from v1? See the [v1-to-v2 migration guide](/agentic/guides/migrate-v1-to-v2) for a detailed mapping of changes. ## Who it's for The Agentic Framework is built for teams working on industry software: * **Software companies** embedding intelligent automation directly into their products * **Enterprise customers** building internal, AI-powered workflows * **Advanced engineering teams** that need flexibility, control, and strong safety guarantees without building bespoke agent infrastructure from scratch The platform is designed to make it easy to go from demo to production-grade AI systems that operate safely in real-world environments. ## Next steps Create your first agent and send a message in minutes. Learn the fundamental building blocks of the Agentic Framework. # Quickstart Source: https://docs.corti.ai/agentic/quickstart Create your first agent, attach a connector, and send a message in minutes. This guide walks you through creating your first agent with a registry connector, sending a message, and receiving a response. By the end, you will have a working end-to-end agent. After completing this quickstart, you will have a working agent and know where to go next based on your use case. ## Prerequisites * API access credentials (client ID and client secret, or an access token) * Development environment set up * Basic understanding of REST APIs If you haven't set up authentication, follow the [Creating Clients](/authentication/creating_clients) and [authentication quickstart](/authentication/quickstart) guides first. Start by creating a project in the [Corti Console](https://console.corti.app). This gives you a workspace and access to manage your clients and credentials. Use the Agentic API to create your first agent. You need an access token (obtained using your client credentials) and your tenant name. The example below creates a persistent agent with a registry connector (`memory-expert`) attached. ```ts title="JavaScript" theme={null} import { CortiClient } from "@corti/sdk"; // Replace these with your values const CLIENT_ID = ""; const CLIENT_SECRET = ""; const ENVIRONMENT = ""; const TENANT = ""; const client = new CortiClient({ tenantName: TENANT, environment: ENVIRONMENT, auth: { clientId: CLIENT_ID, clientSecret: CLIENT_SECRET, }, }); const myAgent = await client.agents.create({ name: "My First Agent", description: "A simple agent to get started with the Corti Agentic Framework", lifecycle: "persistent", connectors: [ { type: "registry", name: "memory-expert" }, ], }); ``` ```csharp title="C# .NET" theme={null} using Corti; // Replace these with your values const string CLIENT_ID = ""; const string CLIENT_SECRET = ""; const string ENVIRONMENT = ""; const string TENANT = ""; var client = new CortiClient( tenantName: TENANT, environment: ENVIRONMENT, auth: CortiClientAuth.ClientCredentials(clientId: CLIENT_ID, clientSecret: CLIENT_SECRET) ); var myAgent = await client.Agents.CreateAsync( new AgentsCreateRequest { Name = "My First Agent", Description = "A simple agent to get started with the Corti Agentic Framework", Lifecycle = AgentsLifecycle.Persistent, Connectors = new[] { new CommonRegistryConnectorCreate { Type = "registry", Name = "memory-expert", }, }, } ); ``` ```python title="Python" expandable theme={null} import requests # Replace these with your values ENVIRONMENT = "" TENANT = "" TOKEN = "" response = requests.post( f"https://api.{ENVIRONMENT}.corti.app/v2/agentic/agents", headers={ "Authorization": f"Bearer {TOKEN}", "Tenant-Name": TENANT, "Content-Type": "application/json", }, json={ "name": "My First Agent", "description": "A simple agent to get started with the Corti Agentic Framework", "lifecycle": "persistent", "connectors": [ {"type": "registry", "name": "memory-expert"}, ], }, ) response.raise_for_status() agent = response.json() ``` ```bash title="cURL" theme={null} # Replace these with your values ENVIRONMENT="" TENANT="" TOKEN="" curl -X POST "https://api.${ENVIRONMENT}.corti.app/v2/agentic/agents" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Tenant-Name: ${TENANT}" \ -H "Content-Type: application/json" \ -d '{ "name": "My First Agent", "description": "A simple agent to get started with the Corti Agentic Framework", "lifecycle": "persistent", "connectors": [ {"type": "registry", "name": "memory-expert"} ] }' ``` The response includes the agent's `id` (a prefixed UUIDv7 like `agt.0192f4c8-...`). Save this ID for the next step. Send a message to your agent using the A2A HTTP+JSON binding. The response is either a `Task` (for longer work) or a `Message` (for quick responses). ```ts title="JavaScript" theme={null} const agentResponse = await client.agents.messageSend(myAgent.id, { message: { role: "ROLE_USER", parts: [{ text: "Hello there. This is my first message.", }], messageId: crypto.randomUUID(), }, }); if (agentResponse.task) { console.log(agentResponse.task.status.state); console.log(agentResponse.task.history?.at(-1)?.parts?.[0]?.text); } else if (agentResponse.message) { console.log(agentResponse.message.parts[0].text); } ``` ```csharp title="C# .NET" theme={null} var agentResponse = await client.Agents.MessageSendAsync( myAgent.Id, new A2ASendMessageRequest { Message = new CommonMessage { Role = CommonRole.RoleUser, Parts = new[] { new CommonPart { Text = "Hello there. This is my first message.", } }, MessageId = Guid.NewGuid().ToString(), }, } ); if (agentResponse.Task != null) { Console.WriteLine(agentResponse.Task.Status.State); } else if (agentResponse.Message != null) { Console.WriteLine(agentResponse.Message.Parts[0].Text); } ``` ```python title="Python" expandable theme={null} import uuid import requests # Replace these with your values AGENT_ID = "" ENVIRONMENT = "" TENANT = "" TOKEN = "" response = requests.post( f"https://api.{ENVIRONMENT}.corti.app/v2/agentic/agents/{AGENT_ID}/a2a/message:send", headers={ "Authorization": f"Bearer {TOKEN}", "Tenant-Name": TENANT, "Content-Type": "application/json", "A2A-Version": "1.0", }, json={ "message": { "role": "ROLE_USER", "parts": [ {"text": "Hello there. This is my first message."} ], "messageId": str(uuid.uuid4()), } }, ) response.raise_for_status() result = response.json() if "task" in result: task = result["task"] print(task["status"]["state"]) if task.get("history"): print(task["history"][-1]["parts"][0]["text"]) elif "message" in result: print(result["message"]["parts"][0]["text"]) ``` ```bash title="cURL" theme={null} # Replace these with your values AGENT_ID="" ENVIRONMENT="" TENANT="" TOKEN="" curl -X POST "https://api.${ENVIRONMENT}.corti.app/v2/agentic/agents/${AGENT_ID}/a2a/message:send" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Tenant-Name: ${TENANT}" \ -H "Content-Type: application/json" \ -H "A2A-Version: 1.0" \ -d '{ "message": { "role": "ROLE_USER", "parts": [{ "text": "Hello there. This is my first message." }] } }' ``` Check the response to confirm your agent processed the message: * If the response contains a `task`, check `task.status.state`. A `TASK_STATE_COMPLETED` state means the task finished successfully. The agent's response is in `task.history` (the last `ROLE_AGENT` message). * If the response contains a `message`, the agent responded directly. The text is in `message.parts[0].text`. The response includes `task.metadata.$usage.credits`, reporting the credits used for the request. See [Tracking credit consumption](/get_started/tracking-credit-consumption) to attribute this to your own customers or features. ## Next steps Learn the fundamental building blocks: agents, connectors, tasks, messages, and contexts. Understand the five connector types and how to attach them to agents. Go deeper into message sending: blocking vs. non-blocking, DataParts, error handling. Stream agent responses in real time via SSE. # Clinical Trials Source: https://docs.corti.ai/agentic/registry/clinicaltrials-gov Search for clinical trials, study protocols, eligibility criteria, and recruitment status ```json theme={null} { "type": "registry", "name": "clinical-trials-expert" } ``` The **Clinical Trials** connector lets agents search for clinical trials, find relevant studies for specific medical conditions, and retrieve detailed protocol information including eligibility criteria and recruitment status. Use this connector when you need to surface ongoing or completed clinical research relevant to a patient's condition or to a research workflow. ## Capabilities The Clinical Trials connector can: * Search clinical trial registries for studies matching a condition or intervention * Retrieve study titles, locations, and recruitment status * Provide eligibility criteria and protocol details * Synthesize concise, factual summaries grounded in registry data ## Use cases * Finding relevant clinical trials for a patient's condition * Research and study discovery * Reviewing protocols and eligibility criteria during clinical decision support * Surfacing recruiting trials for referral workflows ## Detailed information The Clinical Trials connector retrieves study information through a dedicated MCP server. It is designed to operate within a multi-agent system: it follows agent runtime instructions exactly, plans the fewest necessary tool calls, and grounds every output in the data returned by its tools rather than speculating. Outputs are structured (Markdown headings, bullet points) so they remain both human-readable and easy for downstream agents to parse. The connector returns short, factual summaries (including study title, condition, location, recruitment status, and eligibility criteria where available) and never provides medical advice. # DrugBank Source: https://docs.corti.ai/agentic/registry/drugbank Look up detailed drug information, medication profiles, and drug-drug interactions from DrugBank ```json theme={null} { "type": "registry", "name": "drugbank-expert" } ``` The **DrugBank** connector retrieves structured drug information from the DrugBank API. It is suited for product concept lookups, product details, drug profiles, and drug-drug interaction queries, and returns source-grounded results without speculation. Use this connector when you need authoritative, identifier-grounded medication data (for example pharmacology, dosing, or interaction details) to support a prescribing or review workflow. ## Authentication The DrugBank connector connects to the DrugBank MCP server using **bearer-token authentication**, which means **you must supply a DrugBank API key** when registering or invoking this connector. The API key is issued by DrugBank and is provided to the MCP server as a bearer token at runtime. See [Configure connector authentication](/agentic/guides/connector-auth) for the full list of supported auth modes and how to wire credentials through to a connector. If you do not yet have a DrugBank API key, request one from DrugBank before enabling this connector. ## Capabilities The DrugBank connector can: * Find product concepts and retrieve product concept profiles * Look up drug profiles and product details by name * Retrieve detailed drug-drug interaction information * Return identifiers and key fields preserving the original DrugBank wording when quoting ## Use cases * Drug-drug interaction checks during medication review * Medication information and monograph lookups * Pharmacological research and reference workflows * Clinical decision support for prescribing ## Detailed information The DrugBank connector connects to DrugBank's MCP server and exposes tools such as `find_product_concepts`, `get_product_concept_profile`, `get_drug_profile`, `find_product_by_name`, `get_product_details`, and `get_drug_interaction_details`. Operating as a connector within the Corti Agentic Framework, it follows agent runtime instructions precisely and avoids speculative medical interpretation. If DrugBank returns no results or errors, the connector reports that explicitly rather than guessing. # Medical Calculator Source: https://docs.corti.ai/agentic/registry/medical-calculator Perform clinical calculations such as BMI, HbA1c, glucose conversions, and other medical formulas ```json theme={null} { "type": "registry", "name": "medical-calculator-expert" } ``` The **Medical Calculator** connector performs clinical and physiological calculations such as BMI, HbA1c conversions, glucose conversions, and related metrics. Use this connector whenever a workflow needs precise, formula-based medical computations. The Medical Calculator returns clear, concise results suitable for direct display or for further reasoning by another connector, removing the risk of manual calculation errors. ## Capabilities The Medical Calculator connector can: * Compute BMI, HbA1c, eAG, glucose conversions, and other physiological metrics * Run multiple calculations in a single request and return them as a clean list * Skip calculations when required inputs are missing and report what was missing * Return human-readable, plain-text results without unnecessary metadata ## Use cases * Inline clinical metric calculation during a conversation or document generation * Lab value conversion (for example HbA1c ↔ eAG, or glucose unit conversion) * Quick BMI or risk-metric lookups in clinical decision support flows * Combining several calculations into a single, clinician-friendly summary ## Output format When one calculation is requested, the connector returns a short single line, for example: ``` BMI: 24.8 (normal range) ``` When multiple calculations are requested, the connector returns a clean list: ``` BMI: 24.8 (normal range) HbA1c: 6.1% eAG: 128 mg/dL ``` If a value cannot be calculated, the connector notes the reason briefly, for example: ``` HbA1c: cannot calculate (missing fructosamine) ``` ## Detailed information The Medical Calculator is a connector that does not interact with end users directly. It reads input values from the agent runtime's `data_part`, runs the requested formulas via its tools, and returns results grounded in the actual computed values: no extra commentary, no speculative medical advice. # Medical Coding Source: https://docs.corti.ai/agentic/registry/medical-coding Assign medical diagnosis and procedure codes from clinical notes using AI-assisted coding ```json theme={null} { "type": "registry", "name": "coding-expert" } ``` The **Medical Coding** connector assigns diagnosis and procedure codes from clinical text using a structured, AI-assisted workflow. It can identify codes relevant to an admission even from incomplete data, and selects the most appropriate code system at runtime. Medical Coding is essential for billing, claims, CDI, and revenue-cycle workflows where accurate, guideline-compliant codes are required. ## Capabilities The Medical Coding connector can: * Predict candidate codes from a full clinical note * Search a code system from clinical text fragments * Explore the structure of a code system to drill into candidate codes * Retrieve coding guidelines that govern a given code or scenario * Verify a final code sequence against the code system before returning it ## Workflows Every code assignment follows one of the structured workflows below. `explore()`, `guidelines()`, and `verify()` are always called. They are never skipped. * **SEARCH-FIRST** (from clinical text): `search()` → `explore()` → `guidelines()` → `verify()` * **PREDICT-FIRST** (from a full clinical note): `predict()` → `explore()` → `guidelines()` → `verify()` * **GUIDELINES-FIRST** (unfamiliar conventions): `guidelines()` → `search()` → `explore()` → `guidelines()` → `verify()` * **VERIFY-AND-NARROW** (refine predictions): `predict()` → `explore()` + `guidelines()` → `verify()` → `predict(filter=include/exclude)` → `verify()` * **EXPLORE-FIRST** (browse code system): `explore()` → `explore()` drill down → `guidelines()` → `verify()` The connector never relies on `search()` alone. It can return overly specific codes or miss applicable coding rules. Codes already present in the agent runtime's `tool_data_part` are preferred; MCP tools are only invoked if suitable codes are absent or insufficient. ## Use cases * Automated coding for billing and claims * Clinical documentation improvement (CDI) and audit support * Code validation and verification against guidelines * Revenue-cycle management ## Detailed information The Medical Coding connector is wired to a coding MCP server with an extended call timeout to accommodate prediction and verification workflows. The general `coding-expert` calls `list_code_systems()` first and selects from any supported code system at runtime. For details on the supported code systems and use cases, see the [Medical Coding tab](/coding/overview). ## Code-system-specific variants In addition to the general `coding-expert`, the registry exposes pre-configured variants that lock the connector to a single code system. Use these when you know which system you want to code in and don't need the connector to discover code systems at runtime. | Key | Code system | | -------------------------- | ---------------------------------------------------------------------------------------- | | `coding-expert-icd-10-cm` | ICD-10-CM, US diagnosis coding | | `coding-expert-icd-10-int` | ICD-10 WHO, international diagnosis coding (uses `icd-10-who` for search/explore/verify) | | `coding-expert-icd-10-pcs` | ICD-10-PCS, US inpatient procedure coding | | `coding-expert-icd-10-uk` | ICD-10-UK, UK diagnosis coding | Each variant shares the same workflows and capabilities documented above. # Memory Source: https://docs.corti.ai/agentic/registry/memory Recall and analyze content from large contexts and files inside a single request ```json theme={null} { "type": "registry", "name": "memory-expert" } ``` Memory is a **core toolbox connector**, it is foundational to most agent workflows where the agent runtime needs to reason over large inputs without bloating the prompt. The **Memory** connector lets agents recall and analyze content from large contexts (long documents, attached files, transcripts, or other bulky inputs) that would not fit cleanly inside the agent runtime's working prompt. The agent runtime hands the bulky context to the Memory connector and asks targeted questions; the Memory connector searches and analyzes that content and returns the relevant facts. The Memory connector does **not** persist information across sessions, conversations, or context boundaries. It only operates over the context that has already been provided to the agent in the current request. If you need durable, cross-session memory, that has to be implemented at the application layer. Use the Memory connector when a request involves a large body of text or files that the agent runtime should not load into its own prompt (for example a long discharge summary, a multi-page guideline, or an attached transcript) and you only need a few targeted facts back. ## Capabilities The Memory connector can: * Search large bodies of context for information matching a query * Analyze long documents or files and return targeted facts * Surface relevant excerpts so the agent runtime can ground its synthesis * Reduce the size of the agent runtime's working prompt by offloading bulky lookups ## Use cases * Querying a long clinical document or attached file for specific facts * Pulling targeted passages out of a large transcript or note * Summarizing or extracting from inputs that are too large to keep in the agent runtime prompt * Providing analyzed excerpts as evidence for a downstream connector ## How to invoke Send the relevant query or high-level objective as the `text` parameter. Do not pass new raw facts to the Memory connector. Provide only the question or objective so it can search and analyze the existing in-request context itself. For questions that can be answered directly from the agent runtime's own prompt without consulting bulky context, you can answer directly without invoking the Memory connector. # Registry connectors Source: https://docs.corti.ai/agentic/registry/overview A registry of prebuilt connectors available in the Corti Agentic Framework The Corti Agentic Framework ships with a **registry of prebuilt connectors** that the agent runtime can compose into multi-step workflows. Each connector is a discrete capability, ranging from in-context memory recall, to reference lookup, to medical coding, to structured interviewing. Browse the list below to see what is available out of the box. You can also [bring your own connector](/agentic/connectors) by exposing an MCP server and registering it with Corti. Want to inspect the live registry programmatically? Use the [registry connector API](/agentic/guides/use-registry) to retrieve the full list of available connectors, their descriptions, and configuration schemas. ## Core toolbox These connectors are foundational to most agent workflows and should be available to the agent runtime by default. Recall and analyze content from large contexts and files within a single request. Core toolbox connector, keep available across most workflows. ## Knowledge and reference Get medication guidance including dosing, interactions, contraindications, and prescribing considerations from POSOS. Look up detailed drug information, medication profiles, and drug-drug interactions from DrugBank. Search PubMed for scientific articles, abstracts, and citations from biomedical literature. Search for clinical trials, study protocols, eligibility criteria, and recruitment status. Search the web and retrieve up-to-date information from online sources. ## Medical coding Assign medical diagnosis and procedure codes from clinical notes using AI-assisted coding across all supported coding systems. Assign ICD-10-CM diagnosis codes (US standard) from clinical notes. Assign ICD-10-WHO international diagnosis codes from clinical notes. Assign ICD-10-PCS inpatient procedure codes (US standard) from clinical notes. Assign ICD-10-UK diagnosis codes (UK standard) from clinical notes. ## Computation and structured workflows Perform clinical calculations such as BMI, HbA1c, glucose conversions, and other medical formulas. Guide users through structured questionnaires and clinical interviews step by step. Need a connector that isn't listed here? You can [register your own MCP server](/agentic/connectors) as a custom connector, or [contact us](mailto:help@corti.ai) to discuss new integrations. # POSOS Source: https://docs.corti.ai/agentic/registry/posos Get medication guidance including dosing, interactions, contraindications, and prescribing considerations from POSOS ```json theme={null} { "type": "registry", "name": "posos-expert" } ``` The **POSOS** connector is a primary source of truth for medication intelligence and prescription decision support, backed by POSOS (a medication database and clinical decision platform). It should be invoked early and frequently to ground synthesis in structured, patient-contextualized medication guidance and to reduce alert fatigue by focusing on clinically relevant signals. POSOS is not limited to direct medication questions. It can also fact-check, validate, or anchor broader reasoning that relies on pharmacology, dosing, contraindications, interactions, organ-impairment considerations, or pregnancy/lactation considerations. ## Capabilities The POSOS connector can: * Retrieve medication database entities and structured drug information * Surface interaction, contraindication, and high-risk warning signals * Provide dosing guidance and dose adjustments (for example renal or hepatic impairment) * Flag pregnancy and lactation considerations and monitoring requirements * Return interoperability-friendly structured outputs aligned with SNOMED/FHIR concepts where available, with traceability back to POSOS entities ## Use cases * Prescription decision support embedded in EHR or clinician-facing workflows * Reducing alert fatigue by prioritizing clinically relevant medication signals * Patient-contextualized prescribing: indication, current meds, organ impairment, pregnancy/lactation * Validating medication-related assumptions in broader clinical reasoning ## Detailed information The POSOS connector is a clinical decision support and medication knowledge retrieval connector. It treats POSOS (its medication database and CDSS outputs) as the primary source of truth for retrieved content, prefers entity- or signal-level retrieval (drug, interaction, contraindication, dosing adjustment, alert rationale) over broad summaries, and stays concise, clinically accurate, and neutral. Outputs reference POSOS entities and signals clearly so the agent runtime can trace the source (drug entity, interaction signal, alert category, patient-context parameters). The connector connects to POSOS via OAuth 2.0. See [Configure connector authentication](/agentic/guides/connector-auth) for details. # PubMed Source: https://docs.corti.ai/agentic/registry/pubmed Search PubMed for scientific articles, abstracts, and citations from biomedical literature ```json theme={null} { "type": "registry", "name": "pubmed-expert" } ``` The **PubMed** connector retrieves PubMed citations, abstracts, and related-article results via NCBI Entrez E-utilities. It is suited for literature searches, author searches, and fetching abstracts with formatted summaries, returning source-grounded results without speculation. PubMed is the go-to connector for finding peer-reviewed biomedical literature, clinical studies, and citations to support evidence-based reasoning. ## Capabilities The PubMed connector can: * Search PubMed for citations matching a query * Retrieve abstracts and metadata for specific publications * Find related articles for a given citation * Look up publications by author ## Use cases * Literature reviews and evidence retrieval * Pulling abstracts to ground a clinical synthesis * Finding studies relevant to a diagnosis, treatment, or intervention * Author and publication discovery ## Detailed information The PubMed connector connects to an MCP server that exposes tools including `search_pubmed`, `get_pubmed_abstract`, `get_related_articles`, and `find_by_author`. It operates as a focused retrieval connector: it follows agent runtime instructions precisely, avoids speculative medical interpretation, and returns concise structured outputs grounded in tool responses. Results include identifiers (PMID/PMCID when available) and preserve the original wording from PubMed when quoting. If PubMed returns no results or errors, the connector reports that explicitly. # Interviewing Source: https://docs.corti.ai/agentic/registry/questionnaire-interviewing Guide users through structured questionnaires and clinical interviews step by step ```json theme={null} { "type": "registry", "name": "interviewing-expert" } ``` The **Interviewing** connector manages questionnaire-style interviews with the user. Use this connector whenever the user is answering or progressing through a questionnaire, always pass the questionnaire `data_part` (and its ID) when available so the connector can follow the correct structure and logic. When invoking this connector, set the `text` argument to the raw content of what the user just said or answered, with no extra commentary, prompts, or instructions. ## Capabilities The Interviewing connector can: * Drive structured interviews that follow a provided questionnaire definition * Guide the conversation question-by-question and adapt based on responses * Track answers and resolve conditional flow (`next`, `defaultNext`, `conditions`) * Return structured `questionnaire_response` artifacts the agent runtime can consume ## Use cases * Patient intake and history-taking * Clinical assessments and screening questionnaires * Research and survey data collection * Any structured, multi-step information-gathering workflow ## Documentation ### Questionnaire * `questionnaireId`: `string` (required) * `version`: `string | number` (required) * `startQuestion` : `string` (required, but will be optional soon; ID of a question) * `questions`: `Question[]` – list of unique IDs (required) * `title`, `description`, `meta`: optional ### Question (Discriminated by `type`) Common fields for **all** question types: * `id`: `string` (required, unique) * `type`: one of the literals below (required) * `boolean` * `text_short` * `text_long` * `number` * `date_time` (with `mode`: `date` | `time` | `datetime`) * `scale` (with `min`, `max`, optional `step`, optional `labels[]`) * `single_choice` (with `options[]`) * `multi_choice` (with `options[]`, optional `maxSelections`) * `text`: `string` (required) * `guideline`: `string` (optional, instructs model to provide guidelines to the end user, can be used to augment question text) * `facets`: `string` (optional, hints for the LLM on how to populate and interpret user answers) * `required`: `boolean` (optional; if `true`, agent must collect a valid answer) * `conditions`: `Condition[]` (optional; controls visibility/flow) * `defaultNext`: `string` (optional; ID of the next question if no option-level `next` applies) * `meta`: `object` (optional) ### Options (for choice questions) * `value`: `string | number | boolean` * `label`: `string` * `guideline`: `string` (optional) * `next`: `string` (optional, overrides `defaultNext` when chosen) ### Conditions **Operators:** `=`, `!=`, `<`, `<=`, `>`, `>=`, `contains`, `not_contains`, `in`, `not_in`, `exists`, `not_exists` * `question`: `string` – the **source** question ID to evaluate * `operator`: as above * `value`: required for all operators **except** `exists`/`not_exists` ## Example requests ### 1. Initial Request User provides an incomplete or ambiguous answer (starts the flow): ```json theme={null} { "message": { "role": "ROLE_USER", "messageId": "", "parts": [ { "text": "Answer to the questionnaire: I'm ok satisfied" }, { "data": { "type": "questionnaire", "questionnaire": { "questionnaireId": "sleep-survey-v1", "version": "1.0", "startQuestion": "question-1", "questions": [ { "id": "question-1", "type": "scale", "text": "How satisfied are you?", "min": 1, "max": 5 }, { "id": "question-2", "type": "scale", "text": "How many hours of sleep did you have?", "min": 0, "max": 12 }, { "id": "question-3", "type": "text_short", "text": "How well rested do you feel?" }, { "id": "question-4", "type": "single_choice", "text": "How many interruptions did you have?", "options": [ {"value": "none", "label": "None at all"}, {"value": "few", "label": "A few"}, {"value": "lots", "label": "Lots"} ] } ] } } } ] } } ``` **Agent response** ```json theme={null} { "task": { "id": "", "contextId": "", "status": { "state": "TASK_STATE_COMPLETED", "message": { "role": "ROLE_AGENT", "parts": [ { "text": "Thanks, how many hours of sleep did you have?" } ], "messageId": "", // For client use to track messages "taskId": "", "contextId": "" } }, "artifacts": [{ "artifactId": "", "parts": [ { "data": { "answers": { "question-1": 3 }, "is_completed": false, "next_question_id": "question-2", "questionnaire_id": "sleep-survey-v1", "version": "1.0" }, "metadata": { "type": "questionnaire_response" } } ] }], "history": [ // ... ], "metadata": { // contains metadata related to task execution // not relevant for purposes of input/output } } } ``` ### 2. Second Request User clarifies and completes the missing information: ```json theme={null} { "message": { "role": "ROLE_USER", "messageId": "", "contextId": "", "parts": [ { "text": "I had 7 hours of sleep" } ] } } ``` Agent response ```json theme={null} { "task": { "id": "", "contextId": "", "status": { "state": "TASK_STATE_COMPLETED", "message": { "role": "ROLE_AGENT", "parts": [ { "text": "Thanks, how many interruptions did you have while sleeping?" } ], "messageId": "", // For client use to track messages "taskId": "", "contextId": "" } }, "artifacts": [{ "artifactId": "", "parts": [ { "data": { "answers": { "question-1": 3, "question-2": 7 }, "is_completed": false, "next_question_id": "question-3", "questionnaire_id": "sleep-survey-v1", "version": "1.0" }, "metadata": { "type": "questionnaire_response" } } ] }], "history": [ // ... ], "metadata": { // contains metadata related to task execution // not relevant for purposes of input/output } } } ``` ### 3. Final Request User clarifies and completes the missing information: ```json theme={null} { "message": { "role": "ROLE_USER", "messageId": "", "contextId": "", "parts": [ { "text": "I feel well rested and was not interrupted" } ] } } ``` **Agent response (final result):** ```json theme={null} { "task": { "id": "", "contextId": "", "status": { "state": "TASK_STATE_COMPLETED" }, "artifacts": [{ "artifactId": "", "parts": [ { "data": { "type": "questionnaire_response", "answers": { "question-1": 3, "question-2": 7, "question-3": "Well rested", "question-4": "none" }, "is_completed": true, "next_question_id": null, "questionnaire_id": "sleep-survey-v1", "version": "1.0" }, "metadata": { "type": "questionnaire_response" } } ] }], "history": [ //... ], "metadata": {} } } ``` # Web Search Source: https://docs.corti.ai/agentic/registry/web-search Search the web and retrieve up-to-date information from online sources ```json theme={null} { "type": "registry", "name": "web-search-expert" } ``` The **Web Search** connector lets agents search the web, read specific URLs, and gather up-to-date external information. It finds relevant online sources, extracts their content, and returns concise, evidence-based summaries. Web Search is essential when you need information that may not be available in a static knowledge base, for example recent news, fresh research, or dynamic content that changes frequently. ## Capabilities The Web Search connector can: * Search the web for sources relevant to a query * Read specific URLs and extract their content * Synthesize concise, source-grounded summaries * Filter results to or from specific domains and tune search depth (basic vs. advanced) ## Use cases * Surfacing recent medical research and publications * Verifying claims against multiple online sources * Pulling current news, policy updates, or product information * Retrieving information not yet indexed in static reference databases ## Configuration The Web Search connector exposes optional search configuration including `include_domains`, `exclude_domains`, `max_results`, and `search_depth` (`basic` or `advanced`). Use these to constrain results to trusted domains or to trade off latency for thoroughness. ## Detailed information The Web Search connector is a focused research connector: it does not interact with end users directly, but instead follows structured agent runtime instructions and returns factual, well-organized findings grounded entirely in the data its tools return. Outputs use Markdown structure so downstream agents and humans can both consume them easily. # SDKs and integrations Source: https://docs.corti.ai/agentic/sdks-integrations Official SDKs and community integrations for the Agentic Framework. The Agentic Framework provides official SDKs and supports community integrations to help you build quickly. ## Official Corti SDKs Official Agentic Framework SDK for Node and browser environments.
JavaScript SDK docs →
Official Agentic Framework SDK for .NET applications.
C# .NET SDK docs →
Adapter for integrating Corti A2A agents with the Vercel AI SDK. Use `useChat` and streaming patterns to build chat UIs.
AI SDK Adapter docs →
## Official A2A Project SDKs The A2A project maintains open source SDKs in multiple languages. These SDKs implement the A2A v1.0 protocol that the Agentic Framework uses: Build A2A-compliant agents and servers in Python.
a2a-python →
Official JavaScript/TypeScript SDK for A2A.
a2a-js →
Build A2A-compliant agents and services in Java.
a2a-java →
Implement A2A agents and servers in Go.
a2a-go →
Build A2A-compatible agents in .NET ecosystems.
a2a-dotnet →
## Other libraries * **shadcn/ui component library for chatbots**: [`ai-elements` on npm](https://www.npmjs.com/package/ai-elements) * **A2A Inspector**: [a2a-inspector on GitHub](https://github.com/a2aproject/a2a-inspector) * **Awesome A2A**: [awesome-a2a on GitHub](https://github.com/ai-boost/awesome-a2a) # Task lifecycle Source: https://docs.corti.ai/agentic/task-lifecycle Learn how A2A tasks move through states, how streaming events deliver updates, and how to resume interrupted streams. A task is a stateful unit of work performed by an agent. When you send a message to an agent, the response is either a direct `Message` (for quick operations) or a `Task` (for longer-running work). Tasks have a defined lifecycle, can produce artifacts, and can be streamed in real time. ## What is a task A task represents work that an agent performs in response to a message. It has: * A unique prefixed UUIDv7 identifier (`task.0192f4c8-...`) * A `contextId` linking it to a [context](/agentic/context-memory) for conversation continuity * A `status` with a current state and timestamp * A `history` array of messages exchanged during the task * An `artifacts` array of outputs produced by the task * `metadata` including token and credit accounting under `$usage` The agent decides whether to respond with a `Task` or a `Message`. Quick operations (short completions, classifications) often return a `Message` directly. Longer workflows return a `Task` that you monitor for completion. ## Task states A task moves through the following states: | State | Description | | --------------------------- | --------------------------------------------------------------------------------------------- | | `TASK_STATE_SUBMITTED` | The task has been accepted by the agent but processing has not started | | `TASK_STATE_WORKING` | The agent is actively processing the task | | `TASK_STATE_COMPLETED` | The task finished successfully; artifacts are available | | `TASK_STATE_FAILED` | The task failed; check the status message for error details | | `TASK_STATE_CANCELED` | The task was canceled by the client | | `TASK_STATE_INPUT_REQUIRED` | The agent needs additional input before it can continue | | `TASK_STATE_AUTH_REQUIRED` | An MCP connector requires authentication; the task pauses until auth is provided | | `TASK_STATE_REJECTED` | The task was rejected before processing (insufficient credits or invalid agent configuration) | `TASK_STATE_REJECTED` and `TASK_STATE_AUTH_REQUIRED` are only visible on the JSON-RPC binding. On the REST (HTTP+JSON) binding, both are mapped to `TASK_STATE_FAILED` with a descriptive status message. ### Terminal vs non-terminal states | Category | States | | ------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | | **Terminal** (immutable, cannot be canceled or resumed) | `TASK_STATE_COMPLETED`, `TASK_STATE_FAILED`, `TASK_STATE_CANCELED`, `TASK_STATE_REJECTED` | | **Non-terminal** (can be resumed or canceled) | `TASK_STATE_SUBMITTED`, `TASK_STATE_WORKING`, `TASK_STATE_INPUT_REQUIRED`, `TASK_STATE_AUTH_REQUIRED` | Canceling a task in a terminal state returns `409 Conflict`. ### State transitions A task transitions through states as follows: * **`TASK_STATE_SUBMITTED`** to **`TASK_STATE_WORKING`**: The agent begins processing. * **`TASK_STATE_SUBMITTED`** to **`TASK_STATE_REJECTED`**: The task was rejected before processing (insufficient credits or invalid configuration). * **`TASK_STATE_SUBMITTED`** to **`TASK_STATE_AUTH_REQUIRED`**: An MCP connector requires authentication before the agent can proceed. * **`TASK_STATE_WORKING`** to **`TASK_STATE_COMPLETED`**: The task finishes successfully. * **`TASK_STATE_WORKING`** to **`TASK_STATE_FAILED`**: An error occurred during processing. * **`TASK_STATE_WORKING`** to **`TASK_STATE_INPUT_REQUIRED`**: The agent paused and needs user input. * **`TASK_STATE_WORKING`** to **`TASK_STATE_AUTH_REQUIRED`**: An MCP connector encountered an auth error during execution. * **`TASK_STATE_AUTH_REQUIRED`** to **`TASK_STATE_WORKING`**: Auth is resolved and the agent resumes. * **`TASK_STATE_INPUT_REQUIRED`** to **`TASK_STATE_WORKING`**: The user provides input and the agent resumes. * Any non-terminal state to **`TASK_STATE_CANCELED`**: The client cancels the task via `tasks/{id}:cancel`. A task in `TASK_STATE_INPUT_REQUIRED` or `TASK_STATE_AUTH_REQUIRED` state is not terminated. Send a new message with the required input (for `TASK_STATE_INPUT_REQUIRED`) or resolve the connector authentication (for `TASK_STATE_AUTH_REQUIRED`) to resume processing. The SSE stream closes when either state is reached; you must send a new message or re-subscribe to continue receiving updates. ## Task history and messages The `history` array contains all messages exchanged during the task, oldest first. Each message has: * `messageId`: A prefixed UUIDv7 (e.g. `msg.0192f4c8-...`) * `role`: Either `ROLE_USER` (sent by the client) or `ROLE_AGENT` (sent by the agent) * `parts`: An ordered list of content parts (text, file, or data) * `metadata`: Free-form metadata, including Corti's `$timestamp` for timing You can control how much history the agent considers by setting `configuration.historyLength` on the [message send request](/agentic/guides/send-message). ## Artifacts Artifacts are the tangible outputs a task produces. Each artifact has: * `artifactId`: A prefixed UUIDv7 (e.g. `art.0192f4c8-...`) * `name`: An optional human-readable name * `parts`: Content parts containing the artifact data Artifacts are closely tied to the task lifecycle. In production scenarios, artifacts typically correspond to business outputs like notes, coding suggestions, or extracted facts. You can retrieve a specific artifact via: ``` GET /v2/agentic/contexts/{contextId}/tasks/{taskId}/artifacts/{artifactId} ``` ## Streaming events When you stream a message via `message:stream` or subscribe to an existing task via `tasks/{id}:subscribe`, the server sends Server-Sent Events (SSE). Each event carries an `A2AStreamResponse` with exactly one of the following fields: | Field | Description | | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | | `task` | The full task object, sent when the task is first created or when its state changes | | `message` | A direct message response (no task lifecycle) | | `statusUpdate` | An incremental status change with `taskId`, `contextId`, `status`, and `metadata` (no `final` flag; clients infer finality from the task state) | | `artifactUpdate` | An incremental artifact update with `taskId`, `contextId`, `artifact`, and `lastChunk` | ### SSE event format Each SSE frame follows the W3C SSE wire format: | Field | Description | | ------- | -------------------------------------------------------------------------------------------------- | | `data` | The JSON-encoded payload (an `A2AStreamResponse`) | | `event` | Event type (declared for forward compatibility; not currently sent by the server) | | `id` | Opaque event ID (the server writes event IDs but does not yet read `Last-Event-ID` for resumption) | | `retry` | Reconnection interval (declared for forward compatibility; not currently sent by the server) | ### Example SSE event ```json theme={null} data: {"task":{"id":"task.0192f4c8-...","contextId":"ctx.0192f4c8-...","status":{"state":"TASK_STATE_WORKING","timestamp":"2026-05-19T12:00:00Z"}}} data: {"artifactUpdate":{"taskId":"task.0192f4c8-...","contextId":"ctx.0192f4c8-...","artifact":{"artifactId":"art.0192f4c8-...","name":"icd10-result","parts":[{"text":"J45.909"}]},"lastChunk":true}} data: {"statusUpdate":{"taskId":"task.0192f4c8-...","contextId":"ctx.0192f4c8-...","status":{"state":"TASK_STATE_COMPLETED","timestamp":"2026-05-19T12:00:01Z"}}} ``` ## Resumption with Last-Event-ID The server writes event IDs but does not yet read the `Last-Event-ID` request header, so resumption without gaps is not implemented. The section below describes the intended design for when this feature ships. The intended resumption mechanism is to send the `Last-Event-ID` header with the most recent event ID you received. The server will replay events from that point forward once implemented. This will work with both `message:stream` and `tasks/{id}:subscribe`. See [Stream responses](/agentic/guides/stream-responses) for implementation details. ## Task metadata and usage Each task carries `metadata` with Corti's first-party keys prefixed with `$`. The `$usage` object provides token and credit accounting: | Field | Description | | -------------------------- | ----------------------------------------------------------------- | | `model` | The model identifier that served the request | | `inputTokens` | Prompt tokens consumed | | `outputTokens` | Completion tokens produced | | `cachedInputTokens` | Subset of `inputTokens` served from the prompt cache (a discount) | | `cacheCreationInputTokens` | Input tokens written to the prompt cache (cache-write surcharge) | | `totalTokens` | Total tokens billed (`inputTokens` + `outputTokens`) | | `credits` | Corti billing credits charged for the task | ```json theme={null} { "$usage": { "model": "corti-default", "inputTokens": 100, "outputTokens": 20, "cachedInputTokens": 64, "cacheCreationInputTokens": 0, "totalTokens": 120, "credits": 1.2 } } ``` You can use this data for cost tracking, budget monitoring, and evaluating cache effectiveness across your agents. ## Next steps * Learn how to [send a message](/agentic/guides/send-message) and handle task or message responses * Learn how to [stream responses](/agentic/guides/stream-responses) via SSE * Read about [contexts](/agentic/context-memory) to understand how tasks are grouped # A2A Protocol (Agent-to-Agent) Source: https://docs.corti.ai/agentic/v1/a2a-protocol Learn about the Agent-to-Agent protocol for inter-agent communication ### What is the A2A Protocol The **Agent-to-Agent (A2A)** protocol is an open standard that enables secure, framework-agnostic communication between autonomous AI agents. Instead of building bespoke integrations whenever you want agents to collaborate, A2A gives Corti-Agentic and other systems a **common language** agents can use to discover, talk to, and delegate work to one another. For the full technical specification, see the official A2A project docs at [a2a-protocol.org](https://a2a-protocol.org/latest/). The v1 API implements **A2A protocol version 0.3**, not the latest (v1.0) specification. The link above shows the latest spec, which includes features not available in the v0.3 implementation. For the v0.3-specific behavior, refer to the v1 API reference and the migration guide's [A2A v0.3 to v1.0](/agentic/guides/migrate-v1-to-v2#a2a-v03-to-v10) section. Originally developed by Google and now stewarded under the Linux Foundation, A2A solves a core problem in multi-agent systems: interoperability across ecosystems, languages, and vendors. It lets you connect agents built in Python, JavaScript, Java, Go, .NET, or other languages and have them cooperate on complex workflows without exposing internal agent state or proprietary logic. ### Why Corti-Agentic Uses A2A We chose A2A because it: * **Standardizes agent communication.** Agents can talk to each other without siloed, point-to-point integrations. That makes composite workflows easier to build and maintain. * **Supports real workflows.** A2A includes discovery, task negotiation, and streaming updates, so agents can coordinate long-running or multi-step jobs. * **Preserves security and opacity.** Agents exchange structured messages without sharing internal memory or tools. That protects intellectual property and keeps interactions predictable. * **Leverages open tooling.** There are open source SDKs in multiple languages and example implementations you can reuse. In Corti-Agentic, A2A is the backbone for agent collaboration. Whether you’re orchestrating specialist agents, chaining reasoning tasks, or integrating external agent services, A2A gives you a robust, open foundation you don’t have to reinvent. ### V1 API Bindings The v1 API exposes two A2A v0.3 bindings: | Binding | Endpoint | Methods | | ---------------- | ------------------------------------ | ------------------------------------------------------- | | HTTP+JSON (REST) | `POST /agents/{id}/v1/message:send` | Send a message | | HTTP+JSON (REST) | `GET /agents/{id}/v1/tasks/{taskId}` | Get task status and history | | JSON-RPC v0.3 | `POST /agents/{id}/v1` | `message/send`, `tasks/get`, and other JSON-RPC methods | Both bindings share the same underlying handler—the REST endpoints are a convenience wrapper around the same logic as the JSON-RPC binding. The v1 agent card advertises `streaming: true` in its capabilities, but the v1 API surface does **not** provide a streaming endpoint. Streaming (`message:stream` and `tasks:subscribe` SSE endpoints) is only available in the v2 API. Use polling via `GET /agents/{id}/v1/tasks/{taskId}` to track task progress in v1. ### Open Source SDKs and Tooling For links to Corti’s official SDK and the official A2A project SDKs (Python, JavaScript/TypeScript, Java, Go, and .NET), see **[SDKs & Integrations](/agentic/v1/sdks-integrations)**. Please [contact us](mailto:help@corti.ai) if you need more information about the Corti Agentic Framework. # System Architecture Source: https://docs.corti.ai/agentic/v1/architecture Learn about the Agentic Framework system architecture The Corti Agentic Framework adopts a **multi-agent architecture** to power development of healthcare AI solutions. As compared to a monolithic LLM, the Corti Agentic Framework allows for improved specialization and protocol-based composition. ## Architecture Components Diagram illustrating the Corti Agentic Framework architecture, showing the Orchestrator, Experts, and Memory components and how they interact. The architecture consists of three core components working together: * **[Orchestrator](/agentic/v1/orchestrator)** — The central coordinator that receives user requests and delegates tasks to specialized Experts via the A2A protocol. * **[Experts](/agentic/v1/experts)** — Specialized sub-agents that perform domain-specific work, potentially calling external services through MCP. * **[Memory](/agentic/v1/context-memory)** — Maintains persistent context and state, enabling the Orchestrator to make informed decisions and ensuring continuity across conversations. Together, this architecture enables complex workflows through protocol-based composition while maintaining strict data isolation and stateless reasoning agents. ## Interaction mechanisms in Corti The A2A Protocol supports various interaction patterns to accommodate different needs for responsiveness and persistence. Corti builds on these patterns so you can choose the right interaction model for your product: * **Request/Response (Polling)**: Used for many synchronous Corti APIs where you send input and wait for a single response. For long‑running Corti tasks, your client can poll the task endpoint for status and results. * **Streaming with Server-Sent Events (SSE)**: Used by Corti for real-time experiences (for example, ambient notes or live guidance). Your client opens an SSE stream to receive incremental tokens, events, or status updates over an open HTTP connection. The v1 API does not provide a streaming endpoint, even though the agent card advertises `streaming: true`. Streaming (`message:stream` and `tasks:subscribe` SSE endpoints) is only available in the v2 API. In v1, use polling via `GET /agents/{id}/v1/tasks/{taskId}` to track task progress. The SSE pattern described here is available in the v2 API.
Please [contact us](mailto:help@corti.ai) if you need more information about the Corti Agentic Framework. # Beginners' Guide to Agents Source: https://docs.corti.ai/agentic/v1/beginners-guide How LLM agents work in the Corti Agentic Framework In healthcare, an **LLM agent** is not a chatbot trying to answer everything on its own. The language model is used primarily for reasoning and planning, understanding a request, breaking it down, and deciding which experts, tools, or data sources are best suited to handle each part of the task. Instead of relying on internal knowledge, agents retrieve information from trusted external knowledge bases, clinical systems, and customer-owned data at runtime. When appropriate, they can also take controlled actions, such as writing structured data back to an EHR, triggering downstream workflows, or sending information to other systems. The **Corti Agentic Framework** is the healthcare-grade platform that makes this possible in production. It provides the orchestration layer that allows agents to delegate work to specialized experts, operate within strict safety and governance boundaries, and remain fully auditable. This enables AI systems that can reason, look things up, and act, without guessing or bypassing clinical control. # Context & Memory Source: https://docs.corti.ai/agentic/v1/context-memory Learn how context and memory work in the Corti Agentic Framework A **context** in the Corti Agentic Framework makes use of memory from previous text and data in the conversation so far—think of it as a thread that maintains conversation history. Understanding how context works is essential for building effective integrations that maintain continuity across multiple messages. Diagram showing orchestration flow in the agentic framework ## What is Context? A `Context` (identified by a server-generated `contextId`) is a logical grouping of related `Messages`, `Tasks`, and `Artifacts`, providing context across a multi-turn "conversation". It enables you to associate multiple tasks and agents with a single patient encounter, call, or workflow, ensuring continuity and proper scoping of shared knowledge throughout. When you send a message **without** a `contextId`, the server generates one automatically. You can also supply your own `contextId` to continue an existing conversation—pass the same `contextId` you received from a previous response to maintain continuity across messages. ### Data Isolation and Scoping **Contexts provide data isolation across users**: Data can **never** leak across users. Each `contextId` creates a conversation scope where messages, tasks, and artifacts are isolated from other users' contexts. This ensures: * **Privacy and security**: Patient data from one encounter cannot accidentally be exposed to another encounter * **Data integrity**: Information from different workflows remains properly separated * **Compliance**: You can confidently scope sensitive data to specific contexts without risk of cross-contamination Contexts are scoped **per user**, not per agent. Any agent belonging to the same authenticated user can read any of that user's contexts. Do not rely on agent-level context isolation—if you need to restrict which agents can access which contexts, enforce that in your application layer. When you need to share information across contexts, you must explicitly pass it via `DataPart` objects in your messages—there is no automatic data sharing between contexts. ## Using Context for Automatic Memory Management The simplest way to use context is to let the framework automatically manage conversation memory: ### Workflow Pattern 1. **First message**: Send your message **without** a `contextId`. The server will create a new context automatically. 2. **Response**: The server's response includes the newly created `contextId`. 3. **Subsequent messages**: Include that `contextId` in your requests. Memory from previous messages in that context is automatically managed and available to the agent. When you include a `contextId` in your request, the agent has access to all previous messages, artifacts, and state within **that specific context only**. Data from other contexts is completely isolated and inaccessible. This enables natural, continuous conversations without manually passing history, while maintaining strict data boundaries between different encounters or workflows. ### Standalone Requests If you don't want automatic memory management, always send messages **without** a `contextId`. Each message will then be treated as a standalone request without access to prior conversation history. This is useful for: * One-off queries that don't depend on prior context * Testing and debugging individual requests * Scenarios where you want explicit control over what context is included ## Passing Additional Context with Each Request In addition to automatic memory management via `contextId`, you can pass additional context in each request by including `DataPart` objects in your message. This is useful when you want to provide specific structured data, summaries, or other context that should be considered for that particular request. ```json theme={null} { "message": { "role": "user", "parts": [ { "kind": "text", "text": "Generate a summary of this patient encounter" }, { "kind": "data", "data": { "patientId": "pat_12345", "encounterDate": "2025-12-15", "chiefComplaint": "Chest pain", "vitalSigns": { "bloodPressure": "120/80", "heartRate": 72, "temperature": 98.6 } } } ], "messageId": "550e8400-e29b-41d4-a716-446655440000", "kind": "message", "contextId": "ctx_abc123" } } ``` This approach allows you to: * Provide structured data (patient records, clinical facts, etc.) alongside text * Include summaries or distilled information from external sources * Pass metadata or configuration that should be considered for this specific request * Combine automatic memory (via `contextId`) with explicit context (via `DataPart`) ## How Memory Works The Corti Agentic Framework provides an optional memory system that agents can use to search for relevant past content using semantic similarity. Memory is **not** an automatic RAG pipeline. There is no automatic indexing of all content, no automatic semantic search on every message, and no automatic injection of retrieved context into the agent's prompt. Memory retrieval requires a memory MCP connector to be attached to the agent, and the LLM must decide to call the `search_memory` tool. ### Background Embedding When an embedding client is configured (a deployment dependency, not guaranteed), user messages are embedded in the background for potential future retrieval. Agent responses are not embedded. DataParts carrying internal metadata markers (such as `$memoryType` or `$targetExpert`) are skipped. ### Explicit Retrieval via MCP Tool If a memory MCP connector is attached to an agent, the agent's tool list includes a `search_memory` tool. The LLM decides whether to call this tool during processing—it is not called automatically. When the LLM calls `search_memory`, it performs semantic search across the context's embedded content and receives the results as a tool response, which it can then incorporate into its reasoning. ### What This Means in Practice * Memory retrieval is **opt-in**: you need a memory MCP connector and the LLM must choose to call the tool * Not all content is indexed: only user messages are embedded (when an embedding client is configured) * There is no automatic context injection: the LLM receives tool results and incorporates them into its reasoning * If no embedding client is configured or no memory connector is attached, the memory system is inactive ## Context vs. Reference Task IDs The framework provides two mechanisms for linking related work: * **`contextId`** – Groups multiple related `Messages`, `Tasks`, and `Artifacts` together (think of it as the encounter/call/workflow bucket). This provides automatic memory management and is sufficient for most use cases. * **`referenceTaskIds`** – An optional list of specific past `Task` IDs within the same context that should be treated as explicit inputs or background. Note that `referenceTaskIds` are scoped to a context—they reference tasks within the same `contextId`. **In most situations, you can ignore `referenceTaskIds`** since the automatic memory provided by `contextId` is sufficient. Only use `referenceTaskIds` when you need to explicitly direct the agent to pay attention to specific tasks or artifacts within the context, such as in complex multi-step workflows where you want to ensure certain outputs are prioritized. ```json theme={null} { "message": { "role": "user", "parts": [{ "kind": "text", "text": "Summarize the findings from the previous tasks." }], "messageId": "550e8400-e29b-41d4-a716-446655440001", "kind": "message", "contextId": "ctx_abc123", "referenceTaskIds": ["task-uuid-1", "task-uuid-2"] } } ``` ## Message:send Configuration The `message:send` endpoint accepts an optional `configuration` object alongside the `message` object. This controls blocking behavior, history length, output modes, and push notifications. ```json theme={null} { "message": { "role": "user", "parts": [{ "kind": "text", "text": "What was the patient's chief complaint?" }], "messageId": "550e8400-e29b-41d4-a716-446655440002", "kind": "message", "contextId": "ctx_abc123" }, "configuration": { "blocking": true, "historyLength": 10, "acceptedOutputModes": ["text/plain"] } } ``` ### `blocking` Controls whether the server waits for the task to complete before returning a response. * **`true`** (default): The server waits for the task to complete and returns the final result. * **`false`**: The server returns immediately after the first task event (non-blocking mode). Use this for long-running tasks where you want to poll for results later via the get-task endpoint. ### `historyLength` Controls how many previous messages are included in the response. The default `historyLength` is **0** — no history is returned in the response. If you want conversation history included in the response, you must explicitly set `historyLength` to a positive value. ### `acceptedOutputModes` An array of strings specifying the output MIME types the client accepts (for example, `["text/plain"]`). ### `pushNotificationConfig` Optional configuration for push notifications. The agent card advertises `pushNotifications: false`, so this feature may not be fully implemented in all deployments. ## Context Management Endpoints The v1 API provides two endpoints for managing contexts programmatically: ### List tasks in a context ``` GET /agents/{id}/v1/contexts/{contextId}?limit=50&offset=0 ``` Returns the tasks belonging to a context, with `limit` and `offset` pagination. The agent ID in the path is not used for scoping—any agent belonging to the same authenticated user can read any of that user's contexts. ### Delete a context ``` DELETE /agents/{id}/v1/contexts/{contextId} ``` Deletes a context and scrubs all associated data. Returns 204 on success. ## Context Retention Contexts are retained for a limited time. The current time-to-live (TTL) is approximately **30 days**, though this may vary by deployment configuration. After the TTL expires, the context and its associated data are automatically cleaned up. The exact TTL depends on deployment configuration and may change. Contact your Corti representative for the current value in your environment. ## Context and Interaction IDs If you're using contexts alongside Corti's internal interaction representation (for example, when integrating with Corti Assistant or other Corti products that use `interactionId`), note that **these two concepts are currently not linked**. * **`contextId`** (from the Agentic Framework) and **`interactionId`** (from Corti's internal systems) are separate concepts that you will need to map yourself in your application. * There is no automatic association between a Corti `interactionId` and an Agentic Framework `contextId`. **Recommended approach:** * **Use a fresh context per interaction**: When working with a Corti interaction, create a new `contextId` for that interaction. This keeps data properly scoped and isolated per interaction. * Store the mapping between your `interactionId` and `contextId`(s) in your own application state or metadata. * If you need to share data across multiple contexts within the same interaction, explicitly pass it via `DataPart` objects. We're looking into ways to make the relationship between interactions and contexts more ergonomic if this is relevant to your use case. For now, maintaining your own mapping and using one context per interaction is the recommended pattern. For more details on how context relates to other core concepts, see [Core Concepts](/agentic/v1/core-concepts). Please [contact us](mailto:help@corti.ai) if you need more information about context and memory in the Corti Agentic Framework. # Core Concepts Source: https://docs.corti.ai/agentic/v1/core-concepts Learn the fundamental building blocks of the Corti Agentic Framework This page adds Corti-specific detail on top of the core A2A concepts. We have tried to adhere as closely as possible to the intended A2A protocol specification — for the canonical definition of these concepts, see the A2A documentation on [Core Concepts and Components in A2A](https://a2a-protocol.org/latest/topics/key-concepts). The Corti Agentic Framework uses a set of core concepts that define how Corti agents, tools, and external systems interact. Understanding these building blocks is essential for developing on the Corti platform and for integrating your own systems using the A2A Protocol. ## Core Actors At Corti, these actors typically map to concrete products and integrations: * **User**: A clinician, contact-center agent, knowledge worker, or an automated service in your environment. The user initiates a request (for example, “summarize this consultation” or “triage this patient”) that requires assistance from one or more Corti-powered agents. * **A2A Client (Client Agent)**: The application that calls Corti. This is your application/server. The client initiates communication using the A2A Protocol and orchestrates how results are used in your product. * **A2A Server (Remote Agent)**: A Corti agent or agentic system that exposes an HTTP endpoint implementing the A2A Protocol. It receives requests from clients, processes tasks, and returns results or status updates. ## Fundamental Communication Elements The following elements are fundamental to A2A communication and how Corti uses them: A JSON metadata document describing an agent's identity, capabilities, endpoint, skills, and authentication requirements. **Key Purpose:** Enables Corti and your applications to discover agents and understand how to call them securely and effectively. A stateful unit of work initiated by an agent, with a unique ID and defined lifecycle. **Key Purpose:** Powers long‑running operations in Corti (for example, document generation or multi‑step workflows) and enables tracking and collaboration. A single turn of communication between a client and an agent, containing content and a role ("user" or "agent"). **Key Purpose:** Carries instructions, clinical context, user questions, and agent responses between your application, Corti Assistant, and remote agents. The fundamental content container (for example, TextPart, FilePart, DataPart) used within Messages and Artifacts. **Key Purpose:** Lets Corti exchange text, audio transcripts, structured JSON, and files in a consistent way across agents and tools. A tangible output generated by an agent during a task (for example, a document, image, or structured data). **Key Purpose:** Represents concrete Corti results such as SOAP notes, call summaries, recommendations, or other structured outputs. A server-generated identifier (`contextId`) that logically groups multiple related `Task` objects, providing context across a series of interactions. **Key Purpose:** Enables you to associate multiple tasks and agents with a single patient encounter, call, or workflow, ensuring continuity and proper scoping of shared knowledge throughout an interaction. ## Agent Cards in Corti The Agent Card is a JSON document that serves as a digital business card for initial discovery and interaction setup. It provides essential metadata about an agent. Clients parse this information to determine if an agent is suitable for a given task, how to structure requests, and how to communicate securely. Key information includes identity, service endpoint (URL), A2A capabilities, authentication requirements, and a list of skills. Within Corti, Agent Cards are how you: * Discover first‑party Corti agents and their capabilities. * Register and describe your own remote agents so Corti workflows can call them. * Declare authentication and compliance requirements up front, before any PHI or sensitive data is exchanged. ## Messages and Parts in Corti A message represents a single turn of communication between a client and an agent. It includes a role ("user" or "agent") and a unique `messageId`. It contains one or more Part objects, which are granular containers for the actual content. This design allows A2A to be modality independent and lets Corti mix clinical text, transcripts, and structured data safely in a single exchange. The primary part kinds are: * `TextPart`: Contains plain textual content, such as instructions, questions, or generated notes. * `DataPart`: Carries structured JSON data. This is useful for clinical facts, workflow parameters, EHR identifiers, or any machine‑readable information you exchange with Corti. * `FilePart`: Represents a file (for example, a PDF discharge letter or an audio recording). It can be transmitted either inline (Base64 encoded) or through a URI. It includes metadata like "name" and "mimeType". File parts are converted to data-part references—the LLM receives metadata (URL, size, filename, media type) but raw byte contents are not passed through inline. URL-based file parts work fully. ## Artifacts in Corti An artifact represents a tangible output or a concrete result generated by a remote agent during task processing. Unlike general messages, artifacts are the actual deliverables. An artifact has a unique `artifactId`, a human-readable name, and consists of one or more part objects. Artifacts are closely tied to the task lifecycle and can be streamed incrementally to the client. In Corti, artifacts typically correspond to business outputs such as: * Clinical notes (for example, SOAP notes, discharge summaries). * Extracted clinical facts or coding suggestions. * Generated documents, checklists, or other workflow‑specific artifacts. ## Agent response: Task or Message The agent response can be a new `Task` (when the agent needs to perform a long-running operation) or a `Message` (when the agent can respond immediately). On the Corti platform this means: * For quick operations (for example, a short completion or a classification), your agent often responds with a `Message`. * For longer workflows (for example, generating a full clinical document, coordinating multiple tools, or waiting on downstream systems), your agent responds with a `Task` that you can monitor and later retrieve artifacts from. ## Task States Tasks progress through a defined lifecycle. The v1 API uses the following task states: | State | Description | | ---------------- | ------------------------------------------------------------------------------- | | `submitted` | Task has been accepted by the server | | `working` | Agent is actively processing the task | | `input-required` | Agent needs additional input from the user to continue | | `completed` | Task finished successfully; artifacts are available | | `canceled` | Task was canceled | | `failed` | Task failed (for example, max iterations reached without a finishing tool call) | | `rejected` | Task was rejected (for example, insufficient credits for orchestrator runs) | | `auth-required` | Authentication is required to proceed (for example, MCP server returned 401) | | `unknown` | Task state is unknown | ## Conversation History The `message:send` endpoint does **not** return conversation history by default. The `historyLength` field in the `configuration` object defaults to **0**, meaning the response's `history` array will be empty. To include previous messages in the response, set `historyLength` to a positive value in the `configuration` object. See [Context & Memory](/agentic/v1/context-memory#message-send-configuration) for details on the `configuration` object. # Experts Source: https://docs.corti.ai/agentic/v1/experts Learn about Experts available for use with the AI Agent An **Expert** is an LLM-powered capability that an AI agent can utilize. Experts are designed to complete small, discrete tasks efficiently, enabling the Orchestrator to compose complex workflows by chaining multiple experts together. Diagram showing where experts sit in the agentic framework flow ## Expert Registry Corti maintains a **registry of experts** that includes both first-party experts built by Corti and third-party integrations. You can browse the available experts in the [Available Experts overview](/agentic/registry/overview), or discover them programmatically through the Expert Registry API endpoint `GET /agents/registry/experts`, which returns information about all available experts including their capabilities, descriptions, and configuration requirements. The registry includes experts for various healthcare use cases such as: * Clinical reference lookups * Medical coding * Document generation * Data extraction * And more ### Common registry experts A minimal sample of frequently-used experts: | Key | Purpose | | --------------------------- | ------------------------------------------------------------------- | | `memory-expert` | Recall and analyze content from large in-request contexts and files | | `coding-expert` | Assign diagnosis and procedure codes from notes | | `medical-calculator-expert` | Compute BMI, HbA1c, glucose conversions, etc. | | `drugbank-expert` | Drug information and interaction lookups | | `posos-expert` | Medication guidance and prescribing decision support | | `pubmed-expert` | PubMed literature search and abstracts | | `clinical-trials-expert` | Search clinical trial registries | | `web-search-expert` | Search and retrieve up-to-date web content | | `interviewing-expert` | Drive structured questionnaire interviews | See the [Available Experts overview](/agentic/registry/overview) for the full list, including all coding-expert variants and per-expert configuration details. ## Bring Your Own Expert You can create custom experts by exposing an MCP (Model Context Protocol) server. When you register your MCP server, Corti wraps it in a custom LLM agent with a system prompt that you can control. This allows you to: * Integrate your own tools and data sources * Create domain-specific experts tailored to your workflows * Maintain control over the expert's behavior through custom system prompts * Leverage Corti's orchestration and memory management while using your own tools ### Expert Configuration When creating a custom expert, you provide configuration that includes: * **Expert metadata**: ID, name, and description * **System prompt**: Controls how the LLM agent behaves and reasons about tasks * **MCP server configuration**: Details about your MCP server including transport type, authorization, and connection details (see [MCP Authentication](/agentic/v1/mcp-authentication) for details) ```json Expert Configuration expandable theme={null} { "name": "My Agent", "description": "An agent with a custom ECG interpreter expert.", "experts": [ { "type": "new", "name": "ecg_interpreter", "description": "Interprets 12 lead ECGs.", "systemPrompt": "You are an expert ECG interpreter.", "mcpServers": [ { "name": "ECG API Svc", "transportType": "streamable_http", "authorizationType": "none", "url": "https://api.ecg.com/x" } ] } ] } ``` The spec-correct value for inline expert creation is "type": "new". The server also accepts "type": "expert" as an alias, but "new" is the canonical value. ### MCP Server Requirements Your MCP server must: * Implement the [Model Context Protocol](https://modelcontextprotocol.io/) specification * Expose tools via the standard MCP `tools/list` and `tools/call` endpoints * Handle authentication Once registered, your custom expert becomes available to the Orchestrator and can be used alongside Corti's built-in experts in multi-expert workflows. ## Multi-Agent Composition This feature is coming soon. We're working on exposing A2A (Agent-to-Agent) endpoints that will allow you to attach multiple agents together, enabling more sophisticated multi-agent workflows. This will provide: * Direct agent-to-agent communication using the A2A protocol * Composition of complex workflows across multiple agents * Fine-grained control over agent interactions and data flow For now, the Orchestrator handles expert composition automatically. When A2A endpoints are available, you'll be able to build custom agent networks while still leveraging Corti's orchestration capabilities. ## Direct Expert Calls This feature is coming soon. We're also working on enabling direct calls to experts, allowing you to use them directly in your workflows rather than only through agents. This will provide: * Direct API access to individual experts * Integration of experts into custom workflows * More flexible composition patterns beyond agent-based orchestration **While AI chat is a useful mechanism, it's not the only option!** The Corti Agentic Framework is API-first, enabling synchronous or async usage across a range of modalities: scheduled batch jobs, clinical event triggers, UI widgets, and direct EHR system calls. [Let us know](mailto:help@corti.ai) what types of use cases you're exploring, from doctor-facing chat bots to system-facing automation backends. Please [contact us](mailto:help@corti.ai) if you need more information about Experts or creating custom experts in the Corti Agentic Framework. # FAQ Source: https://docs.corti.ai/agentic/v1/faq Frequently asked questions about the Corti Agentic Framework Common questions and answers to help you get the most out of the Corti Agentic Framework and the underlying A2A-based APIs. The **Orchestrator** is the central coordinator of the Corti Agentic Framework. It receives user requests, reasons about what needs to be done, and delegates work to specialized Experts. The Orchestrator doesn't perform specialized work itself—instead, it plans, selects appropriate Experts, and coordinates their activities to accomplish complex workflows. An **Expert** is a specialized sub-agent that performs domain-specific tasks. Experts are designed to complete small, discrete tasks efficiently, such as clinical reference lookups, medical coding, or document generation. The Orchestrator composes complex workflows by chaining multiple Experts together. In summary: the Orchestrator coordinates and delegates; Experts execute specialized work. For more details, see [Orchestrator](/agentic/v1/orchestrator) and [Experts](/agentic/v1/experts). **A2A (Agent-to-Agent)** is the protocol used for accessing the Corti API and for communication between agents. It's the standard protocol that your application uses to interact with Corti agents, send messages, receive tasks, and manage the agent lifecycle. A2A enables secure, framework-agnostic communication between autonomous AI agents. **MCP (Model Context Protocol)** is the way to connect additional Experts. When you create custom Experts by exposing an MCP server, Corti wraps it in a custom LLM agent. MCP handles agent-to-tool interactions, allowing Experts to interact with external systems and resources. In the Corti Agentic Framework: A2A handles agent-to-agent communication (including your API calls to Corti), while MCP handles agent-to-tool interactions for Expert integrations. For more information, see [A2A Protocol](/agentic/v1/a2a-protocol) and [MCP Protocol](/agentic/v1/mcp-protocol). The Corti agent can return either a **Task** or a **Message** depending on the processing. If the agent completes the work synchronously, it returns a `Message`. If it creates a stateful unit of work, it returns a `Task` with a unique ID and lifecycle that you can monitor. Tasks are used for: * Long-running operations (for example, generating a full clinical document) * Multi-step workflows that coordinate multiple Experts * Operations that may need to wait on downstream systems * Any work that benefits from tracking and monitoring Messages (with immediate responses) are returned for quick operations like simple classifications or completions that can be resolved immediately without asynchronous processing. For task states and their meanings, see [Core Concepts](/agentic/v1/core-concepts#task-states). Use **`TextPart`** for messages that will be directly exposed to the Orchestrator and the LLM. TextPart content is immediately available for reasoning and response generation. Use **`DataPart`** for structured JSON data that will be stored in memory first and accessed through more indirect manipulation. DataPart content is automatically indexed and stored in the context's memory, enabling semantic retrieval when needed. DataPart is JSON-only and is useful for structured data like patient records, clinical facts, workflow parameters, or EHR identifiers. You can combine both in a single message: use TextPart for the primary instruction or question, and DataPart to provide structured context that will be semantically retrieved when relevant. For more details, see [Core Concepts](/agentic/v1/core-concepts) and [Context & Memory](/agentic/v1/context-memory). Both `Message` and `Artifact` use the same underlying `Part` primitives, but they serve different roles: * **`Message` (with `role: "agent"`)** * Represents a **single turn of communication** from the agent to the client. * Best for ephemeral conversational output, intermediate reasoning, clarifications, or status updates. * Typically tied to a particular task step but not necessarily considered a durable business deliverable. * **`Artifact`** * Represents a **tangible, durable output** of a task (for example, a SOAP note, coding suggestions, a structured fact bundle, or a generated document). * Has its own `artifactId`, name/metadata, and lifecycle; can be streamed, versioned, and reused by later tasks. * Is what downstream systems, UIs, or audits usually consume as the final result. A useful mental model is: **Messages are how agents "talk"; Artifacts are what they "produce".** You might see several `agent` messages during a task (status, intermediate commentary), but only a small number of artifacts that represent the completed work. The Corti Agentic Framework provides automatic memory management through contexts. When you send your first message without a `contextId`, the server generates one and returns it in the response. You can also supply your own `contextId` to continue an existing conversation—include that `contextId` in subsequent messages to maintain conversation continuity. You can also pass additional context in each request using `DataPart` objects to include structured data, summaries, or other specific context alongside the automatic memory. For comprehensive guidance on context and memory management, see [Context & Memory](/agentic/v1/context-memory). No, you cannot share data between different contexts. Contexts provide data isolation—data cannot leak across users. Each `contextId` creates a conversation scope where messages, tasks, and artifacts are isolated from other users' contexts. Contexts are scoped per user, not per agent. Any agent belonging to the same authenticated user can read any of that user's contexts. If you need to restrict which agents can access which contexts, enforce that in your application layer. If you need to share information across contexts, you must explicitly pass it via `DataPart` objects in your messages—there is no automatic data sharing between contexts. For more details, see [Context & Memory](/agentic/v1/context-memory). The current time-to-live (TTL) for context memory is approximately **30 days**, though this may vary by deployment configuration. After this period, the context and its associated memory are automatically cleaned up. Contact your Corti representative for the current value in your environment. For more information about context lifecycle and memory management, see [Context & Memory](/agentic/v1/context-memory). The Orchestrator analyzes incoming requests and uses reasoning to determine which Expert(s) are needed to fulfill the task. It considers the nature of the request, the available Experts, and their capabilities. You can control Expert selection by writing additional system prompts, both in the Orchestrator configuration and in individual Expert configurations. System prompts guide how the Orchestrator reasons about task decomposition and Expert selection, and how Experts interpret and execute their assigned work. The Orchestrator can compose multiple Experts together, calling them in sequence or parallel as needed to accomplish complex workflows. For more information, see [Orchestrator](/agentic/v1/orchestrator) and [Experts](/agentic/v1/experts). Please [contact us](mailto:help@corti.ai) if you need more information about the Corti Agentic Framework. # MCP Authentication Source: https://docs.corti.ai/agentic/v1/mcp-authentication Learn how to authenticate MCP server calls in the Agentic Framework This document covers how to register MCP servers and how to pass authentication data in A2A message DataParts so MCP tools can be registered. ## MCP server registration Each MCP server record includes an `authorizationType` field that controls how the Agent API authenticates when registering tools and calling that server. DataParts provide credentials at runtime but do not change the configured authorization type. ### authorizationType = none **Meaning**: MCP server is callable without authentication. **Behavior**: No Authorization header or OAuth flow is used. Auth DataParts for this server are ignored. **Registration example:** ```json theme={null} { "name": "medical-calculator", "transportType": "streamable_http", "authorizationType": "none", "url": "http://mcp-server-medical-calculator.agents:80/mcp" } ``` ### authorizationType = inherit **Meaning**: Reuse the incoming Agent API bearer token. **Behavior**: Uses the token from the request `Authorization` header. The API request must include a valid bearer token or the request fails with `missing_inherited_token`. The inherit type is also used internally by the Memory MCP server. Internal MCP servers are filtered from API responses, so you won't see them when listing an agent's MCP servers. This is why inherit exists as an option even if you don't use it directly. **DataPart override**: If a token DataPart is supplied for this server name, that token is used instead of the inherited token. **Registration example:** ```json theme={null} { "name": "medical-coding", "transportType": "streamable_http", "authorizationType": "inherit", "url": "http://mcp-server-medical-coding.agents/mcp" } ``` ### authorizationType = bearer **Meaning**: MCP server expects a bearer token. **Behavior**: Uses the token from a matching DataPart (type=token). If the token is missing or invalid, the MCP server typically returns 401 and the task becomes `auth-required`. **Registration example:** ```json theme={null} { "name": "medical-coding", "transportType": "streamable_http", "authorizationType": "bearer", "url": "http://mcp-server-medical-coding.agents/mcp" } ``` ### authorizationType = oauth2.0 **Meaning**: MCP server expects OAuth client credentials. **Behavior**: Uses `client_id` and `client_secret` from a matching DataPart (type=credentials) and performs a client\_credentials flow. Supported for `streamable_http` transport only; `sse` is not supported. **Registration example:** ```json theme={null} { "name": "medical-coding", "transportType": "streamable_http", "authorizationType": "oauth2.0", "url": "http://mcp-server-medical-coding.agents/mcp" } ``` ## Authorization via message DataParts Authentication is supplied as an A2A DataPart with `kind: "data"` and the auth payload under `data`. The following fields are used: * `type`: `token` or `credentials` (case-insensitive) * `mcp_name`: MCP server name as registered (case-sensitive, trimmed) * `token`: required when `type=token` * `client_id` and `client_secret`: required when `type=credentials` ### Token example (for authorizationType=bearer or inherit override) ```json theme={null} { "kind": "data", "data": { "type": "token", "mcp_name": "crm-mcp", "token": "eyJhbGciOi..." } } ``` ### Credentials example (for authorizationType=oauth2.0) ```json theme={null} { "kind": "data", "data": { "type": "credentials", "mcp_name": "crm-mcp", "client_id": "abc", "client_secret": "def" } } ``` ## Processing rules and errors * `type` is normalized to lowercase; only `token` and `credentials` are extracted * DataParts do not change the MCP server `authorizationType`—make sure the DataPart type matches the server configuration * Unknown or invalid auth DataParts are left in the message as normal parts * `mcp_name` must be unique per message; duplicates return `mcp_auth_duplicate_name` * Missing fields return: * `mcp_auth_missing_name` * `mcp_auth_missing_token` * `mcp_auth_missing_credentials` * If `mcp_name` does not match any configured server, the DataPart is ignored ## When DataParts are used * MCP tools are registered when a new thread is created (the first message). Include auth DataParts on that first message * Later messages on the same thread do not re-register tools, so auth DataParts will be ignored for MCP registration * In the API flow, extracted auth DataParts are removed from the message before it is stored or sent to reasoning For more information about the A2A protocol and DataParts, see [A2A Protocol](/agentic/v1/a2a-protocol). For general information about MCP, see [MCP Protocol](/agentic/v1/mcp-protocol). Please [contact us](mailto:help@corti.ai) if you need more information about the Corti Agentic Framework. # MCP Protocol (Model Context Protocol) Source: https://docs.corti.ai/agentic/v1/mcp-protocol Learn about the Model Context Protocol for tool integration The Model Context Protocol (MCP) provides a standardized way for agents to interact with tools and external resources in the Corti Agentic Framework. ## What is MCP? The Model Context Protocol (MCP) is a standardized protocol for exposing tools and resources to AI agents. ## When to Use MCP MCP is ideal when you need to expose tools, APIs, or data sources to agents in a standardized way. ## How MCP Complements A2A While A2A handles agent-to-agent communication, MCP handles agent-to-tool interactions, creating a complete communication framework. ## MCP in the Agentic Ecosystem MCP plays a crucial role in the agentic ecosystem by enabling agents to interact with external systems and resources. MCP handles tool execution, while A2A handles agent-to-agent communication. See [A2A Protocol](/agentic/v1/a2a-protocol) for more on inter-agent communication. For information about authenticating MCP server calls, see [MCP Authentication](/agentic/v1/mcp-authentication). For the complete MCP specification, visit [modelcontextprotocol.io](https://modelcontextprotocol.io/specification/2025-06-18). Please [contact us](mailto:help@corti.ai) if you need more information about the Corti Agentic Framework. # Orchestrator Source: https://docs.corti.ai/agentic/v1/orchestrator Learn about the Orchestration Agent at the center of the Agentic Framework The **Orchestrator** is the central intelligence layer of the Corti Agentic Framework. It serves as the primary interface between users and the multi-agent system, coordinating the flow of conversations and tasks. Diagram showing guardrails in the agentic framework ## What the Orchestrator Does The Orchestrator reasons about incoming requests and determines how to fulfill them by coordinating with specialized [Experts](/agentic/v1/experts). Its core responsibilities include: * **Reasoning and planning**: Analyzes user requests and determines the necessary steps to complete them * **Expert selection**: Decides which Expert(s) to call, in what order, and with what data * **Task decomposition**: Breaks complex requests into discrete tasks that can be handled by individual Experts * **Response generation**: Aggregates results from Experts and typically generates the final response to the user * **Context management**: Has full access to the [context](/agentic/v1/context-memory), while Experts typically only have scoped access to relevant portions * **Safety enforcement**: Enforces guardrails, type validation, and policy-driven constraints to ensure safe operation in clinical environments The Orchestrator does not perform specialized work itself—instead, it delegates to appropriate Experts and coordinates their activities to accomplish complex workflows. ## Safety Mechanisms ### Max Iterations The agent loop runs a maximum of 5 iterations by default (configurable per expert). After hitting the limit, it enters `forceFinish` mode, which makes up to 2 additional LLM calls restricted to terminal tools (`complete_tool`, `input_required_tool`, `task_failure_tool`). If those also fail to produce a terminal result, the task fails with "Max attempts reached without a finishing tool call." ### Credit Pre-flight Check For orchestrator runs, the agent performs a pre-flight credit check before processing. If the metering service reports insufficient credits, the task is immediately rejected with state `rejected` and a message about insufficient credits. ### Expert Invocation Progress When the orchestrator calls an expert tool, it emits a `Working` status update with the text "Calling expert: \". These intermediate status messages are part of the task's history but are not user-facing. If you are polling for task status, you may see these intermediate working states. *** For more information about how the Orchestrator fits into the overall architecture, see [Architecture](/agentic/v1/architecture). To understand how context and memory work, see [Context & Memory](/agentic/v1/context-memory). Please [contact us](mailto:help@corti.ai) if you need more information about the Orchestrator in the Corti Agentic Framework. # Overview of the Corti Agentic Framework Source: https://docs.corti.ai/agentic/v1/overview AI for every healthcare app The Corti Agentic Framework is a modular artificial intelligence system for software developers to build advanced AI agents that perform high-quality clinical and operational tasks, without having to spend months on complex architecture work. The AI Agent is designed to support use cases across the healthcare spectrum, from chat-based assistants for doctors to automating EHR data entry and powering clinical decision support workflows. ## What Problems It Solves Modern LLMs are powerful, but on their own they are insufficient and unsafe for clinical use. The Corti Agent Platform addresses two fundamental gaps: ### 1. LLMs Do Not Have Reliable Access to Clinical Data LLMs cannot be trusted to rely on internal knowledge alone. In healthcare, responses must be grounded in clinically validated reference sources, real-time patient and system data, and customer-owned systems and APIs. Without access to these sources at runtime, models are forced to infer or guess, which is unacceptable in clinical settings. The Corti Agentic Framework addresses this by enabling agents to retrieve information directly from trusted external tools as part of their reasoning process. Instead of hallucinating answers, agents are designed to look things up, verify context, and base their outputs on authoritative data. ### 2. LLMs Cannot Safely Act on the World Clinical workflows require more than generating text. They involve interacting with real systems: querying EHRs, drafting and updating documentation, preparing prescriptions, and triggering downstream processes. The framework provides a controlled execution layer that allows agents to plan actions, invoke tools, and coordinate multi-step workflows while remaining within clearly defined safety boundaries. Where necessary, agents can pause execution, request human approval, and resume only once explicit consent is given. This ensures that automation enhances clinical workflows without bypassing governance or control. *** ## What You Can Build With It Using the Corti Agent Platform, teams can build: * **Clinician-facing assistants** * Documentation editing * Guideline and reference lookup * Coding and administrative support * **Programmatic agent endpoints** * Embedded into existing clinical software * Triggered by events, APIs, or workflows * **Customer-embedded agents** * Customers bring their own tools and systems * Agents combine Corti, third-party, and customer capabilities All of these share the same underlying agent runtime, safety model, and orchestration layer. *** ## Built for Healthcare by Design Healthcare is not a general-purpose domain, and this platform reflects that reality. **Key design principles include:** Typed inputs and outputs, explicit tool schemas, and guardrails around action-taking ensure safe operation in clinical environments. Every decision and tool call is observable with replayable traces and structured logs for transparency, compliance, and quality assurance. Fine-tuned reasoning layers optimized for healthcare language, workflows, and compliance needs. Corti Agentic Framework uses a state-of-the-art multi-agent architecture to enable greater scale, accuracy, and resilience in AI-driven workflows. Maintain persistent, context-aware conversations and manage multiple active contexts (threads) without losing information throughout the session. Access a library of prebuilt Experts: specialized agents that connect to data sources, tools, and services to execute clinical and operational tasks. Plug directly into EHRs, clinical decision support systems, and medical knowledge bases with minimal setup. Pass relevant context with each query, including structured data formats like FHIR resources, enabling Experts to work with rich, domain-specific information. *** ## Who It’s For The Corti Agent Platform is built for teams working on healthcare software. It is intended for: * **Healthcare software companies** embedding intelligent automation directly into their products * **Enterprise customers** building internal, AI-powered clinical workflows * **Advanced engineering teams** that need flexibility, control, and strong safety guarantees without building bespoke agent infrastructure from scratch The platform is not limited to simple prompt-based chatbots. It is designed to make it easy to go from demo to **production-grade clinical AI systems** that operate safely in real-world healthcare environments. *** ## Agents vs. Workflows Understanding the difference between agents and workflows helps you choose the right approach for your use case: **Agents** are autonomous systems that can think, reason, and adapt to new situations. They use AI to understand context, make decisions dynamically, and take actions based on the task at hand—even when encountering scenarios they haven't seen before. Like a chef who can create a meal based on what's available, agents excel at handling unpredictable, open-ended tasks that require flexibility and judgment. **Workflows** are structured, step-by-step processes that follow predefined paths. They execute tasks in a fixed order, like following a recipe or checklist. Workflows are ideal for repeatable processes that require consistency and compliance, such as automated approval processes or scheduled maintenance tasks. For workflow-oriented needs, you can leverage our toolkit of other APIs to orchestrate well-defined, repeatable flows throughout your solution. In the Corti Agentic Framework, agents leverage the Orchestrator to compose experts dynamically, adapting their approach based on the situation. Workflows, on the other hand, provide deterministic execution paths for tasks with well-defined steps and requirements—supported by our robust library of workflow APIs and integrations. ## V1 API Notes ### Usage Endpoint The v1 API provides a usage endpoint for per-agent metrics: ``` GET /agents/{id}/usage?from=YYYY-MM-DD&to=YYYY-MM-DD ``` Returns per-day invocation metrics and unique context counts. The `from` and `to` query parameters accept dates in `YYYY-MM-DD` format, defaulting to a 30-day window. ### Agent Type Field The v1 API accepts an optional `agentType` field on agent creation with enum values `expert`, `orchestrator`, and `interviewing-expert`. However, this field is **silently ignored** — the server always creates an orchestrator-type agent regardless of the value provided. Specialization comes from the system prompt and connected experts, not from `agentType`. This field is removed in v2. ### Description Field The `description` field is required on agent creation per the v1 OpenAPI spec. A missing `description` key triggers a validation error (422), but an empty string (`"description": ""`) is accepted. In v2, `description` is optional. Please [contact us](mailto:help@corti.ai) if you need more information about the Corti Agentic Framework. # Quickstart (v1) Source: https://docs.corti.ai/agentic/v1/quickstart Get started with the Corti Agentic Framework (v1 archived documentation) This is archived v1 documentation. For the current v2 docs, see the [Agentic Framework overview](/agentic/overview) and [v2 quickstart](/agentic/quickstart). For migrating from v1 to v2, see the [migration guide](/agentic/guides/migrate-v1-to-v2). This guide will walk you through setting up your first agent and getting it running end-to-end. After completing this quickstart, you'll have a working agent and know where to go next based on your use case. ### Prerequisites * API access credentials * Development environment set up * Basic understanding of REST APIs Start by creating a project in the Corti Console. This gives you a workspace and access to manage your clients and credentials. If you haven't set up authentication before, follow the Creating Clients and authentication quickstart guides. Use the Corti Agentic API to create your first agent. You'll need an access token (obtained using your client credentials) and your tenant name. ```bash theme={null} # Replace these with your values ENVIRONMENT="" TENANT="" TOKEN="" curl -X POST "https://api.${ENVIRONMENT}.corti.app/agents" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Tenant-Name: ${TENANT}" \ -H "Content-Type: application/json" \ -d '{ "name": "My First Agent", "description": "A simple agent to get started with the Corti Agentic Framework" }' ``` Use your stored credentials to authenticate, then run your agent end-to-end and verify it processes input and returns the expected outputs. ```bash theme={null} # Replace these with your values AGENT_ID="" ENVIRONMENT="" TENANT="" TOKEN="" curl -X POST "https://api.${ENVIRONMENT}.corti.app/agents/${AGENT_ID}/v1/message:send" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Tenant-Name: ${TENANT}" \ -H "Content-Type: application/json" \ -d '{ "message": { "role": "user", "parts": [{ "kind": "text", "text": "Hello there. This is my first message." }], "messageId": "550e8400-e29b-41d4-a716-446655440000", "kind": "message" } }' ``` ### Next Steps Depending on your use case: * **Building custom agents**: See [Core Concepts](/agentic/v1/core-concepts) * **Integrating with existing systems**: See [SDKs & Integrations](/agentic/v1/sdks-integrations) * **Understanding the architecture**: See [Architecture Overview](/agentic/v1/architecture) * **Working with protocols**: See [A2A Protocol](/agentic/v1/a2a-protocol) and [MCP Protocol](/agentic/v1/mcp-protocol) Please [contact us](mailto:help@corti.ai) if you need more information about the Corti Agentic Framework. # SDKs & Integrations Source: https://docs.corti.ai/agentic/v1/sdks-integrations Official SDKs and integration options for the Corti Agentic Framework The Corti Agentic Framework provides official SDKs and supports community integrations to help you build quickly. ## Official Corti SDKs Official Corti Agentic SDK for Node and browser environments.
JavaScript SDK docs →
Official Corti Agentic SDK for .NET applications.
C# .NET SDK docs →
Adapter for integrating Corti A2A agents with the Vercel AI SDK. Use `useChat` and streaming patterns to build chat UIs.
AI SDK Adapter docs →
## Official A2A Project SDKs These are community/open-source SDKs maintained by the A2A project. Check each repository for current maturity and compatibility status. Build A2A-compliant agents and servers in Python.
a2a-python →
Official JavaScript/TypeScript SDK for A2A.
a2a-js →
Build A2A-compliant agents and services in Java.
a2a-java →
Implement A2A agents and servers in Go.
a2a-go →
Build A2A-compatible agents in .NET ecosystems.
a2a-dotnet →
## Other libraries * **shadcn/ui component library for chatbots**: [`ai-elements` on npm](https://www.npmjs.com/package/ai-elements) * **A2A Inspector**: [a2a-inspector on GitHub](https://github.com/a2aproject/a2a-inspector) * **Awesome A2A**: [awesome-a2a on GitHub](https://github.com/ai-boost/awesome-a2a) # Authenticate user and get access token Source: https://docs.corti.ai/api-reference/admin/auth/authenticate-user-and-get-access-token /api-reference/admin/admin-openapi.yml post /auth/token Authenticate using email and password to receive an access token. This `Admin API` is separate from the `Corti API` used for speech recognition, text generation, and agentic workflows:
  • Authentication and scope for the `Admin API` uses email-and-password to obtain a bearer token via `/auth/token`. This token is only used for API administration.
Please [contact us](https://help.corti.app) if you have interest in this functionality or further questions.
# Create a new customer Source: https://docs.corti.ai/api-reference/admin/customers/create-a-new-customer /api-reference/admin/admin-openapi.yml post /projects/{projectId}/customers # Delete a customer Source: https://docs.corti.ai/api-reference/admin/customers/delete-a-customer /api-reference/admin/admin-openapi.yml delete /projects/{projectId}/customers/{customerId} # Get quotas for a customer Source: https://docs.corti.ai/api-reference/admin/customers/get-quotas-for-a-customer /api-reference/admin/admin-openapi.yml get /projects/{projectId}/customers/{customerId}/quotas # List customers for a project Source: https://docs.corti.ai/api-reference/admin/customers/list-customers-for-a-project /api-reference/admin/admin-openapi.yml get /projects/{projectId}/customers # Update a customer Source: https://docs.corti.ai/api-reference/admin/customers/update-a-customer /api-reference/admin/admin-openapi.yml patch /projects/{projectId}/customers/{customerId} Update specific fields of a customer. Only provided fields will be updated. # Get quotas for all tenants within a project Source: https://docs.corti.ai/api-reference/admin/projects/get-quotas-for-all-tenants-within-a-project /api-reference/admin/admin-openapi.yml get /projects/{projectId}/quotas # Create a new user and add it to the customer Source: https://docs.corti.ai/api-reference/admin/users/create-a-new-user-and-add-it-to-the-customer /api-reference/admin/admin-openapi.yml post /projects/{projectId}/customers/{customerId}/users # Delete a user Source: https://docs.corti.ai/api-reference/admin/users/delete-a-user /api-reference/admin/admin-openapi.yml delete /projects/{projectId}/customers/{customerId}/users/{userId}/consumption # Get usage consumption for a user within a time window Source: https://docs.corti.ai/api-reference/admin/users/get-usage-consumption-for-a-user-within-a-time-window /api-reference/admin/admin-openapi.yml get /projects/{projectId}/customers/{customerId}/users/{userId}/consumption # List users for a customer Source: https://docs.corti.ai/api-reference/admin/users/list-users-for-a-customer /api-reference/admin/admin-openapi.yml get /projects/{projectId}/customers/{customerId}/users # Update a user Source: https://docs.corti.ai/api-reference/admin/users/update-a-user /api-reference/admin/admin-openapi.yml patch /projects/{projectId}/customers/{customerId}/users/{userId} # Generate Codes Source: https://docs.corti.ai/api-reference/codes-legacy/generate-codes /api-reference/auto-generated-openapi.yml post /interactions/{id}/codes/ `Limited Access - Contact us for more information`

Generate codes within the context of an interaction.
This endpoint is only accessible within specific customer tenants. It is not available in the public API.

For stateless code prediction based on input text string or documentId, please refer to the [Predict Codes](/api-reference/codes/predict-codes) API, or [contact us](https://help.corti.app) for more information.
# List Codes Source: https://docs.corti.ai/api-reference/codes-legacy/list-codes /api-reference/auto-generated-openapi.yml get /interactions/{id}/codes/ `Limited Access - Contact us for more information`

List predicted codes within the context of an interaction.
This endpoint is only accessible within specific customer tenants. It is not available in the public API.

For stateless code prediction based on input text string or documentId, please refer to the [Predict Codes](/api-reference/codes/predict-codes) API, or [contact us](https://help.corti.app) for more information.
# Select Codes Source: https://docs.corti.ai/api-reference/codes-legacy/select-codes /api-reference/auto-generated-openapi.yml put /interactions/{id}/codes/ `Limited Access - Contact us for more information`

Select predicted codes within the context of an interaction.
This endpoint is only accessible within specific customer tenants. It is not available in the public API.

For stateless code prediction based on input text string or documentId, please refer to the [Predict Codes](/api-reference/codes/predict-codes) API, or [contact us](https://help.corti.app) for more information.
# Predict Codes Source: https://docs.corti.ai/api-reference/codes/predict-codes /api-reference/auto-generated-openapi.yml post /tools/coding/ Predict medical codes from provided context.
This is a stateless endpoint, designed to predict ICD-10-CM, ICD-10-PCS, ICD-10 (international), ICD-10-UK, CIM-10-FR, ICD-10-GM, OPCS-4, OPS, CCAM and CPT codes based on input text string or documentId.

More than one code system may be defined in a single request.

Code prediction requests have two possible values for context:
- `text`: One set of code prediction results will be returned based on all input text defined.
- `documentId`: Code prediction will be based on that defined document only.

The response includes two sets of results:
- `Codes`: Codes predicted by the model.
- `Candidates`: Lower-confidence codes the model considered potentially relevant but excluded from the predicted set.

All predicted code results are based on input context defined in the request only (not other external data or assets associated with an interaction).
# Create message (Anthropic) Source: https://docs.corti.ai/api-reference/corti-models/anthropic/create-message-anthropic /api-reference/corti-models/corti-models-openapi.yml post /messages Sends a structured list of input messages and generates the next message in the conversation. This is the Anthropic Messages API. Reasoning behavior mirrors the Chat Completions API: `corti-s1` and `corti-s1-mini` generate chain-of-thought reasoning, while `-instant` variants do not. However, the Anthropic Messages API response does not expose reasoning as a separate field — content is returned as text blocks only. # Create chat completion Source: https://docs.corti.ai/api-reference/corti-models/chat/create-chat-completion /api-reference/corti-models/corti-models-openapi.yml post /chat/completions Creates a model response for the given conversation. This is the OpenAI Chat Completions API. Some models return chain-of-thought reasoning in the `reasoning` field of the response message. The `-instant` model variants do not produce reasoning on this endpoint. # Create completion Source: https://docs.corti.ai/api-reference/corti-models/completions/create-completion /api-reference/corti-models/corti-models-openapi.yml post /completions Creates a text completion for the given prompt. Supports streaming via SSE. # Create embeddings Source: https://docs.corti.ai/api-reference/corti-models/embeddings/create-embeddings /api-reference/corti-models/corti-models-openapi.yml post /embeddings Creates embeddings for the given input text. # List models Source: https://docs.corti.ai/api-reference/corti-models/models/list-models /api-reference/corti-models/corti-models-openapi.yml get /models Returns the list of available models. # Create response Source: https://docs.corti.ai/api-reference/corti-models/responses/create-response /api-reference/corti-models/corti-models-openapi.yml post /responses Creates a model response for the given input. This is the OpenAI Responses API. Supports `instructions` (system message equivalent), streaming, tool calling, JSON mode, and multi-turn conversations. When present, reasoning is returned as a structured output item with `type: "reasoning"`, separate from the message output. # Delete Document Source: https://docs.corti.ai/api-reference/documents-classic/delete-document /api-reference/auto-generated-openapi.yml delete /interactions/{id}/documents/{documentId} # Generate Document Source: https://docs.corti.ai/api-reference/documents-classic/generate-document /api-reference/auto-generated-openapi.yml post /interactions/{id}/documents/ This endpoint offers different ways to generate a document. Find guides to document generation [here](/textgen/documents-standard). # Get Document Source: https://docs.corti.ai/api-reference/documents-classic/get-document /api-reference/auto-generated-openapi.yml get /interactions/{id}/documents/{documentId} Get Document. # List Documents Source: https://docs.corti.ai/api-reference/documents-classic/list-documents /api-reference/auto-generated-openapi.yml get /interactions/{id}/documents/ List Documents # Update Document Source: https://docs.corti.ai/api-reference/documents-classic/update-document /api-reference/auto-generated-openapi.yml patch /interactions/{id}/documents/{documentId} # Errors Source: https://docs.corti.ai/api-reference/errors Error messages and solutions from Corti's API Corti uses standard HTTP status codes. `4xx` errors indicate a problem with the request; `5xx` errors indicate a problem on Corti's side. Error responses are in JSON and differ between the SDKs and the REST API. The tabs below cover each. Some Corti products handle errors and authentication differently than what is described below: * **Admin API** — Separate from the `Corti API` used for speech to text, text generation, and agentic workflows. See the [Administration API](/about/admin-api) reference for details. Please [contact us](mailto:help@corti.ai) if you have interest in this functionality or further questions. * **Agents API** — The JSON format of the error response is different. See the [Agents API](/agentic/overview) docs for more info. * **Embedded Assistant** — Authentication is handled differently. See the [Embedded Assistant Authentication](https://docs.corti.ai/assistant/authentication) docs for those methods.
The SDK throws typed error classes on any non-2xx response or internal failure. Each class exposes a different set of attributes. See [Error Handling](/sdk/js/overview#error-handling) for full details. ## Error Classes | Class | Thrown when | Attributes | | --------------- | ----------------------------------------------------------------- | ---------------------------------------------- | | `CortiError` | API returned a non-2xx HTTP response (4xx / 5xx) | `message`, `statusCode`, `body`, `rawResponse` | | `CortiSDKError` | SDK infrastructure error (e.g. localStorage unavailable) | `message`, `code`, `cause` | | `ParseError` | Input validation failed (e.g. missing PKCE verifier, invalid JWT) | `message`, `errors` | | `JsonError` | Response body could not be parsed as JSON | `message`, `errors` | ## HTTP Status Codes | Error code | When it occurs | How to resolve | | ------------- | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------ | | `bad_request` | The request body or query parameters are malformed or missing required fields. | Double check the fields submitted to be sure of correct format and necessary fields. | Token endpoint errors are returned as JSON in the response body. Corti API errors are returned in the `WWW-Authenticate` response header with no body. The header is not accessible from the exception directly; use `.WithRawResponse()` on the request if you need it. | Error code | When it occurs | How to resolve | | --------------------- | -------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- | | `invalid_client` | Client authentication failed. There is a problem with the client ID. | Verify your `client_id` is correct. | | `unauthorized_client` | Client authentication failed. There is a problem with the client secret. | Verify your `client_secret` is correct. | | `invalid_token` | The bearer token is invalid or has expired. Check the `WWW-Authenticate` header for `error_description`. | Re-authenticate to obtain a fresh token and retry the request. | | When it occurs | How to resolve | | ------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- | | Accessing a resource (interaction, recording, transcript, document) that belongs to a different client. | Verify the resource ID was created by the authenticated client. | | Error | When it occurs | How to resolve | | ------------------------------------------- | -------------------------------------- | ----------------------------------------------------------------------------------------------- | | `Invalid '{resource_name}' '{resource_id}'` | The requested resource does not exist. | Verify the ID and parent resource ID is correct and was created under the authenticated tenant. | Attempting to access a resource that belongs to a different OAuth client also returns a 404, not a 403. This is intentional. Returning a 403 would confirm that the resource exists, which is a security risk. You have exceeded the rate limit for the API. The SDK automatically retries with exponential backoff. The default retry limit is 2. Override per request: ```js theme={null} const response = await client.interactions.create(..., { maxRetries: 0, // disable retries for this request }); ``` If the error persists, reduce request frequency or contact [support](mailto:help@corti.ai) to discuss your rate limit requirements. An unexpected error occurred on the Corti side. Double check the request format and retry the request. If the issue persists, contact [support](mailto:help@corti.ai). The service is temporarily unavailable. Retry with exponential backoff. Monitor the [status page](https://status.corti.ai). ## WebSocket APIs WebSocket errors from `/streams` and `/transcribe` are surfaced as plain `Error` objects whose `message` is the server status code (e.g. `CONFIG_DENIED`, `CONFIG_MISSING`, `CONFIG_TIMEOUT`). Every `CONFIG_*` server message also carries a `reason` field with human-readable details behind the failure. Whether you can read it depends on the mode you connected in: * **Default mode** (`client.stream.connect({ id, configuration })` / `client.transcribe.connect({ configuration })`): the SDK handles the handshake-phase and `CONFIG_*` messages internally. It rejects `connect()` with just the bare code. The `reason` is not exposed. * **`awaitConfiguration: false`** or **[manual mode](/sdk/js/websockets#connecting-without-configuration)**: the raw message, including `msg.reason`, flows through `socket.on('message', …)`. > You sent a config message, but it's invalid. | `reason` from server | Likely cause | Fix | | ----------------------------------------------------------------------------------- | ------------------------------------------------------------- | ------------------------------------------------------------------------------- | | `invalid language code: must be a valid BCP 47 language tag` | `transcription.primaryLanguage` is not a supported code. | Use a [supported language code](https://docs.corti.ai/stt/languages). | | `invalid output locale: language should not be empty` | `mode.type: "facts"` without `mode.outputLocale`. | Add `outputLocale`. | | `invalid output locale: invalid language code: must be a valid BCP 47 language tag` | `mode.type: "facts"` with invalid `mode.outputLocale` code. | Use a [supported language code](https://docs.corti.ai/stt/languages) | | `unknown mode` | `mode.type` is not `"transcription"` or `"facts"`. | Use one of the two supported values. | | `invalid participants` | Participant `role` outside `doctor` / `patient` / `multiple`. | Use one of the three supported roles. | | `invalid audio format: MIME type not allowed...` | `audioFormat` MIME type is not in the supported list. | Use a [supported MIME type](https://docs.corti.ai/stt/audio) or omit the field. | > You sent a non-config frame before any valid `config` message arrived. Only reachable when you opt out of the [automatic handshake](/sdk/js/websockets#connecting). Default mode sends config for you. | `reason` from server | Cause | Fix | | ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- | | `No valid configuration provided for interaction` | A non-config frame reached the server before a config message. Typically sendAudio() / sendFlush() / sendEnd() in manual mode, or a raw socket.send() queued before OPEN in awaitConfiguration: false. | Be sure to call sendConfiguration() immediately after waitForOpen(), or pass configuration to connect() and let the SDK handle the handshake.\` | > The 10-second handshake window elapsed without any `config` message arriving. Only reachable in [manual mode](/sdk/js/websockets#connecting-without-configuration). The [default handshake](/sdk/js/websockets#connecting) sends config well within the 10s budget. | `reason` from server | Cause | Fix | | ------------------------------------------------- | ------------------------------------- | ------------------------------------------------------------- | | Manual mode: no configuration provided within 10s | Server's 10s handshake timer expired. | Call `sendConfiguration()` immediately after `waitForOpen()`. | The SDK throws typed exceptions on any non-2xx response. Each exception extends `CortiClientApiException`, which exposes `Message`, `StatusCode`, and `Body`. See [Error Handling](/sdk/dotnet/overview#error-handling) for full details. ## Exceptions | Exception class | Thrown when | Attributes | | ------------------------- | ------------------------- | ------------------------------- | | `CortiClientApiException` | Any non-2xx HTTP response | `Message`, `StatusCode`, `Body` | Each status code throws a typed subclass with a more specific `Body`. See the accordions below for details. ## HTTP Status Codes | Exception class | Error code | When it occurs | How to resolve | | ----------------- | ------------- | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------ | | `BadRequestError` | `bad_request` | The request body or query parameters are malformed or missing required fields. | Double check the fields submitted to be sure of correct format and necessary fields. | Token endpoint errors are returned as JSON in the response body. Corti API errors are returned in the `WWW-Authenticate` response header with no body. The header is not accessible from the exception directly; use `.WithRawResponse()` on the request if you need it. | Exception class | Error code | When it occurs | How to resolve | | ------------------- | --------------------- | ------------------------------------------------------------------------ | -------------------------------------------------------------- | | `UnauthorizedError` | `invalid_client` | Client authentication failed. There is a problem with the client ID. | Verify your `client_id` is correct. | | `UnauthorizedError` | `unauthorized_client` | Client authentication failed. There is a problem with the client secret. | Verify your `client_secret` is correct. | | `UnauthorizedError` | `invalid_token` | The bearer token is invalid or has expired. | Re-authenticate to obtain a fresh token and retry the request. | | Exception class | When it occurs | How to resolve | | ---------------- | ------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- | | `ForbiddenError` | Accessing a resource (interaction, recording, transcript, document) that belongs to a different client. | Verify the resource ID was created by the authenticated client. | | Exception class | Error code | When it occurs | How to resolve | | --------------- | ------------------------------------------- | -------------------------------------- | ----------------------------------------------------------------------------------------------- | | `NotFoundError` | `Invalid '{resource_name}' '{resource_id}'` | The requested resource does not exist. | Verify the ID and parent resource ID is correct and was created under the authenticated tenant. | Attempting to access a resource that belongs to a different OAuth client also returns a 404, not a 403. This is intentional. Returning a 403 would confirm that the resource exists, which is a security risk. | Exception class | When it occurs | How to resolve | | -------------------------- | -------------------------- | --------------------------------------------------------------------------------- | | `UnprocessableEntityError` | Request validation failed. | Check `Body` for `Code`, `Description`, `HowToFix`, `Detail`, and `Cause` fields. | | Exception class | When it occurs | How to resolve | | ------------------------- | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------ | | `CortiClientApiException` | Rate limit exceeded after retries exhausted (default: 2 retries with backoff). | Reduce request frequency or contact [support](mailto:help@corti.ai) to discuss your rate limit requirements. | | Exception class | When it occurs | How to resolve | | --------------------- | ------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------- | | `InternalServerError` | An unexpected error occurred on the Corti side. `Body` contains `Detail`, `Type`, `Status`, and `RequestId`. | Double check your request format and retry the request. If the issue persists, contact [support](mailto:help@corti.ai). | | Exception class | When it occurs | How to resolve | | ----------------- | -------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | | `BadGatewayError` | A gateway error occurred. `Body` contains `Detail`, `Type`, `Status`, and `RequestId`. | Retry the request. If the issue persists, contact [support](mailto:help@corti.ai). | | Exception class | When it occurs | How to resolve | | ------------------------- | --------------------------------------- | ----------------------------------------------------------------------------------- | | `CortiClientApiException` | The service is temporarily unavailable. | Retry with exponential backoff. Monitor the [status page](https://status.corti.ai). | | Exception class | When it occurs | How to resolve | | --------------------- | ----------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | | `GatewayTimeoutError` | The gateway timed out. `Body` contains `Detail`, `Type`, `Status`, and `RequestId`. | Retry the request. If the issue persists, contact [support](mailto:help@corti.ai). | ## WebSocket APIs WebSocket errors from `/streams` and `/transcribe` surface as exceptions thrown by `ConnectAsync()` (with `Message` set to the server status code, e.g. `CONFIG_DENIED`, `CONFIG_MISSING`, `CONFIG_TIMEOUT`) or as typed events on the socket. Every `CONFIG_*` server message also carries a `Reason` field with human-readable details behind the failure. Whether you can read it depends on how you connect: * **Default mode** (`stream.ConnectAsync(new StreamConfig { … })` / `transcribe.ConnectAsync(new TranscribeConfig { … })`): the SDK handles the handshake-phase and `CONFIG_*` messages internally. It throws from `ConnectAsync()` with just the bare code. The `Reason` is not exposed. * **[Manual mode](/sdk/dotnet/websockets#connecting-without-configuration)** (`ConnectAsync()` with no config): the raw message, including `msg.Reason`, flows through the `StreamConfigStatusMessage` / `TranscribeConfigStatusMessage` event. Subscribe **before** calling `ConnectAsync()` to catch handshake-phase events. > You sent a config message, but it's invalid. | `reason` from server | Likely cause | Fix | | ----------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | | `invalid language code: must be a valid BCP 47 language tag` | `transcription.primaryLanguage` is not a syntactically valid BCP-47 tag (e.g. `"english"`, `"en_US"`, empty string). | Use a [supported language code](https://docs.corti.ai/stt/languages). | | `invalid output locale: language should not be empty` | `mode.type: "facts"` without `mode.outputLocale` (or with an empty value). | Set `mode.outputLocale` to a valid BCP-47 code whenever `mode.type` is `facts`. | | `invalid output locale: invalid language code: must be a valid BCP 47 language tag` | `mode.type: "facts"` with a syntactically invalid `mode.outputLocale`. | Use a [supported language code](https://docs.corti.ai/stt/languages) for `outputLocale`. | | `unknown mode` | `mode.type` is not `transcription` or `facts`. | Use one of the two supported values. | | `invalid participants` | Participant `role` outside `doctor` / `patient` / `multiple`. | Restrict to the three supported roles. | | `invalid audio format: MIME type not allowed...` | `audioFormat` MIME type is not in the supported list. | Use a [supported MIME type](https://docs.corti.ai/stt/audio) or omit the field. | > You sent a non-config frame before any valid `config` message arrived. Only reachable in [manual mode](/sdk/dotnet/websockets#connecting-without-configuration). Default mode sends config for you. | `Reason` from server | Cause | Fix | | ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `No valid configuration provided for interaction` | A non-config frame reached the server before a config message. Typically a `Send(byte[], …)` audio call, or a `Send(StreamFlushMessage, …)` / `Send(StreamEndMessage, …)` call before any `Send(StreamConfigMessage, …)`. | Call `Send(new StreamConfigMessage { … }, …)` (or `TranscribeConfigMessage`) as the first frame after `ConnectAsync()` returns, **or** pass the config directly to `ConnectAsync(new StreamConfig { … })` and let the SDK handle the handshake. | > The 10-second handshake window elapsed without any `config` message arriving. Only reachable in [manual mode](/sdk/dotnet/websockets#connecting-without-configuration). The default handshake sends config well within the 10s budget. | `Reason` from server | Cause | Fix | | -------------------------------------- | ------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `no configuration provided within 10s` | Server's 10s handshake timer expired without receiving a config message. | Call `Send(new StreamConfigMessage { … }, …)` (or `TranscribeConfigMessage`) immediately after `ConnectAsync()` returns, **or** pass the config directly to `ConnectAsync(new StreamConfig { … })` and let the SDK handle the handshake. | **Resource and API errors** (all endpoints except the OAuth token endpoint) return JSON with `requestid`, `status`, `type`, `detail`, and `validationErrors` fields: ```json theme={null} { "requestid": "", "status": 400, "type": "", "detail": "", "validationErrors": [ {} ] } ``` **Authentication errors** from the OAuth token endpoint return `error` and `error_description` fields: ```json theme={null} { "error": "invalid_client", "error_description": "Client authentication failed." } ``` ## HTTP Status Codes | Error code | When it occurs | How to resolve | | ------------------------ | ----------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | | `invalid_request` | The request is missing a required parameter or is otherwise malformed. | Ensure required fields (`grant_type`,`scope`) are present and correctly formatted. | | `unsupported_grant_type` | The `grant_type` value is not recognised by the server. | This API requires a `grant_type` of `client_credentials`. Corti Assistant requires alternate methods for `grant_type`. | | `invalid_scope` | The requested scope does not exist or is not permitted for this client. | Use a valid scope. This API requires `openid`. | Token endpoint errors are returned as JSON in the response body. Corti API errors are returned in the `WWW-Authenticate` response header with no body. Check the header directly if the response body is empty. | Error code | When it occurs | How to resolve | | --------------------- | -------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- | | `invalid_client` | Client authentication failed. There is a problem with the client ID. | Verify your `client_id` is correct. | | `unauthorized_client` | Client authentication failed. There is a problem with the client secret | Verify your `client_secret` is correct. | | `invalid_token` | The bearer token is invalid or has expired. Check the `WWW-Authenticate` header for `error_description`. | Re-authenticate to obtain a fresh token and retry the request. | | When it occurs | How to resolve | | ------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- | | Accessing a resource (interaction, recording, transcript, document) that belongs to a different client. | Verify the resource ID was created by the authenticated client. | | Error | When it occurs | How to resolve | | ------------------------------------------- | -------------------------------------- | ----------------------------------------------------------------------------------------------- | | `Invalid '{resource_name}' '{resource_id}'` | The requested resource does not exist. | Verify the ID and parent resource ID is correct and was created under the authenticated tenant. | | `'{resource_name}' not found` | The requested resource does not exist. | Verify the ID and parent resource ID is correct and was created under the authenticated tenant. | You have exceeded the rate limit for the API. Retry the request after a delay, using exponential backoff. If the error persists, reduce request frequency or contact [support](mailto:help@corti.ai) to discuss your rate limit requirements. An unexpected error occurred on the Corti side. Double check your request format and retry the request. If the issue persists, contact [support](mailto:help@corti.ai). The service is temporarily unavailable. Retry with exponential backoff. Monitor the [status page](https://status.corti.ai). ## WebSocket APIs WebSocket errors from `/streams` and `/transcribe` arrive as plain JSON messages on the socket: `{ type: "CONFIG_DENIED", reason: "…" }`, `{ type: "CONFIG_MISSING", reason: "…" }`, etc. Without an SDK in the middle, you see every server message verbatim, including the `reason` field on every `CONFIG_*` event. **The raw protocol is manual mode out of the box.** There's no SDK layer handling the handshake for you. You open the socket, send `{ type: "config", configuration: { … } }` yourself, and listen for `CONFIG_ACCEPTED` before sending audio. See the [Streams API reference](/api-reference/streams) for the full protocol. **The server does not close the socket on `CONFIG_DENIED` or `CONFIG_MISSING`.** Both codes reject the input but keep the connection open. Each subsequent non-config frame triggers a fresh `CONFIG_MISSING` response, one per frame, until the client closes the socket. SDK consumers don't see this because the SDK calls `close()` on rejection internally. Raw-protocol clients should close the socket themselves once they receive either rejection. > You sent a config message, but it's invalid. | `reason` from server | Likely cause | Fix | | ----------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | | `invalid language code: must be a valid BCP 47 language tag` | `transcription.primaryLanguage` is not a syntactically valid BCP-47 tag (e.g. `"english"`, `"en_US"`, empty string). | Use a [supported language code](https://docs.corti.ai/stt/languages). | | `invalid output locale: language should not be empty` | `mode.type: "facts"` without `mode.outputLocale` (or with an empty value). | Set `mode.outputLocale` to a valid BCP-47 code whenever `mode.type` is `facts`. | | `invalid output locale: invalid language code: must be a valid BCP 47 language tag` | `mode.type: "facts"` with a syntactically invalid `mode.outputLocale`. | Use a [supported language code](https://docs.corti.ai/stt/languages) for `outputLocale`. | | `unknown mode` | `mode.type` is not `transcription` or `facts`. | Use one of the two supported values. | | `invalid participants` | Participant `role` outside `doctor` / `patient` / `multiple`. | Restrict to the three supported roles. | | `invalid audio format: MIME type not allowed...` | `audioFormat` MIME type is not in the supported list. | Use a [supported MIME type](https://docs.corti.ai/stt/audio) or omit the field. | > You sent a non-config frame before any valid `config` message arrived. | `reason` from server | Cause | Fix | | ------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | | `No valid configuration provided for interaction` | A non-config frame (binary audio, `{ type: "flush" }`, `{ type: "end" }`) reached the server before a `{ type: "config", configuration: { … } }` message. | Send the config message as the first frame after the WebSocket opens. Wait for `CONFIG_ACCEPTED` before sending any other frame. | > The 10-second handshake window elapsed without any `config` message arriving. | `reason` from server | Cause | Fix | | -------------------------------------- | ------------------------------------------------------------------------ | -------------------------------------------------------------------------------------- | | `no configuration provided within 10s` | Server's 10s handshake timer expired without receiving a config message. | Send `{ type: "config", configuration: { … } }` immediately after the WebSocket opens. |
# Add Facts Source: https://docs.corti.ai/api-reference/facts/add-facts /api-reference/auto-generated-openapi.yml post /interactions/{id}/facts/ Adds new facts to an interaction. # Extract Facts Source: https://docs.corti.ai/api-reference/facts/extract-facts /api-reference/auto-generated-openapi.yml post /tools/extract-facts Extract facts from provided text, without storing them. # List Fact Groups Source: https://docs.corti.ai/api-reference/facts/list-fact-groups /api-reference/auto-generated-openapi.yml get /factgroups/ Returns a list of available fact groups, used to categorize facts associated with an interaction. # List Facts Source: https://docs.corti.ai/api-reference/facts/list-facts /api-reference/auto-generated-openapi.yml get /interactions/{id}/facts/ Retrieves a list of facts for a given interaction. # Update Fact Source: https://docs.corti.ai/api-reference/facts/update-fact /api-reference/auto-generated-openapi.yml patch /interactions/{id}/facts/{factId} Updates an existing fact associated with a specific interaction. # Update Facts Source: https://docs.corti.ai/api-reference/facts/update-facts /api-reference/auto-generated-openapi.yml patch /interactions/{id}/facts/ Updates multiple facts associated with an interaction. # Delete document Source: https://docs.corti.ai/api-reference/guided-documents/delete-document /api-reference/auto-generated-openapi.yml delete /documents/{documentID} Deletes the document. This cannot be undone. # Generate a structured document Source: https://docs.corti.ai/api-reference/guided-documents/generate-a-structured-document /api-reference/auto-generated-openapi.yml post /documents/ Generates a structured document using one of three template-supply paths: a stored template reference (optionally with runtime overrides), an ad-hoc assembly of stored sections, or a fully inline dynamic template. Exactly one of `templateRef`, `assemblyTemplate`, or `dynamicTemplate` must be provided. Context can combine different types or reference an interactionId to automatically fetch existing context to pass to the LLM. Note that discarded facts are not passed to the LLM. With the exception of the plain `templateRef` path (no overrides), every call creates a new auto-generated template aggregate that snapshots the resolved prompts as a drift-proof receipt, persisted for 30 days. # Get document Source: https://docs.corti.ai/api-reference/guided-documents/get-document /api-reference/auto-generated-openapi.yml get /documents/{documentID} Returns a previously generated document by ID, including its rendered string output and structured object. # List documents Source: https://docs.corti.ai/api-reference/guided-documents/list-documents /api-reference/auto-generated-openapi.yml get /documents/ Returns a list of previously generated documents. Use query parameters to filter by template, interaction, or label. # Update document Source: https://docs.corti.ai/api-reference/guided-documents/update-document /api-reference/auto-generated-openapi.yml patch /documents/{documentID} Updates the document's `name`, `labels`, or rendered output (`stringDocument` / `structuredDocument`). Use this to persist edits made to a previously generated document. # Create section Source: https://docs.corti.ai/api-reference/guided-sections/create-section /api-reference/auto-generated-openapi.yml post /documents/sections/ Creates a new section with an initial version. When `publish` is true (default), the response includes the published version with full inheritance resolution applied (section inheritance chain walked to fill missing fields). # Create section version Source: https://docs.corti.ai/api-reference/guided-sections/create-section-version /api-reference/auto-generated-openapi.yml post /documents/sections/{sectionID}/versions/ Creates a new section version. Returns raw authored values without inheritance resolution. # Delete section Source: https://docs.corti.ai/api-reference/guided-sections/delete-section /api-reference/auto-generated-openapi.yml delete /documents/sections/{sectionID} Deletes a section and its versions. Returns 409 if other sections inherit from this section. # Delete section version Source: https://docs.corti.ai/api-reference/guided-sections/delete-section-version /api-reference/auto-generated-openapi.yml delete /documents/sections/{sectionID}/versions/{versionID} Currently published version cannot be deleted. Last remaining version can be deleted, simply create a new section version again if needed. # Get section Source: https://docs.corti.ai/api-reference/guided-sections/get-section /api-reference/auto-generated-openapi.yml get /documents/sections/{sectionID} Returns the section with its published version fully resolved (inheritance chain walked to fill missing fields). To see raw authored values without inheritance, use GET /documents/sections/{sectionID}/versions/{versionID}. # Get section version Source: https://docs.corti.ai/api-reference/guided-sections/get-section-version /api-reference/auto-generated-openapi.yml get /documents/sections/{sectionID}/versions/{versionID} Returns raw authored section version without inheritance resolution. To see resolved content, use GET /sections/{sectionID} instead. # List section versions Source: https://docs.corti.ai/api-reference/guided-sections/list-section-versions /api-reference/auto-generated-openapi.yml get /documents/sections/{sectionID}/versions/ Returns raw authored section versions without inheritance resolution. To see resolved content, use GET /sections/{sectionID} instead. # List sections Source: https://docs.corti.ai/api-reference/guided-sections/list-sections /api-reference/auto-generated-openapi.yml get /documents/sections/ Returns a list of sections and their metadata. Fetch a sectionId to get the full generation content. Use query parameters to filter by language, region, specialty, label, publish status, or source. # Publish section version Source: https://docs.corti.ai/api-reference/guided-sections/publish-section-version /api-reference/auto-generated-openapi.yml post /documents/sections/{sectionID}/versions/{versionID}/publish Sets this version as the published version of the section. # Update section metadata Source: https://docs.corti.ai/api-reference/guided-sections/update-section-metadata /api-reference/auto-generated-openapi.yml patch /documents/sections/{sectionID} Updates the section's metadata fields (name, description, languages, regions, specialties, labels). Generation content (instructions, output schema) is managed through versions and cannot be updated here. # Create template Source: https://docs.corti.ai/api-reference/guided-templates/create-template /api-reference/auto-generated-openapi.yml post /documents/templates/ Creates a new template with an initial version. When `publish` is true (default), the response includes the published version with full inheritance resolution applied (template-level and section-level inheritance walked). # Create template version Source: https://docs.corti.ai/api-reference/guided-templates/create-template-version /api-reference/auto-generated-openapi.yml post /documents/templates/{templateID}/versions/ Creates a new template version. Returns raw authored values without inheritance resolution or section expansion. # Delete template Source: https://docs.corti.ai/api-reference/guided-templates/delete-template /api-reference/auto-generated-openapi.yml delete /documents/templates/{templateID} Deletes a template and its versions. Returns 409 if other templates or sections inherit from this template. # Delete template version Source: https://docs.corti.ai/api-reference/guided-templates/delete-template-version /api-reference/auto-generated-openapi.yml delete /documents/templates/{templateID}/versions/{versionID} A published version cannot be deleted. When deleting a last remaining version of a template, simply create a new version again if needed. # Get template Source: https://docs.corti.ai/api-reference/guided-templates/get-template /api-reference/auto-generated-openapi.yml get /documents/templates/{templateID} Returns the template with its published version fully resolved (inheritance walked, sections expanded with their own inheritance applied). To see raw authored values without inheritance, use GET /documents/templates/{templateID}/versions/{versionID}. # Get template version Source: https://docs.corti.ai/api-reference/guided-templates/get-template-version /api-reference/auto-generated-openapi.yml get /documents/templates/{templateID}/versions/{versionID} Returns the raw authored template version without inheritance resolution or section expansion. To see resolved content, use GET /documents/templates/{templateID} instead. # List template versions Source: https://docs.corti.ai/api-reference/guided-templates/list-template-versions /api-reference/auto-generated-openapi.yml get /documents/templates/{templateID}/versions/ Returns raw authored template versions without inheritance resolution or section expansion. To see resolved content, use GET /documents/templates/{templateID} instead. # List templates Source: https://docs.corti.ai/api-reference/guided-templates/list-templates /api-reference/auto-generated-openapi.yml get /documents/templates/ Returns a list of templates and their metadata. Fetch a specific templateId to get the expanded sections. Use query parameters to filter by language, region, specialty, label, publish status, or source. # Publish template version Source: https://docs.corti.ai/api-reference/guided-templates/publish-template-version /api-reference/auto-generated-openapi.yml post /documents/templates/{templateID}/versions/{versionID}/publish Sets this version as the published version of the template. # Update template metadata Source: https://docs.corti.ai/api-reference/guided-templates/update-template-metadata /api-reference/auto-generated-openapi.yml patch /documents/templates/{templateID} Updates the template's metadata fields (name, description, languages, regions, specialties, labels). Generation content (template instructions, section composition) is managed through versions and cannot be updated here. # Create Interaction Source: https://docs.corti.ai/api-reference/interactions/create-interaction /api-reference/auto-generated-openapi.yml post /interactions/ Creates a new interaction. # Delete Interaction Source: https://docs.corti.ai/api-reference/interactions/delete-interaction /api-reference/auto-generated-openapi.yml delete /interactions/{id} Deletes an existing interaction. # Get Existing Interaction Source: https://docs.corti.ai/api-reference/interactions/get-existing-interaction /api-reference/auto-generated-openapi.yml get /interactions/{id} Retrieves a previously recorded interaction by its unique identifier (interaction ID). # List All Interactions Source: https://docs.corti.ai/api-reference/interactions/list-all-interactions /api-reference/auto-generated-openapi.yml get /interactions/ Lists all existing interactions. Results can be filtered by encounter status and patient identifier. # Update Interaction Source: https://docs.corti.ai/api-reference/interactions/update-interaction /api-reference/auto-generated-openapi.yml patch /interactions/{id} Modifies an existing interaction by updating specific fields without overwriting the entire record. # List Languages Source: https://docs.corti.ai/api-reference/languages/list-languages /api-reference/auto-generated-openapi.yml get /languages/ Returns a list of available languages with their enabled endpoints details. # Delete Recording Source: https://docs.corti.ai/api-reference/recordings/delete-recording /api-reference/auto-generated-openapi.yml delete /interactions/{id}/recordings/{recordingId} Delete a specific recording for a given interaction. # Get Recording Source: https://docs.corti.ai/api-reference/recordings/get-recording /api-reference/auto-generated-openapi.yml get /interactions/{id}/recordings/{recordingId} Retrieve a specific recording for a given interaction. # List Recordings Source: https://docs.corti.ai/api-reference/recordings/list-recordings /api-reference/auto-generated-openapi.yml get /interactions/{id}/recordings/ Retrieve a list of recordings for a given interaction. # Upload Recording Source: https://docs.corti.ai/api-reference/recordings/upload-recording /api-reference/auto-generated-openapi.yml post /interactions/{id}/recordings/ Upload a recording for a given interaction. There is a maximum limit of 120 minutes in audio duration and 150 MB in file size. # Real-time conversational transcript generation and fact extraction (FactsR™) Source: https://docs.corti.ai/api-reference/streams WebSocket Secure (WSS) API Documentation for /streams endpoint ## Overview The WebSocket Secure (WSS) `/streams` API enables real-time, bidirectional communication with the Corti system for interaction streaming. Clients can send and receive structured data, including transcripts and fact updates. Learn more about [FactsR™ here](/textgen/factsr/). This documentation provides a structured guide for integrating the Corti WSS API for real-time interaction streaming. This `/streams` endpoint supports real-time ambient documentation interactions and clinical decision support workflows. * If you are looking for a stateless endpoint that is geared towards front-end dictation workflows you should use the [/transcribe WSS](/api-reference/transcribe) * If you are looking for asynchronous ambient documentation interactions, then please refer to the [/documents endpoint](/api-reference/documents-classic/generate-document) *** ## 1. Establishing a Connection Clients must initiate a WebSocket connection using the `wss://` scheme and provide a valid interaction ID in the URL. When creating an interaction, the 200 response provides a `websocketUrl` for that interaction including the `tenant-name` as url parameter. The authentication for the WSS streams requires in addition to the `tenant-name` parameter a `token` parameter to pass in the Bearer access token. ### Path Parameters Unique interaction identifier ### Query Parameters `eu` or `us` Specifies the tenant context Bearer \$token ```ts title="JavaScript" theme={null} import { CortiClient } from "@corti/sdk"; // Replace these with your values const ACCESS_TOKEN = ""; const client = new CortiClient({ auth: { accessToken: ACCESS_TOKEN, }, }); ``` ```csharp title="C# .NET" theme={null} using Corti; // Replace these with your values const string ACCESS_TOKEN = ""; var client = new CortiClient( auth: CortiClientAuth.Bearer(accessToken: ACCESS_TOKEN) ); ``` ```ts title="JavaScript" theme={null} const streamSocket = await client.stream.connect({ id: "" }); ``` ```csharp title="C# .NET" theme={null} var stream = await client.CreateStreamApiAsync(""); await stream.ConnectAsync(); ``` *** ## 2. Handshake Responses ### 101 Switching Protocols Indicates a successful WebSocket connection. Upon successful connection, send a `config` message to define the configuration: Specify the input language and expected output preferences. The config message must be sent within 10 seconds of the web socket being opened to prevent `CONFIG-TIMEOUT`, which will require establishing a new wss connection. *** ## 3. Sending Messages ### Configuration Declare your `/streams` configuration using the message `"type": "config"` followed by defining the `"configuration": {}`. Defining the type is required along with `transcription: primaryLanguage` and `mode: type and outputLocale` configuration parameters. The other parameters are optional for use, depending on your need and workflow. Configuration notes: * Clients must send a streams configuration message and wait for a response of type `CONFIG_ACCEPTED` before transmitting other data. * If the configuration is not valid it will return `CONFIG_DENIED`. * The configuration must be committed within 10 seconds of opening the WebSocket, else it will time-out with `CONFIG_NOT_PROVIDED`. Define parameters for speech to text processing: The primary spoken language for transcription. See supported languages codes and more information [here](/stt/languages). Set to true to enable speaker diarization in mono channel audio Note the legacy parameter `isDiarization` is still accepted Set to true to enable transcription of a multi-channel audio stream. List of participants with roles assigned to a channel Audio channel number (e.g., 0 or 1) Label for audio channel participant (e.g., Doctor, patient, or multiple) Define facts or transcript as desired output, depending on workflow need: Set as `facts` to receive structured facts output along with transcripts, or `transcription` to only receive transcript output Output language for extracted `facts` (required for `type: "facts"`). See supported languages codes and more information [here](/stt/languages). (Note: This may be different than the `primaryLanguage` defined for transcript output; see details [here](/textgen/facts_realtime).) Beta Rate at which fact generation should process and return results (optional for `type: "facts"`). Possible values: (empty), `fixed`, `fast_init`. If no value is set, the default is `fixed` and will trigger fact generation at the standard interval of around 60s. With `fast_init`, fact generation will follow a logarithmic curve, currently with the initial generation at roughly around 10s, then 20s, then 26s and continuously increasing interval length until the default 60s interval is reached. Note: Increased fact processing rate can result in increased (near) duplicates, quantities and increased credit consumption. With the optional parameter `retentionPolicy:none` the API will generate and return the transcripts and facts as expected, but the generated output will not be saved to the database. If the configuration option is omitted, then the default retention policy will apply and the output will be stored in the database. Available options: `none`, `retain` Define the audio format of the incoming audio stream - optional but recommended. * When omitted, the server auto-detects the format from the first audio chunk using ffprobe. Supported audio will be processed. Unsupported audio returns an error, but in some cases might error silently. * If provided (recommended), the provided MIME type must be supported and the audio must match the MIME type. An unsupported MIME type results in `CONFIG_REJECTED`. Audio that differs from the MIME type will return audio validation errors on the socket. See more about supported formats [here](/stt/audio). When true, enables audio quality and speech activity events to be sent over the WebSocket. Disabled by default. The following events are supported: * Speech quality issue detected / recovered * Long silence detected / recovered Don't like how Corti STT outputs certain words, phrases, or acronyms? Define `replacements` to have terms (single words or multi-word phrases) replaced in final text output with your preferred style. Speech-to-text output to be replaced (case insensitive) Replacement text to be used final transcript A limit of 1,000 items is enforced on configuration validation. Define words, terms, and phrases to be recognized by Corti speech-to-text. Especially useful for proper nouns (e.g., surnames), but also supportive of words not being recognized consistently. Ordered list of words to be recognized (case sensitive) Word to be recognized, defined in it's expected "written form" A defined `term` is limited to a length of 50 characters A limit of 1,000 items is enforced on configuration validation #### Example ```json title="Raw message" theme={null} { "type": "config", "configuration": { "transcription": { "primaryLanguage": "en", "diarize": false, "isMultichannel": false, "participants": [ { "channel": 0, "role": "multiple" } ] }, "mode": { "type": "facts", "outputLocale": "en" }, "retentionPolicy": "retain", "audioFormat": "audio/ogg", "audioEvents":{ "enabled": true }, "replacements": [ { "find": "BID", "replace": "twice daily" } ], "keyterms": { "terms": [ { "term": "Corti" } ] } } } ``` ```ts title="JavaScript" theme={null} const configuration = { transcription: { primaryLanguage: "en", diarize: false, isMultichannel: false, participants: [ { channel: 0, role: "multiple" } ] }, mode: { type: "facts", outputLocale: "en" }, xCortiRetentionPolicy: "retain", replacements: [ { find: "BID", replace: "twice daily" } ], keyterms: { terms: [{ term: "Corti" }] } }; // When configuration is provided, connect() resolves after CONFIG_ACCEPTED (or throws on failure). const streamSocket = await client.stream.connect({ id: "", configuration }); ``` ```csharp title="C# .NET" theme={null} var stream = await client.CreateStreamApiAsync(""); // ConnectAsync(configuration) waits for CONFIG_ACCEPTED and throws on rejection. await stream.ConnectAsync(new StreamConfig { Transcription = new StreamConfigTranscription { PrimaryLanguage = "en", diarize = false, IsMultichannel = false, Participants = new List { new() { Channel = 0, Role = StreamConfigParticipantRole.Multiple }, }, }, Mode = new StreamConfigMode { Type = StreamConfigModeType.Facts, OutputLocale = "en", }, XCortiRetentionPolicy = StreamConfigXCortiRetentionPolicy.Retain, Replacements = new List { new() { Find = "BID", Replace = "twice daily" }, }, Keyterms = new StreamConfigKeyterms { Terms = new List { new() { Term = "Corti" }, }, }, }); ``` ### Sending Audio Ensure that your configuration was accepted before sending audio, and that the initial audio chunk is not too small as it needs to contain the headers to properly decode the audio. We recommend sending audio in chunks of 250-500ms. In terms of buffering, the limit is 64000 bytes per chunk. Audio data should be sent as raw binary without JSON wrapping. A variety of common audio formats are supported; audio will be passed through a transcoder before speech-to-text processing. Similarly, specification of sample rate, depth or other audio settings is not required at this time. See more details on supported audio formats [here](/stt/audio). #### Channels, participants, and speakers In most workflows, especially **in-person settings**, mono-channel audio should be used. If the microphone is a stereo-microphone, then ensure to set `isMultichannel: false` and audio will be converted to mono-channel, preventing duplicate transcripts from being returned. In a telehealth workflow, or other **virtual setting**, the virtual audio may be on one channel (e.g., from webRTC) with audio from the microphone of the local client on a separate channel. In this scenario, define `isMultichannel: true` and assign each channel the relevant participant role (e.g., if the doctor is on the local client, then set that to channel 0 with participant defined as `doctor` and the virtual audio for patient on channel `defined as participant`patient\`). **Diarization** is independent of audio channels and participant roles as it enables speaker separation for mono audio. With configuration `diarize: true`, transcript segments will be assigned to automatically with first speaker identified being channel 0, second on channel 1, etc. If `diarize:false`, then transcript segments will all be assigned with `speakerId: -1`. Read more [here](/stt/diarization). ```ts title="JavaScript" theme={null} streamSocket.sendAudio(chunk); // method doesn't do the chunking ``` ```csharp title="C# .NET" theme={null} await stream.Send(audioChunkBytes); // method doesn't do the chunking ``` ### Flush the Audio Buffer To flush the audio buffer, forcing transcript segments to be returned over the web socket (e.g., when turning off or muting the microphone for the patient to share something private, not to be recorded, during the conversation), send a message - ```json title="Raw message" theme={null} { "type": "flush" } ``` ```ts title="JavaScript" theme={null} streamSocket.sendFlush({ type: "flush" }); ``` ```csharp title="C# .NET" theme={null} await stream.Send(new StreamFlushMessage()); ``` The server will return text for audio sent before the `flush` message and then respond with messages - ```json theme={null} { "type": "flushed" } ``` ```json theme={null} { "type": "delta_usage", "credits": 0.00116 } ``` `Delta usage` represents incremental credit consumption between recording initiation and `flush` events. Delta usage is approximate and may differ slightly from final `usage` sent after `end` message is processed (see [below](/api-reference/streams#usage)). Final, end session usage will be reflected in API billing. The web socket will remain open after `flush` processing so recording can continue. Beta FactsR generation (i.e., when working in `configuration.mode: facts`) will be triggered upon `flush` event. Facts will only be returned if the sliding transcript window, including the forced transcript segment after `flush`, contains relevant new information for FactsR. Note: Too frequent triggering of `flush` can negatively impact FactsR, e.g. partial transcripts, increased (near) duplicates or fact quantities, and credits usage can increase. Client side considerations: 1 If you rely on a `flush` event to separate data (e.g., for different sections in an EHR template), then be sure to receive the `flushed` event before moving on to the next data field. 2 When using a web browser `MediaRecorder` API, audio is buffered and only emitted at the configured timeslice interval. Therefore, *before* sending a `flush` message, call `MediaRecorder.requestData()` to force any remaining buffered audio on the client to be transmitted to the server. This ensures all audio reaches the server before the `flush` is processed. ### Ending the Session To end the `/streams` session, send a message - ```json title="Raw message" theme={null} { "type": "end" } ``` ```ts title="JavaScript" theme={null} streamSocket.sendEnd({ type: "end" }); ``` ```csharp title="C# .NET" theme={null} await stream.Send(new StreamEndMessage()); ``` This will signal the server to send any remaining transcript segments and facts (depending on `mode` configuration). Then, the server will send two messages - ```json theme={null} { "type": "usage", "credits":0.1 } ``` ```json theme={null} { "type": "ENDED" } ``` Following the message type `ENDED`, the server will close the web socket. You can at any time open the WebSocket again by sending the configuration. *** ## 4. Responses ### Configuration Returned when sending a valid configuration. Response body will include the full configuration object to confirm values applied in the configuration. Returned when sending a valid configuration. The resolved configuration, including accepted client-defined values and server-applied defaults for parameters not defined in client configuration. ### Transcripts Array of transcript segments. Each `transcript` message may carry one or more segments; with diarization, segments for different speakers can appear in the same message and may not be in chronological order (order by `time.start`). Interaction ID that the transcript segments are associated with Transcript text segments Start time of the transcript segment in seconds End time of the transcript segment in seconds Indicates whether the transcript text results are final or interim (Note: only final transcripts are supported in Streams workflows) Speaker identification (Note: value of `-1` is returned when diarization is disabled) Audio channel number (e.g., 0 or 1) ```json Transcript response theme={null} { "type": "transcript", "data": [ { "id": "UUID", "transcript": "Patient presents with fever and cough.", "time": { "start": 1.71, "end": 11.296 }, "final": true, "speakerId": -1, "participant": { "channel": 0 } } ] } ``` Transcript output will automatically apply server-side default values documented [here](/stt/formatting). Configuration of output formatting is not supported on the `streams` endpoint at this time as it is with `transcribe`. ### Facts Unique identifier for the fact Text description of the fact Categorization of the fact (e.g., "medical-history") Deprecated. Response still includes groupID but will always be empty `""` Default response is `false`. Use [PATCH /facts](/api-reference/facts/update-facts) to set to `true` if you want to keep track that a clinician discarded a fact. Indicates the source of the fact (e.g., "core", "user"). Default response for LLM-generated is `core`. Use [PATCH /facts](/api-reference/facts/update-facts) if you want to keep track that a clinician edited or added a fact. Timestamp when the fact was created Timestamp when the fact was last updated via [PATCH /facts](/api-reference/facts/update-facts). Default response equals `createdAt`. ```json Fact response theme={null} { "type": "facts", "fact": [ { "id": "UUID", "text": "Patient has a history of hypertension.", "group": "medical-history", "groupId": "" // deprecated, "isDiscarded": false, "source": "core", "createdAt": "2024-02-28T12:34:56Z", "updatedAt": "" } ] } ``` By default, incoming audio and returned data streams are persisted on the server, associated with the interactionId. You may query the interaction to retrieve the stored `recordings`, `transcripts`, and `facts` via the relevant REST endpoints. Audio recordings are saved as .webm format; transcripts and facts as json objects. Data persistence can be disabled by Corti upon request when needed to support compliance with your applicable regulations and data handling preferences. ```ts expandable theme={null} socket.on("message", (msg) => { switch (msg.type) { case "transcript": console.log("Transcript:", msg.data); break; case "facts": console.log("Facts:", msg.fact); break; case "flushed": console.log("Flush complete"); break; case "ENDED": console.log("Stream ended"); socket.close(); break; case "usage": console.log("Credits used:", msg.credits); break; case "error": console.error("Server error:", msg.error); break; } }); ``` ```csharp title="C# .NET" expandable theme={null} stream.StreamTranscriptMessage.Subscribe(message => { Console.WriteLine($"Transcript: {message.Data.Text}"); }); stream.StreamFactsMessage.Subscribe(message => { Console.WriteLine("Facts received"); }); stream.StreamFlushedMessage.Subscribe(_ => { Console.WriteLine("Flush complete"); }); stream.StreamEndedMessage.Subscribe(_ => { Console.WriteLine("Stream ended"); }); stream.StreamUsageMessage.Subscribe(message => { Console.WriteLine($"Credits used: {message.Credits}"); }); stream.StreamErrorMessage.Subscribe(message => { Console.Error.WriteLine($"Server error: {message.Error.Title} ({message.Error.Status})"); }); ``` ### Audio Events Server message indicating an audio quality or speech activity event The type of audio quality or speech activity event. Possible values: `speechQualityIssueDetected`, `speechQualityIssueRecovered`, `longSilenceDetected`, `longSilenceRecovered` Audio channel identifier Start time of the event in milliseconds ```json Audio Event response theme={null} { "type": "audioEvent", "data": { "event": "speechQualityIssueDetected", "channel": 0, "startTimeMs": 4200 } } ``` ### Flushed Returned by server, after processing `flush` event from client, to return transcript segments ```json theme={null} { "type":"flushed" } ``` ### Usage Returned by server, after processing `flush` event from client, to convey amount of credits consumed since recording started. Delta usage is approximate and may differ slightly from final `usage` sent after `end` message is processed. ```json theme={null} { "type": "delta_usage", "credits": 0.00116 } ``` Returned by server, after processing `end` event from client, to convey amount of credits consumed ```json theme={null} { "type":"usage", "credits":0.1 } ``` ### Ended Returned by server, after processing `end` event from client, before closing the web socket ```json theme={null} { "type":"ENDED" } ``` *** ## 5. Error Handling For the full catalog of error codes, real `reason` strings, and SDK / REST-specific behavior, see the [Errors reference](/api-reference/errors). In case of an invalid or missing interaction ID, the server will return an error before opening the WebSocket. In case of an invalid configuration, the server will return one of the following errors: Returned when sending an invalid configuration. Possible errors: `CONFIG_DENIED`, `CONFIG_NOT_PROVIDED`, `CONFIG_ALREADY_RECEIVED`, `CONFIG_MISSING` The reason the configuration is invalid. The interaction ID. Once configuration has been accepted and the session is running, you may encounter runtime or application-level errors. These are sent as JSON objects with the following structure: ```json theme={null} { "type": "error", "error": { "requestid": "", "details": "error details" } } ``` ```ts title="JavaScript" expandable theme={null} try { const socket = await client.stream.connect({ id: interactionId, configuration: { transcription: { primaryLanguage: "en", participants: [{ channel: 0, role: "doctor" }] }, mode: { type: "facts", outputLocale: "en" }, }, }); socket.on("error", (err) => { // Network errors and reconnect failures console.error("Socket error:", err.message); }); socket.on("message", (msg) => { if (msg.type === "error") { // Server-sent runtime error (e.g. audio format issue) console.error("Server error:", msg.error); } }); } catch (err) { // CONFIG_DENIED, CONFIG_TIMEOUT, CONFIG_MISSING, or connection failure console.error("Connect failed:", err.message); } ``` ```csharp title="C# .NET" expandable theme={null} var stream = await client.CreateStreamApiAsync(interactionId); stream.ExceptionOccurred.Subscribe(ex => { // Network errors, reconnect failures Console.Error.WriteLine($"Socket error: {ex.Message}"); }); stream.StreamErrorMessage.Subscribe(message => { // Server-sent runtime error (e.g. audio format issue) Console.Error.WriteLine($"Server error: {message.Error.Title} ({message.Error.Status})"); }); try { // ConnectAsync waits for CONFIG_ACCEPTED and throws if configuration is denied 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" }, }); } catch (InvalidOperationException ex) { // CONFIG_DENIED, CONFIG_TIMEOUT, CONFIG_MISSING, or connection failure Console.Error.WriteLine($"Connect failed: {ex.Message}"); throw; } ``` # Get Template Source: https://docs.corti.ai/api-reference/templates-classic/get-template /api-reference/auto-generated-openapi.yml get /templates/{key} Retrieves template by key. # List Template Sections Source: https://docs.corti.ai/api-reference/templates-classic/list-template-sections /api-reference/auto-generated-openapi.yml get /templateSections/ Retrieves a list of template sections with optional filters for organization and language. # List Templates Source: https://docs.corti.ai/api-reference/templates-classic/list-templates /api-reference/auto-generated-openapi.yml get /templates/ Retrieves a list of templates with optional filters for organization, language, and status. # Real-time stateless dictation Source: https://docs.corti.ai/api-reference/transcribe WebSocket Secure (WSS) API Documentation for /transcribe endpoint ## Overview The WebSocket Secure (WSS) `/transcribe` API enables real-time, bidirectional communication with the Corti system for stateless speech to text. Clients can send and receive structured data, including transcripts and detected commands. This documentation provides a comprehensive guide for integrating these capabilities. This `/transcribe` endpoint supports real-time stateless dictation. * If you are looking for real-time ambient documentation interactions, you should use the [/streams WSS](/api-reference/streams) * If you are looking for transcript generation based on a pre-recorded audio file, then please refer to the [/transcripts endpoint](/api-reference/transcripts/create-transcript) *** ## 1. Establishing a Connection Clients must initiate a WebSocket connection using the `wss://` scheme. The authentication for the WSS streams requires in addition to the `tenant-name` parameter a `token` parameter to pass in the Bearer access token. ### Query Parameters `eu` or `us` Specifies the tenant context Bearer \$token ```ts title="JavaScript" theme={null} import { CortiClient } from "@corti/sdk"; // Replace these with your values const ACCESS_TOKEN = ""; const client = new CortiClient({ auth: { accessToken: ACCESS_TOKEN, }, }); ``` ```csharp title="C# .NET" theme={null} using Corti; // Replace these with your values const string ACCESS_TOKEN = ""; var client = new CortiClient( auth: CortiClientAuth.Bearer(accessToken: ACCESS_TOKEN) ); ``` ```bash title="cURL" theme={null} # Replace these with your values ACCESS_TOKEN="" ENVIRONMENT="" TENANT="" curl --request GET \ --url "wss://api.${ENVIRONMENT}.corti.app/audio-bridge/v2/transcribe?tenant-name=${TENANT}&token=Bearer%20${ACCESS_TOKEN}" ``` ```ts title="JavaScript" theme={null} const transcribeSocket = await client.transcribe.connect(); ``` ```csharp title="C# .NET" theme={null} var transcribe = await client.CreateTranscribeApiAsync(); await transcribe.ConnectAsync(); ``` *** ## 2. Handshake Response ### 101 Switching Protocols Indicates a successful WebSocket connection. Upon successful connection, send a `config` message to define the configuration: Specify the input language and expected output preferences. The config message must be sent within 10 seconds of the web socket being opened to prevent `CONFIG-TIMEOUT`, which will require establishing a new wss connection. *** ## 3. Sending Messages ### Configuration Declare your `/transcribe` configuration using the message `"type": "config"` followed by defining the `"configuration": {...}`. Defining the type is required along with the `primaryLanguage` configuration parameter. The other parameters are optional for use, depending on your need and workflow. Configuration Notes: * The configuration must be committed within 10 seconds of opening the WebSocket, else it will time-out with `CONFIG_TIMEOUT`. * Clients must wait for a response of type `CONFIG_ACCEPTED` before transmitting other data. * If the configuration is not valid it will return `CONFIG_DENIED`. The locale of the primary spoken language. See supported languages codes and more information [here](/stt/languages). When true, interim (preview) transcript results (`"isFinal"=false`) will be returned with lower latency than final transcript results. When true, converts spoken punctuation such as `period` or `slash` into `.`or `/`. Read more about supported punctuation [here](/stt/punctuation). When true, automatically punctuates and capitalizes in the final transcript. Spoken and Automatic Punctuation are mutually exclusive - only one should be set to true in a given configuration request. If both are included and set to `true`, then `spokenPunctuation` will take precedence and override `automaticPunctuation`. Define dictation commands to be registered and detected during audio recording. Read more about commands, with examples, [here](/stt/commands). Unique value to identify the command. This, along with the command phrase, will be returned by the API when the command is recognized during dictation. One or more word sequence(s) that can be spoken to trigger the command. At least one phrase is required per command. Placeholders that can (optionally) be added in `phrases` to provide flexibility and extensibility for triggering commands. Define the variable used in command phrase here. Possible values: * `enum` - use to define a list of values that can be recognized for a given command phrase * `wildcard` - use for any free text utterance to be recognized in a command phrase When using `wildcard` type, the phrase must include a literal trigger word before the variable, and multiple wildcard variables in one phrase must be separated by a non-empty literal string. Required when `type` is `"enum"`. List of values that should be recognized for the defined variable. Not used (and ignored) when `type` is `"wildcard"`. Define preferences for output formatting using the `enum` options described below. Formatting configuration is optional, and one or more fields can be defined individually. When a property is not provided for a given field, then the values listed as `default` will be applied automatically. Read more about formatting options, including how localization is handled per language, and detailed examples, [here](/stt/formatting). * Default option: `locale:long` * Alternative options: `locale:medium`, `locale:short`, `iso`, `as_dictated` * Default option: `locale` * Alternative options: `h24`, `h12` * Default option: `numerals_above_nine` * Alternative options: `numerals`, `as_dictated` Localization of numbers applied automatically for thousands and decimal separators * Default option: `abbreviated` * Alternative options: `as_dictated` [Click here](/stt/formatting#units-and-measurements) to see a full list of supported units and measurements * Default option: `numerals` * Alternative options: `as_dictated` * Default option: `numerals_above_nine` * Alternative options: `numerals`, `as_dictated` Define the audio format of the incoming audio stream - optional but recommended. * When omitted, the server auto-detects the format from the first audio chunk using ffprobe. Supported audio will be processed. Unsupported return an error but might in some cases error silently. * If provided (recommended), the provided MIME type must be supported and the audio must match the MIME type. An unsupported MIME type results in `CONFIG_REJECTED`. Audio that differs from the MIME type will return audio validation errors on the socket. See more about supported formats [here](/stt/audio). Enables audio quality and speech activity events to be sent over the WebSocket. The following events are supported: * Speech quality issue detected / recovered * Long silence detected / recovered When true, enables audio quality and speech activity events to be sent over the WebSocket. Disabled by default. Don't like how Corti STT outputs certain words, phrases, or acronyms? Define `replacements` to have terms (single words or multi-word phrases) replaced in final text output with your preferred style. Speech-to-text output to be replaced (case insensitive) Replacement text to be used final transcript A limit of 1,000 items is enforced on configuration validation. Define words, terms, and phrases to be recognized by Corti speech-to-text. Especially useful for proper nouns (e.g., surnames), but also supportive of words not being recognized consistently. Ordered list of words to be recognized (case sensitive) Word to be recognized, defined in it's expected "written form" A defined `term` is limited to a length of 50 characters A limit of 1,000 items is enforced on configuration validation #### Example Here is an example configuration for transcription of dictated audio in English with spoken punctuation enabled, two commands defined, and (default) formatting options defined: ```json title="wss:/transcribe configuration example" theme={null} { "type": "config", "configuration":{ "primaryLanguage": "en", "interimResults": true, "spokenPunctuation": true, "commands": [ { "id": "next_section", "phrases": ["next section", "go to next section"] }, { "id": "insert_template", "phrases": ["insert my {template_name} template", "insert {template_name} template"], "variables": [ { "key": "template_name", "type": "enum", "enum": ["soap", "radiology", "referral"] } ] }, { "id": "select_text", "phrases": ["select {text}"], "variables": [ { "key": "text", "type": "wildcard" } ] }, ], "formatting": { "dates": "locale:long", "times": "locale", "numbers": "numerals_above_nine", "measurements": "abbreviated", "numericRanges": "numerals", "ordinals": "numerals_above_nine" }, "audioFormat": "audio/ogg", "audioEvents":{ "enabled": true }, "replacements": [ { "find": "BID", "replace": "twice daily" } ], "keyterms": { "terms": [ { "term": "Corti" } ] } } } ``` ```ts title="JavaScript" theme={null} const configuration = { primaryLanguage: "en", spokenPunctuation: true, commands: [ { id: "next_section", phrases: ["next section", "go to next section"] }, { id: "insert_template", phrases: ["insert my {template_name} template", "insert {template_name} template"], variables: [ { key: "template_name", type: "enum", enum: ["soap", "radiology", "referral"] } ] }, { id: "select_text", phrases: ["select {text}"], variables: [ { key: "text", type: "wildcard" } ] }, ], audioEvents: { enabled: true }, replacements: [ { find: "BID", replace: "twice daily" } ], keyterms: { terms: [{ term: "Corti" }] } }; const transcribeSocket = await client.transcribe.connect( { configuration } ); ``` ```csharp title="C# .NET" theme={null} var transcribe = await client.CreateTranscribeApiAsync(); await transcribe.ConnectAsync(new TranscribeConfig { PrimaryLanguage = "en", SpokenPunctuation = true, Commands = new List { new() { Id = "next_section", Phrases = new List { "next section", "go to next section" }, }, new() { Id = "insert_template", Phrases = new List { "insert my {template_name} template", "insert {template_name} template", }, Variables = new List { new() { Key = "template_name", Type = TranscribeCommandVariableType.Enum, Enum = new List { "soap", "radiology", "referral" }, }, }, }, new() { Id = "select_text", Phrases = new List { "select {text}" }, Variables = new List { new() { Key = "text", Type = TranscribeCommandVariableType.Wildcard, }, }, }, }, AudioEvents = new TranscribeAudioEventsConfig { Enabled = true }, Replacements = new List { new() { Find = "BID", Replace = "twice daily" }, }, Keyterms = new TranscribeConfigKeyterms { Terms = new List { new() { Term = "Corti" }, }, }, }); ``` ### Sending Audio Ensure that your configuration was accepted before sending audio, and that the initial audio chunk is not too small as it needs to contain the headers to properly decode the audio. We recommend sending audio in chunks of 250-500ms. In terms of buffering, the limit is 64000 bytes per chunk. Audio data should be sent as raw binary without JSON wrapping. A variety of common audio formats are supported; audio will be passed through a transcoder before speech-to-text processing. Similarly, specification of sample rate, depth or other audio settings is not required at this time. See more details on supported audio formats [here](/stt/audio). ```ts title="JavaScript" theme={null} transcribeSocket.sendAudio(audioChunk); // method doesn't do the chunking ``` ```csharp title="C# .NET" theme={null} await transcribe.Send(audioChunkBytes); // method doesn't do the chunking ``` ### Flush the Audio Buffer To flush the audio buffer, forcing transcript segments and detected commands to be returned over the web socket (e.g., when turning off or muting the microphone in a "hold-to-talk" dictation workflow, or in applications that support mic "go to sleep"), send a message - ```json title="Raw message" theme={null} { "type":"flush" } ``` ```ts title="JavaScript" theme={null} transcribeSocket.sendFlush({ type: "flush" }); ``` ```csharp title="C# .NET" theme={null} await transcribe.Send(new TranscribeFlushMessage()); ``` The server will return text/commands for audio sent before the `flush` message and then respond with messages - ```json theme={null} { "type": "flushed" } ``` ```json theme={null} { "type": "delta_usage", "credits": 0.00116 } ``` `Delta usage` represents incremental credit consumption between recording initiation and `flush` events. Delta usage is approximate and may differ slightly from final `usage` sent after `end` message is processed (see [below](/api-reference/transcribe#usage)). Final, end session usage will be reflected in API billing. The web socket will remain open after `flush` processing so dictation can continue. Client side considerations: 1 If you rely on a `flush` event to separate data (e.g., for different sections in an EHR template), then be sure to receive the `flushed` event before moving on to the next data field. 2 When using a web browser `MediaRecorder` API, audio is buffered and only emitted at the configured timeslice interval. Therefore, *before* sending a `flush` message, call `MediaRecorder.requestData()` to force any remaining buffered audio on the client to be transmitted to the server. This ensures all audio reaches the server before the `flush` is processed. ### Ending the Session To end the `/transcribe` session, send a message - ```json title="Raw message" theme={null} { "type": "end" } ``` ```ts title="JavaScript" theme={null} transcribeSocket.sendEnd({ type: "end" }); ``` ```csharp title="C# .NET" theme={null} await transcribe.Send(new TranscribeEndMessage()); ``` This will signal the server to send any remaining transcript segments and/or detected commands. Then, the server will send two messages - ```json theme={null} { "type": "usage", "credits": 0.1 } ``` ```json theme={null} { "type": "ended" } ``` Following the message type `ended`, the server will close the web socket. *** ## 4. Responses ### Configuration Returned when sending a valid configuration. Returned when sending a valid configuration. The resolved configuration, including accepted client-defined values and server-applied defaults for parameters not defined in client configuration. ### Transcripts Server message indicated recognized speech to text Transcript segment with punctuations applied and command phrases removed The raw transcript without spoken punctuation applied and without command phrases removed Start time of the transcript segment in seconds End time of the transcript segment in seconds If false, then interim transcript result ```json Transcript response theme={null} { "type": "transcript", "data": { "text": "patient reports mild chest pain.", "rawTranscriptText": "patient reports mild chest pain period", "start": 0.0, "end": 3.2, "isFinal": true } } ``` **[Click here](/stt/best-practices-transcribe)** for detailed guide on how to properly insert transcript segments with proper handling of whitespace, interim vs. final results, and `text` vs. `rawTranscriptText` fields. ### Commands Server message indicating a recognized Command To identify the command when it gets detected and returned over the WebSocket Command variables as key-value pairs The raw transcript without spoken punctuation applied and without command phrases removed Start time of the transcript segment in seconds End time of the transcript segment in seconds ```json Command response theme={null} { "type": "command", "data": { "id": "insert_template", "variables": { "template_name": "radiology" }, "rawTranscriptText": "insert my radiology template", "start": 2.3, "end": 2.9 } } ``` ### Audio Events Server message indicating an audio quality or speech activity event The type of audio quality or speech activity event. Possible values: `speechQualityIssueDetected`, `speechQualityIssueRecovered`, `longSilenceDetected`, `longSilenceRecovered` Audio channel identifier Start time of the event in milliseconds ```json Audio Event response theme={null} { "type": "audioEvent", "data": { "event": "speechQualityIssueDetected", "channel": 0, "startTimeMs": 4200 } } ``` ### Flushed Returned by server, after processing `flush` event from client, to return transcript segments/ detected commands ```json theme={null} { "type": "flushed" } ``` ### Usage Returned by server, after processing `flush` event from client, to convey amount of credits consumed since recording started. Delta usage is approximate and may differ slightly from final `usage` sent after `end` message is processed. ```json theme={null} { "type": "delta_usage", "credits": 0.00116 } ``` Returned by server, after processing `end` event from client, to convey amount of credits consumed ```json theme={null} { "type": "usage", "credits": 0.1 } ``` ### Ended Returned by server, after processing `end` event from client, before closing the web socket ```json theme={null} { "type": "ended" } ``` ### Subscribe to messages in SDK ```ts expandable theme={null} socket.on("message", (msg) => { switch (msg.type) { case "transcript": console.log("Transcript:", msg.data.text, "final:", msg.data.isFinal); break; case "command": console.log("Command:", msg.data.id, msg.data.variables); break; case "flushed": console.log("Flush complete"); break; case "ended": console.log("Session ended"); socket.close(); break; case "usage": console.log("Credits used:", msg.credits); break; case "error": console.error("Server error:", msg.error); break; } }); ``` ```csharp title="C# .NET" expandable theme={null} transcribe.TranscribeTranscriptMessage.Subscribe(message => { Console.WriteLine($"Transcript: {message.Data.Text}"); }); transcribe.TranscribeCommandMessage.Subscribe(message => { Console.WriteLine($"Command: {message.Data.Id}"); }); transcribe.TranscribeFlushedMessage.Subscribe(_ => { Console.WriteLine("Flush complete"); }); transcribe.TranscribeEndedMessage.Subscribe(_ => { Console.WriteLine("Session ended"); }); transcribe.TranscribeUsageMessage.Subscribe(message => { Console.WriteLine($"Credits used: {message.Credits}"); }); transcribe.TranscribeErrorMessage.Subscribe(message => { Console.Error.WriteLine($"Server error: {message.Error.Title} ({message.Error.Status})"); }); ``` *** ## 5. Error Handling For the full catalog of error codes, real `reason` strings, and SDK / REST-specific behavior, see the [Errors reference](/api-reference/errors). Returned when sending an invalid configuration. Possible errors: `CONFIG_DENIED`, `CONFIG_TIMEOUT`, `CONFIG_ALREADY_RECEIVED`, `CONFIG_MISSING` The reason the configuration is invalid. The session ID. Once configuration has been accepted and the session is running, you may encounter runtime or application-level errors. These are sent as JSON objects with the following structure: ```json theme={null} { "type": "error", "error": { "requestid": "", "details": "error details" } } ``` ### Handle errors in SDK With the recommended approach (passing configuration as part of `connect`), configuration errors are raised during `connect` (the call fails). Runtime errors are emitted via the error event; you can also inspect the original message in the message handler. ```ts title="JavaScript" expandable theme={null} try { const socket = await client.transcribe.connect({ configuration: { primaryLanguage: "en", automaticPunctuation: true, }, }); socket.on("error", (err) => { // Network errors and reconnect failures console.error("Socket error:", err.message); }); socket.on("message", (msg) => { if (msg.type === "error") { // Server-sent runtime error console.error("Server error:", msg.error); } }); } catch (err) { // CONFIG_DENIED, CONFIG_TIMEOUT, or connection failure console.error("Connect failed:", err.message); } ``` ```csharp title="C# .NET" expandable theme={null} var transcribe = await client.CreateTranscribeApiAsync(); transcribe.ExceptionOccurred.Subscribe(ex => { Console.Error.WriteLine($"Socket error: {ex.Message}"); }); transcribe.TranscribeErrorMessage.Subscribe(message => { Console.Error.WriteLine($"Server error: {message.Error.Title} ({message.Error.Status})"); }); try { await transcribe.ConnectAsync(new TranscribeConfig { PrimaryLanguage = "en", AutomaticPunctuation = true, }); } catch (InvalidOperationException ex) { // CONFIG_DENIED, CONFIG_TIMEOUT, or connection failure Console.Error.WriteLine($"Connect failed: {ex.Message}"); throw; } ``` # Create Transcript Source: https://docs.corti.ai/api-reference/transcripts/create-transcript /api-reference/auto-generated-openapi.yml post /interactions/{id}/transcripts/ Create a transcript from an audio file uploaded to the interaction via `/recordings` endpoint.
Each interaction may have more than one audio file and transcript associated with it. Audio files up to 120 minutes in total audio duration and 150 MB in size may be used.

By default, requests will process synchronously for 25 seconds before timeout, upon which processing will continue asynchronously. Set the `async` parameter to true to receive the location header immediately and process the request asynchronously. Read more [here](https://docs.corti.ai/stt/transcripts).
# Delete Transcript Source: https://docs.corti.ai/api-reference/transcripts/delete-transcript /api-reference/auto-generated-openapi.yml delete /interactions/{id}/transcripts/{transcriptId} Deletes a specific transcript associated with an interaction. # Get Transcript Source: https://docs.corti.ai/api-reference/transcripts/get-transcript /api-reference/auto-generated-openapi.yml get /interactions/{id}/transcripts/{transcriptId} Retrieve a transcript from a specific interaction.
Each interaction may have more than one transcript associated with it. Use the List Transcript request (`GET /interactions/{id}/transcripts/`) to see all transcriptIds available for the interaction.

The client can poll this Get Transcript endpoint (`GET /interactions/{id}/transcripts/{transcriptId}/status`) for transcript status changes:
- `200 OK` with status `processing`, `completed`, or `failed`
- `404 Not Found` if the `interactionId` or `transcriptId` are invalid

Status of `completed` indicates the transcript is finalized. If the transcript is retrieved while status is `processing`, then it will be incomplete.
# Get Transcript Status Source: https://docs.corti.ai/api-reference/transcripts/get-transcript-status /api-reference/auto-generated-openapi.yml get /interactions/{id}/transcripts/{transcriptId}/status Poll for transcript creation status.
Status of `completed` indicates the transcript is finalized.
If the transcript is retrieved while status is `processing`, then it will be incomplete.
Status of `failed` indicate the transcript was not created successfully; please retry.
# List Transcripts Source: https://docs.corti.ai/api-reference/transcripts/list-transcripts /api-reference/auto-generated-openapi.yml get /interactions/{id}/transcripts/ Retrieves a list of transcripts for a given interaction. # Welcome to the Corti API Reference Source: https://docs.corti.ai/api-reference/welcome AI platform for healthcare developers This API Reference provides detailed specifications for integrating with the Corti API, enabling organizations to build bespoke healthcare AI solutions that meet their specific needs. JavaScript and C# .NET SDKs with quickstart guides Download the Corti API Postman collection to start building *** #### Most Popular Detailed spec for real-time dictation and voice commands Detailed spec for real-time conversational intelligence Start here for opening a contextual messaging thread Detailed spec for creating an interaction and setting appropriate context Attach an audio file to the interaction Start or continue your contextual chat and agentic tasks Convert audio files to text Create one to many documents for an interaction Retrieve information about all available experts for use with your agents OpenAI-compatible chat, responses, and embeddings on EU infrastructure *** ### More resources | Resource | Description | | :--------------------------------------------- | :----------------------------------------------------------------------------------- | | [Release notes](/release-notes/overview/) | Upcoming changes and recent API, language model, and app updates. | | [Support center ↗](mailto:help@corti.ai) | Help articles and documentation, contact the Corti team, and manage support tickets. | | [Compliance & Trust ↗](https://trust.corti.ai) | Detailed compliance standards and security certifications. |
[Contact us](mailto:help@corti.ai) if you need more information about the Corti API. # API Reference Source: https://docs.corti.ai/assistant/api-reference Overview of the Embedded API surface, method groups, error model, and common workflows. This page gives you the structure of the Embedded API and links to the individual method reference pages. **Applies to all integration methods** The Embedded API is available through the [Web Component API](/assistant/web-component-api), [Window API](/assistant/window-api), and [PostMessage API](/assistant/postmessage-api). The underlying capabilities and error model are shared, but the invocation shape differs by integration method. **Examples use the Web Component API shape** The linked method reference pages use the Web Component or React API shape for consistency. When you use the Window API or PostMessage, the same operation often keeps the same payload but changes how it is invoked. ## How to use this reference Use the method pages in the sidebar when you need payload details, validation rules, error cases, or return types for a specific method. The API Reference is grouped into: * **Application** methods for authentication, app-level configuration, navigation, account state, template lookup, and mobile companion pairing * **Interaction** methods for interaction setup, session defaults, contextual facts, and recording control * **Deprecated** methods within those groups when you still need the legacy configuration flow during the deprecation period If you need configuration behavior, scenarios, or migration help, use these pages alongside the method reference: * [Configuration Scenarios](/assistant/configuration-scenarios) * [Config Migration Guide](/assistant/configuration-migration) * [Scheduled Deprecations](/assistant/deprecation-timeline) ## Method groups ### Application * [auth()](/assistant/api/auth) - Authenticate the embedded user session * [configureApp()](/assistant/api/configure-app) - Apply app-level UI, appearance, locale, and network configuration * [navigate()](/assistant/api/navigate) - Navigate to an internal Assistant route * [setCredentials()](/assistant/api/set-credentials) - Update the authenticated user's credentials * [getStatus()](/assistant/api/get-status) - Read current application and interaction state * [getTemplates()](/assistant/api/get-templates) - Retrieve available templates for the authenticated user * [showDeviceLinkQR()](/assistant/api/show-device-link-qr) - Display the QR pairing flow for the Corti mobile companion app * [configure() (Deprecated)](/assistant/api/configure) - Legacy app configuration structure ### Interaction * [createInteraction()](/assistant/api/create-interaction) - Create a new interaction session * [setInteractionOptions()](/assistant/api/set-interaction-options) - Apply interaction-level defaults and options * [addFacts()](/assistant/api/add-facts) - Add contextual facts to the current interaction * [startRecording()](/assistant/api/start-recording) - Start recording in the current session * [stopRecording()](/assistant/api/stop-recording) - Stop recording in the current session * [configureSession() (Deprecated)](/assistant/api/configure-session) - Legacy session-level defaults ## Error codes All API actions may return errors with the following structure: ```typescript theme={null} { message: string, code: "UNAUTHORIZED" | "NOT_READY" | "NOT_FOUND" | "INVALID_PAYLOAD" | "INTERNAL_ERROR", details?: unknown, } ``` | Code | Description | Common causes | | ----------------- | ------------------------------------------------ | ------------------------------------------------------------------------------- | | `UNAUTHORIZED` | User is not authenticated or the session expired | `auth()` was not called, or tokens are no longer valid | | `NOT_READY` | A required precondition is missing | No active interaction, not in recording context, or a required view is not open | | `NOT_FOUND` | A requested resource does not exist | Invalid interaction ID, user not found, or template not found | | `INVALID_PAYLOAD` | Request payload validation failed | Missing required fields, invalid formats, unsupported values | | `INTERNAL_ERROR` | Unexpected client or server failure | Retry the request or contact support if the issue persists | ## Workflows Workflow examples now live on [API Reference Workflows](/assistant/api-reference-workflows). Use that page when you want end-to-end examples that combine multiple methods into a realistic integration flow. ## Configuration-specific reference Configuration-specific lookup data such as supported interface languages, dictation language codes, and string override keys lives in the configuration documentation rather than in the method reference. Use these pages when you need those details: * [Supported Values](/assistant/configuration-values) * [Configuration Scenarios](/assistant/configuration-scenarios) * [Config Migration Guide](/assistant/configuration-migration) ## Related documentation * [Web Component API](/assistant/web-component-api) - Package API and integration overview * [Window API](/assistant/window-api) - Same-origin method invocation * [PostMessage API](/assistant/postmessage-api) - Cross-frame method invocation * [Welcome](/assistant/welcome) - Overview of the embedded Assistant Please [contact us](mailto:help@corti.ai) if you need help with a specific API method or integration pattern. # API Reference Workflows Source: https://docs.corti.ai/assistant/api-reference-workflows End-to-end Embedded API workflow examples that combine multiple methods into realistic integration flows. Use this page when you want end-to-end examples that combine multiple Embedded API methods into a realistic integration flow. These workflows complement the individual method reference pages. Use the method pages when you need payload details, validation rules, or return shapes for one specific method. ## Initialize and start recording In the examples below, `api` refers to your Embedded API instance. * Web Component / vanilla: `const api = document.querySelector("corti-embedded");` * React: `const api = useCortiEmbeddedApi(cortiRef);` `navigate({ path: ... })` is used for cross-surface consistency. * On `@corti/embedded-web` versions earlier than `0.3.0`, use `navigate("/session/...")`. * On `@corti/embedded-web@0.3.0` and later, both shapes are accepted. ```typescript theme={null} try { // 1. Authenticate const user = await api.auth({ access_token: "...", refresh_token: "...", id_token: "...", token_type: "Bearer", }); console.log("Authenticated as:", user.email); // 2. Configure app-level settings (optional) await api.configureApp({ locale: { dictationLanguage: "en" }, }); // 3. Configure interaction defaults (optional) await api.setInteractionOptions({ mode: { fallback: "virtual", options: ["in-person", "virtual"], }, }); // 4. Create interaction const interaction = await api.createInteraction({ encounter: { identifier: "enc-123", status: "planned", type: "ambulatory", period: { startedAt: new Date().toISOString() }, title: "Patient Visit", }, }); // 5. Navigate to the session await api.navigate({ path: `/session/${interaction.id}` }); // 6. Start recording await api.startRecording(); } catch (error) { console.error("Workflow failed:", error.message, error.code); } ``` ## Handle recording startup errors ```typescript theme={null} async function startRecordingWithRetry() { try { await api.startRecording(); } catch (error) { switch (error.code) { case "UNAUTHORIZED": await reauthenticate(); await api.startRecording(); break; case "NOT_READY": if (error.message.includes("createInteraction")) { await api.createInteraction({ encounter: { identifier: `enc-${Date.now()}`, status: "planned", type: "ambulatory", period: { startedAt: new Date().toISOString() }, }, }); await api.startRecording(); } break; default: console.error("Unexpected error:", error); } } } ``` ## Complete session lifecycle ```typescript theme={null} let currentInteractionId = null; async function initializeSession() { await api.auth({ /* tokens */ }); await api.configureApp({ ui: { aiChat: true, }, }); await api.setInteractionOptions({ mode: { fallback: "virtual", options: ["in-person", "virtual"], }, spokenLanguage: { fallback: "en-US", }, }); } async function startNewInteraction(encounterData) { const interaction = await api.createInteraction({ encounter: encounterData, }); currentInteractionId = interaction.id; await api.addFacts([ { text: "Chief complaint: Headache", group: "symptoms" }, ]); await api.navigate({ path: `/session/${interaction.id}` }); await api.startRecording(); return interaction; } async function endInteraction() { await api.stopRecording(); const status = await api.getStatus(); return status.interaction; } ``` ## Related reference * [API Reference Overview](/assistant/api-reference) * [auth()](/assistant/api/auth) * [createInteraction()](/assistant/api/create-interaction) * [startRecording()](/assistant/api/start-recording) * [setInteractionOptions()](/assistant/api/set-interaction-options) # addFacts() Source: https://docs.corti.ai/assistant/api/add-facts Reference for the Embedded API addFacts() method. Use `addFacts()` to attach contextual facts to the current interaction. Applies to Web Component, Window API, and PostMessage. Examples on this page use the Web Component API shape. ## Usage ```typescript theme={null} await api.addFacts([ { text: "Chest pain", group: "other" }, { text: "Shortness of breath", group: "other" }, { text: "Fatigue", group: "other" }, ]); ``` ## Prerequisites * User must be authenticated. * An interaction must already exist. ## Input validation * Web Component / React: pass an array of facts. * Window API / PostMessage: pass `{ facts: [...] }`. * `facts[].text`: Required, non-empty string. * `facts[].group`: Optional string, defaults to `"other"`. * `facts[].source`: Optional, defaults to `"user"`. ## Possible errors * `NOT_READY`: No active interaction. * `UNAUTHORIZED`: User not authenticated. * `INVALID_PAYLOAD`: Empty facts array or missing text field. * `INTERNAL_ERROR`: Failed to save facts. ## Returns `void` ## Patient gender guidance In languages where patient gender affects phrasing in documentation, provide a clear sentence in the target document language through `addFacts()`. Common German examples: ```text theme={null} Das Geschlecht des Patienten ist männlich. Das Geschlecht des Patienten ist weiblich. Das Geschlecht des Patienten ist divers. Das Geschlecht des Patienten ist unbekannt. ``` ## Related reference * [API Reference Overview](/assistant/api-reference) * [createInteraction()](/assistant/api/create-interaction) # auth() Source: https://docs.corti.ai/assistant/api/auth Reference for the Embedded API auth() method. Use `auth()` to authenticate the current user session with the embedded Assistant. Applies to Web Component, Window API, and PostMessage. Examples on this page use the Web Component API shape. ## Usage ```typescript theme={null} const user = await api.auth({ access_token: string, refresh_token: string, id_token: string, token_type: string, }); ``` ## Prerequisites * None ## Input validation * All fields (`access_token`, `refresh_token`, `id_token`, `token_type`) are required. * Tokens must be valid JWT strings. ## Possible errors * `INVALID_PAYLOAD`: Missing required authentication fields. * `UNAUTHORIZED`: Invalid or expired tokens. * `INTERNAL_ERROR`: Authentication service unavailable. ## Returns ```typescript theme={null} { id: string, email: string } ``` ## Related reference * [API Reference Overview](/assistant/api-reference) * [createInteraction()](/assistant/api/create-interaction) # configure() (Deprecated) Source: https://docs.corti.ai/assistant/api/configure Reference for the deprecated Embedded API configure() method. Use `configure()` only if you still depend on the legacy configuration structure. `configure()` is deprecated in favor of [configureApp()](/assistant/api/configure-app) and [setInteractionOptions()](/assistant/api/set-interaction-options). It continues to work during the deprecation period. See the [Migration Guide](/assistant/configuration-migration) and [Scheduled Deprecations](/assistant/deprecation-timeline). ## Usage ```typescript theme={null} const config = await api.configure({ features: { interactionTitle: boolean, aiChat: boolean, documentFeedback: boolean, navigation: boolean, virtualMode: boolean, syncDocumentAction: boolean, templateEditor: boolean, }, appearance: { primaryColor: string | null, }, locale: { interfaceLanguage: string | null, dictationLanguage: string, overrides: Record, }, }); ``` ## Prerequisites * None ## Input validation * `appearance.primaryColor`: Must be valid CSS color or `null`. * `locale.interfaceLanguage`: Must be a supported interface language or `null`. * `locale.dictationLanguage`: Must be a supported dictation language. * `locale.overrides`: Keys must match known override strings. * `features.*`: Must be boolean values. * `network.websocketBaseUrl`: Must be a valid URL if provided. ## Possible errors * `INVALID_PAYLOAD`: Invalid color format, unsupported language code, or non-boolean feature flag. * `INTERNAL_ERROR`: Failed to apply configuration. ## Returns ```typescript theme={null} { features: { interactionTitle: boolean, aiChat: boolean, documentFeedback: boolean, navigation: boolean, virtualMode: boolean, syncDocumentAction: boolean, templateEditor: boolean, }, appearance: { primaryColor: string | null, }, locale: { interfaceLanguage: string | null, dictationLanguage: string, overrides: Record, } } ``` ## Defaults * `features.interactionTitle: true` * `features.aiChat: true` * `features.documentFeedback: true` * `features.navigation: false` * `features.virtualMode: true` * `features.syncDocumentAction: false` * `features.templateEditor: true` * `appearance.primaryColor: null` * `locale.interfaceLanguage: null` * `locale.dictationLanguage: "en"` * `locale.overrides: {}` ## Related reference * [API Reference Overview](/assistant/api-reference) * [configureApp()](/assistant/api/configure-app) * [setInteractionOptions()](/assistant/api/set-interaction-options) * [Configuration Guide (Deprecated)](/assistant/configuration) # configureApp() Source: https://docs.corti.ai/assistant/api/configure-app Reference for the Embedded API configureApp() method. Use `configureApp()` for app-level configuration such as UI visibility, companion app access, appearance, locale, and network settings. `debug` enables a debug panel intended for development only. Do not enable it in staging or production. Applies to Web Component, Window API, and PostMessage. Examples on this page use the Web Component API shape. `configureApp()` is patchable and may be called multiple times. Use [Configuration Scenarios](/assistant/configuration-scenarios) for practical examples and [Supported Values](/assistant/configuration-values) for supported languages and override keys. ## Payload shape ```typescript theme={null} type ConfigureAppPayload = { debug?: boolean; ui?: { interactionTitle?: boolean; aiChat?: boolean; documentFeedback?: boolean; navigation?: boolean; }; companionApp?: { enabled: boolean; }; appearance?: { primaryColor?: string | null; }; locale?: { interfaceLanguage?: string; dictationLanguage?: string | null; overrides?: Record; }; network?: { websocketBaseUrl?: string | null; }; }; ``` ## Usage ```typescript theme={null} await api.configureApp({ debug: false, ui: { interactionTitle: true, aiChat: true, documentFeedback: true, navigation: false, }, companionApp: { enabled: true, }, appearance: { primaryColor: "#000000", }, locale: { interfaceLanguage: "da-DK", dictationLanguage: "da", overrides: { "interview.document.syncDocument.label": "Sync Document", }, }, network: { websocketBaseUrl: "wss://proxy.example.com", }, }); ``` ## Prerequisites * None ## Input validation * `debug`, `ui.interactionTitle`, `ui.aiChat`, `ui.documentFeedback`, `ui.navigation`, and `companionApp.enabled`: Must be boolean values when provided. * `appearance.primaryColor`: Must be a valid CSS color string or `null` when provided. * `locale.interfaceLanguage`: Must be a supported interface language when provided. * `locale.dictationLanguage`: Must be a supported dictation language or `null` when provided. * `locale.overrides`: Keys must match known override strings. * `network.websocketBaseUrl`: Must be a valid WebSocket URL or `null` when provided. This is mainly for proxy deployments. See the [Proxy guide](/assistant/proxy). ## Possible errors * `INVALID_PAYLOAD`: Invalid color, URL, language code, or non-boolean UI value. * `INTERNAL_ERROR`: Failed to apply configuration. ## Returns This method is documented as a configuration action and does not expose a response payload in the current public reference. ## Related reference * [API Reference Overview](/assistant/api-reference) * [Supported Values](/assistant/configuration-values) * [Configuration Scenarios](/assistant/configuration-scenarios) * [showDeviceLinkQR()](/assistant/api/show-device-link-qr) * [Configuration Guide (Deprecated)](/assistant/configuration) # configureSession() (Deprecated) Source: https://docs.corti.ai/assistant/api/configure-session Reference for the deprecated Embedded API configureSession() method. Use `configureSession()` only if you still depend on the legacy session-configuration structure. `configureSession()` is deprecated in favor of [setInteractionOptions()](/assistant/api/set-interaction-options). It continues to work during the deprecation period. If your integration depends on `defaultOutputLanguage`, keep using its legacy-compatible behavior during this period. See the [Migration Guide](/assistant/configuration-migration) and [Scheduled Deprecations](/assistant/deprecation-timeline). ## Usage ```typescript theme={null} await api.configureSession({ defaultLanguage: "en", defaultOutputLanguage: "en", defaultTemplateKey: "corti-soap", defaultMode: "virtual", }); ``` ```javascript theme={null} await api.configureSession({ defaultTemplateKey: "corti-soap", defaultOutputLanguage: "en", }); ``` ## Prerequisites * User must be authenticated. ## Input validation * `defaultLanguage`: Must be a valid language code. * `defaultOutputLanguage`: Must be a valid language code. * `defaultTemplateKey`: Must be a language-agnostic template identifier. * `defaultMode`: Must be either `"virtual"` or `"in-person"`. * If either `defaultTemplateKey` or `defaultOutputLanguage` is provided, both must be provided together. ## Possible errors * `UNAUTHORIZED`: User not authenticated. * `INVALID_PAYLOAD`: Invalid payload, including when only one of `defaultTemplateKey` or `defaultOutputLanguage` is provided. * `NOT_FOUND`: No template matches `defaultTemplateKey` plus `defaultOutputLanguage`. * `INTERNAL_ERROR`: Failed to update session settings. ## Returns `void` ## Related reference * [API Reference Overview](/assistant/api-reference) * [setInteractionOptions()](/assistant/api/set-interaction-options) * [getTemplates()](/assistant/api/get-templates) # createInteraction() Source: https://docs.corti.ai/assistant/api/create-interaction Reference for the Embedded API createInteraction() method. Use `createInteraction()` to create a new interaction session before navigating into it or starting recording. Applies to Web Component, Window API, and PostMessage. Examples on this page use the Web Component API shape. ## Usage ```typescript theme={null} const interaction = await api.createInteraction({ assignedUserId: null, encounter: { identifier: `encounter-${Date.now()}`, status: "planned", type: "first_consultation", period: { startedAt: new Date().toISOString(), }, title: "Initial Consultation", }, }); ``` ## Prerequisites * User must be authenticated. ## Input validation * `encounter.identifier`: Required, non-empty string. * `encounter.status`: Must be one of `"planned"`, `"in-progress"`, `"completed"`, or `"cancelled"`. * `encounter.type`: Must be one of the supported encounter types. * `encounter.period.startedAt`: Required ISO 8601 datetime string. * `encounter.title`: Optional string. * `assignedUserId`: Optional string or `null`. ## Possible errors * `UNAUTHORIZED`: User not authenticated. * `INVALID_PAYLOAD`: Missing required fields, invalid encounter status or type, or invalid date format. * `INTERNAL_ERROR`: Failed to create interaction. ## Returns ```typescript theme={null} { id: string, createdAt: string, status?: string } ``` ## Notes * Create the interaction before calling [startRecording()](/assistant/api/start-recording). ## Related reference * [API Reference Overview](/assistant/api-reference) * [addFacts()](/assistant/api/add-facts) * [startRecording()](/assistant/api/start-recording) # getStatus() Source: https://docs.corti.ai/assistant/api/get-status Reference for the Embedded API getStatus() method. Use `getStatus()` to inspect the current application state, including authentication, current URL, interaction details, documents, and facts. Applies to Web Component, Window API, and PostMessage. Examples on this page use the Web Component API shape. ## Usage ```typescript theme={null} const status = await api.getStatus(); ``` ## Prerequisites * None ## Input validation * None ## Possible errors * This action does not typically throw structured error codes. * In rare cases, partial data may be returned if specific state queries fail. ## Returns ```typescript theme={null} { applicationVersion: string, auth: { isAuthenticated: boolean, user: { id: string, email: string } | null }, currentUrl: string, isMedicalDevice: boolean, medicalDevice?: { buildDate: string, deviceIdentifier: string, udi: string }, interaction: { id: string, title: string, state: "planned" | "ongoing" | "paused" | "disconnected" | "ending" | "parsing" | "ended", startedAt: string, endedAt: string | null, endsAt: string | null, transcripts: Array, documents: Array, facts: Array, websocketUrl: string } | null } ``` ## Notes * This method is useful for debugging and for checking whether prerequisites are already satisfied. * `applicationVersion` is the application release version. * `isMedicalDevice` indicates whether the current build is a regulated medical device. When `true`, the `medicalDevice` object is included with the device identifier, build date, and composed UDI. When `false`, `medicalDevice` is omitted. * `interaction` may be `null` if no interaction is active or if interaction lookup fails. ## Related reference * [API Reference Overview](/assistant/api-reference) * [auth()](/assistant/api/auth) * [createInteraction()](/assistant/api/create-interaction) # getTemplates() Source: https://docs.corti.ai/assistant/api/get-templates Reference for the Embedded API getTemplates() method. Use `getTemplates()` to retrieve all document templates available to the authenticated user. Applies to Web Component, Window API, and PostMessage. Examples on this page use the Web Component API shape. Use this method before `setInteractionOptions()` when you need to verify the template id and language combination you want to use as the default. ## Usage ```typescript theme={null} const response = await api.getTemplates(); console.log(response.templates); ``` ## Prerequisites * User must be authenticated. ## Input validation * None ## Possible errors * `UNAUTHORIZED`: User not authenticated. * `INTERNAL_ERROR`: Failed to fetch templates from the server. ## Returns ```typescript theme={null} { templates: Array<{ id: string; name: string; description?: string; language: { code: string; name: string; locale?: string; }; sections: Array<{ id: string; title: string; }>; isCustom: boolean; }>; } ``` ## Use cases * Display a template picker. * Filter templates by language. * Distinguish between built-in and custom templates. * Pre-populate template selection based on user preferences or defaults. ## Related reference * [API Reference Overview](/assistant/api-reference) * [setInteractionOptions()](/assistant/api/set-interaction-options) # navigate() Source: https://docs.corti.ai/assistant/api/navigate Reference for the Embedded API navigate() method. Use `navigate()` to move the embedded Assistant to a specific internal route. Applies to Web Component, Window API, and PostMessage. Examples on this page use the Web Component API shape. ## Usage ```typescript theme={null} await api.navigate({ path: "/session/interaction-123" }); ``` For consistency across transports, prefer object payload form. Web Component / React compatibility: * `@corti/embedded-web` earlier than `0.3.0`: only `api.navigate("/session/interaction-123")` is accepted. * `@corti/embedded-web@0.3.0` and later: both `api.navigate("/session/interaction-123")` and `api.navigate({ path: "/session/interaction-123" })` are accepted. ## Prerequisites * None ## Input validation * Window API / PostMessage: payload must be an object with `path`. * Web Component / React on `@corti/embedded-web@0.3.0` and later: payload can be an object with `path` (recommended) or a path string. * Web Component / React on versions earlier than `0.3.0`: payload must be a path string. * `path`: Required. * `path`: Must be a string that starts with `/`. * `path`: Cannot be a full URL. * `path`: Must stay within the embedded application. ## Possible errors * `INVALID_PAYLOAD`: * Path does not start with `/`. * Path is a full URL. * `INTERNAL_ERROR`: Navigation failed. ## Returns `void` ## Valid path patterns * `/` - start a new session * `/session/` - open an existing session * `/templates` - browse and create templates * `/settings/preferences` - edit defaults like languages and default session settings * `/settings/input` - edit dictation input settings * `/settings/account` - edit general account settings * `/settings/archive` - view items in and restore from archive ## Related reference * [API Reference Overview](/assistant/api-reference) * [createInteraction()](/assistant/api/create-interaction) # setCredentials() Source: https://docs.corti.ai/assistant/api/set-credentials Reference for the Embedded API setCredentials() method. Use `setCredentials()` to change the credentials of the currently authenticated user. Applies to Web Component, Window API, and PostMessage. Examples on this page use the Web Component API shape. ## Usage ```typescript theme={null} await api.setCredentials({ password: "YOUR_NEW_PASSWORD" }); ``` ## Prerequisites * User must be authenticated. ## Input validation * `password`: Required string. * Password must meet policy requirements: * Minimum 8 characters. * At least 1 uppercase letter. * At least 1 lowercase letter. * At least 1 number. * At least 1 special character. ## Possible errors * `UNAUTHORIZED`: User not authenticated. * `INVALID_PAYLOAD`: Password does not meet requirements. * `INTERNAL_ERROR`: Failed to update password. ## Returns `void` ## Related reference * [API Reference Overview](/assistant/api-reference) * [auth()](/assistant/api/auth) # setInteractionOptions() Source: https://docs.corti.ai/assistant/api/set-interaction-options Reference for the Embedded API setInteractionOptions() method. Use `setInteractionOptions()` for interaction-level defaults such as mode, spoken language, template defaults, personal templates, standard templates, project templates, inline templates, and document actions. When you set `templates.defaultTemplate`, it can provide a fallback template for new sessions or force the first generated document to use a specific template. Fallback templates are used only when the user does not already have their own default template set. Applies to Web Component, Window API, and PostMessage. Examples on this page use the Web Component API shape. `setInteractionOptions()` is a one-shot configuration for the embedded instance. Set it before the user starts or opens an interaction. Use [Configuration Scenarios](/assistant/configuration-scenarios) for practical examples and [Supported Values](/assistant/configuration-values) for supported languages and other lookup values. `templates.sources.standard`, `templates.sources.project`, and `templates.sources.inline` apply only when [Guided Document Generation](/release-notes/corti-assistant#v12-19-0) is enabled for your account. If you send these sources before rollout reaches your environment, the options are inert and do not change behavior. Assistant only dispatches `embedded.interactionOptionsWarning` with `unsupportedSources`. ## Usage ```typescript theme={null} await api.setInteractionOptions({ mode: { fallback: "in-person", options: ["in-person", "virtual"], }, spokenLanguage: { fallback: "da", options: ["da", "en"], }, templates: { sources: { personal: { enabled: true, sectionFields: { heading: { editable: true }, description: { editable: true }, miscPrompt: { visible: true, editable: true }, outputSchema: { visible: true, editable: true }, }, }, standard: { enabled: true, include: { regions: ["BEL"], families: ["soap"], }, exclude: { regions: ["USA"], families: ["letter"], }, }, project: { enabled: true, include: { ids: ["7a0ab5b8-1f62-4f75-86a7-4fcd8fd07b5d"], }, exclude: { ids: ["d7a7fd8c-8892-4475-81d3-3414b7d8e27d"], }, }, inline: { enabled: true, templates: [ { id: "inline-soap", name: "Embedded SOAP", generation: { instructions: { prompt: "Generate a concise SOAP note for the current encounter.", }, sections: [ { heading: "Subjective", instructions: { contentPrompt: "Summarize the patient's subjective symptoms.", }, }, ], }, }, ], }, }, defaultTemplate: { behaviour: "force-first-document", template: { source: "inline", id: "inline-soap", }, allowUserSelection: false, }, }, documents: { actions: { sync: false, }, allowedLanguages: ["en", "da"], maxGenerated: 1, }, }); ``` ## Prerequisites * User must be authenticated. ## Defaults and omitted values `setInteractionOptions()` applies only the option slices you provide. When you omit an option, the embedded Assistant does not apply an override for that setting and keeps the current or product default behavior. Inline templates are available only to the embedded instance that receives the payload. Use an `id` when another option, such as `templates.defaultTemplate`, needs to reference the inline template. For personal template section fields, the default editor behavior is: | Field | Default visibility | Default editability | | -------------- | ------------------ | --------------------- | | `heading` | Visible | Editable | | `description` | Visible | Editable | | `miscPrompt` | Hidden | Editable when visible | | `outputSchema` | Hidden | Editable when visible | Use `visible: true` to show a prompt field. Use `editable: false` to make a visible field read-only. ## Input validation * `mode.fallback`: Must be either `"in-person"` or `"virtual"`. * `mode.options`: Must contain one or both of `"in-person"` and `"virtual"`. * `spokenLanguage.fallback`: Must be a valid language code when provided. * `spokenLanguage.options`: Must be an array of valid language codes when provided. If you provide both `fallback` and `options`, `fallback` should be one of the listed options. * `templates.sources.personal.enabled`: Must be a boolean when provided. * `templates.sources.personal.sectionFields`: Optional object controlling field-level access in the guided template editor. `heading` and `description` accept an optional `{ editable?: boolean }` object. `miscPrompt` and `outputSchema` accept an optional `{ visible?: boolean; editable?: boolean }` object. * `templates.sources.standard.enabled`: Must be a boolean when provided. * `templates.sources.standard.include.regions`: Must be an array of ISO 3166-1 alpha-3 template region codes when provided. * `templates.sources.standard.include.families`: Must be an array of standard template family identifiers when provided. * `templates.sources.standard.exclude.regions`: Must be an array of ISO 3166-1 alpha-3 template region codes when provided. * `templates.sources.standard.exclude.families`: Must be an array of standard template family identifiers when provided. * `templates.sources.project.enabled`: Must be a boolean when provided. * `templates.sources.project.include.ids`: Must be an array of project template UUID strings when provided. * `templates.sources.project.exclude.ids`: Must be an array of project template UUID strings when provided. * `templates.sources.inline.enabled`: Must be a boolean when provided. * `templates.sources.inline.templates`: Must be an array of inline template objects when provided. Inline template IDs only need to be unique within the current `setInteractionOptions()` payload and are required only when referenced by another option. * `templates.defaultTemplate.behaviour`: Must be `"fallback"` or `"force-first-document"` when provided. * `templates.defaultTemplate.template.source`: Must be `"standard"`, `"project"`, or `"inline"` when provided. * `templates.defaultTemplate.template.id`: Must be a valid template identifier for the selected source when provided. Standard templates use fully resolved identifiers such as `"corti-soap-en"`; inline templates use the local inline template ID. * `templates.defaultTemplate.allowUserSelection`: Must be a boolean when provided. Set it to `false` to hide user controls for selecting and persisting a default template. * `documents.actions.sync`: Must be a boolean when provided. * `documents.allowedLanguages`: Must be an array of valid BCP 47 locale codes when provided. Region-less codes (e.g., `"en"`, `"da"`) are recommended; region variants (e.g., `"en-US"`) are supported if the region-less code does not provide your desired results. * `documents.maxGenerated`: Must be a number or `"unlimited"` when provided. `0` is treated as unlimited. ## Possible errors * `UNAUTHORIZED`: User not authenticated. * `INVALID_PAYLOAD`: Invalid mode, language, template, or boolean value. * `INTERNAL_ERROR`: Failed to apply interaction options. ## Returns This method is documented as a configuration action and does not expose a response payload in the current public reference. ## Related reference * [API Reference Overview](/assistant/api-reference) * [Supported Values](/assistant/configuration-values) * [Configuration Scenarios](/assistant/configuration-scenarios) * [configureSession() (Deprecated)](/assistant/api/configure-session) # showDeviceLinkQR() Source: https://docs.corti.ai/assistant/api/show-device-link-qr Reference for the Embedded API showDeviceLinkQR() method. Use `showDeviceLinkQR()` to initiate device pairing for the Corti mobile companion app. The method opens a QR pairing screen that lives on a separate URL. How you surface this is up to you, but a common pattern is a "Pair mobile device" option in your application's settings that presents the URL in a modal or dedicated panel. The method resolves when the pairing request settles and returns the final status. Applies to Web Component, Window API, and PostMessage. Examples on this page use the Web Component API shape. Enable the companion app surface with [`configureApp()`](/assistant/api/configure-app) before offering mobile pairing controls in your host application. QR code generation requires a **second** Keycloak token response for the same user — do not reuse the token from your original authentication request. Each application (desktop and companion) needs its own token so that both can refresh their sessions independently. ## Usage ```ts title="TypeScript" theme={null} // Ensure that you have authenticated your user with your chosen method // and have obtained the required tokens // Replace these with your values const ACCESS_TOKEN = ""; const REFRESH_TOKEN = ""; await api.configureApp({ companionApp: { enabled: true, }, }); // Generate the QR code for pairing the companion app const result = await api.showDeviceLinkQR({ access_token: ACCESS_TOKEN, refresh_token: REFRESH_TOKEN, token_type: "Bearer", }); if (result.status === "approved") { // The mobile companion app was paired successfully. } ``` ## Prerequisites * User must be authenticated in the Embedded Assistant. * `companionApp.enabled` should be set to `true` in `configureApp()`. * The payload must include a valid access token and refresh token for the same user session. ## Input validation * `access_token`: Must be a valid OAuth access token. * `refresh_token`: Must be present. The mobile companion app uses it to complete pairing after approval. * `token_type`: Must match the OAuth token response, usually `"Bearer"`. ## Returns ```typescript theme={null} type ShowDeviceLinkQRResponse = { status: "approved" | "denied" | "expired" | "dismissed"; }; ``` * `approved`: The user approved the mobile device pairing request. * `denied`: The user denied the mobile device pairing request. * `expired`: The QR code or approval window expired before pairing completed. * `dismissed`: A new QR request replaced the previous one, the embedded route was dismissed, or the pairing flow could not complete. ## Possible errors * `UNAUTHORIZED`: User not authenticated. * `INVALID_PAYLOAD`: Token payload is missing required values. * `INTERNAL_ERROR`: Failed to create or display the pairing request. ## Companion app installation The pairing page displays a QR code. Users scan it from the Corti Companion app or enter the code manually. A confirmation prompt then appears on the desktop showing the device name, so the user can approve or deny to make sure they are pairing the intended device. Once paired, the companion app must be open and running for the phone to appear as a selectable microphone source in the Embedded Assistant. If you want to surface download links in your own application's help center or onboarding flow, the app is available on iOS and Android: ## Related reference * [API Reference Overview](/assistant/api-reference) * [configureApp()](/assistant/api/configure-app) * [Web Component API](/assistant/web-component-api) # startRecording() Source: https://docs.corti.ai/assistant/api/start-recording Reference for the Embedded API startRecording() method. Use `startRecording()` to begin recording within the current embedded session. Applies to Web Component, Window API, and PostMessage. Examples on this page use the Web Component API shape. ## Usage ```typescript theme={null} await api.startRecording(); ``` ## Prerequisites * User must be authenticated. * An interaction must exist. * The application must be in an interview or session context. ## Input validation * None ## Possible errors * `NOT_READY`: Not in interview context or no interaction exists. * `UNAUTHORIZED`: User not authenticated. * `NOT_FOUND`: Interaction not found. * `INTERNAL_ERROR`: Failed to connect to the recording service. ## Returns `void` ## Related reference * [API Reference Overview](/assistant/api-reference) * [createInteraction()](/assistant/api/create-interaction) * [stopRecording()](/assistant/api/stop-recording) # stopRecording() Source: https://docs.corti.ai/assistant/api/stop-recording Reference for the Embedded API stopRecording() method. Use `stopRecording()` to end recording within the current embedded session. Applies to Web Component, Window API, and PostMessage. Examples on this page use the Web Component API shape. ## Usage ```typescript theme={null} await api.stopRecording(); ``` ## Prerequisites * Recording must already be active. ## Input validation * None ## Possible errors * `NOT_READY`: Not in an active recording session. * `INTERNAL_ERROR`: Failed to disconnect from the recording service. ## Returns `void` ## Related reference * [API Reference Overview](/assistant/api-reference) * [startRecording()](/assistant/api/start-recording) * [getStatus()](/assistant/api/get-status) # Authentication for Embedded Users Source: https://docs.corti.ai/assistant/authentication Choosing the right OAuth2 flow for Corti Embedded integrations ## Background OAuth (Open Authorization) is an open-standard framework for access delegation, allowing applications to securely access a user's protected resources without exposing their login credentials. By keeping passwords private and limiting access to sensitive information, OAuth improves both security and access management across web, mobile, and desktop applications. OAuth 2.0, the current and most widely adopted version, expands upon the original protocol to support APIs, mobile apps, and connected devices, offering multiple authorization flows tailored to different application types. Corti Assistant requires user-based authentication. Client credentials flows and other machine-to-machine authentication methods are NOT supported for embedded Corti Assistant integrations. You must authenticate as an end user, not as an application. When embedding Corti Assistant in your application (via iFrame/WebView), **it's important to use the right OAuth2 grant type** that supports user-based authentication. This guide explains the OAuth flows that work with embedded Corti Assistant, focusing on user-based authentication methods that are suitable for interactive scenarios. *** ## Supported OAuth grant types for Embedded Corti Assistant The following OAuth flows support user-based authentication and can be used with embedded Corti Assistant. **Client credentials grant is NOT supported** as it does not provide user context. ### 1. Authorization code flow with PKCE (recommended) **Best for:** Native apps, single-page apps, or any browser-based integration where a user is present. **Why:** This flow is secure, interactive, and doesn't require a client secret (ideal for public clients). Proof Key for Code Exchange (PKCE) protects against code interception attacks. **How it works:** 1. Your app redirects the user to Corti's OAuth2 authorization server. 2. The user logs in and grants permission. 3. Corti redirects back with an authorization code. 4. Your app exchanges the code (with the PKCE verifier) for an access token. **Key advantages:** * Secure and suitable for embedded web apps. * No client secret is required. * Enforces user interaction. This example uses the [Corti JavaScript SDK](/sdk/js/overview) (`@corti/sdk`) ```javascript Step 1: Generate Authorization URL (Frontend) [expandable] theme={null} import { CortiAuth, CortiEnvironment } from "@corti/sdk"; const auth = new CortiAuth({ environment: CortiEnvironment.Eu, tenantName: "YOUR_TENANT_NAME", }); // SDK automatically generates code verifier, stores it in localStorage and makes redirect await auth.authorizePkceUrl({ clientId: "YOUR_CLIENT_ID", redirectUri: "https://your-app.com/callback" }); ``` ```javascript Step 2: Handle the Callback and Exchange Code for Tokens [expandable] theme={null} // Extract the authorization code from URL parameters const urlParams = new URLSearchParams(window.location.search); const code = urlParams.get('code'); const error = urlParams.get('error'); if (error) { console.error('Authorization failed:', error); return; } if (code) { // Exchange the authorization code for tokens using SDK const tokenResponse = await auth.getPkceFlowToken({ clientId: "YOUR_CLIENT_ID", redirectUri: "https://your-app.com/callback", code: code, }); const { accessToken, refreshToken } = tokenResponse; } ``` This flow requires two stages: generating the code verifier/challenge and handling the token exchange after the redirect. ```bash Step 1: Redirect User to Authorize [expandable] theme={null} // Generate code verifier + challenge const code_verifier = crypto.randomUUID().replace(/-/g, ''); const code_challenge = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(code_verifier)); const base64url = btoa(String.fromCharCode(...new Uint8Array(code_challenge))) .replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""); // Store verifier for use after redirect localStorage.setItem('pkce_verifier', code_verifier); // Redirect to authorization server window.location.href = `https://auth.us.corti.app/realms//protocol/openid-connect/auth?response_type=code&client_id=YOUR_CLIENT_ID&redirect_uri=https://yourapp.com/callback&code_challenge=${base64url}&code_challenge_method=S256&scope=openid`; ``` ```bash Step 2: Exchange Code for Token (after redirect) [expandable] theme={null} const code = new URLSearchParams(window.location.search).get('code'); const code_verifier = localStorage.getItem('pkce_verifier'); const response = await fetch('https://auth.us.corti.app/realms//protocol/openid-connect/token', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ grant_type: 'authorization_code', client_id: 'YOUR_CLIENT_ID', redirect_uri: 'https://yourapp.com/callback', code, code_verifier }) }); const data = await response.json(); console.log('Access Token:', data.access_token); ``` This example uses standard .NET libraries for OAuth2 PKCE flow ```csharp Step 1: Generate Authorization URL (Frontend) [expandable] theme={null} using System; using System.Security.Cryptography; using System.Text; using System.Web; public class PkceAuth { private const string ClientId = "YOUR_CLIENT_ID"; private const string RedirectUri = "https://your-app.com/callback"; private const string AuthBaseUrl = "https://auth.us.corti.app/realms//protocol/openid-connect"; public static (string authUrl, string codeVerifier) GenerateAuthorizationUrl() { // Generate code verifier (random string) var codeVerifier = GenerateCodeVerifier(); // Generate code challenge (SHA256 hash, base64url encoded) var codeChallenge = GenerateCodeChallenge(codeVerifier); // Store code verifier (e.g., in session or secure storage) // In a real app, store this securely for use after redirect HttpContext.Current.Session["pkce_verifier"] = codeVerifier; // Build authorization URL var authUrl = $"{AuthBaseUrl}/auth?" + $"response_type=code&" + $"client_id={HttpUtility.UrlEncode(ClientId)}&" + $"redirect_uri={HttpUtility.UrlEncode(RedirectUri)}&" + $"code_challenge={codeChallenge}&" + $"code_challenge_method=S256&" + $"scope=openid"; return (authUrl, codeVerifier); } private static string GenerateCodeVerifier() { var bytes = new byte[32]; using (var rng = RandomNumberGenerator.Create()) { rng.GetBytes(bytes); } return Convert.ToBase64String(bytes) .TrimEnd('=') .Replace('+', '-') .Replace('/', '_'); } private static string GenerateCodeChallenge(string codeVerifier) { using (var sha256 = SHA256.Create()) { var challengeBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(codeVerifier)); return Convert.ToBase64String(challengeBytes) .TrimEnd('=') .Replace('+', '-') .Replace('/', '_'); } } } ``` ```csharp Step 2: Exchange Code for Token (after redirect) [expandable] theme={null} using System; using System.Collections.Generic; using System.Net.Http; using System.Text.Json; using System.Threading.Tasks; using System.Web; public class TokenExchange { private const string ClientId = "YOUR_CLIENT_ID"; private const string RedirectUri = "https://your-app.com/callback"; private const string TokenUrl = "https://auth.us.corti.app/realms//protocol/openid-connect/token"; public static async Task ExchangeCodeForTokenAsync(string code, string codeVerifier) { using var httpClient = new HttpClient(); var formData = new List> { new KeyValuePair("grant_type", "authorization_code"), new KeyValuePair("client_id", ClientId), new KeyValuePair("redirect_uri", RedirectUri), new KeyValuePair("code", code), new KeyValuePair("code_verifier", codeVerifier) }; var content = new FormUrlEncodedContent(formData); var response = await httpClient.PostAsync(TokenUrl, content); response.EnsureSuccessStatusCode(); var responseBody = await response.Content.ReadAsStringAsync(); var tokenData = JsonSerializer.Deserialize(responseBody); return new TokenResponse { AccessToken = tokenData.GetProperty("access_token").GetString(), RefreshToken = tokenData.GetProperty("refresh_token").GetString(), ExpiresIn = tokenData.GetProperty("expires_in").GetInt32() }; } } public class TokenResponse { public string AccessToken { get; set; } public string RefreshToken { get; set; } public int ExpiresIn { get; set; } } // Usage in callback handler (e.g., ASP.NET MVC Controller) public class AuthController : Controller { public async Task Callback(string code) { if (string.IsNullOrEmpty(code)) { return View("Error"); } // Retrieve stored code verifier var codeVerifier = Session["pkce_verifier"] as string; if (string.IsNullOrEmpty(codeVerifier)) { return View("Error"); } var tokenResponse = await TokenExchange.ExchangeCodeForTokenAsync(code, codeVerifier); // Store tokens securely (e.g., in session or secure cookie) Session["access_token"] = tokenResponse.AccessToken; return RedirectToAction("Index", "Home"); } } ``` Use this gold standard for Corti Assistant embedded use cases. *** ### 2. Authorization code flow (without PKCE) **Best for:** Server-side web applications embedding Corti Assistant where the client secret can be safely stored on the backend. **Why:** Similar to PKCE, but requires storing a client secret — which is *not* safe in public or browser-based clients. **Key Concerns:** * Unsafe for apps where the frontend or iFrame can be inspected. * Only acceptable in secure backend environments where the client secret can be protected. This example uses the [Corti JavaScript SDK](/sdk/js/overview) (`@corti/sdk`) ```javascript Step 1: Create Authorization URL [expandable] theme={null} import { CortiAuth, CortiEnvironment } from "@corti/sdk"; const auth = new CortiAuth({ environment: CortiEnvironment.Eu, tenantName: "YOUR_TENANT_NAME", }); // Generate authorization URL await auth.authorizeURL({ clientId: "YOUR_CLIENT_ID", redirectUri: "https://your-app.com/callback", }); ``` ```javascript Step 2: Handle the Callback and Exchange Code for Tokens [expandable] theme={null} // Extract the authorization code from URL parameters const urlParams = new URLSearchParams(window.location.search); const code = urlParams.get('code'); const error = urlParams.get('error'); if (error) { console.error('Authorization failed:', error); return; } if (code) { // Exchange the authorization code for tokens using SDK (client-side) const response = await fetch('http://localhost:3000/callback', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ code }), }); if (!response.ok) { throw new Error('Authentication failed'); } const tokens = await response.json(); } ``` ```javascript Step 3: Exchange Code for Access Token (Backend) [expandable] theme={null} // server.js or routes/callback.js import express from "express"; import { CortiAuth } from "@corti/sdk"; const app = express(); const CLIENT_ID = "YOUR_CLIENT_ID"; const CLIENT_SECRET = "YOUR_CLIENT_SECRET"; const TENANT_NAME = "YOUR_TENANT_NAME"; const ENVIRONMENT = "YOUR_ENVIRONMENT"; const REDIRECT_URI = "https://yourapp.com/callback"; // must match OAuth settings app.get("/callback", async (req, res) => { const authCode = req.query.code; if (!authCode) { return res.status(400).send("Missing authorization code"); } try { // Initialize CortiAuth SDK client const auth = new CortiAuth({ environment: ENVIRONMENT, tenantName: TENANT_NAME, }); // Exchange code for token const tokens = await auth.getCodeFlowToken({ clientId: CLIENT_ID, clientSecret: CLIENT_SECRET, redirectUri: REDIRECT_URI, code: authCode, }); // Example "do something": simply log the access token console.log("Access Token:", tokens.accessToken); // Redirect back to app with token in query param (example) res.json(tokens); } catch (err) { console.error("OAuth error:", err); return res.status(500).send("Failed to exchange authorization code"); } }); app.listen(3000, () => { console.log("Server running at http://localhost:3000"); }); ``` This version assumes: * Your app has a **frontend (e.g., React)** that initiates the login. * Your app has a **backend (e.g., Node.js + Express)** that securely stores the `client_secret` and handles the token exchange. Use this only if your backend can securely store the client secret (e.g. not in a browser or mobile app). Ideal for server-rendered or hybrid web apps. ```bash Step 1: Frontend – Redirect the User to Log In [expandable] theme={null} // login.tsx or similar const clientId = 'YOUR_CLIENT_ID'; const redirectUri = 'https://yourapp.com/callback'; // Must match what's registered in Corti OAuth const login = () => { const authUrl = new URL('https://auth.us.corti.app/realms//protocol/openid-connect/auth'); authUrl.searchParams.set('response_type', 'code'); authUrl.searchParams.set('client_id', clientId); authUrl.searchParams.set('redirect_uri', redirectUri); authUrl.searchParams.set('scope', 'openid profile'); window.location.href = authUrl.toString(); // Send user to Corti login page }; ``` ```bash Step 2: Corti Redirects Back with a Code theme={null} https://yourapp.com/callback?code=abc123xyz ``` ```bash Step 3: Backend – Exchange Code for Access Token [expandable] theme={null} // server.js or routes/callback.js const express = require('express'); const fetch = require('node-fetch'); const app = express(); const CLIENT_ID = 'YOUR_CLIENT_ID'; const CLIENT_SECRET = 'YOUR_CLIENT_SECRET'; const REDIRECT_URI = 'https://yourapp.com/callback'; // Must match Step 1 app.get('/callback', async (req, res) => { const authCode = req.query.code; if (!authCode) { return res.status(400).send('Missing authorization code'); } // Exchange code for access token const tokenResponse = await fetch('https://auth.us.corti.app/realms//protocol/openid-connect/token', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ grant_type: 'authorization_code', code: authCode, client_id: CLIENT_ID, client_secret: CLIENT_SECRET, redirect_uri: REDIRECT_URI }) }); const tokenData = await tokenResponse.json(); if (tokenData.error) { return res.status(500).send(`Token error: ${tokenData.error_description}`); } // Do something useful with the token, like create a session console.log('Access Token:', tokenData.access_token); // Optional: send token data to frontend or store in session res.redirect(`/app?token=${tokenData.access_token}`); }); app.listen(3000, () => console.log('App listening on http://localhost:3000')); ``` This example uses standard .NET libraries for OAuth2 authorization code flow ```csharp Step 1: Frontend – Redirect the User to Log In [expandable] theme={null} using System; using System.Web; public class AuthHelper { private const string ClientId = "YOUR_CLIENT_ID"; private const string RedirectUri = "https://yourapp.com/callback"; private const string AuthBaseUrl = "https://auth.us.corti.app/realms//protocol/openid-connect"; public static string GetAuthorizationUrl() { var authUrl = $"{AuthBaseUrl}/auth?" + $"response_type=code&" + $"client_id={HttpUtility.UrlEncode(ClientId)}&" + $"redirect_uri={HttpUtility.UrlEncode(RedirectUri)}&" + $"scope=openid profile"; return authUrl; } } // Usage in Razor view or controller // Response.Redirect(AuthHelper.GetAuthorizationUrl()); ``` ```csharp Step 2: Corti Redirects Back with a Code theme={null} https://yourapp.com/callback?code=abc123xyz ``` ```csharp Step 3: Backend – Exchange Code for Access Token [expandable] theme={null} using System; using System.Collections.Generic; using System.Net.Http; using System.Text.Json; using System.Threading.Tasks; using System.Web.Mvc; public class TokenExchange { private const string ClientId = "YOUR_CLIENT_ID"; private const string ClientSecret = "YOUR_CLIENT_SECRET"; private const string RedirectUri = "https://yourapp.com/callback"; private const string TokenUrl = "https://auth.us.corti.app/realms//protocol/openid-connect/token"; public static async Task ExchangeCodeForTokenAsync(string code) { using var httpClient = new HttpClient(); var formData = new List> { new KeyValuePair("grant_type", "authorization_code"), new KeyValuePair("code", code), new KeyValuePair("client_id", ClientId), new KeyValuePair("client_secret", ClientSecret), new KeyValuePair("redirect_uri", RedirectUri) }; var content = new FormUrlEncodedContent(formData); var response = await httpClient.PostAsync(TokenUrl, content); if (!response.IsSuccessStatusCode) { var errorBody = await response.Content.ReadAsStringAsync(); throw new Exception($"Token exchange failed: {errorBody}"); } var responseBody = await response.Content.ReadAsStringAsync(); var tokenData = JsonSerializer.Deserialize(responseBody); if (tokenData.TryGetProperty("error", out var error)) { var errorDescription = tokenData.TryGetProperty("error_description", out var desc) ? desc.GetString() : "Unknown error"; throw new Exception($"Token error: {errorDescription}"); } return new TokenResponse { AccessToken = tokenData.GetProperty("access_token").GetString(), RefreshToken = tokenData.TryGetProperty("refresh_token", out var refresh) ? refresh.GetString() : null, ExpiresIn = tokenData.GetProperty("expires_in").GetInt32() }; } } public class TokenResponse { public string AccessToken { get; set; } public string RefreshToken { get; set; } public int ExpiresIn { get; set; } } // Usage in ASP.NET MVC Controller public class AuthController : Controller { public async Task Callback(string code) { if (string.IsNullOrEmpty(code)) { return new HttpStatusCodeResult(400, "Missing authorization code"); } try { var tokenResponse = await TokenExchange.ExchangeCodeForTokenAsync(code); // Store tokens securely (e.g., in session or secure cookie) Session["access_token"] = tokenResponse.AccessToken; // Redirect to app with token return RedirectToAction("Index", "Home"); } catch (Exception ex) { return new HttpStatusCodeResult(500, $"Token error: {ex.Message}"); } } } ``` Summary of the flow: | Step | Component | Description | | :--- | :-------- | :-------------------------------------------------------------------------------------------------- | | 1 | Frontend | Redirects user to Corti login | | 2 | Corti | Redirects back to your app with `code` | | 3 | Backend | Exchanges code + client secret for tokens
Responds with session/token, redirects to frontend | Requirements: * Must use **HTTPS** for `redirect_uri` * Your `client_secret` **must not** be exposed to the frontend * The code returned is **valid for one use and short-lived**
Use only if the OAuth2 flow is entirely server-to-server. *** ### 3. Resource Owner Password Credentials (ROPC) grant (use with caution) **Best for:** Controlled environments embedding Corti Assistant with trusted clients (e.g., internal tools). **Why:** Allows username/password login directly in the app — but **bypasses the authorization server UI**. **Risks:** * Trains users to enter passwords into third-party apps. * Easy to misuse, violates best practices. * Only viable where UI constraints prevent redirecting (e.g., native kiosk apps without browsers). This example uses the [Corti JavaScript SDK](/sdk/js/overview) (`@corti/sdk`) ```javascript [expandable] theme={null} import { CortiAuth, CortiClient, CortiEnvironment } from "@corti/sdk"; const CLIENT_ID = "YOUR_CLIENT_ID"; const USERNAME = "user@example.com"; const PASSWORD = "your-password"; // Step 1: Exchange credentials for tokens using ROPC flow const auth = new CortiAuth({ environment: CortiEnvironment.Eu, tenantName: "YOUR_TENANT_NAME", }); const tokenResponse = await auth.getRopcFlowToken({ clientId: CLIENT_ID, username: USERNAME, password: PASSWORD, }); const { accessToken, refreshToken } = tokenResponse; ``` Use only in trusted/internal scenarios. Here's a simple fetch example: ```bash [expandable] theme={null} const response = await fetch('https://auth.us.corti.app/realms//protocol/openid-connect/token', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ grant_type: 'password', client_id: 'YOUR_CLIENT_ID', username: 'user@example.com', password: 'yourpassword' }) }); const data = await response.json(); console.log('Access Token:', data.access_token); ``` This example uses standard .NET libraries for OAuth2 ROPC flow Use only in trusted/internal scenarios. ```csharp [expandable] theme={null} using System; using System.Collections.Generic; using System.Net.Http; using System.Text.Json; using System.Threading.Tasks; public class RopcAuth { private const string ClientId = "YOUR_CLIENT_ID"; private const string TokenUrl = "https://auth.us.corti.app/realms//protocol/openid-connect/token"; public static async Task GetTokenAsync(string username, string password) { using var httpClient = new HttpClient(); var formData = new List> { new KeyValuePair("grant_type", "password"), new KeyValuePair("client_id", ClientId), new KeyValuePair("username", username), new KeyValuePair("password", password) }; var content = new FormUrlEncodedContent(formData); var response = await httpClient.PostAsync(TokenUrl, content); response.EnsureSuccessStatusCode(); var responseBody = await response.Content.ReadAsStringAsync(); var tokenData = JsonSerializer.Deserialize(responseBody); if (tokenData.TryGetProperty("error", out var error)) { var errorDescription = tokenData.TryGetProperty("error_description", out var desc) ? desc.GetString() : "Unknown error"; throw new Exception($"Authentication failed: {errorDescription}"); } return new TokenResponse { AccessToken = tokenData.GetProperty("access_token").GetString(), RefreshToken = tokenData.TryGetProperty("refresh_token", out var refresh) ? refresh.GetString() : null, ExpiresIn = tokenData.GetProperty("expires_in").GetInt32() }; } } public class TokenResponse { public string AccessToken { get; set; } public string RefreshToken { get; set; } public int ExpiresIn { get; set; } } // Example usage class Program { static async Task Main() { try { var tokenResponse = await RopcAuth.GetTokenAsync( "user@example.com", "yourpassword" ); Console.WriteLine($"Access Token: {tokenResponse.AccessToken}"); Console.WriteLine($"Expires in: {tokenResponse.ExpiresIn} seconds"); } catch (Exception ex) { Console.WriteLine($"Error: {ex.Message}"); } } } ``` This authentication method is not recommended, but sometimes necessary. *** ## Final guidance **Important:** Embedded Corti Assistant requires user-based authentication. **Client credentials grant is NOT supported** as it does not provide user context. Pick the flow that matches your interaction model as offered above. See further details and contact us for [support here](mailto:help@corti.aien/articles/11156327-choosing-the-right-oauth2-flow-for-corti-integrations). **What to use when:** * **For embedded Corti Assistant** (e.g., in an iFrame/WebView): \ **Use Authorization Code Flow with PKCE** to authenticate the end user securely. This is the recommended flow for most embedded integrations. * **For server-side integrations** where you can securely store a client secret: \ **Authorization Code Flow (without PKCE)** may be used, but ensure the client secret is never exposed to the frontend. * **For constrained environments** without redirect capabilities: \ **ROPC** may be used with caution, but only in trusted internal environments. * **Client Credentials is NOT supported** — it does not provide user context and will not work with embedded Corti Assistant. **Additional reference**: * [OAuth 2.0 Authorization Framework (RFC 6749)](https://datatracker.ietf.org/doc/html/rfc6749) * [IBM](https://www.ibm.com/think/topics/oauth) # Configuration Guide Source: https://docs.corti.ai/assistant/configuration In-depth reference for configuring the embedded Assistant interface, features, appearance, and locale settings The `configure` method allows you to customize the Corti Assistant interface to match your application's needs and user preferences. This guide provides a comprehensive reference for all available configuration options, their behavior, and best practices for customization. This guide documents the deprecated `configure()` structure. For new integrations, use the [Config Migration Guide](/assistant/configuration-migration) and then implement the new structure with [Configuration Scenarios](/assistant/configuration-scenarios). For rollout timing, see [Scheduled Deprecations](/assistant/deprecation-timeline). ## Overview Configuration is applied using the `configure` method, which accepts three main categories of settings: * **Features**: Toggle UI components and functionality * **Appearance**: Customize visual styling and branding * **Locale**: Set interface and dictation languages, plus custom string overrides Configuration can be applied incrementally. You can call `configure` with only the properties you want to change, and the rest will remain at their current values. The method returns the full current configuration object, allowing you to read the current state. For implementation examples, see the [PostMessage API](/assistant/postmessage-api) or [Window API](/assistant/window-api) documentation. ## Feature toggles Feature toggles control which UI components and functionality are visible and available in the embedded Assistant. Each feature can be independently enabled or disabled, allowing you to create a tailored experience that matches your application's workflow and user needs. ### interactionTitle **Type**: `boolean`\ **Default**: `true` Controls whether the interaction title field is displayed in the interface. The title typically shows the encounter title that was provided when creating the interaction. **When to disable:** * You want a more minimal, streamlined interface * You're managing the interaction title externally in your application * The title is redundant with information already displayed in your host application **When to keep enabled:** * Users need to see or edit the interaction title within the Assistant * The title provides important context for the current session * You want users to be able to identify different interactions **Impact**: When disabled, the title field is completely hidden from the interface, reducing visual clutter but removing the ability for users to see or modify the interaction title within the Assistant. ### aiChat **Type**: `boolean`\ **Default**: `true` Controls whether the AI chat feature is available to users. The AI chat allows users to interact with Corti's AI assistant for questions, clarifications, and document-related queries. **When to disable:** * You want to restrict AI chat functionality for compliance or policy reasons * Your workflow doesn't require AI assistance features * You're providing alternative support channels for user questions **When to keep enabled:** * Users benefit from AI-powered assistance during documentation * You want to provide contextual help and clarifications * AI chat enhances the user experience and productivity **Impact**: When disabled, all AI chat UI elements and functionality are hidden. Users will not be able to access AI assistance features within the Assistant interface. ### documentFeedback **Type**: `boolean`\ **Default**: `true` Controls whether users can provide feedback on generated documents. This includes the ability to rate document quality, report issues, or provide corrections. **When to disable:** * You handle document feedback through external systems * Feedback collection is not part of your workflow * You want to simplify the interface by removing feedback mechanisms **When to keep enabled:** * You want to collect user feedback to improve document quality * Feedback helps identify issues or areas for improvement * You want users to have a way to report problems with generated documents **Impact**: When disabled, all document feedback controls and related UI are hidden. Users will not be able to provide feedback on documents directly within the Assistant. ### navigation **Type**: `boolean`\ **Default**: `false` Controls whether the navigation sidebar is visible, providing access to settings, archive, and other areas of the Assistant - primarily allowing them to freely revisit previous interactions and even create new ones. **When to enable:** * You want users to have full access to Assistant features * Users should be able to access settings and preferences * Archive functionality is needed for your workflow **When to keep disabled:** * You want a focused, minimal interface for specific workflows * Navigation is handled externally in your application * You're creating a single-purpose integration that doesn't need full navigation **Impact**: When enabled, the navigation sidebar becomes visible, allowing users to access: * `/settings/preferences` - Edit defaults like languages and default session settings * `/settings/input` - Edit dictation input settings * `/settings/account` - Edit general account settings * `/settings/archive` - View items in and restore from archive **Best Practice**: Enable navigation when you want users to have self-service access to Assistant features. Disable it for tightly controlled, workflow-specific integrations where you manage navigation externally. When **disabled**, users can only access the current interaction. With navigation **enabled**, users can freely access previous interactions or even create new ones. Consider your workflow needs and user preferences when deciding whether to enable navigation. ### virtualMode **Type**: `boolean`\ **Default**: `true` Controls whether the interface operates in virtual mode (for remote/telemedicine consultations) or live mode (for in-person consultations). This affects the recording interface, audio handling, and user experience. **When to set to `false` (live mode):** * Consultations are conducted in-person * Recording happens locally or through local audio devices * You're integrating with physical clinic environments * The consultation type is explicitly in-person **When to keep as `true` (virtual mode):** * Consultations are conducted remotely (telemedicine) * Audio is captured through web-based or remote systems * The default virtual consultation workflow applies * Remote consultation is the primary or only mode **Impact**: * **Virtual mode (`true`)**: Optimized for remote consultations with web-based audio capture * **Live mode (`false`)**: Optimized for in-person consultations with local audio handling **Best Practice**: Match this setting to your actual consultation type. If your application supports both modes, you may want to configure this dynamically based on the specific encounter type. ### syncDocumentAction **Type**: `boolean`\ **Default**: `false` Controls whether the "Synchronize document" button is available for syncing documents directly to EHR systems or external systems. **When to enable:** * You want users to be able to sync documents directly from the Assistant interface * Direct EHR synchronization is part of your workflow * Users need immediate access to sync functionality **When to keep disabled:** * Document syncing is handled externally through your application * You manage document export through your own systems * You want to control the sync process outside the Assistant **Impact**: When enabled, the synchronize document button appears in the document interface. The button text can be customized using locale overrides (see [String Overrides](#string-overrides)). **Best Practice**: Enable this if you want users to have direct control over document synchronization. If you handle syncing programmatically through your application, you may prefer to keep this disabled and manage syncing externally. ### templateEditor **Type**: `boolean`\ **Default**: `true` Controls whether the Template Assembler is available to end users. When enabled, it allows users to create custom templates for documentation tailored to their needs and preferences. **When to enable:** * You want to give end users flexibility to create and tailor their own templates. **When to keep disabled:** * You want end users to work only with a predefined, limited set of templates. * Your integration relies on a fixed mapping of section keys, and you’re not set up to support an evolving list of new section keys. **Impact**: When the Template Assembler is disabled, users are restricted to a fixed set of templates and cannot customize them. **Best Practice**: Keep the Template Assembler enabled unless there is a strong reason to restrict template customization. ## Appearance Customization ### Primary Color **Type**: `string | null`\ **Default**: `null` (uses built-in default theme) You can customize the primary accent color used throughout the Corti Assistant interface to match your application's branding. This color is applied to interactive elements, buttons, links, focus indicators, and other accent elements throughout the interface. **Format**: Hex color code as a string (e.g., `"#00a6ff"`, `"#1a73e8"`) **When to customize:** * You want to maintain brand consistency with your host application * Your application has a specific brand color palette * You need to match existing design systems or style guides * Visual consistency across your integrated experience is important **When to use default:** * Brand consistency is not a priority * You prefer Corti's default accessible color scheme * You want to minimize customization complexity **Impact**: The primary color affects: * Button backgrounds and hover states * Link colors * Focus indicators and active states * Accent elements and highlights * Selected states and active UI elements **Important considerations:** * The color must meet WCAG 2.2 AA contrast requirements (see [WCAG Compliance](#wcag-22-aa-compliance)) * The color is applied across all UI states (default, hover, active, focus, disabled) * Test thoroughly to ensure accessibility and usability ```javascript theme={null} await api.configure({ appearance: { primaryColor: "#00a6ff", }, }); ``` ### WCAG 2.2 AA compliance Always ensure WCAG 2.2 AA conformance when customizing appearance Corti Assistant's default theme has been evaluated against WCAG 2.2 Level AA and meets applicable success criteria in our supported browsers. **Important**: This conformance claim applies only to the default configuration. Customer changes (e.g., color palettes, CSS overrides, third-party widgets, or content) are outside the scope of this claim. Customers are responsible for ensuring their customizations continue to meet WCAG 2.2 AA. #### Required WCAG 2.2 AA criteria When supplying a custom accent color or theme, you must ensure WCAG 2.2 AA conformance, including: * **1.4.3 Contrast (Minimum)**: * Normal text: ≥ 4.5:1 contrast ratio * Large text: ≥ 3:1 contrast ratio * **1.4.11 Non-text Contrast**: * UI boundaries, focus rings, and selected states: ≥ 3:1 contrast ratio * **2.4.11 Focus Not Obscured (Minimum)**: * Focus indicators must remain visible and unobstructed #### Best practice for color customization 1. **Test contrast ratios**: Use tools like [WebAIM Contrast Checker](https://webaim.org/resources/contrastchecker/) to verify your color choices 2. **Test all states**: Verify contrast for default, hover, active, disabled, and focus states 3. **Test on all backgrounds**: Ensure your color works on both light and dark backgrounds if applicable 4. **Maintain focus visibility**: Ensure focus indicators remain clearly visible with your custom color 5. **Consider color blindness**: Test with color blindness simulators to ensure usability Corti provides accessible defaults. If you override them, verify contrast for all states (default, hover, active, disabled, focus) and on all backgrounds you use. ## Locale settings Locale settings control the language used for the interface and dictation, as well as allow for custom string overrides. ### Interface Language **Type**: `string | null`\ **Default**: `null` (uses user's default or browser setting) Sets the language for the user interface, including buttons, labels, menus, messages, and all UI text. This is separate from the dictation language, allowing you to have a German interface with English dictation, for example. **Format**: Language code as a string (e.g., `"en"`, `"de-DE"`, `"fr-FR"`) **When to set:** * Your application serves users in specific languages * You want to ensure a consistent language experience * Users prefer a specific interface language regardless of browser settings * You're building a localized application **When to use default (`null`):** * You want to respect user browser/OS language preferences * Language detection should be automatic * You support multiple languages and want automatic selection **Available languages**: See [Available Interface Languages](#available-interface-languages) **Best Practice**: Set the interface language based on your application's user preferences or locale settings. You can also allow users to change it if navigation is enabled. ### Dictation Language **Type**: `string`\ **Default**: `"en"` Sets the language for speech recognition and dictation. This determines which language model is used for transcribing spoken audio. The dictation language can be different from the interface language. **Format**: Language code as a string (e.g., `"en"`, `"de"`, `"fr"`, `"da-DK"`) **When to customize:** * Users dictate in a language different from the interface language * You need to support multilingual dictation * Regional variations require specific language codes (e.g., `"en-GB"` vs `"en"`) **Important Notes:** * **Region-specific**: Different regions (EU, US) support different dictation languages * **Separate from interface language**: Interface and dictation languages are independent * **Must be valid**: The language code must be supported in your region (see [Available Dictation Languages](#available-dictation-languages)) **Available languages**: See [Available Dictation Languages](#available-dictation-languages) **Best Practice**: Set the dictation language based on the actual language users will speak during consultations. This may differ from the interface language, especially in multilingual environments. ### String Overrides **Type**: `Record`\ **Default**: `{}` (empty object) Allows you to customize specific UI strings in the interface by providing key-value pairs where keys are string identifiers and values are the replacement text. **Format**: Object with string keys and string values: ```typescript theme={null} { "string.key": "Custom text", "another.key": "Another custom text" } ``` **When to use:** * You need to customize button labels to match your terminology * You want to localize specific strings that aren't covered by interface language * You need to match your application's terminology (e.g., "EHR" vs "EMR") * You want to provide context-specific labels **Limitations:** * Only specific keys are available for override (see [Available String Overrides](#available-string-overrides)) * Not all UI strings can be customized * Overrides apply regardless of interface language **Best practice**: Use string overrides sparingly for critical terminology that must match your application. For broader localization, use the interface language setting instead. #### Available string overrides Currently, the following keys are exposed for override: | Key | Default value | Purpose | | --------------------------------------- | ------------------------ | ------------------------------------------------------------------- | | `interview.document.syncDocument.label` | *"Synchronize document"* | The button text for the *"synchronize document"* button if enabled. | More string override keys may be added in future releases. Contact support if you need additional strings customized. ### Available interface languages Updated February 2026 | Language code | Language | | ------------- | ----------------- | | `en` | English | | `de-DE` | German | | `fr-FR` | French | | `it-IT` | Italian | | `sv-SE` | Swedish | | `da-DK` | Danish | | `nb-NO` | Norwegian Bokmål | | `nn-NO` | Norwegian Nynorsk | ### Available dictation languages Updated February 2026 #### EU Region | Language code | Language | | ------------- | --------------- | | `en` | English | | `en-GB` | British English | | `de` | German | | `fr` | French | | `sv` | Swedish | | `da` | Danish | | `nl` | Dutch | | `no` | Norwegian | #### US Region | Language code | Language | | ------------- | -------- | | `en` | English | ## Configuration structure The `configure` action accepts a configuration object with three main sections: ```typescript theme={null} { features: { interactionTitle: boolean, aiChat: boolean, documentFeedback: boolean, navigation: boolean, virtualMode: boolean, syncDocumentAction: boolean templateEditor: boolean }, appearance: { primaryColor: string | null }, locale: { interfaceLanguage: string | null, dictationLanguage: string, overrides: Record } } ``` All properties are optional. You can configure only the sections you need, and the rest will maintain their current values. The action returns the complete current configuration object, allowing you to read the current state. ## Best practice ### Minimum supported resolution To ensure a reliable and accessible experience, Assistant requires a minimum viewport size depending on how it is deployed. Resolution requirements are defined by **functional guarantees**, not device types. At the minimum supported size, all critical workflows must remain fully usable without layout breakage or hidden actions. #### Embedded (iframe / SDK) * **Recommended Width:** ≥ `768px` (Note: Template Preview in the Template Picker Modal is not supported below this width) * **Minimum Width:** ≥ `640px` * **Minimum Height:** ≥ `480px` At minimum width: * No horizontal scrolling is required. * Primary actions (recording, transcript, facts, document editing) remain visible and accessible. * All core workflows can be completed without layout overlap or clipping. If embedded in a narrower container, the Embedded Assistant will switch to a compact layout. Below the minimum supported width, layout integrity is not guaranteed. As an integrator, you should ensure that your embedding container respects the minimum supported width to guarantee full functionality. #### Narrow containers (mobile) The app calls `getIsMobile()`, which returns true if browser UA matches: `Mobile|Android|iPhone|iPad|iPod`. So the “switch point” is effectively: when the browser identifies as a mobile device, not at XY pixels wide. #### Accessibility requirements At the minimum supported resolution, Assistant: * Complies with WCAG 2.2 AA reflow requirements. * Supports up to **200% browser zoom** without loss of functionality. * Does not require two-dimensional scrolling to complete primary workflows. ### Configuration timing and lifecycle **Apply configuration early**: Configure the Assistant as soon as it's ready (after the `ready` event) and before users interact with it. This ensures a consistent experience from the start. **Configure before navigation**: If you're navigating to specific routes, set your configuration first to ensure the target page loads with the correct settings. **Reconfigure dynamically**: You can update configuration at any time during a session. This is useful for: * Adapting to different user roles or contexts * Responding to user preferences changes * Switching between different workflow modes **Configuration persistence**: Configuration settings persist for the duration of the session but do not persist across page reloads or new sessions. You'll need to reapply configuration each time the Assistant is initialized. ### Incremental configuration The `configure` action supports incremental updates. You can update only specific sections without affecting others: * Update only `features` to change UI visibility * Update only `appearance` to change branding * Update only `locale` to change language settings This allows you to make targeted changes without needing to specify the entire configuration object each time. ### Reading current configuration The `configure` action always returns the complete current configuration object, regardless of what you pass in. This allows you to: * Read the current state of all settings * Verify that your configuration was applied correctly * Build upon existing configuration rather than replacing it entirely ### Error handling Always implement proper error handling when configuring: * **Handle configuration failures**: Configuration may fail due to invalid values, network issues, or other errors * **Provide fallbacks**: Have default configuration ready in case of failures * **Log errors appropriately**: Log configuration errors for debugging while maintaining user experience * **Validate before applying**: Validate configuration values (especially colors and language codes) before sending them ### Accessibility best practice **Color customization:** * Always test color contrast ratios using tools like [WebAIM Contrast Checker](https://webaim.org/resources/contrastchecker/) * Test all UI states: default, hover, active, disabled, and focus * Ensure focus indicators remain clearly visible * Test on both light and dark backgrounds if applicable * Consider color blindness: Test with simulators to ensure usability **Feature toggles:** * Ensure that disabling features doesn't break keyboard navigation * Verify that screen readers can still navigate the interface * Test that essential functionality remains accessible when features are disabled * Consider the impact on users who rely on specific features **Language settings:** * Ensure interface language changes don't break layout or functionality * Test that all UI elements are properly localized * Verify that right-to-left languages (if supported) work correctly ### Performance optimization **Configure once at startup**: Apply your primary configuration once when the Assistant is ready, rather than repeatedly calling `configure`. **Batch configuration changes**: If you need to change multiple settings, do it in a single `configure` call rather than multiple separate calls. **Cache configuration objects**: Store your configuration objects and reuse them across sessions to avoid reconstructing them each time. **Minimize reconfiguration**: Avoid unnecessary reconfiguration during a session. Only update configuration when user preferences or context actually change. ### User experience guidelines **Match user workflows**: Configure features based on your users' actual workflow needs. Consider: * What features do users actually need for their tasks? * What can be simplified or hidden to reduce cognitive load? * How does the Assistant fit into the broader application workflow? **Consistent branding**: Use appearance settings to maintain visual consistency with your host application. This creates a more cohesive, integrated experience. **Respect user preferences**: * Use interface language settings that match your application's user preferences * Set dictation language based on the actual language users will speak * Consider allowing users to change settings if navigation is enabled **Context-aware configuration**: * Configure differently for different user roles (e.g., physicians vs. nurses) * Adapt configuration based on consultation type (virtual vs. in-person) * Consider workflow-specific configurations for different use cases **Progressive disclosure**: Start with a minimal configuration and enable additional features as needed. This reduces initial complexity while allowing power users to access advanced features. ### Security and compliance **No sensitive data in configuration**: Configuration values are visible in client-side code. Never include sensitive information like API keys, tokens, or personal data in configuration. **Validate user permissions**: If you're configuring based on user roles or permissions, validate those permissions server-side before applying configuration. **Compliance considerations**: * Ensure customizations maintain compliance with relevant regulations (HIPAA, GDPR, etc.) * Document any customizations that affect compliance * Test that disabled features don't impact required functionality ### Testing recommendations **Test all feature combinations**: Test how different feature toggle combinations affect the interface and functionality. **Test color customizations thoroughly**: * Test with various color values * Verify accessibility in all states * Test with different screen sizes and resolutions **Test language settings**: * Verify all supported languages work correctly * Test interface language and dictation language combinations * Ensure string overrides work as expected **Test incremental updates**: Verify that partial configuration updates work correctly and don't reset other settings. **Test error scenarios**: Test what happens when invalid configuration values are provided. ## Related documentation * [configure() (Deprecated)](/assistant/api/configure) - Complete reference for the deprecated `configure` method * [PostMessage API](/assistant/postmessage-api) - Learn how to use the PostMessage integration method * [Window API](/assistant/window-api) - Learn how to use the Window API integration method * [Welcome](/assistant/welcome) - Overview of Corti Assistant and integration options * [Proxy Guide](/assistant/proxy) - How to run the embedded Assistant behind a reverse proxy Please [contact us](mailto:help@corti.ai) for help or questions about configuration. # Config Migration Guide Source: https://docs.corti.ai/assistant/configuration-migration Move from configure() and configureSession() to configureApp() and setInteractionOptions(). Use this guide to move an existing Embedded API integration from the legacy configuration structure to the new split between app-level configuration and interaction options. Due by 2026-11-29. `configure()` and `configureSession()` remain supported until then, but new integrations should move to `configureApp()` and `setInteractionOptions()` now. See [Scheduled Deprecations](/assistant/deprecation-timeline) for rollout timing. ## Overview The new configuration structure separates two responsibilities: * `configureApp()` is for app-level configuration such as UI settings, appearance, locale, and network settings. It is patchable and you may call it multiple times. * `setInteractionOptions()` is for interaction or session-level configuration such as mode, spoken language, template defaults, and document actions. Set it before the user starts or opens an interaction. `configure()` and `configureSession()` keep working during the deprecation period. Use [Scheduled Deprecations](/assistant/deprecation-timeline) if you need rollout timing and compatibility details. ## Reference ### Mapping from `configure()` The second column shows the request shape to use in the new method call, not a response path. | Current field | Use in | Notes | | ----------------------------- | ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | | `features.interactionTitle` | `configureApp({ ui: { interactionTitle: ... } })` | App-level UI setting | | `features.aiChat` | `configureApp({ ui: { aiChat: ... } })` | App-level UI setting | | `features.documentFeedback` | `configureApp({ ui: { documentFeedback: ... } })` | App-level UI setting | | `features.navigation` | `configureApp({ ui: { navigation: ... } })` | App-level UI setting | | `features.virtualMode` | `setInteractionOptions({ mode: { options: [...] } })` | Use `options: ["in-person", "virtual"]` when virtual mode should be available, or `options: ["in-person"]` when it should not be available | | `features.syncDocumentAction` | `setInteractionOptions({ documents: { actions: { sync: ... } } })` | Interaction-level document action | | `features.templateEditor` | `setInteractionOptions({ templates: { sources: { personal: { enabled: ... } } } })` | Keeps the existing personal template capability | | `appearance.primaryColor` | `configureApp({ appearance: { primaryColor: ... } })` | App branding | | `locale.interfaceLanguage` | `configureApp({ locale: { interfaceLanguage: ... } })` | UI language | | `locale.dictationLanguage` | `configureApp({ locale: { dictationLanguage: ... } })` | Default dictation language | | `locale.overrides` | `configureApp({ locale: { overrides: ... } })` | String overrides | | `network.websocketBaseUrl` | `configureApp({ network: { websocketBaseUrl: ... } })` | Proxy-only WebSocket endpoint override | `configureApp({ debug: true })` enables a debug panel for development. There is no legacy `configure()` equivalent for this setting, and you should not enable it in staging or production. ### Mapping from `configureSession()` | Current field | Use in | Notes | | ----------------------- | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | | `defaultMode` | `setInteractionOptions({ mode: { fallback: ... } })` | Pair this with `mode.options` to decide which modes are available | | `defaultLanguage` | `setInteractionOptions({ spokenLanguage: { fallback: ... } })` | Interaction-level spoken language fallback | | `defaultTemplateKey` | `setInteractionOptions({ templates: { defaultTemplate: ... } })` | Use `source: "standard"`, `behaviour: "fallback"`, and the fully resolved template id | | `defaultOutputLanguage` | `templates.defaultTemplate.template.id` | Compose the new template id from the old key plus language, for example `corti-soap` + `en` becomes `corti-soap-en` | ## Migration path Move UI, appearance, locale, and network settings out of `configure()` and into `configureApp()`. Move mode, spoken language, template defaults, document sync, and personal template availability out of `configure()` or `configureSession()` and into `setInteractionOptions()`. ## Before and after examples ### App appearance and UI settings
**Before** ```javascript theme={null} await api.configure({ features: { interactionTitle: true, aiChat: false, documentFeedback: false, navigation: true, }, appearance: { primaryColor: "#0f766e", }, }); ```
**After** ```javascript theme={null} await api.configureApp({ ui: { interactionTitle: true, aiChat: false, documentFeedback: false, navigation: true, }, appearance: { primaryColor: "#0f766e", }, }); ```
### Development-only debug panel Use `debug` when you need the embedded Assistant debug panel during local development or troubleshooting. ```javascript theme={null} await api.configureApp({ debug: true, }); ``` The debug panel is intended for development only. Keep `debug` disabled in staging and production. ### Interface language, dictation language, and string overrides
**Before** ```javascript theme={null} await api.configure({ locale: { interfaceLanguage: "da-DK", dictationLanguage: "da", overrides: { "interview.document.syncDocument.label": "Sync Document", }, }, }); ```
**After** ```javascript theme={null} await api.configureApp({ locale: { interfaceLanguage: "da-DK", dictationLanguage: "da", overrides: { "interview.document.syncDocument.label": "Sync Document", }, }, }); ```
### Default mode and virtual mode Use `mode.options` to replace `features.virtualMode` and `mode.fallback` to replace `defaultMode`. When `virtualMode` is `false`, the key migration step is to remove `"virtual"` from `mode.options`.
**Before** ```javascript theme={null} await api.configure({ features: { virtualMode: true, }, }); await api.configureSession({ defaultMode: "virtual", }); ```
**After** ```javascript theme={null} await api.setInteractionOptions({ mode: { fallback: "virtual", options: ["in-person", "virtual"], }, }); ```
For the more interesting non-default case where virtual mode should not be available:
**Before** ```javascript theme={null} await api.configure({ features: { virtualMode: false, }, }); await api.configureSession({ defaultMode: "in-person", }); ```
**After** ```javascript theme={null} await api.setInteractionOptions({ mode: { fallback: "in-person", options: ["in-person"], }, }); ```
### Default spoken language
**Before** ```javascript theme={null} await api.configureSession({ defaultLanguage: "da", }); ```
**After** ```javascript theme={null} await api.setInteractionOptions({ spokenLanguage: { fallback: "da", }, }); ```
### Default template This remains fallback behavior, just like before. Map `defaultTemplateKey` to a standard template source and pass the fully resolved template id. In the new API, pass the fully resolved template id. If the old configuration used `defaultTemplateKey: "corti-soap"` together with `defaultOutputLanguage: "en"`, the new `template.id` should be `"corti-soap-en"`.
**Before** ```javascript theme={null} await api.configureSession({ defaultTemplateKey: "corti-soap", defaultOutputLanguage: "en", }); ```
**After** ```javascript theme={null} await api.setInteractionOptions({ templates: { defaultTemplate: { behaviour: "fallback", template: { source: "standard", id: "corti-soap-en", }, }, }, }); ```
The old configuration resolved the selected template from the pair `defaultTemplateKey` plus `defaultOutputLanguage`. In the new API, you must pass the already resolved template id directly, for example `corti-soap-en`. ### Document sync action
**Before** ```javascript theme={null} await api.configure({ features: { syncDocumentAction: true, }, }); ```
**After** ```javascript theme={null} await api.setInteractionOptions({ documents: { actions: { sync: true, }, }, }); ```
### Personal template editor and template management
**Before** ```javascript theme={null} await api.configure({ features: { templateEditor: true, }, }); ```
**After** ```javascript theme={null} await api.setInteractionOptions({ templates: { sources: { personal: { enabled: true, }, }, }, }); ```
## Timeline * **Current**: `configure()` and `configureSession()` still work during the deprecation period * **Future**: Legacy configuration support ends on 2026-11-29 * **Action required**: Move to `configureApp()` and `setInteractionOptions()` before the deadline ## Related pages See the same settings organized by implementation scenario instead of field mapping. Review timing, compatibility expectations, and rollout guidance. Review `configureApp()` and `setInteractionOptions()` alongside the legacy methods. # Embedded API Configuration Scenarios Source: https://docs.corti.ai/assistant/configuration-scenarios Scenario-based examples for configureApp() and setInteractionOptions(). Use this page when you want practical examples for the current Embedded API configuration options. If you need to combine branding, UI, and interaction defaults in one setup flow, jump to [I need to configure several settings together](#i-need-to-configure-several-settings-together). ## Intended usage pattern * Call `configureApp()` for app-level settings. * Call `setInteractionOptions()` before the user starts or opens an interaction. If you are moving from `configure()` or `configureSession()`, use the [Config Migration Guide](/assistant/configuration-migration). For timelines and compatibility, see [Scheduled Deprecations](/assistant/deprecation-timeline). ## App UI and navigation Use this when the interaction title is already shown elsewhere in your product and you do not want to repeat it inside the embedded assistant. ```javascript theme={null} await api.configureApp({ ui: { interactionTitle: false, }, }); ``` Use this when you do not want clinicians to submit document feedback from inside the embedded assistant. ```javascript theme={null} await api.configureApp({ ui: { documentFeedback: false, }, }); ``` Use this when your workflow does not allow in-product AI chat or when you want to keep the embedded experience focused on recording and documentation only. ```javascript theme={null} await api.configureApp({ ui: { aiChat: false, }, }); ``` Use this when you do not want clinicians to open settings, browse older interactions, or navigate away from the flow controlled by your host application. ```javascript theme={null} await api.configureApp({ ui: { navigation: false, }, }); ``` Use this when you need the embedded Assistant debug panel while developing or troubleshooting an integration locally. ```javascript theme={null} await api.configureApp({ debug: true, }); ``` `debug` is only intended for development. Do not enable it in staging or production. Use this when your product already provides the surrounding navigation and context, and you want the embedded assistant to stay limited to the current interaction. ```javascript theme={null} await api.configureApp({ ui: { interactionTitle: false, aiChat: false, documentFeedback: false, navigation: false, }, }); ``` ## Appearance, language, and network Use this when you want the embedded experience to match your product branding. Always ensure WCAG 2.2 AA conformance when customizing appearance ```javascript theme={null} await api.configureApp({ appearance: { primaryColor: "#0f766e", }, }); ``` Use this when you want to control the language of the embedded UI. ```javascript theme={null} await api.configureApp({ locale: { interfaceLanguage: "da-DK", }, }); ``` Use this when you want the embedded experience to start with a specific dictation language. ```javascript theme={null} await api.configureApp({ locale: { dictationLanguage: "da", }, }); ``` Use this when you need to replace specific interface strings with product-specific wording. ```javascript theme={null} await api.configureApp({ locale: { overrides: { "interview.document.syncDocument.label": "Sync Document", }, }, }); ``` Use this only for proxy setups that need a custom WebSocket endpoint. See the [Proxy guide](/assistant/proxy). ```javascript theme={null} await api.configureApp({ network: { websocketBaseUrl: "wss://proxy.example.com", }, }); ``` ## Interaction mode and spoken language Use this when you want to set the initial interaction mode while keeping both modes available. ```javascript theme={null} await api.setInteractionOptions({ mode: { fallback: "in-person", options: ["in-person", "virtual"], }, }); ``` Use this when users should still be able to switch modes, but one mode should be the default. ```javascript theme={null} await api.setInteractionOptions({ mode: { fallback: "virtual", options: ["in-person", "virtual"], }, }); ``` Use this when your workflow only supports one interaction type. ```javascript Virtual only theme={null} await api.setInteractionOptions({ mode: { fallback: "virtual", options: ["virtual"], }, }); ``` ```javascript In-person only theme={null} await api.setInteractionOptions({ mode: { fallback: "in-person", options: ["in-person"], }, }); ``` Use this when a session should default to a specific spoken language. ```javascript theme={null} await api.setInteractionOptions({ spokenLanguage: { fallback: "da", }, }); ``` Use this when an embedded deployment supports only a specific set of spoken languages. If you provide one option, users cannot change the spoken language. If you provide a `fallback`, make sure it is included in `options`. ```javascript Multiple spoken languages theme={null} // Replace these with your values const FALLBACK_LANGUAGE = "da"; const SPOKEN_LANGUAGE_OPTIONS = ["da", "en"]; await api.setInteractionOptions({ spokenLanguage: { fallback: FALLBACK_LANGUAGE, options: SPOKEN_LANGUAGE_OPTIONS, }, }); ``` ```javascript Single spoken language theme={null} // Replace this with your value const SPOKEN_LANGUAGE = "da"; await api.setInteractionOptions({ spokenLanguage: { fallback: SPOKEN_LANGUAGE, options: [SPOKEN_LANGUAGE], }, }); ``` ## Template defaults and personal templates Use this when you want to provide a fallback standard template for new sessions using the fully resolved template id. It is used only when the user does not already have their own default template set. ```javascript theme={null} await api.setInteractionOptions({ templates: { defaultTemplate: { behaviour: "fallback", template: { source: "standard", id: "corti-soap-en", }, }, }, }); ``` If your old integration used `defaultTemplateKey: "corti-soap"` together with `defaultOutputLanguage: "en"`, the new `template.id` should be `"corti-soap-en"`. Use this when your host application controls the default template and clinicians should not be able to choose or persist their own default from inside the embedded Assistant. ```javascript theme={null} // Replace this with your value const DEFAULT_TEMPLATE_ID = "corti-soap-en"; await api.setInteractionOptions({ templates: { defaultTemplate: { allowUserSelection: false, behaviour: "fallback", template: { source: "standard", id: DEFAULT_TEMPLATE_ID, }, }, }, }); ``` Use this when the first document in an interaction must use a configured template. After the first document has been generated, normal template selection applies within the available templates. ```javascript theme={null} // Replace this with your value const DEFAULT_TEMPLATE_ID = "corti-soap-en"; await api.setInteractionOptions({ templates: { defaultTemplate: { behaviour: "force-first-document", template: { source: "standard", id: DEFAULT_TEMPLATE_ID, }, }, }, }); ``` If your effective template list contains only one template, the embedded Assistant treats that template as the forced first-document template. Use this when you want clinicians to create, edit, copy, view, or delete personal templates through the Guided Document Generation template editor. ```javascript theme={null} await api.setInteractionOptions({ templates: { sources: { personal: { enabled: true, }, }, }, }); ``` Requires [Guided Document Generation](/release-notes/corti-assistant#v12-19-0). Only applies to new templates. Use this when you want to hide or lock specific section-level fields in the guided template creation flow. For example, to let users edit headings but hide the misc prompt: ```javascript theme={null} await api.setInteractionOptions({ templates: { sources: { personal: { enabled: true, sectionFields: { heading: { editable: true }, description: { editable: false }, miscPrompt: { visible: false }, }, }, }, }, }); ``` `heading` and `description` are always visible; only `editable` applies. For `miscPrompt` and `outputSchema`, omitting `visible` or setting `visible: false` hides the field and prevents editing. When a prompt field is visible, it is editable by default unless you set `editable: false`. Use this when the host application needs to provide a runtime template without first persisting it as a standard or project template. Inline template IDs are local to the current `setInteractionOptions()` payload. Add an `id` only when another option needs to reference the inline template. ```javascript theme={null} // Replace these with your values const PLAN_FIELD_KEY = "plan"; const SUBJECTIVE_FIELD_KEY = "subjective"; const TEMPLATE_FIELD_KEY = "soap-note"; await api.setInteractionOptions({ templates: { sources: { inline: { enabled: true, templates: [ { name: "Embedded SOAP", labels: [{ key: "ehr-field", value: TEMPLATE_FIELD_KEY }], generation: { instructions: { prompt: "Generate a concise SOAP note for the current encounter.", }, sections: [ { heading: "Subjective", labels: [{ key: "ehr-field", value: SUBJECTIVE_FIELD_KEY }], instructions: { contentPrompt: "Summarize the patient's subjective symptoms.", writingStylePrompt: "Use concise clinical language.", }, }, { heading: "Plan", labels: [{ key: "ehr-field", value: PLAN_FIELD_KEY }], instructions: { contentPrompt: "Summarize the agreed treatment plan.", }, outputSchema: { type: "string" }, }, ], }, }, ], }, }, }, }); ``` Use this when you provide a runtime template and want the first generated document to use it automatically. ```javascript theme={null} // Replace this with your value const INLINE_TEMPLATE_ID = ""; await api.setInteractionOptions({ templates: { sources: { inline: { enabled: true, templates: [ { id: INLINE_TEMPLATE_ID, name: "Embedded SOAP", generation: { instructions: { prompt: "Generate a SOAP note for the current encounter.", }, sections: [ { heading: "Assessment", instructions: { contentPrompt: "Summarize the clinical assessment.", }, }, ], }, }, ], }, }, defaultTemplate: { behaviour: "force-first-document", template: { source: "inline", id: INLINE_TEMPLATE_ID, }, }, }, }); ``` ## Standard and project template sources Requires [Guided Document Generation](/release-notes/corti-assistant#v12-19-0). Use this when standard templates should remain available without filtering. ```javascript theme={null} await api.setInteractionOptions({ templates: { sources: { standard: { enabled: true, }, }, }, }); ``` Requires [Guided Document Generation](/release-notes/corti-assistant#v12-19-0). Use this when the embedded assistant should not show any standard templates in the picker. ```javascript theme={null} await api.setInteractionOptions({ templates: { sources: { standard: { enabled: false, }, }, }, }); ``` Requires [Guided Document Generation](/release-notes/corti-assistant#v12-19-0). Use this when the embedded experience should only present standard templates from specific regions using ISO 3166-1 alpha-3 codes such as `BEL`, `DNK`, or `CAN`. When limited to a single region, the region headline is automatically hidden. ```javascript theme={null} await api.setInteractionOptions({ templates: { sources: { standard: { enabled: true, include: { regions: ["BEL"], }, }, }, }, }); ``` Requires [Guided Document Generation](/release-notes/corti-assistant#v12-19-0). Use this when you want to narrow standard templates by family, such as `soap`. ```javascript theme={null} await api.setInteractionOptions({ templates: { sources: { standard: { enabled: true, include: { families: ["soap"], }, }, }, }, }); ``` Requires [Guided Document Generation](/release-notes/corti-assistant#v12-19-0). Use this when standard templates should stay available, but specific regions should be removed from the picker. ```javascript theme={null} await api.setInteractionOptions({ templates: { sources: { standard: { enabled: true, exclude: { regions: ["USA"], }, }, }, }, }); ``` Requires [Guided Document Generation](/release-notes/corti-assistant#v12-19-0). Use this when you want to remove a family such as `letter` while keeping other standard templates available. ```javascript theme={null} await api.setInteractionOptions({ templates: { sources: { standard: { enabled: true, exclude: { families: ["letter"], }, }, }, }, }); ``` Requires [Guided Document Generation](/release-notes/corti-assistant#v12-19-0). Use this when you need both narrowing and removal in the same configuration. The embedded assistant applies `include` first and `exclude` second. ```javascript theme={null} await api.setInteractionOptions({ templates: { sources: { standard: { enabled: true, include: { regions: ["BEL", "DNK"], families: ["soap", "letter"], }, exclude: { regions: ["DNK"], families: ["letter"], }, }, }, }, }); ``` Region values use ISO 3166-1 alpha-3 codes such as `BEL`, `DNK`, or `CAN`. Family values are standard template family identifiers such as `soap`, depending on the available standard template metadata. Requires [Guided Document Generation](/release-notes/corti-assistant#v12-19-0). Use this when you want to expose project templates selectively. Project templates are referenced by UUID in `include.ids` and `exclude.ids`. ```javascript theme={null} await api.setInteractionOptions({ templates: { sources: { project: { enabled: true, include: { ids: ["", ""], }, }, }, }, }); ``` Requires [Guided Document Generation](/release-notes/corti-assistant#v12-19-0). Use this when project templates should remain available, but a specific UUID or set of UUIDs must be hidden. ```javascript theme={null} await api.setInteractionOptions({ templates: { sources: { project: { enabled: true, exclude: { ids: [""], }, }, }, }, }); ``` Requires [Guided Document Generation](/release-notes/corti-assistant#v12-19-0). Use this when the embedded assistant should not show any project templates in the picker. ```javascript theme={null} await api.setInteractionOptions({ templates: { sources: { project: { enabled: false, }, }, }, }); ``` ## Documents and companion workflows Use this when you want to show or hide the document sync action inside the embedded experience. ```javascript theme={null} await api.setInteractionOptions({ documents: { actions: { sync: true, }, }, }); ``` Use this when an embedded workflow should allow only one or two generated documents for the current interaction. When the limit is reached, document generation controls are hidden. ```javascript One generated document theme={null} const MAX_GENERATED_DOCUMENTS = 1; await api.setInteractionOptions({ documents: { maxGenerated: MAX_GENERATED_DOCUMENTS, }, }); ``` ```javascript Two generated documents theme={null} const MAX_GENERATED_DOCUMENTS = 2; await api.setInteractionOptions({ documents: { maxGenerated: MAX_GENERATED_DOCUMENTS, }, }); ``` ```javascript Unlimited documents theme={null} const MAX_GENERATED_DOCUMENTS = "unlimited"; await api.setInteractionOptions({ documents: { maxGenerated: MAX_GENERATED_DOCUMENTS, }, }); ``` Use this when you want to restrict the output language options shown to users. For [Guided Document Generation](/release-notes/corti-assistant#v12-19-0), the output language dropdown displays only the allowed languages; if restricted to a single language, the dropdown is disabled. For older document generation, this limits which template language groups appear in the left sidebar. ```javascript theme={null} await api.setInteractionOptions({ documents: { allowedLanguages: ["en", "da"], }, }); ``` Use this when your embedded deployment supports phone-based audio capture via the Corti Companion app. Enable the companion app surface at app level, then call `showDeviceLinkQR()` with the current user's OAuth token response when the user initiates pairing. Once paired, the phone appears as a selectable microphone source in the Embedded Assistant. The companion app must be open and running for the phone to appear in the microphone list. ```typescript theme={null} // Replace these with your values const ACCESS_TOKEN = ""; const REFRESH_TOKEN = ""; await api.configureApp({ companionApp: { enabled: true, }, }); const result = await api.showDeviceLinkQR({ access_token: ACCESS_TOKEN, refresh_token: REFRESH_TOKEN, token_type: "Bearer", }); if (result.status === "approved") { // The mobile companion app was paired successfully. } ``` See [`showDeviceLinkQR()`](/assistant/api/show-device-link-qr) for the full parameter reference, return statuses, error handling, and companion app installation. ## Combined setup flows Use this when you need to apply several settings together. If the settings all belong to `configureApp()`, you can group them into one call or chain multiple `configureApp()` calls. Because `configureApp()` is patchable, both patterns are valid. ```javascript theme={null} await api.configureApp({ ui: { interactionTitle: true, aiChat: false, navigation: false, }, appearance: { primaryColor: "#0f766e", }, locale: { interfaceLanguage: "da-DK", dictationLanguage: "da", }, }); ``` ```javascript theme={null} await api.configureApp({ ui: { interactionTitle: true, aiChat: false, }, }); await api.configureApp({ appearance: { primaryColor: "#0f766e", }, }); await api.configureApp({ locale: { interfaceLanguage: "da-DK", dictationLanguage: "da", }, }); ``` If you need both app-level settings and interaction-level settings, group app-level settings inside `configureApp()` as needed, then call `setInteractionOptions()` before the user starts or opens an interaction. ```javascript theme={null} await api.configureApp({ ui: { navigation: false, documentFeedback: false, }, locale: { interfaceLanguage: "da-DK", }, }); await api.setInteractionOptions({ mode: { fallback: "virtual", options: ["in-person", "virtual"], }, spokenLanguage: { fallback: "da", }, documents: { actions: { sync: true, }, }, }); ``` A practical rule is: optional app-level settings can be grouped or split across `configureApp()` calls, but interaction options should be finalized in `setInteractionOptions()` before the interaction is opened. ## Related pages Review timing, compatibility expectations, and rollout guidance. Review method-level details for `configureApp()` and `setInteractionOptions()`. # Embedded API Supported Values Source: https://docs.corti.ai/assistant/configuration-values Supported values and lookup tables for Embedded API configuration. Use this page when you need supported values for the current Embedded API configuration methods rather than step-by-step examples. Pair it with [Configuration Scenarios](/assistant/configuration-scenarios) when you want practical usage examples. ## Supported string overrides Use these keys with `configureApp({ locale: { overrides: ... } })`. | Key | Default value | Purpose | | --------------------------------------- | ------------------------ | ------------------------------------------------------------------- | | `interview.document.syncDocument.label` | *"Synchronize document"* | The button text for the *"synchronize document"* button if enabled. | More string override keys may be added in future releases. Contact support if you need additional strings customized. ## Supported interface languages Use these values with `configureApp({ locale: { interfaceLanguage: ... } })`. Updated May 2026 | Language code | Language | | ------------- | ----------------- | | `en` | English | | `de-DE` | German | | `fr-FR` | French | | `it-IT` | Italian | | `sv-SE` | Swedish | | `da-DK` | Danish | | `nb-NO` | Norwegian Bokmål | | `nn-NO` | Norwegian Nynorsk | ## Supported dictation languages Use these values with `configureApp({ locale: { dictationLanguage: ... } })`. Updated May 2026 #### EU Region | Language code | Language | | ------------- | --------------- | | `en` | English | | `en-GB` | British English | | `de` | German | | `fr` | French | | `sv` | Swedish | | `da` | Danish | | `nl` | Dutch | | `no` | Norwegian | #### US Region | Language code | Language | | ------------- | -------- | | `en` | English | ## Related documentation * [Configuration Scenarios](/assistant/configuration-scenarios) * [Config Migration Guide](/assistant/configuration-migration) * [configureApp()](/assistant/api/configure-app) * [setInteractionOptions()](/assistant/api/set-interaction-options) * [Configuration Guide (Deprecated)](/assistant/configuration) # Scheduled Deprecations Source: https://docs.corti.ai/assistant/deprecation-timeline Track active Embedded API deprecations, required customer actions, and scheduled shutdown dates. Use this page to track announced Embedded API changes that have a scheduled end-of-support date and may require action in existing Corti Assistant integrations. ## How to use this page * This page lists only deprecations with a dated migration window and a clear customer action. * Newly announced but fully backward-compatible improvements stay in the [Corti Assistant release notes](/release-notes/corti-assistant) until there is a scheduled removal date to track here. * Each entry includes what is changing, when it was announced, the expected shutdown date, and where to migrate next. Deprecated Embedded API functionality remains functional for 6 months after the deprecation is announced in release notes or on this page. When it is technically possible, deprecations also include at least 2 months of warnings on use before shutdown. ## Subscribe to updates * Use the RSS feed for this page if you want embedded-specific deprecation updates only. * Follow the [Corti Assistant release notes](/release-notes/corti-assistant) for broader Embedded API and application changes. ## Highest-priority migration ### Legacy embedded events (due August 20, 2026) This is the next Embedded API change with a scheduled shutdown date. | Item | Details | | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Status | Migrate now | | What is changing | The [legacy embedded event system](/assistant/events/legacy-events) is being removed in favor of the [current embedded event system](/assistant/events/index). | | Announced | 2026-02-20 in the [Corti Assistant release notes](/release-notes/corti-assistant) (`v12.10.0`) | | Expected shutdown date | 2026-08-20 | | Required action | Update event subscriptions to the current event names and payloads before the shutdown date. | | Migration path | Use the [Events Migration Guide](/assistant/events/legacy-events) for migration recommendations, then update subscriptions using the current event reference pages. | Existing integrations continue to work during the deprecation period, but this is the next scheduled embedded change that can break an integration if it is left untouched. ## Other scheduled changes ### Embedded configuration API split (due November 29, 2026) `configureApp()` and `setInteractionOptions()` were added to the Embedded API, splitting app-level configuration from interaction-level options such as mode, spoken language, template defaults, and document actions. | Item | Details | | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Status | Plan migration | | What is changing | The existing `configure()` and `configureSession()` methods are deprecated and are being replaced by [`configureApp()`](/assistant/api/configure-app) and [`setInteractionOptions()`](/assistant/api/set-interaction-options). | | Before | `configure()` and `configureSession()` handled a mix of app-level configuration and interaction defaults. | | Going forward | Use `configureApp()` for app-level settings. Use `setInteractionOptions()` for interaction-level options such as mode, spoken language, template defaults, and document actions. | | Announced | 2026-05-29 in the [Corti Assistant release notes](/release-notes/corti-assistant) (`v12.18.0`) | | Expected shutdown date | 2026-11-29 | | Required action | Move app-level settings to `configureApp()` and move interaction-level options to `setInteractionOptions()`. | | Migration path | Use the [Config Migration Guide](/assistant/configuration-migration) and [Configuration Scenarios](/assistant/configuration-scenarios). | A deprecation means you should plan a migration. It does not mean you need to stop using the existing API immediately. ## Related pages Follow Embedded API announcements and broader Assistant changes. Move existing `configure()` and `configureSession()` calls to the new configuration structure. See scenario-based examples for app-level settings and interaction options. # account.creditsConsumed Source: https://docs.corti.ai/assistant/events/generated/account/creditsConsumed Emitted when account credits are consumed. ## Event Properties | Field | Value | | -------------- | --------------------------- | | `event` | `"account.creditsConsumed"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------------ | ---------------------------------------------------- | ---------------------------------------------------------------------------- | | `creditsConsumed` | `number` | Number of credits used | | `reason` | `"stream" \| "transcription" \| "document-creation"` | What operation consumed the credits | | `documentId` | `string` | Unique document identifier | | `documentType` | `"legacy" \| "guided"` | Document generation type | | `documentName` | `string` | Document name or title | | `outputLanguage` | `string` | Output language code (e.g., "en", "de") | | `templateType` | `"built-in" \| "custom" \| "schema-driven"` | Type of template used: built-in, custom, or schema-driven | | `templateId` | `string` | Template identifier (either ref for built-in or customTemplateId for custom) | | `documentLabels` | `Record` | Key/value labels attached to the document | | `interactionId` | `string` | Unique interaction identifier | | `interactionState` | `InteractionState` | Current state of the interaction | #### Example ```json theme={null} { "event": "account.creditsConsumed", "confidential": false, "payload": { "creditsConsumed": 12, "reason": "stream", "documentId": "doc_123", "documentType": "legacy", "documentName": "SOAP note", "outputLanguage": "en", "templateType": "built-in", "templateId": "soap", "documentLabels": {}, "interactionId": "int_123", "interactionState": "ongoing" } } ``` | Field | Type | Description | | ----------------- | ---------------------------------------------------- | --------------------------------------------------------------- | | `creditsConsumed` | `number` | Number of credits used | | `reason` | `"stream" \| "transcription" \| "document-creation"` | What operation consumed the credits | | `document` | `ExternalPatientDocument` | Complete document object with all sections and metadata | | `interaction` | `ExternalInteraction` | Interaction context including transcripts, documents, and facts | | Field | Type | Description | | ---------------- | ------------------------------------------- | ---------------------------------------------------------------------------- | | `id` | `string` | Unique document identifier | | `documentType` | `"legacy" \| "guided"` | Document generation type | | `name` | `string` | Document name or title | | `outputLanguage` | `string` | Output language code (e.g., "en", "de") | | `sections` | `Section[]` | Document sections containing the generated content | | `templateType` | `"built-in" \| "custom" \| "schema-driven"` | Type of template used: built-in, custom, or schema-driven | | `templateId` | `string` | Template identifier (either ref for built-in or customTemplateId for custom) | | `labels` | `Record` | Key/value labels attached to the document | **Section properties:** | Field | Type | Description | | ------------------ | ------------------------ | ----------------------------------------------------------------- | | `key` | `string` | Unique key identifying the section type | | `name` | `string` | Display name of the section | | `text` | `string` | Section content in markdown or plain text | | `structuredOutput` | `unknown` | Structured output for guided documents, null for legacy documents | | `labels` | `Record` | Key/value labels attached to the section | | Field | Type | Description | | ----------------- | ------------------------ | --------------------------------------------------- | | `id` | `string` | Unique interaction identifier | | `title` | `string \| null` | Interaction title | | `state` | `InteractionState` | Current state of the interaction | | `startedAt` | `string (ISO 8601 date)` | When the interaction started (ISO 8601 date string) | | `transcriptCount` | `number` | Number of transcripts in the interaction | | `documentCount` | `number` | Number of documents in the interaction | | `facts` | `Fact[]` | Facts extracted during the interaction | **Fact properties:** | Field | Type | Description | | ------------- | ------------------------------ | -------------------------------------------------------- | | `id` | `string` | Unique fact identifier | | `text` | `string` | Fact text content | | `group` | `string` | Fact group/category name | | `source` | `"core" \| "system" \| "user"` | Source of the fact: core (AI extracted), system, or user | | `isDiscarded` | `boolean` | Whether the fact has been discarded | #### Example ```json theme={null} { "event": "account.creditsConsumed", "confidential": true, "payload": { "creditsConsumed": 12, "reason": "stream", "document": { "id": "doc_123", "documentType": "legacy", "name": "SOAP note", "outputLanguage": "en", "sections": [ { "key": "hpi", "name": "HPI", "text": "...", "structuredOutput": null, "labels": {} } ], "templateType": "built-in", "templateId": "soap", "labels": {} }, "interaction": { "id": "int_123", "title": "Visit", "state": "ongoing", "startedAt": "2024-01-01T00:00:00.000+00:00", "transcriptCount": 0, "documentCount": 0, "facts": [] } } } ``` # account.loggedIn Source: https://docs.corti.ai/assistant/events/generated/account/loggedIn Emitted when a user logs in. ## Event Properties | Field | Value | | -------------- | -------------------- | | `event` | `"account.loggedIn"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------ | -------- | ------------------------------------ | | `authMethod` | `string` | Authentication method used for login | #### Example ```json theme={null} { "event": "account.loggedIn", "confidential": false, "payload": { "authMethod": "password" } } ``` | Field | Type | Description | | ------------ | -------- | ------------------------------------ | | `authMethod` | `string` | Authentication method used for login | | `email` | `string` | User's email address | #### Example ```json theme={null} { "event": "account.loggedIn", "confidential": true, "payload": { "authMethod": "password", "email": "user@example.com" } } ``` # account.loggedOut Source: https://docs.corti.ai/assistant/events/generated/account/loggedOut Emitted when a user logs out. ## Event Properties | Field | Value | | -------------- | --------------------- | | `event` | `"account.loggedOut"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | -------- | -------- | ------------------------- | | `reason` | `string` | What triggered the logout | #### Example ```json theme={null} { "event": "account.loggedOut", "confidential": false, "payload": { "reason": "sign-out" } } ``` | Field | Type | Description | | -------- | -------- | ------------------------- | | `reason` | `string` | What triggered the logout | #### Example ```json theme={null} { "event": "account.loggedOut", "confidential": true, "payload": { "reason": "sign-out" } } ``` # chat.answerCopied Source: https://docs.corti.ai/assistant/events/generated/ai-chat/answerCopied Emitted when a chat answer is copied. ## Event Properties | Field | Value | | -------------- | --------------------- | | `event` | `"chat.answerCopied"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------------ | ------------------------------------------- | ---------------------------------------------------------------------------- | | `answerLength` | `number` | Character count of the copied answer | | `documentId` | `string` | Unique document identifier | | `documentType` | `"legacy" \| "guided"` | Document generation type | | `documentName` | `string` | Document name or title | | `outputLanguage` | `string` | Output language code (e.g., "en", "de") | | `templateType` | `"built-in" \| "custom" \| "schema-driven"` | Type of template used: built-in, custom, or schema-driven | | `templateId` | `string` | Template identifier (either ref for built-in or customTemplateId for custom) | | `documentLabels` | `Record` | Key/value labels attached to the document | | `interactionId` | `string` | Unique interaction identifier | | `interactionState` | `InteractionState` | Current state of the interaction | #### Example ```json theme={null} { "event": "chat.answerCopied", "confidential": false, "payload": { "answerLength": 200, "documentId": "doc_123", "documentType": "legacy", "documentName": "SOAP note", "outputLanguage": "en", "templateType": "built-in", "templateId": "soap", "documentLabels": {}, "interactionId": "int_123", "interactionState": "ongoing" } } ``` | Field | Type | Description | | -------------- | ------------------------- | --------------------------------------------------------------- | | `answer` | `string` | Content that was copied | | `answerLength` | `number` | Character count of the copied answer | | `document` | `ExternalPatientDocument` | Complete document object with all sections and metadata | | `interaction` | `ExternalInteraction` | Interaction context including transcripts, documents, and facts | | Field | Type | Description | | ---------------- | ------------------------------------------- | ---------------------------------------------------------------------------- | | `id` | `string` | Unique document identifier | | `documentType` | `"legacy" \| "guided"` | Document generation type | | `name` | `string` | Document name or title | | `outputLanguage` | `string` | Output language code (e.g., "en", "de") | | `sections` | `Section[]` | Document sections containing the generated content | | `templateType` | `"built-in" \| "custom" \| "schema-driven"` | Type of template used: built-in, custom, or schema-driven | | `templateId` | `string` | Template identifier (either ref for built-in or customTemplateId for custom) | | `labels` | `Record` | Key/value labels attached to the document | **Section properties:** | Field | Type | Description | | ------------------ | ------------------------ | ----------------------------------------------------------------- | | `key` | `string` | Unique key identifying the section type | | `name` | `string` | Display name of the section | | `text` | `string` | Section content in markdown or plain text | | `structuredOutput` | `unknown` | Structured output for guided documents, null for legacy documents | | `labels` | `Record` | Key/value labels attached to the section | | Field | Type | Description | | ----------------- | ------------------------ | --------------------------------------------------- | | `id` | `string` | Unique interaction identifier | | `title` | `string \| null` | Interaction title | | `state` | `InteractionState` | Current state of the interaction | | `startedAt` | `string (ISO 8601 date)` | When the interaction started (ISO 8601 date string) | | `transcriptCount` | `number` | Number of transcripts in the interaction | | `documentCount` | `number` | Number of documents in the interaction | | `facts` | `Fact[]` | Facts extracted during the interaction | **Fact properties:** | Field | Type | Description | | ------------- | ------------------------------ | -------------------------------------------------------- | | `id` | `string` | Unique fact identifier | | `text` | `string` | Fact text content | | `group` | `string` | Fact group/category name | | `source` | `"core" \| "system" \| "user"` | Source of the fact: core (AI extracted), system, or user | | `isDiscarded` | `boolean` | Whether the fact has been discarded | #### Example ```json theme={null} { "event": "chat.answerCopied", "confidential": true, "payload": { "answer": "...", "answerLength": 200, "document": { "id": "doc_123", "documentType": "legacy", "name": "SOAP note", "outputLanguage": "en", "sections": [ { "key": "hpi", "name": "HPI", "text": "...", "structuredOutput": null, "labels": {} } ], "templateType": "built-in", "templateId": "soap", "labels": {} }, "interaction": { "id": "int_123", "title": "Visit", "state": "ongoing", "startedAt": "2024-01-01T00:00:00.000+00:00", "transcriptCount": 0, "documentCount": 0, "facts": [] } } } ``` # chat.asked Source: https://docs.corti.ai/assistant/events/generated/ai-chat/asked Emitted when AI chat is asked a question. ## Event Properties | Field | Value | | -------------- | -------------- | | `event` | `"chat.asked"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------------ | ------------------------------------------- | ---------------------------------------------------------------------------- | | `questionLength` | `number` | Character count of the user's question | | `promptType` | `"question" \| "update"` | Type of prompt sent to the AI | | `documentId` | `string` | Unique document identifier | | `documentType` | `"legacy" \| "guided"` | Document generation type | | `documentName` | `string` | Document name or title | | `outputLanguage` | `string` | Output language code (e.g., "en", "de") | | `templateType` | `"built-in" \| "custom" \| "schema-driven"` | Type of template used: built-in, custom, or schema-driven | | `templateId` | `string` | Template identifier (either ref for built-in or customTemplateId for custom) | | `documentLabels` | `Record` | Key/value labels attached to the document | | `interactionId` | `string` | Unique interaction identifier | | `interactionState` | `InteractionState` | Current state of the interaction | #### Example ```json theme={null} { "event": "chat.asked", "confidential": false, "payload": { "questionLength": 120, "promptType": "question", "documentId": "doc_123", "documentType": "legacy", "documentName": "SOAP note", "outputLanguage": "en", "templateType": "built-in", "templateId": "soap", "documentLabels": {}, "interactionId": "int_123", "interactionState": "ongoing" } } ``` | Field | Type | Description | | ---------------- | ------------------------- | --------------------------------------------------------------- | | `questionLength` | `number` | Character count of the user's question | | `promptType` | `"question" \| "update"` | Type of prompt sent to the AI | | `prompt` | `string` | Full text of the user's question | | `reply` | `string` | AI's generated response | | `document` | `ExternalPatientDocument` | Complete document object with all sections and metadata | | `interaction` | `ExternalInteraction` | Interaction context including transcripts, documents, and facts | | Field | Type | Description | | ---------------- | ------------------------------------------- | ---------------------------------------------------------------------------- | | `id` | `string` | Unique document identifier | | `documentType` | `"legacy" \| "guided"` | Document generation type | | `name` | `string` | Document name or title | | `outputLanguage` | `string` | Output language code (e.g., "en", "de") | | `sections` | `Section[]` | Document sections containing the generated content | | `templateType` | `"built-in" \| "custom" \| "schema-driven"` | Type of template used: built-in, custom, or schema-driven | | `templateId` | `string` | Template identifier (either ref for built-in or customTemplateId for custom) | | `labels` | `Record` | Key/value labels attached to the document | **Section properties:** | Field | Type | Description | | ------------------ | ------------------------ | ----------------------------------------------------------------- | | `key` | `string` | Unique key identifying the section type | | `name` | `string` | Display name of the section | | `text` | `string` | Section content in markdown or plain text | | `structuredOutput` | `unknown` | Structured output for guided documents, null for legacy documents | | `labels` | `Record` | Key/value labels attached to the section | | Field | Type | Description | | ----------------- | ------------------------ | --------------------------------------------------- | | `id` | `string` | Unique interaction identifier | | `title` | `string \| null` | Interaction title | | `state` | `InteractionState` | Current state of the interaction | | `startedAt` | `string (ISO 8601 date)` | When the interaction started (ISO 8601 date string) | | `transcriptCount` | `number` | Number of transcripts in the interaction | | `documentCount` | `number` | Number of documents in the interaction | | `facts` | `Fact[]` | Facts extracted during the interaction | **Fact properties:** | Field | Type | Description | | ------------- | ------------------------------ | -------------------------------------------------------- | | `id` | `string` | Unique fact identifier | | `text` | `string` | Fact text content | | `group` | `string` | Fact group/category name | | `source` | `"core" \| "system" \| "user"` | Source of the fact: core (AI extracted), system, or user | | `isDiscarded` | `boolean` | Whether the fact has been discarded | #### Example ```json theme={null} { "event": "chat.asked", "confidential": true, "payload": { "questionLength": 120, "promptType": "question", "prompt": "...", "reply": "...", "document": { "id": "doc_123", "documentType": "legacy", "name": "SOAP note", "outputLanguage": "en", "sections": [ { "key": "hpi", "name": "HPI", "text": "...", "structuredOutput": null, "labels": {} } ], "templateType": "built-in", "templateId": "soap", "labels": {} }, "interaction": { "id": "int_123", "title": "Visit", "state": "ongoing", "startedAt": "2024-01-01T00:00:00.000+00:00", "transcriptCount": 0, "documentCount": 0, "facts": [] } } } ``` # chat.dictationStarted Source: https://docs.corti.ai/assistant/events/generated/ai-chat/dictationStarted Emitted when chat dictation starts. ## Event Properties | Field | Value | | -------------- | ------------------------- | | `event` | `"chat.dictationStarted"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------------ | ------------------------------------------- | ---------------------------------------------------------------------------- | | `inputLanguage` | `string` | Language used for voice recognition | | `documentId` | `string` | Unique document identifier | | `documentType` | `"legacy" \| "guided"` | Document generation type | | `documentName` | `string` | Document name or title | | `outputLanguage` | `string` | Output language code (e.g., "en", "de") | | `templateType` | `"built-in" \| "custom" \| "schema-driven"` | Type of template used: built-in, custom, or schema-driven | | `templateId` | `string` | Template identifier (either ref for built-in or customTemplateId for custom) | | `documentLabels` | `Record` | Key/value labels attached to the document | | `interactionId` | `string` | Unique interaction identifier | | `interactionState` | `InteractionState` | Current state of the interaction | #### Example ```json theme={null} { "event": "chat.dictationStarted", "confidential": false, "payload": { "inputLanguage": "en", "documentId": "doc_123", "documentType": "legacy", "documentName": "SOAP note", "outputLanguage": "en", "templateType": "built-in", "templateId": "soap", "documentLabels": {}, "interactionId": "int_123", "interactionState": "ongoing" } } ``` | Field | Type | Description | | --------------- | ------------------------- | --------------------------------------------------------------- | | `inputLanguage` | `string` | Language used for voice recognition | | `document` | `ExternalPatientDocument` | Complete document object with all sections and metadata | | `interaction` | `ExternalInteraction` | Interaction context including transcripts, documents, and facts | | Field | Type | Description | | ---------------- | ------------------------------------------- | ---------------------------------------------------------------------------- | | `id` | `string` | Unique document identifier | | `documentType` | `"legacy" \| "guided"` | Document generation type | | `name` | `string` | Document name or title | | `outputLanguage` | `string` | Output language code (e.g., "en", "de") | | `sections` | `Section[]` | Document sections containing the generated content | | `templateType` | `"built-in" \| "custom" \| "schema-driven"` | Type of template used: built-in, custom, or schema-driven | | `templateId` | `string` | Template identifier (either ref for built-in or customTemplateId for custom) | | `labels` | `Record` | Key/value labels attached to the document | **Section properties:** | Field | Type | Description | | ------------------ | ------------------------ | ----------------------------------------------------------------- | | `key` | `string` | Unique key identifying the section type | | `name` | `string` | Display name of the section | | `text` | `string` | Section content in markdown or plain text | | `structuredOutput` | `unknown` | Structured output for guided documents, null for legacy documents | | `labels` | `Record` | Key/value labels attached to the section | | Field | Type | Description | | ----------------- | ------------------------ | --------------------------------------------------- | | `id` | `string` | Unique interaction identifier | | `title` | `string \| null` | Interaction title | | `state` | `InteractionState` | Current state of the interaction | | `startedAt` | `string (ISO 8601 date)` | When the interaction started (ISO 8601 date string) | | `transcriptCount` | `number` | Number of transcripts in the interaction | | `documentCount` | `number` | Number of documents in the interaction | | `facts` | `Fact[]` | Facts extracted during the interaction | **Fact properties:** | Field | Type | Description | | ------------- | ------------------------------ | -------------------------------------------------------- | | `id` | `string` | Unique fact identifier | | `text` | `string` | Fact text content | | `group` | `string` | Fact group/category name | | `source` | `"core" \| "system" \| "user"` | Source of the fact: core (AI extracted), system, or user | | `isDiscarded` | `boolean` | Whether the fact has been discarded | #### Example ```json theme={null} { "event": "chat.dictationStarted", "confidential": true, "payload": { "inputLanguage": "en", "document": { "id": "doc_123", "documentType": "legacy", "name": "SOAP note", "outputLanguage": "en", "sections": [ { "key": "hpi", "name": "HPI", "text": "...", "structuredOutput": null, "labels": {} } ], "templateType": "built-in", "templateId": "soap", "labels": {} }, "interaction": { "id": "int_123", "title": "Visit", "state": "ongoing", "startedAt": "2024-01-01T00:00:00.000+00:00", "transcriptCount": 0, "documentCount": 0, "facts": [] } } } ``` # chat.dictationStopped Source: https://docs.corti.ai/assistant/events/generated/ai-chat/dictationStopped Emitted when chat dictation stops. ## Event Properties | Field | Value | | -------------- | ------------------------- | | `event` | `"chat.dictationStopped"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------------ | ------------------------------------------- | ---------------------------------------------------------------------------- | | `inputLanguage` | `string` | Language used for voice recognition | | `wordsDictated` | `number` | Number of words captured during this session | | `documentId` | `string` | Unique document identifier | | `documentType` | `"legacy" \| "guided"` | Document generation type | | `documentName` | `string` | Document name or title | | `outputLanguage` | `string` | Output language code (e.g., "en", "de") | | `templateType` | `"built-in" \| "custom" \| "schema-driven"` | Type of template used: built-in, custom, or schema-driven | | `templateId` | `string` | Template identifier (either ref for built-in or customTemplateId for custom) | | `documentLabels` | `Record` | Key/value labels attached to the document | | `interactionId` | `string` | Unique interaction identifier | | `interactionState` | `InteractionState` | Current state of the interaction | #### Example ```json theme={null} { "event": "chat.dictationStopped", "confidential": false, "payload": { "inputLanguage": "en", "wordsDictated": 44, "documentId": "doc_123", "documentType": "legacy", "documentName": "SOAP note", "outputLanguage": "en", "templateType": "built-in", "templateId": "soap", "documentLabels": {}, "interactionId": "int_123", "interactionState": "ongoing" } } ``` | Field | Type | Description | | --------------- | ------------------------- | --------------------------------------------------------------- | | `inputLanguage` | `string` | Language used for voice recognition | | `text` | `string` | Transcribed text from dictation | | `wordsDictated` | `number` | Number of words captured during this session | | `document` | `ExternalPatientDocument` | Complete document object with all sections and metadata | | `interaction` | `ExternalInteraction` | Interaction context including transcripts, documents, and facts | | Field | Type | Description | | ---------------- | ------------------------------------------- | ---------------------------------------------------------------------------- | | `id` | `string` | Unique document identifier | | `documentType` | `"legacy" \| "guided"` | Document generation type | | `name` | `string` | Document name or title | | `outputLanguage` | `string` | Output language code (e.g., "en", "de") | | `sections` | `Section[]` | Document sections containing the generated content | | `templateType` | `"built-in" \| "custom" \| "schema-driven"` | Type of template used: built-in, custom, or schema-driven | | `templateId` | `string` | Template identifier (either ref for built-in or customTemplateId for custom) | | `labels` | `Record` | Key/value labels attached to the document | **Section properties:** | Field | Type | Description | | ------------------ | ------------------------ | ----------------------------------------------------------------- | | `key` | `string` | Unique key identifying the section type | | `name` | `string` | Display name of the section | | `text` | `string` | Section content in markdown or plain text | | `structuredOutput` | `unknown` | Structured output for guided documents, null for legacy documents | | `labels` | `Record` | Key/value labels attached to the section | | Field | Type | Description | | ----------------- | ------------------------ | --------------------------------------------------- | | `id` | `string` | Unique interaction identifier | | `title` | `string \| null` | Interaction title | | `state` | `InteractionState` | Current state of the interaction | | `startedAt` | `string (ISO 8601 date)` | When the interaction started (ISO 8601 date string) | | `transcriptCount` | `number` | Number of transcripts in the interaction | | `documentCount` | `number` | Number of documents in the interaction | | `facts` | `Fact[]` | Facts extracted during the interaction | **Fact properties:** | Field | Type | Description | | ------------- | ------------------------------ | -------------------------------------------------------- | | `id` | `string` | Unique fact identifier | | `text` | `string` | Fact text content | | `group` | `string` | Fact group/category name | | `source` | `"core" \| "system" \| "user"` | Source of the fact: core (AI extracted), system, or user | | `isDiscarded` | `boolean` | Whether the fact has been discarded | #### Example ```json theme={null} { "event": "chat.dictationStopped", "confidential": true, "payload": { "inputLanguage": "en", "text": "...", "wordsDictated": 44, "document": { "id": "doc_123", "documentType": "legacy", "name": "SOAP note", "outputLanguage": "en", "sections": [ { "key": "hpi", "name": "HPI", "text": "...", "structuredOutput": null, "labels": {} } ], "templateType": "built-in", "templateId": "soap", "labels": {} }, "interaction": { "id": "int_123", "title": "Visit", "state": "ongoing", "startedAt": "2024-01-01T00:00:00.000+00:00", "transcriptCount": 0, "documentCount": 0, "facts": [] } } } ``` # chat.failed Source: https://docs.corti.ai/assistant/events/generated/ai-chat/failed Emitted when AI chat fails. ## Event Properties | Field | Value | | -------------- | --------------- | | `event` | `"chat.failed"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------------ | ------------------------------------------- | ---------------------------------------------------------------------------- | | `errorCode` | `string` | Machine-readable error identifier | | `documentId` | `string` | Unique document identifier | | `documentType` | `"legacy" \| "guided"` | Document generation type | | `documentName` | `string` | Document name or title | | `outputLanguage` | `string` | Output language code (e.g., "en", "de") | | `templateType` | `"built-in" \| "custom" \| "schema-driven"` | Type of template used: built-in, custom, or schema-driven | | `templateId` | `string` | Template identifier (either ref for built-in or customTemplateId for custom) | | `documentLabels` | `Record` | Key/value labels attached to the document | | `interactionId` | `string` | Unique interaction identifier | | `interactionState` | `InteractionState` | Current state of the interaction | #### Example ```json theme={null} { "event": "chat.failed", "confidential": false, "payload": { "errorCode": "timeout", "documentId": "doc_123", "documentType": "legacy", "documentName": "SOAP note", "outputLanguage": "en", "templateType": "built-in", "templateId": "soap", "documentLabels": {}, "interactionId": "int_123", "interactionState": "ongoing" } } ``` | Field | Type | Description | | ------------- | ------------------------- | --------------------------------------------------------------- | | `prompt` | `string` | Question that failed to process | | `errorCode` | `string` | Machine-readable error identifier | | `document` | `ExternalPatientDocument` | Complete document object with all sections and metadata | | `interaction` | `ExternalInteraction` | Interaction context including transcripts, documents, and facts | | Field | Type | Description | | ---------------- | ------------------------------------------- | ---------------------------------------------------------------------------- | | `id` | `string` | Unique document identifier | | `documentType` | `"legacy" \| "guided"` | Document generation type | | `name` | `string` | Document name or title | | `outputLanguage` | `string` | Output language code (e.g., "en", "de") | | `sections` | `Section[]` | Document sections containing the generated content | | `templateType` | `"built-in" \| "custom" \| "schema-driven"` | Type of template used: built-in, custom, or schema-driven | | `templateId` | `string` | Template identifier (either ref for built-in or customTemplateId for custom) | | `labels` | `Record` | Key/value labels attached to the document | **Section properties:** | Field | Type | Description | | ------------------ | ------------------------ | ----------------------------------------------------------------- | | `key` | `string` | Unique key identifying the section type | | `name` | `string` | Display name of the section | | `text` | `string` | Section content in markdown or plain text | | `structuredOutput` | `unknown` | Structured output for guided documents, null for legacy documents | | `labels` | `Record` | Key/value labels attached to the section | | Field | Type | Description | | ----------------- | ------------------------ | --------------------------------------------------- | | `id` | `string` | Unique interaction identifier | | `title` | `string \| null` | Interaction title | | `state` | `InteractionState` | Current state of the interaction | | `startedAt` | `string (ISO 8601 date)` | When the interaction started (ISO 8601 date string) | | `transcriptCount` | `number` | Number of transcripts in the interaction | | `documentCount` | `number` | Number of documents in the interaction | | `facts` | `Fact[]` | Facts extracted during the interaction | **Fact properties:** | Field | Type | Description | | ------------- | ------------------------------ | -------------------------------------------------------- | | `id` | `string` | Unique fact identifier | | `text` | `string` | Fact text content | | `group` | `string` | Fact group/category name | | `source` | `"core" \| "system" \| "user"` | Source of the fact: core (AI extracted), system, or user | | `isDiscarded` | `boolean` | Whether the fact has been discarded | #### Example ```json theme={null} { "event": "chat.failed", "confidential": true, "payload": { "prompt": "...", "errorCode": "timeout", "document": { "id": "doc_123", "documentType": "legacy", "name": "SOAP note", "outputLanguage": "en", "sections": [ { "key": "hpi", "name": "HPI", "text": "...", "structuredOutput": null, "labels": {} } ], "templateType": "built-in", "templateId": "soap", "labels": {} }, "interaction": { "id": "int_123", "title": "Visit", "state": "ongoing", "startedAt": "2024-01-01T00:00:00.000+00:00", "transcriptCount": 0, "documentCount": 0, "facts": [] } } } ``` # document.copied Source: https://docs.corti.ai/assistant/events/generated/document/copied Emitted when a document is copied. ## Event Properties | Field | Value | | -------------- | ------------------- | | `event` | `"document.copied"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------------ | ------------------------------------------- | ---------------------------------------------------------------------------- | | `documentId` | `string` | Unique document identifier | | `documentType` | `"legacy" \| "guided"` | Document generation type | | `documentName` | `string` | Document name or title | | `outputLanguage` | `string` | Output language code (e.g., "en", "de") | | `templateType` | `"built-in" \| "custom" \| "schema-driven"` | Type of template used: built-in, custom, or schema-driven | | `templateId` | `string` | Template identifier (either ref for built-in or customTemplateId for custom) | | `documentLabels` | `Record` | Key/value labels attached to the document | | `interactionId` | `string` | Unique interaction identifier | | `interactionState` | `InteractionState` | Current state of the interaction | #### Example ```json theme={null} { "event": "document.copied", "confidential": false, "payload": { "documentId": "doc_123", "documentType": "legacy", "documentName": "SOAP note", "outputLanguage": "en", "templateType": "built-in", "templateId": "soap", "documentLabels": {}, "interactionId": "int_123", "interactionState": "ongoing" } } ``` | Field | Type | Description | | ------------- | ------------------------- | --------------------------------------------------------------- | | `text` | `string` | Complete document content copied to clipboard | | `document` | `ExternalPatientDocument` | Complete document object with all sections and metadata | | `interaction` | `ExternalInteraction` | Interaction context including transcripts, documents, and facts | | Field | Type | Description | | ---------------- | ------------------------------------------- | ---------------------------------------------------------------------------- | | `id` | `string` | Unique document identifier | | `documentType` | `"legacy" \| "guided"` | Document generation type | | `name` | `string` | Document name or title | | `outputLanguage` | `string` | Output language code (e.g., "en", "de") | | `sections` | `Section[]` | Document sections containing the generated content | | `templateType` | `"built-in" \| "custom" \| "schema-driven"` | Type of template used: built-in, custom, or schema-driven | | `templateId` | `string` | Template identifier (either ref for built-in or customTemplateId for custom) | | `labels` | `Record` | Key/value labels attached to the document | **Section properties:** | Field | Type | Description | | ------------------ | ------------------------ | ----------------------------------------------------------------- | | `key` | `string` | Unique key identifying the section type | | `name` | `string` | Display name of the section | | `text` | `string` | Section content in markdown or plain text | | `structuredOutput` | `unknown` | Structured output for guided documents, null for legacy documents | | `labels` | `Record` | Key/value labels attached to the section | | Field | Type | Description | | ----------------- | ------------------------ | --------------------------------------------------- | | `id` | `string` | Unique interaction identifier | | `title` | `string \| null` | Interaction title | | `state` | `InteractionState` | Current state of the interaction | | `startedAt` | `string (ISO 8601 date)` | When the interaction started (ISO 8601 date string) | | `transcriptCount` | `number` | Number of transcripts in the interaction | | `documentCount` | `number` | Number of documents in the interaction | | `facts` | `Fact[]` | Facts extracted during the interaction | **Fact properties:** | Field | Type | Description | | ------------- | ------------------------------ | -------------------------------------------------------- | | `id` | `string` | Unique fact identifier | | `text` | `string` | Fact text content | | `group` | `string` | Fact group/category name | | `source` | `"core" \| "system" \| "user"` | Source of the fact: core (AI extracted), system, or user | | `isDiscarded` | `boolean` | Whether the fact has been discarded | #### Example ```json theme={null} { "event": "document.copied", "confidential": true, "payload": { "text": "...", "document": { "id": "doc_123", "documentType": "legacy", "name": "SOAP note", "outputLanguage": "en", "sections": [ { "key": "hpi", "name": "HPI", "text": "...", "structuredOutput": null, "labels": {} } ], "templateType": "built-in", "templateId": "soap", "labels": {} }, "interaction": { "id": "int_123", "title": "Visit", "state": "ongoing", "startedAt": "2024-01-01T00:00:00.000+00:00", "transcriptCount": 0, "documentCount": 0, "facts": [] } } } ``` # document.dictationStarted Source: https://docs.corti.ai/assistant/events/generated/document/dictationStarted Emitted when document dictation starts. ## Event Properties | Field | Value | | -------------- | ----------------------------- | | `event` | `"document.dictationStarted"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------------ | ------------------------------------------- | ---------------------------------------------------------------------------- | | `sectionKey` | `string` | Which section dictation is targeting | | `documentId` | `string` | Unique document identifier | | `documentType` | `"legacy" \| "guided"` | Document generation type | | `documentName` | `string` | Document name or title | | `outputLanguage` | `string` | Output language code (e.g., "en", "de") | | `templateType` | `"built-in" \| "custom" \| "schema-driven"` | Type of template used: built-in, custom, or schema-driven | | `templateId` | `string` | Template identifier (either ref for built-in or customTemplateId for custom) | | `documentLabels` | `Record` | Key/value labels attached to the document | | `interactionId` | `string` | Unique interaction identifier | | `interactionState` | `InteractionState` | Current state of the interaction | #### Example ```json theme={null} { "event": "document.dictationStarted", "confidential": false, "payload": { "sectionKey": "assessment", "documentId": "doc_123", "documentType": "legacy", "documentName": "SOAP note", "outputLanguage": "en", "templateType": "built-in", "templateId": "soap", "documentLabels": {}, "interactionId": "int_123", "interactionState": "ongoing" } } ``` | Field | Type | Description | | ------------- | ------------------------- | --------------------------------------------------------------- | | `sectionKey` | `string` | Which section dictation is targeting | | `document` | `ExternalPatientDocument` | Complete document object with all sections and metadata | | `interaction` | `ExternalInteraction` | Interaction context including transcripts, documents, and facts | | Field | Type | Description | | ---------------- | ------------------------------------------- | ---------------------------------------------------------------------------- | | `id` | `string` | Unique document identifier | | `documentType` | `"legacy" \| "guided"` | Document generation type | | `name` | `string` | Document name or title | | `outputLanguage` | `string` | Output language code (e.g., "en", "de") | | `sections` | `Section[]` | Document sections containing the generated content | | `templateType` | `"built-in" \| "custom" \| "schema-driven"` | Type of template used: built-in, custom, or schema-driven | | `templateId` | `string` | Template identifier (either ref for built-in or customTemplateId for custom) | | `labels` | `Record` | Key/value labels attached to the document | **Section properties:** | Field | Type | Description | | ------------------ | ------------------------ | ----------------------------------------------------------------- | | `key` | `string` | Unique key identifying the section type | | `name` | `string` | Display name of the section | | `text` | `string` | Section content in markdown or plain text | | `structuredOutput` | `unknown` | Structured output for guided documents, null for legacy documents | | `labels` | `Record` | Key/value labels attached to the section | | Field | Type | Description | | ----------------- | ------------------------ | --------------------------------------------------- | | `id` | `string` | Unique interaction identifier | | `title` | `string \| null` | Interaction title | | `state` | `InteractionState` | Current state of the interaction | | `startedAt` | `string (ISO 8601 date)` | When the interaction started (ISO 8601 date string) | | `transcriptCount` | `number` | Number of transcripts in the interaction | | `documentCount` | `number` | Number of documents in the interaction | | `facts` | `Fact[]` | Facts extracted during the interaction | **Fact properties:** | Field | Type | Description | | ------------- | ------------------------------ | -------------------------------------------------------- | | `id` | `string` | Unique fact identifier | | `text` | `string` | Fact text content | | `group` | `string` | Fact group/category name | | `source` | `"core" \| "system" \| "user"` | Source of the fact: core (AI extracted), system, or user | | `isDiscarded` | `boolean` | Whether the fact has been discarded | #### Example ```json theme={null} { "event": "document.dictationStarted", "confidential": true, "payload": { "sectionKey": "assessment", "document": { "id": "doc_123", "documentType": "legacy", "name": "SOAP note", "outputLanguage": "en", "sections": [ { "key": "hpi", "name": "HPI", "text": "...", "structuredOutput": null, "labels": {} } ], "templateType": "built-in", "templateId": "soap", "labels": {} }, "interaction": { "id": "int_123", "title": "Visit", "state": "ongoing", "startedAt": "2024-01-01T00:00:00.000+00:00", "transcriptCount": 0, "documentCount": 0, "facts": [] } } } ``` # document.dictationStopped Source: https://docs.corti.ai/assistant/events/generated/document/dictationStopped Emitted when document dictation stops. ## Event Properties | Field | Value | | -------------- | ----------------------------- | | `event` | `"document.dictationStopped"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------------ | ------------------------------------------- | ---------------------------------------------------------------------------- | | `sectionKey` | `string` | Which section was being dictated into | | `wordsDictated` | `number` | Number of words captured during this dictation session | | `documentId` | `string` | Unique document identifier | | `documentType` | `"legacy" \| "guided"` | Document generation type | | `documentName` | `string` | Document name or title | | `outputLanguage` | `string` | Output language code (e.g., "en", "de") | | `templateType` | `"built-in" \| "custom" \| "schema-driven"` | Type of template used: built-in, custom, or schema-driven | | `templateId` | `string` | Template identifier (either ref for built-in or customTemplateId for custom) | | `documentLabels` | `Record` | Key/value labels attached to the document | | `interactionId` | `string` | Unique interaction identifier | | `interactionState` | `InteractionState` | Current state of the interaction | #### Example ```json theme={null} { "event": "document.dictationStopped", "confidential": false, "payload": { "sectionKey": "assessment", "wordsDictated": 120, "documentId": "doc_123", "documentType": "legacy", "documentName": "SOAP note", "outputLanguage": "en", "templateType": "built-in", "templateId": "soap", "documentLabels": {}, "interactionId": "int_123", "interactionState": "ongoing" } } ``` | Field | Type | Description | | --------------- | ------------------------- | --------------------------------------------------------------- | | `sectionKey` | `string` | Which section was being dictated into | | `text` | `string` | Full text content after dictation | | `wordsDictated` | `number` | Number of words captured during this dictation session | | `document` | `ExternalPatientDocument` | Complete document object with all sections and metadata | | `interaction` | `ExternalInteraction` | Interaction context including transcripts, documents, and facts | | Field | Type | Description | | ---------------- | ------------------------------------------- | ---------------------------------------------------------------------------- | | `id` | `string` | Unique document identifier | | `documentType` | `"legacy" \| "guided"` | Document generation type | | `name` | `string` | Document name or title | | `outputLanguage` | `string` | Output language code (e.g., "en", "de") | | `sections` | `Section[]` | Document sections containing the generated content | | `templateType` | `"built-in" \| "custom" \| "schema-driven"` | Type of template used: built-in, custom, or schema-driven | | `templateId` | `string` | Template identifier (either ref for built-in or customTemplateId for custom) | | `labels` | `Record` | Key/value labels attached to the document | **Section properties:** | Field | Type | Description | | ------------------ | ------------------------ | ----------------------------------------------------------------- | | `key` | `string` | Unique key identifying the section type | | `name` | `string` | Display name of the section | | `text` | `string` | Section content in markdown or plain text | | `structuredOutput` | `unknown` | Structured output for guided documents, null for legacy documents | | `labels` | `Record` | Key/value labels attached to the section | | Field | Type | Description | | ----------------- | ------------------------ | --------------------------------------------------- | | `id` | `string` | Unique interaction identifier | | `title` | `string \| null` | Interaction title | | `state` | `InteractionState` | Current state of the interaction | | `startedAt` | `string (ISO 8601 date)` | When the interaction started (ISO 8601 date string) | | `transcriptCount` | `number` | Number of transcripts in the interaction | | `documentCount` | `number` | Number of documents in the interaction | | `facts` | `Fact[]` | Facts extracted during the interaction | **Fact properties:** | Field | Type | Description | | ------------- | ------------------------------ | -------------------------------------------------------- | | `id` | `string` | Unique fact identifier | | `text` | `string` | Fact text content | | `group` | `string` | Fact group/category name | | `source` | `"core" \| "system" \| "user"` | Source of the fact: core (AI extracted), system, or user | | `isDiscarded` | `boolean` | Whether the fact has been discarded | #### Example ```json theme={null} { "event": "document.dictationStopped", "confidential": true, "payload": { "sectionKey": "assessment", "text": "...", "wordsDictated": 120, "document": { "id": "doc_123", "documentType": "legacy", "name": "SOAP note", "outputLanguage": "en", "sections": [ { "key": "hpi", "name": "HPI", "text": "...", "structuredOutput": null, "labels": {} } ], "templateType": "built-in", "templateId": "soap", "labels": {} }, "interaction": { "id": "int_123", "title": "Visit", "state": "ongoing", "startedAt": "2024-01-01T00:00:00.000+00:00", "transcriptCount": 0, "documentCount": 0, "facts": [] } } } ``` # document.edited Source: https://docs.corti.ai/assistant/events/generated/document/edited Emitted when a document section is edited. ## Event Properties | Field | Value | | -------------- | ------------------- | | `event` | `"document.edited"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------------ | ------------------------------------------------------- | ---------------------------------------------------------------------------- | | `sectionKey` | `string` | Which section was modified | | `method` | `"direct" \| "dictation" \| "chat" \| "undo" \| "redo"` | How the edit was performed | | `documentId` | `string` | Unique document identifier | | `documentType` | `"legacy" \| "guided"` | Document generation type | | `documentName` | `string` | Document name or title | | `outputLanguage` | `string` | Output language code (e.g., "en", "de") | | `templateType` | `"built-in" \| "custom" \| "schema-driven"` | Type of template used: built-in, custom, or schema-driven | | `templateId` | `string` | Template identifier (either ref for built-in or customTemplateId for custom) | | `documentLabels` | `Record` | Key/value labels attached to the document | | `interactionId` | `string` | Unique interaction identifier | | `interactionState` | `InteractionState` | Current state of the interaction | #### Example ```json theme={null} { "event": "document.edited", "confidential": false, "payload": { "sectionKey": "hpi", "method": "direct", "documentId": "doc_123", "documentType": "legacy", "documentName": "SOAP note", "outputLanguage": "en", "templateType": "built-in", "templateId": "soap", "documentLabels": {}, "interactionId": "int_123", "interactionState": "ongoing" } } ``` | Field | Type | Description | | ------------- | ------------------------------------------------------- | --------------------------------------------------------------- | | `sectionKey` | `string` | Which section was modified | | `method` | `"direct" \| "dictation" \| "chat" \| "undo" \| "redo"` | How the edit was performed | | `text` | `string` | Full text content after the edit | | `document` | `ExternalPatientDocument` | Complete document object with all sections and metadata | | `interaction` | `ExternalInteraction` | Interaction context including transcripts, documents, and facts | | Field | Type | Description | | ---------------- | ------------------------------------------- | ---------------------------------------------------------------------------- | | `id` | `string` | Unique document identifier | | `documentType` | `"legacy" \| "guided"` | Document generation type | | `name` | `string` | Document name or title | | `outputLanguage` | `string` | Output language code (e.g., "en", "de") | | `sections` | `Section[]` | Document sections containing the generated content | | `templateType` | `"built-in" \| "custom" \| "schema-driven"` | Type of template used: built-in, custom, or schema-driven | | `templateId` | `string` | Template identifier (either ref for built-in or customTemplateId for custom) | | `labels` | `Record` | Key/value labels attached to the document | **Section properties:** | Field | Type | Description | | ------------------ | ------------------------ | ----------------------------------------------------------------- | | `key` | `string` | Unique key identifying the section type | | `name` | `string` | Display name of the section | | `text` | `string` | Section content in markdown or plain text | | `structuredOutput` | `unknown` | Structured output for guided documents, null for legacy documents | | `labels` | `Record` | Key/value labels attached to the section | | Field | Type | Description | | ----------------- | ------------------------ | --------------------------------------------------- | | `id` | `string` | Unique interaction identifier | | `title` | `string \| null` | Interaction title | | `state` | `InteractionState` | Current state of the interaction | | `startedAt` | `string (ISO 8601 date)` | When the interaction started (ISO 8601 date string) | | `transcriptCount` | `number` | Number of transcripts in the interaction | | `documentCount` | `number` | Number of documents in the interaction | | `facts` | `Fact[]` | Facts extracted during the interaction | **Fact properties:** | Field | Type | Description | | ------------- | ------------------------------ | -------------------------------------------------------- | | `id` | `string` | Unique fact identifier | | `text` | `string` | Fact text content | | `group` | `string` | Fact group/category name | | `source` | `"core" \| "system" \| "user"` | Source of the fact: core (AI extracted), system, or user | | `isDiscarded` | `boolean` | Whether the fact has been discarded | #### Example ```json theme={null} { "event": "document.edited", "confidential": true, "payload": { "sectionKey": "hpi", "method": "direct", "text": "...", "document": { "id": "doc_123", "documentType": "legacy", "name": "SOAP note", "outputLanguage": "en", "sections": [ { "key": "hpi", "name": "HPI", "text": "...", "structuredOutput": null, "labels": {} } ], "templateType": "built-in", "templateId": "soap", "labels": {} }, "interaction": { "id": "int_123", "title": "Visit", "state": "ongoing", "startedAt": "2024-01-01T00:00:00.000+00:00", "transcriptCount": 0, "documentCount": 0, "facts": [] } } } ``` # document.feedbackSubmitted Source: https://docs.corti.ai/assistant/events/generated/document/feedbackSubmitted Emitted when document feedback is submitted. ## Event Properties | Field | Value | | -------------- | ------------------------------ | | `event` | `"document.feedbackSubmitted"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------------ | ------------------------------------------- | ---------------------------------------------------------------------------- | | `score` | `number` | User rating for the document | | `maxScore` | `number` | Max possible rating for the document | | `documentId` | `string` | Unique document identifier | | `documentType` | `"legacy" \| "guided"` | Document generation type | | `documentName` | `string` | Document name or title | | `outputLanguage` | `string` | Output language code (e.g., "en", "de") | | `templateType` | `"built-in" \| "custom" \| "schema-driven"` | Type of template used: built-in, custom, or schema-driven | | `templateId` | `string` | Template identifier (either ref for built-in or customTemplateId for custom) | | `documentLabels` | `Record` | Key/value labels attached to the document | | `interactionId` | `string` | Unique interaction identifier | | `interactionState` | `InteractionState` | Current state of the interaction | #### Example ```json theme={null} { "event": "document.feedbackSubmitted", "confidential": false, "payload": { "score": 4, "maxScore": 5, "documentId": "doc_123", "documentType": "legacy", "documentName": "SOAP note", "outputLanguage": "en", "templateType": "built-in", "templateId": "soap", "documentLabels": {}, "interactionId": "int_123", "interactionState": "ongoing" } } ``` | Field | Type | Description | | ------------- | ------------------------- | --------------------------------------------------------------- | | `score` | `number` | User rating for the document | | `maxScore` | `number` | Max possible rating for the document | | `document` | `ExternalPatientDocument` | Complete document object with all sections and metadata | | `interaction` | `ExternalInteraction` | Interaction context including transcripts, documents, and facts | | Field | Type | Description | | ---------------- | ------------------------------------------- | ---------------------------------------------------------------------------- | | `id` | `string` | Unique document identifier | | `documentType` | `"legacy" \| "guided"` | Document generation type | | `name` | `string` | Document name or title | | `outputLanguage` | `string` | Output language code (e.g., "en", "de") | | `sections` | `Section[]` | Document sections containing the generated content | | `templateType` | `"built-in" \| "custom" \| "schema-driven"` | Type of template used: built-in, custom, or schema-driven | | `templateId` | `string` | Template identifier (either ref for built-in or customTemplateId for custom) | | `labels` | `Record` | Key/value labels attached to the document | **Section properties:** | Field | Type | Description | | ------------------ | ------------------------ | ----------------------------------------------------------------- | | `key` | `string` | Unique key identifying the section type | | `name` | `string` | Display name of the section | | `text` | `string` | Section content in markdown or plain text | | `structuredOutput` | `unknown` | Structured output for guided documents, null for legacy documents | | `labels` | `Record` | Key/value labels attached to the section | | Field | Type | Description | | ----------------- | ------------------------ | --------------------------------------------------- | | `id` | `string` | Unique interaction identifier | | `title` | `string \| null` | Interaction title | | `state` | `InteractionState` | Current state of the interaction | | `startedAt` | `string (ISO 8601 date)` | When the interaction started (ISO 8601 date string) | | `transcriptCount` | `number` | Number of transcripts in the interaction | | `documentCount` | `number` | Number of documents in the interaction | | `facts` | `Fact[]` | Facts extracted during the interaction | **Fact properties:** | Field | Type | Description | | ------------- | ------------------------------ | -------------------------------------------------------- | | `id` | `string` | Unique fact identifier | | `text` | `string` | Fact text content | | `group` | `string` | Fact group/category name | | `source` | `"core" \| "system" \| "user"` | Source of the fact: core (AI extracted), system, or user | | `isDiscarded` | `boolean` | Whether the fact has been discarded | #### Example ```json theme={null} { "event": "document.feedbackSubmitted", "confidential": true, "payload": { "score": 4, "maxScore": 5, "document": { "id": "doc_123", "documentType": "legacy", "name": "SOAP note", "outputLanguage": "en", "sections": [ { "key": "hpi", "name": "HPI", "text": "...", "structuredOutput": null, "labels": {} } ], "templateType": "built-in", "templateId": "soap", "labels": {} }, "interaction": { "id": "int_123", "title": "Visit", "state": "ongoing", "startedAt": "2024-01-01T00:00:00.000+00:00", "transcriptCount": 0, "documentCount": 0, "facts": [] } } } ``` # document.generated Source: https://docs.corti.ai/assistant/events/generated/document/generated Emitted when document generation completes. ## Event Properties | Field | Value | | -------------- | ---------------------- | | `event` | `"document.generated"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------------ | ------------------------------------------- | ---------------------------------------------------------------------------- | | `reason` | `"generation" \| "regeneration"` | Was this a newly generated document or a update to existing? | | `durationMs` | `number` | Duration of the generation request | | `documentId` | `string` | Unique document identifier | | `documentType` | `"legacy" \| "guided"` | Document generation type | | `documentName` | `string` | Document name or title | | `outputLanguage` | `string` | Output language code (e.g., "en", "de") | | `templateType` | `"built-in" \| "custom" \| "schema-driven"` | Type of template used: built-in, custom, or schema-driven | | `templateId` | `string` | Template identifier (either ref for built-in or customTemplateId for custom) | | `documentLabels` | `Record` | Key/value labels attached to the document | | `interactionId` | `string` | Unique interaction identifier | | `interactionState` | `InteractionState` | Current state of the interaction | #### Example ```json theme={null} { "event": "document.generated", "confidential": false, "payload": { "reason": "generation", "durationMs": 1840, "documentId": "doc_123", "documentType": "legacy", "documentName": "SOAP note", "outputLanguage": "en", "templateType": "built-in", "templateId": "soap", "documentLabels": {}, "interactionId": "int_123", "interactionState": "ongoing" } } ``` | Field | Type | Description | | ------------- | -------------------------------- | --------------------------------------------------------------- | | `reason` | `"generation" \| "regeneration"` | Was this a newly generated document or a update to existing? | | `durationMs` | `number` | Duration of the generation request | | `document` | `ExternalPatientDocument` | Complete document object with all sections and metadata | | `interaction` | `ExternalInteraction` | Interaction context including transcripts, documents, and facts | | Field | Type | Description | | ---------------- | ------------------------------------------- | ---------------------------------------------------------------------------- | | `id` | `string` | Unique document identifier | | `documentType` | `"legacy" \| "guided"` | Document generation type | | `name` | `string` | Document name or title | | `outputLanguage` | `string` | Output language code (e.g., "en", "de") | | `sections` | `Section[]` | Document sections containing the generated content | | `templateType` | `"built-in" \| "custom" \| "schema-driven"` | Type of template used: built-in, custom, or schema-driven | | `templateId` | `string` | Template identifier (either ref for built-in or customTemplateId for custom) | | `labels` | `Record` | Key/value labels attached to the document | **Section properties:** | Field | Type | Description | | ------------------ | ------------------------ | ----------------------------------------------------------------- | | `key` | `string` | Unique key identifying the section type | | `name` | `string` | Display name of the section | | `text` | `string` | Section content in markdown or plain text | | `structuredOutput` | `unknown` | Structured output for guided documents, null for legacy documents | | `labels` | `Record` | Key/value labels attached to the section | | Field | Type | Description | | ----------------- | ------------------------ | --------------------------------------------------- | | `id` | `string` | Unique interaction identifier | | `title` | `string \| null` | Interaction title | | `state` | `InteractionState` | Current state of the interaction | | `startedAt` | `string (ISO 8601 date)` | When the interaction started (ISO 8601 date string) | | `transcriptCount` | `number` | Number of transcripts in the interaction | | `documentCount` | `number` | Number of documents in the interaction | | `facts` | `Fact[]` | Facts extracted during the interaction | **Fact properties:** | Field | Type | Description | | ------------- | ------------------------------ | -------------------------------------------------------- | | `id` | `string` | Unique fact identifier | | `text` | `string` | Fact text content | | `group` | `string` | Fact group/category name | | `source` | `"core" \| "system" \| "user"` | Source of the fact: core (AI extracted), system, or user | | `isDiscarded` | `boolean` | Whether the fact has been discarded | #### Example ```json theme={null} { "event": "document.generated", "confidential": true, "payload": { "reason": "generation", "durationMs": 1840, "document": { "id": "doc_123", "documentType": "legacy", "name": "SOAP note", "outputLanguage": "en", "sections": [ { "key": "hpi", "name": "HPI", "text": "...", "structuredOutput": null, "labels": {} } ], "templateType": "built-in", "templateId": "soap", "labels": {} }, "interaction": { "id": "int_123", "title": "Visit", "state": "ongoing", "startedAt": "2024-01-01T00:00:00.000+00:00", "transcriptCount": 0, "documentCount": 0, "facts": [] } } } ``` # document.generationFailed Source: https://docs.corti.ai/assistant/events/generated/document/generationFailed Emitted when document generation fails. ## Event Properties | Field | Value | | -------------- | ----------------------------- | | `event` | `"document.generationFailed"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------------ | ------------------------------------------- | ---------------------------------------------------------------------------- | | `reason` | `string` | Human-readable description of the failure | | `errorCode` | `string` | Machine-readable error identifier | | `documentId` | `string` | Unique document identifier | | `documentType` | `"legacy" \| "guided"` | Document generation type | | `documentName` | `string` | Document name or title | | `outputLanguage` | `string` | Output language code (e.g., "en", "de") | | `templateType` | `"built-in" \| "custom" \| "schema-driven"` | Type of template used: built-in, custom, or schema-driven | | `templateId` | `string` | Template identifier (either ref for built-in or customTemplateId for custom) | | `documentLabels` | `Record` | Key/value labels attached to the document | | `interactionId` | `string` | Unique interaction identifier | | `interactionState` | `InteractionState` | Current state of the interaction | #### Example ```json theme={null} { "event": "document.generationFailed", "confidential": false, "payload": { "reason": "timeout", "errorCode": "timeout", "documentId": "doc_123", "documentType": "legacy", "documentName": "SOAP note", "outputLanguage": "en", "templateType": "built-in", "templateId": "soap", "documentLabels": {}, "interactionId": "int_123", "interactionState": "ongoing" } } ``` | Field | Type | Description | | ------------- | ------------------------- | --------------------------------------------------------------- | | `reason` | `string` | Human-readable description of the failure | | `errorCode` | `string` | Machine-readable error identifier | | `document` | `ExternalPatientDocument` | Complete document object with all sections and metadata | | `interaction` | `ExternalInteraction` | Interaction context including transcripts, documents, and facts | | Field | Type | Description | | ---------------- | ------------------------------------------- | ---------------------------------------------------------------------------- | | `id` | `string` | Unique document identifier | | `documentType` | `"legacy" \| "guided"` | Document generation type | | `name` | `string` | Document name or title | | `outputLanguage` | `string` | Output language code (e.g., "en", "de") | | `sections` | `Section[]` | Document sections containing the generated content | | `templateType` | `"built-in" \| "custom" \| "schema-driven"` | Type of template used: built-in, custom, or schema-driven | | `templateId` | `string` | Template identifier (either ref for built-in or customTemplateId for custom) | | `labels` | `Record` | Key/value labels attached to the document | **Section properties:** | Field | Type | Description | | ------------------ | ------------------------ | ----------------------------------------------------------------- | | `key` | `string` | Unique key identifying the section type | | `name` | `string` | Display name of the section | | `text` | `string` | Section content in markdown or plain text | | `structuredOutput` | `unknown` | Structured output for guided documents, null for legacy documents | | `labels` | `Record` | Key/value labels attached to the section | | Field | Type | Description | | ----------------- | ------------------------ | --------------------------------------------------- | | `id` | `string` | Unique interaction identifier | | `title` | `string \| null` | Interaction title | | `state` | `InteractionState` | Current state of the interaction | | `startedAt` | `string (ISO 8601 date)` | When the interaction started (ISO 8601 date string) | | `transcriptCount` | `number` | Number of transcripts in the interaction | | `documentCount` | `number` | Number of documents in the interaction | | `facts` | `Fact[]` | Facts extracted during the interaction | **Fact properties:** | Field | Type | Description | | ------------- | ------------------------------ | -------------------------------------------------------- | | `id` | `string` | Unique fact identifier | | `text` | `string` | Fact text content | | `group` | `string` | Fact group/category name | | `source` | `"core" \| "system" \| "user"` | Source of the fact: core (AI extracted), system, or user | | `isDiscarded` | `boolean` | Whether the fact has been discarded | #### Example ```json theme={null} { "event": "document.generationFailed", "confidential": true, "payload": { "reason": "timeout", "errorCode": "timeout", "document": { "id": "doc_123", "documentType": "legacy", "name": "SOAP note", "outputLanguage": "en", "sections": [ { "key": "hpi", "name": "HPI", "text": "...", "structuredOutput": null, "labels": {} } ], "templateType": "built-in", "templateId": "soap", "labels": {} }, "interaction": { "id": "int_123", "title": "Visit", "state": "ongoing", "startedAt": "2024-01-01T00:00:00.000+00:00", "transcriptCount": 0, "documentCount": 0, "facts": [] } } } ``` # document.sectionCopied Source: https://docs.corti.ai/assistant/events/generated/document/sectionCopied Emitted when a document section is copied. ## Event Properties | Field | Value | | -------------- | -------------------------- | | `event` | `"document.sectionCopied"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------------ | ------------------------------------------- | ---------------------------------------------------------------------------- | | `sectionKey` | `string` | Which section was copied | | `documentId` | `string` | Unique document identifier | | `documentType` | `"legacy" \| "guided"` | Document generation type | | `documentName` | `string` | Document name or title | | `outputLanguage` | `string` | Output language code (e.g., "en", "de") | | `templateType` | `"built-in" \| "custom" \| "schema-driven"` | Type of template used: built-in, custom, or schema-driven | | `templateId` | `string` | Template identifier (either ref for built-in or customTemplateId for custom) | | `documentLabels` | `Record` | Key/value labels attached to the document | | `interactionId` | `string` | Unique interaction identifier | | `interactionState` | `InteractionState` | Current state of the interaction | #### Example ```json theme={null} { "event": "document.sectionCopied", "confidential": false, "payload": { "sectionKey": "assessment", "documentId": "doc_123", "documentType": "legacy", "documentName": "SOAP note", "outputLanguage": "en", "templateType": "built-in", "templateId": "soap", "documentLabels": {}, "interactionId": "int_123", "interactionState": "ongoing" } } ``` | Field | Type | Description | | ------------- | ------------------------- | --------------------------------------------------------------- | | `sectionKey` | `string` | Which section was copied | | `text` | `string` | Content that was copied to clipboard | | `document` | `ExternalPatientDocument` | Complete document object with all sections and metadata | | `interaction` | `ExternalInteraction` | Interaction context including transcripts, documents, and facts | | Field | Type | Description | | ---------------- | ------------------------------------------- | ---------------------------------------------------------------------------- | | `id` | `string` | Unique document identifier | | `documentType` | `"legacy" \| "guided"` | Document generation type | | `name` | `string` | Document name or title | | `outputLanguage` | `string` | Output language code (e.g., "en", "de") | | `sections` | `Section[]` | Document sections containing the generated content | | `templateType` | `"built-in" \| "custom" \| "schema-driven"` | Type of template used: built-in, custom, or schema-driven | | `templateId` | `string` | Template identifier (either ref for built-in or customTemplateId for custom) | | `labels` | `Record` | Key/value labels attached to the document | **Section properties:** | Field | Type | Description | | ------------------ | ------------------------ | ----------------------------------------------------------------- | | `key` | `string` | Unique key identifying the section type | | `name` | `string` | Display name of the section | | `text` | `string` | Section content in markdown or plain text | | `structuredOutput` | `unknown` | Structured output for guided documents, null for legacy documents | | `labels` | `Record` | Key/value labels attached to the section | | Field | Type | Description | | ----------------- | ------------------------ | --------------------------------------------------- | | `id` | `string` | Unique interaction identifier | | `title` | `string \| null` | Interaction title | | `state` | `InteractionState` | Current state of the interaction | | `startedAt` | `string (ISO 8601 date)` | When the interaction started (ISO 8601 date string) | | `transcriptCount` | `number` | Number of transcripts in the interaction | | `documentCount` | `number` | Number of documents in the interaction | | `facts` | `Fact[]` | Facts extracted during the interaction | **Fact properties:** | Field | Type | Description | | ------------- | ------------------------------ | -------------------------------------------------------- | | `id` | `string` | Unique fact identifier | | `text` | `string` | Fact text content | | `group` | `string` | Fact group/category name | | `source` | `"core" \| "system" \| "user"` | Source of the fact: core (AI extracted), system, or user | | `isDiscarded` | `boolean` | Whether the fact has been discarded | #### Example ```json theme={null} { "event": "document.sectionCopied", "confidential": true, "payload": { "sectionKey": "assessment", "text": "...", "document": { "id": "doc_123", "documentType": "legacy", "name": "SOAP note", "outputLanguage": "en", "sections": [ { "key": "hpi", "name": "HPI", "text": "...", "structuredOutput": null, "labels": {} } ], "templateType": "built-in", "templateId": "soap", "labels": {} }, "interaction": { "id": "int_123", "title": "Visit", "state": "ongoing", "startedAt": "2024-01-01T00:00:00.000+00:00", "transcriptCount": 0, "documentCount": 0, "facts": [] } } } ``` # document.synced Source: https://docs.corti.ai/assistant/events/generated/document/synced Emitted when a document sync completes. ## Event Properties | Field | Value | | -------------- | ------------------- | | `event` | `"document.synced"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------------ | ------------------------------------------- | ---------------------------------------------------------------------------- | | `documentId` | `string` | Unique document identifier | | `documentType` | `"legacy" \| "guided"` | Document generation type | | `documentName` | `string` | Document name or title | | `outputLanguage` | `string` | Output language code (e.g., "en", "de") | | `templateType` | `"built-in" \| "custom" \| "schema-driven"` | Type of template used: built-in, custom, or schema-driven | | `templateId` | `string` | Template identifier (either ref for built-in or customTemplateId for custom) | | `documentLabels` | `Record` | Key/value labels attached to the document | | `interactionId` | `string` | Unique interaction identifier | | `interactionState` | `InteractionState` | Current state of the interaction | #### Example ```json theme={null} { "event": "document.synced", "confidential": false, "payload": { "documentId": "doc_123", "documentType": "legacy", "documentName": "SOAP note", "outputLanguage": "en", "templateType": "built-in", "templateId": "soap", "documentLabels": {}, "interactionId": "int_123", "interactionState": "ongoing" } } ``` | Field | Type | Description | | ------------- | ------------------------- | --------------------------------------------------------------- | | `document` | `ExternalPatientDocument` | Complete document object with all sections and metadata | | `interaction` | `ExternalInteraction` | Interaction context including transcripts, documents, and facts | | Field | Type | Description | | ---------------- | ------------------------------------------- | ---------------------------------------------------------------------------- | | `id` | `string` | Unique document identifier | | `documentType` | `"legacy" \| "guided"` | Document generation type | | `name` | `string` | Document name or title | | `outputLanguage` | `string` | Output language code (e.g., "en", "de") | | `sections` | `Section[]` | Document sections containing the generated content | | `templateType` | `"built-in" \| "custom" \| "schema-driven"` | Type of template used: built-in, custom, or schema-driven | | `templateId` | `string` | Template identifier (either ref for built-in or customTemplateId for custom) | | `labels` | `Record` | Key/value labels attached to the document | **Section properties:** | Field | Type | Description | | ------------------ | ------------------------ | ----------------------------------------------------------------- | | `key` | `string` | Unique key identifying the section type | | `name` | `string` | Display name of the section | | `text` | `string` | Section content in markdown or plain text | | `structuredOutput` | `unknown` | Structured output for guided documents, null for legacy documents | | `labels` | `Record` | Key/value labels attached to the section | | Field | Type | Description | | ----------------- | ------------------------ | --------------------------------------------------- | | `id` | `string` | Unique interaction identifier | | `title` | `string \| null` | Interaction title | | `state` | `InteractionState` | Current state of the interaction | | `startedAt` | `string (ISO 8601 date)` | When the interaction started (ISO 8601 date string) | | `transcriptCount` | `number` | Number of transcripts in the interaction | | `documentCount` | `number` | Number of documents in the interaction | | `facts` | `Fact[]` | Facts extracted during the interaction | **Fact properties:** | Field | Type | Description | | ------------- | ------------------------------ | -------------------------------------------------------- | | `id` | `string` | Unique fact identifier | | `text` | `string` | Fact text content | | `group` | `string` | Fact group/category name | | `source` | `"core" \| "system" \| "user"` | Source of the fact: core (AI extracted), system, or user | | `isDiscarded` | `boolean` | Whether the fact has been discarded | #### Example ```json theme={null} { "event": "document.synced", "confidential": true, "payload": { "document": { "id": "doc_123", "documentType": "legacy", "name": "SOAP note", "outputLanguage": "en", "sections": [ { "key": "hpi", "name": "HPI", "text": "...", "structuredOutput": null, "labels": {} } ], "templateType": "built-in", "templateId": "soap", "labels": {} }, "interaction": { "id": "int_123", "title": "Visit", "state": "ongoing", "startedAt": "2024-01-01T00:00:00.000+00:00", "transcriptCount": 0, "documentCount": 0, "facts": [] } } } ``` # document.updated Source: https://docs.corti.ai/assistant/events/generated/document/updated Emitted when a document is updated. ## Event Properties | Field | Value | | -------------- | -------------------- | | `event` | `"document.updated"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------------ | ------------------------------------------- | ---------------------------------------------------------------------------- | | `reason` | `string` | What triggered the update | | `documentId` | `string` | Unique document identifier | | `documentType` | `"legacy" \| "guided"` | Document generation type | | `documentName` | `string` | Document name or title | | `outputLanguage` | `string` | Output language code (e.g., "en", "de") | | `templateType` | `"built-in" \| "custom" \| "schema-driven"` | Type of template used: built-in, custom, or schema-driven | | `templateId` | `string` | Template identifier (either ref for built-in or customTemplateId for custom) | | `documentLabels` | `Record` | Key/value labels attached to the document | | `interactionId` | `string` | Unique interaction identifier | | `interactionState` | `InteractionState` | Current state of the interaction | #### Example ```json theme={null} { "event": "document.updated", "confidential": false, "payload": { "reason": "edit", "documentId": "doc_123", "documentType": "legacy", "documentName": "SOAP note", "outputLanguage": "en", "templateType": "built-in", "templateId": "soap", "documentLabels": {}, "interactionId": "int_123", "interactionState": "ongoing" } } ``` | Field | Type | Description | | ------------- | ------------------------- | --------------------------------------------------------------- | | `reason` | `string` | What triggered the update | | `document` | `ExternalPatientDocument` | Complete document object with all sections and metadata | | `interaction` | `ExternalInteraction` | Interaction context including transcripts, documents, and facts | | Field | Type | Description | | ---------------- | ------------------------------------------- | ---------------------------------------------------------------------------- | | `id` | `string` | Unique document identifier | | `documentType` | `"legacy" \| "guided"` | Document generation type | | `name` | `string` | Document name or title | | `outputLanguage` | `string` | Output language code (e.g., "en", "de") | | `sections` | `Section[]` | Document sections containing the generated content | | `templateType` | `"built-in" \| "custom" \| "schema-driven"` | Type of template used: built-in, custom, or schema-driven | | `templateId` | `string` | Template identifier (either ref for built-in or customTemplateId for custom) | | `labels` | `Record` | Key/value labels attached to the document | **Section properties:** | Field | Type | Description | | ------------------ | ------------------------ | ----------------------------------------------------------------- | | `key` | `string` | Unique key identifying the section type | | `name` | `string` | Display name of the section | | `text` | `string` | Section content in markdown or plain text | | `structuredOutput` | `unknown` | Structured output for guided documents, null for legacy documents | | `labels` | `Record` | Key/value labels attached to the section | | Field | Type | Description | | ----------------- | ------------------------ | --------------------------------------------------- | | `id` | `string` | Unique interaction identifier | | `title` | `string \| null` | Interaction title | | `state` | `InteractionState` | Current state of the interaction | | `startedAt` | `string (ISO 8601 date)` | When the interaction started (ISO 8601 date string) | | `transcriptCount` | `number` | Number of transcripts in the interaction | | `documentCount` | `number` | Number of documents in the interaction | | `facts` | `Fact[]` | Facts extracted during the interaction | **Fact properties:** | Field | Type | Description | | ------------- | ------------------------------ | -------------------------------------------------------- | | `id` | `string` | Unique fact identifier | | `text` | `string` | Fact text content | | `group` | `string` | Fact group/category name | | `source` | `"core" \| "system" \| "user"` | Source of the fact: core (AI extracted), system, or user | | `isDiscarded` | `boolean` | Whether the fact has been discarded | #### Example ```json theme={null} { "event": "document.updated", "confidential": true, "payload": { "reason": "edit", "document": { "id": "doc_123", "documentType": "legacy", "name": "SOAP note", "outputLanguage": "en", "sections": [ { "key": "hpi", "name": "HPI", "text": "...", "structuredOutput": null, "labels": {} } ], "templateType": "built-in", "templateId": "soap", "labels": {} }, "interaction": { "id": "int_123", "title": "Visit", "state": "ongoing", "startedAt": "2024-01-01T00:00:00.000+00:00", "transcriptCount": 0, "documentCount": 0, "facts": [] } } } ``` # embedded.appConfigured Source: https://docs.corti.ai/assistant/events/generated/embedded-api/appConfigured Emitted when the embedded configureApp method is successfully called. ## Event Properties | Field | Value | | -------------- | -------------------------- | | `event` | `"embedded.appConfigured"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | --------------- | ---------------------------------------------------------- | ------------------------------------------ | | `appearance` | `Record \| undefined` | Visual customization options | | `ui` | `Record \| undefined` | App-level UI toggles | | `companionApp` | `{ enabled: boolean; } \| undefined` | Companion app configuration | | `locale` | `Record \| undefined` | Language and localization settings | | `overrideCount` | `number \| undefined` | Count of configuration properties provided | | Field | Type | Description | | --------- | --------- | ----------- | | `enabled` | `boolean` | - | #### Example ```json theme={null} { "event": "embedded.appConfigured", "confidential": false, "payload": { "appearance": { "primaryColor": "#3366FF" }, "ui": { "interactionTitle": true, "aiChat": true, "navigation": false, "documentFeedback": true }, "companionApp": { "enabled": false }, "locale": { "interfaceLanguage": "en", "dictationLanguage": "en" }, "overrideCount": 2 } } ``` | Field | Type | Description | | --------------- | ---------------------------------------------------------- | ------------------------------------------ | | `appearance` | `Record \| undefined` | Visual customization options | | `ui` | `Record \| undefined` | App-level UI toggles | | `companionApp` | `{ enabled: boolean; } \| undefined` | Companion app configuration | | `locale` | `Record \| undefined` | Language and localization settings | | `overrideCount` | `number \| undefined` | Count of configuration properties provided | | Field | Type | Description | | --------- | --------- | ----------- | | `enabled` | `boolean` | - | #### Example ```json theme={null} { "event": "embedded.appConfigured", "confidential": true, "payload": { "appearance": { "primaryColor": "#3366FF" }, "ui": { "interactionTitle": true, "aiChat": true, "navigation": false, "documentFeedback": true }, "companionApp": { "enabled": false }, "locale": { "interfaceLanguage": "en", "dictationLanguage": "en" }, "overrideCount": 2 } } ``` # embedded.authenticated Source: https://docs.corti.ai/assistant/events/generated/embedded-api/authenticated Emitted when the embedded auth() method is successfully called. ## Event Properties | Field | Value | | -------------- | -------------------------- | | `event` | `"embedded.authenticated"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------------ | ----------------------------- | ---------------------------------------- | | `tokenType` | `string \| undefined` | Type of authentication token | | `scope` | `string \| undefined` | OAuth scope granted | | `expiresIn` | `number \| null \| undefined` | Access token expiration time in seconds | | `refreshExpiresIn` | `number \| null \| undefined` | Refresh token expiration time in seconds | | `hasRefreshToken` | `boolean` | Whether a refresh token was provided | | `hasIdToken` | `boolean` | Whether an ID token was provided | | `hasProfile` | `boolean` | Whether user profile data was included | #### Example ```json theme={null} { "event": "embedded.authenticated", "confidential": false, "payload": { "tokenType": "Bearer", "scope": "openid profile", "expiresIn": 3600, "refreshExpiresIn": 7200, "hasRefreshToken": true, "hasIdToken": true, "hasProfile": true } } ``` | Field | Type | Description | | ------------------ | ----------------------------- | ---------------------------------------- | | `tokenType` | `string \| undefined` | Type of authentication token | | `scope` | `string \| undefined` | OAuth scope granted | | `expiresIn` | `number \| null \| undefined` | Access token expiration time in seconds | | `refreshExpiresIn` | `number \| null \| undefined` | Refresh token expiration time in seconds | | `hasRefreshToken` | `boolean` | Whether a refresh token was provided | | `hasIdToken` | `boolean` | Whether an ID token was provided | | `hasProfile` | `boolean` | Whether user profile data was included | #### Example ```json theme={null} { "event": "embedded.authenticated", "confidential": true, "payload": { "tokenType": "Bearer", "scope": "openid profile", "expiresIn": 3600, "refreshExpiresIn": 7200, "hasRefreshToken": true, "hasIdToken": true, "hasProfile": true } } ``` # embedded.credentialsUpdated Source: https://docs.corti.ai/assistant/events/generated/embedded-api/credentialsUpdated Emitted when the embedded setCredentials method is successfully called. ## Event Properties | Field | Value | | -------------- | ------------------------------- | | `event` | `"embedded.credentialsUpdated"` | | `confidential` | `boolean` | | `payload` | `object` | No payload properties. #### Example ```json theme={null} { "event": "embedded.credentialsUpdated", "confidential": false, "payload": {} } ``` No payload properties. #### Example ```json theme={null} { "event": "embedded.credentialsUpdated", "confidential": true, "payload": {} } ``` # embedded.deprecationWarning Source: https://docs.corti.ai/assistant/events/generated/embedded-api/deprecationWarning Emitted when a deprecated embedded API method is called and a migration warning is shown. ## Event Properties | Field | Value | | -------------- | ------------------------------- | | `event` | `"embedded.deprecationWarning"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------------- | ---------------------------------------------- | ---------------------------------------- | | `methodName` | `"configure" \| "configureSession"` | Deprecated embedded API method name | | `replacement` | `"configureApp() and setInteractionOptions()"` | Suggested replacement method or methods | | `migrationGuideUrl` | `string` | Documentation URL for migration guidance | #### Example ```json theme={null} { "event": "embedded.deprecationWarning", "confidential": false, "payload": { "methodName": "configure", "replacement": "configureApp() and setInteractionOptions()", "migrationGuideUrl": "https://docs.corti.ai/assistant/configuration-migration" } } ``` | Field | Type | Description | | ------------------- | ---------------------------------------------- | ---------------------------------------- | | `methodName` | `"configure" \| "configureSession"` | Deprecated embedded API method name | | `replacement` | `"configureApp() and setInteractionOptions()"` | Suggested replacement method or methods | | `migrationGuideUrl` | `string` | Documentation URL for migration guidance | #### Example ```json theme={null} { "event": "embedded.deprecationWarning", "confidential": true, "payload": { "methodName": "configure", "replacement": "configureApp() and setInteractionOptions()", "migrationGuideUrl": "https://docs.corti.ai/assistant/configuration-migration" } } ``` # embedded.factsAdded Source: https://docs.corti.ai/assistant/events/generated/embedded-api/factsAdded Emitted when the embedded addFacts method is successfully called. ## Event Properties | Field | Value | | -------------- | ----------------------- | | `event` | `"embedded.factsAdded"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------------ | ------------------ | -------------------------------- | | `factCount` | `number` | Number of facts being added | | `interactionId` | `string` | Unique interaction identifier | | `interactionState` | `InteractionState` | Current state of the interaction | #### Example ```json theme={null} { "event": "embedded.factsAdded", "confidential": false, "payload": { "factCount": 2, "interactionId": "int_123", "interactionState": "ongoing" } } ``` | Field | Type | Description | | ------------- | --------------------- | --------------------------------------------------------------- | | `factCount` | `number` | Number of facts being added | | `facts` | `Fact[]` | Array of facts with text, group, and source | | `interaction` | `ExternalInteraction` | Interaction context including transcripts, documents, and facts | | Field | Type | Description | | -------- | ------------------------------------------- | ----------- | | `text` | `string` | - | | `group` | `string` | - | | `source` | `"core" \| "system" \| "user" \| undefined` | - | | Field | Type | Description | | ----------------- | ------------------------ | --------------------------------------------------- | | `id` | `string` | Unique interaction identifier | | `title` | `string \| null` | Interaction title | | `state` | `InteractionState` | Current state of the interaction | | `startedAt` | `string (ISO 8601 date)` | When the interaction started (ISO 8601 date string) | | `transcriptCount` | `number` | Number of transcripts in the interaction | | `documentCount` | `number` | Number of documents in the interaction | | `facts` | `Fact[]` | Facts extracted during the interaction | **Fact properties:** | Field | Type | Description | | ------------- | ------------------------------ | -------------------------------------------------------- | | `id` | `string` | Unique fact identifier | | `text` | `string` | Fact text content | | `group` | `string` | Fact group/category name | | `source` | `"core" \| "system" \| "user"` | Source of the fact: core (AI extracted), system, or user | | `isDiscarded` | `boolean` | Whether the fact has been discarded | #### Example ```json theme={null} { "event": "embedded.factsAdded", "confidential": true, "payload": { "factCount": 2, "facts": [ { "text": "Chest pain", "group": "other", "source": "user" }, { "text": "Shortness of breath", "group": "other", "source": "system" } ], "interaction": { "id": "int_123", "title": "Visit", "state": "ongoing", "startedAt": "2024-01-01T00:00:00.000+00:00", "transcriptCount": 0, "documentCount": 0, "facts": [] } } } ``` # embedded.interactionCreated Source: https://docs.corti.ai/assistant/events/generated/embedded-api/interactionCreated Emitted when the embedded createInteraction method is successfully called. ## Event Properties | Field | Value | | -------------- | ------------------------------- | | `event` | `"embedded.interactionCreated"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------------ | -------------------------------------------------------------------------------------- | --------------------------------- | | `encounterType` | `"first_consultation" \| "consultation" \| "emergency" \| "inpatient" \| "outpatient"` | Type of clinical encounter | | `encounterStatus` | `"planned" \| "in-progress" \| "cancelled" \| "deleted" \| "on-hold" \| "completed"` | Current status of the encounter | | `hasPatient` | `boolean` | Whether patient data was provided | | `interactionId` | `string` | Unique interaction identifier | | `interactionState` | `InteractionState` | Current state of the interaction | #### Example ```json theme={null} { "event": "embedded.interactionCreated", "confidential": false, "payload": { "encounterType": "consultation", "encounterStatus": "planned", "hasPatient": true, "interactionId": "int_123", "interactionState": "ongoing" } } ``` | Field | Type | Description | | --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- | | `encounterType` | `"first_consultation" \| "consultation" \| "emergency" \| "inpatient" \| "outpatient"` | Type of clinical encounter | | `encounterStatus` | `"planned" \| "in-progress" \| "cancelled" \| "deleted" \| "on-hold" \| "completed"` | Current status of the encounter | | `encounterIdentifier` | `string` | External system's encounter identifier | | `encounterStartedAt` | `string (ISO 8601 date)` | When the encounter began | | `encounterTitle` | `string \| undefined` | Optional descriptive title for the encounter | | `assignedUserId` | `string \| null \| undefined` | User responsible for this interaction | | `hasPatient` | `boolean` | Whether patient data was provided | | `patient` | `{ identifier?: string \| undefined; name?: string \| undefined; gender?: "male" \| "female" \| "other" \| "unknown" \| undefined; birthDate?: string \| null \| undefined; pronouns?: string \| undefined; } \| undefined` | Patient demographic data | | `interaction` | `ExternalInteraction` | Interaction context including transcripts, documents, and facts | | Field | Type | Description | | ------------ | -------- | ----------- | | `identifier` | `string` | - | | `name` | `string` | - | | `gender` | `string` | - | | `birthDate` | `string` | - | | `pronouns` | `string` | - | | Field | Type | Description | | ----------------- | ------------------------ | --------------------------------------------------- | | `id` | `string` | Unique interaction identifier | | `title` | `string \| null` | Interaction title | | `state` | `InteractionState` | Current state of the interaction | | `startedAt` | `string (ISO 8601 date)` | When the interaction started (ISO 8601 date string) | | `transcriptCount` | `number` | Number of transcripts in the interaction | | `documentCount` | `number` | Number of documents in the interaction | | `facts` | `Fact[]` | Facts extracted during the interaction | **Fact properties:** | Field | Type | Description | | ------------- | ------------------------------ | -------------------------------------------------------- | | `id` | `string` | Unique fact identifier | | `text` | `string` | Fact text content | | `group` | `string` | Fact group/category name | | `source` | `"core" \| "system" \| "user"` | Source of the fact: core (AI extracted), system, or user | | `isDiscarded` | `boolean` | Whether the fact has been discarded | #### Example ```json theme={null} { "event": "embedded.interactionCreated", "confidential": true, "payload": { "encounterType": "consultation", "encounterStatus": "planned", "encounterIdentifier": "encounter-123", "encounterStartedAt": "2024-01-01T00:00:00.000Z", "encounterTitle": "Initial Consultation", "assignedUserId": "user_123", "hasPatient": true, "patient": { "identifier": "patient-123", "name": "Jane Doe", "gender": "female", "birthDate": "1980-01-01", "pronouns": "she/her" }, "interaction": { "id": "int_123", "title": "Visit", "state": "ongoing", "startedAt": "2024-01-01T00:00:00.000+00:00", "transcriptCount": 0, "documentCount": 0, "facts": [] } } } ``` # embedded.interactionOptionsSet Source: https://docs.corti.ai/assistant/events/generated/embedded-api/interactionOptionsSet Emitted when the embedded setInteractionOptions method is successfully called. ## Event Properties | Field | Value | | -------------- | ---------------------------------- | | `event` | `"embedded.interactionOptionsSet"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------- | | `mode` | `{ fallback?: "virtual" \| "in-person" \| null \| undefined; options?: ("virtual" \| "in-person")[] \| null \| undefined; } \| null \| undefined` | Allowed interaction modes and fallback selection | | `spokenLanguage` | `{ fallback?: string \| null \| undefined; options?: string[] \| null \| undefined; } \| null \| undefined` | Spoken language fallback to apply | | `templates` | `{ sources?: { personal?: { enabled?: boolean \| null \| undefined; } \| undefined; standard?: { enabled?: boolean \| null \| undefined; include?: { regions?: string[] \| null \| undefined; families?: string[] \| ... 1 more ... \| undefined; } \| undefined; } \| undefined; project?: { ...; } \| undefined; } \| undefined; defaultT...` | Template options applied by the embedded API | | `documents` | `{ actions?: { sync?: boolean \| null \| undefined; } \| undefined; allowedLanguages?: string[] \| null \| undefined; maxGenerated?: number \| "unlimited" \| null \| undefined; } \| undefined` | Document action options applied by the embedded API | | Field | Type | Description | | ---------- | -------- | ----------- | | `fallback` | `string` | - | | `options` | `array` | - | | Field | Type | Description | | ---------- | -------- | ----------- | | `fallback` | `string` | - | | `options` | `array` | - | | Field | Type | Description | | ----------------- | -------- | ----------- | | `sources` | `object` | - | | `defaultTemplate` | `object` | - | | Field | Type | Description | | ------------------ | -------- | ----------- | | `actions` | `object` | - | | `allowedLanguages` | `array` | - | | `maxGenerated` | `number` | - | #### Example ```json theme={null} { "event": "embedded.interactionOptionsSet", "confidential": false, "payload": { "mode": { "fallback": "virtual", "options": [ "virtual", "in-person" ] }, "spokenLanguage": { "fallback": "en", "options": [ "en", "de" ] }, "templates": { "sources": { "personal": { "enabled": true }, "standard": { "enabled": true, "include": { "regions": [ "BE" ], "families": [ "soap" ] } }, "project": { "enabled": true, "include": { "ids": [ "7a0ab5b8-1f62-4f75-86a7-4fcd8fd07b5d" ] }, "exclude": { "ids": [ "d7a7fd8c-8892-4475-81d3-3414b7d8e27d" ] } } }, "defaultTemplate": { "behaviour": "fallback", "template": { "source": "standard", "id": "soap" }, "allowUserSelection": false } }, "documents": { "actions": { "sync": true }, "allowedLanguages": [ "nl", "fr", "de-CHE" ], "maxGenerated": 2 } } } ``` | Field | Type | Description | | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------- | | `mode` | `{ fallback?: "virtual" \| "in-person" \| null \| undefined; options?: ("virtual" \| "in-person")[] \| null \| undefined; } \| null \| undefined` | Allowed interaction modes and fallback selection | | `spokenLanguage` | `{ fallback?: string \| null \| undefined; options?: string[] \| null \| undefined; } \| null \| undefined` | Spoken language fallback to apply | | `templates` | `{ sources?: { personal?: { enabled?: boolean \| null \| undefined; } \| undefined; standard?: { enabled?: boolean \| null \| undefined; include?: { regions?: string[] \| null \| undefined; families?: string[] \| ... 1 more ... \| undefined; } \| undefined; } \| undefined; project?: { ...; } \| undefined; } \| undefined; defaultT...` | Template options applied by the embedded API | | `documents` | `{ actions?: { sync?: boolean \| null \| undefined; } \| undefined; allowedLanguages?: string[] \| null \| undefined; maxGenerated?: number \| "unlimited" \| null \| undefined; } \| undefined` | Document action options applied by the embedded API | | Field | Type | Description | | ---------- | -------- | ----------- | | `fallback` | `string` | - | | `options` | `array` | - | | Field | Type | Description | | ---------- | -------- | ----------- | | `fallback` | `string` | - | | `options` | `array` | - | | Field | Type | Description | | ----------------- | -------- | ----------- | | `sources` | `object` | - | | `defaultTemplate` | `object` | - | | Field | Type | Description | | ------------------ | -------- | ----------- | | `actions` | `object` | - | | `allowedLanguages` | `array` | - | | `maxGenerated` | `number` | - | #### Example ```json theme={null} { "event": "embedded.interactionOptionsSet", "confidential": true, "payload": { "mode": { "fallback": "virtual", "options": [ "virtual", "in-person" ] }, "spokenLanguage": { "fallback": "en", "options": [ "en", "de" ] }, "templates": { "sources": { "personal": { "enabled": true }, "standard": { "enabled": true, "include": { "regions": [ "BE" ], "families": [ "soap" ] } }, "project": { "enabled": true, "include": { "ids": [ "7a0ab5b8-1f62-4f75-86a7-4fcd8fd07b5d" ] }, "exclude": { "ids": [ "d7a7fd8c-8892-4475-81d3-3414b7d8e27d" ] } } }, "defaultTemplate": { "behaviour": "fallback", "template": { "source": "standard", "id": "soap" }, "allowUserSelection": false } }, "documents": { "actions": { "sync": true }, "allowedLanguages": [ "nl", "fr", "de-CHE" ], "maxGenerated": 2 } } } ``` # embedded.interactionOptionsWarning Source: https://docs.corti.ai/assistant/events/generated/embedded-api/interactionOptionsWarning Emitted when setInteractionOptions skips invalid, unsupported, or over-constraining interaction option values. ## Event Properties | Field | Value | | -------------- | -------------------------------------- | | `event` | `"embedded.interactionOptionsWarning"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- | | `code` | `"INTERACTION_OPTIONS_VALUES_SKIPPED"` | Stable warning code for integrators | | `message` | `string` | Human-readable warning summary | | `skippedValues` | `{ spokenLanguage: { fallback: string[]; options: string[]; }; templates: { unsupportedSources: ("standard" \| "project")[]; standard: { include: { regions: string[]; families: string[]; }; }; project: { include: { ...; }; exclude: { ...; }; }; }; documents: { ...; }; }` | Interaction option values ignored while applying setInteractionOptions | | Field | Type | Description | | ---------------- | -------- | ----------- | | `spokenLanguage` | `object` | - | | `templates` | `object` | - | | `documents` | `object` | - | #### Example ```json theme={null} { "event": "embedded.interactionOptionsWarning", "confidential": false, "payload": { "code": "INTERACTION_OPTIONS_VALUES_SKIPPED", "message": "Some interaction option values were skipped because they are invalid, unsupported, or would leave no templates available.", "skippedValues": { "spokenLanguage": { "fallback": [], "options": [] }, "templates": { "unsupportedSources": [ "standard", "project" ], "standard": { "include": { "regions": [ "ZZ" ], "families": [ "unknown-family" ] } }, "project": { "include": { "ids": [ "not-a-uuid" ] }, "exclude": { "ids": [] } } }, "documents": { "allowedLanguages": [ "oo" ] } } } } ``` | Field | Type | Description | | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- | | `code` | `"INTERACTION_OPTIONS_VALUES_SKIPPED"` | Stable warning code for integrators | | `message` | `string` | Human-readable warning summary | | `skippedValues` | `{ spokenLanguage: { fallback: string[]; options: string[]; }; templates: { unsupportedSources: ("standard" \| "project")[]; standard: { include: { regions: string[]; families: string[]; }; }; project: { include: { ...; }; exclude: { ...; }; }; }; documents: { ...; }; }` | Interaction option values ignored while applying setInteractionOptions | | Field | Type | Description | | ---------------- | -------- | ----------- | | `spokenLanguage` | `object` | - | | `templates` | `object` | - | | `documents` | `object` | - | #### Example ```json theme={null} { "event": "embedded.interactionOptionsWarning", "confidential": true, "payload": { "code": "INTERACTION_OPTIONS_VALUES_SKIPPED", "message": "Some interaction option values were skipped because they are invalid, unsupported, or would leave no templates available.", "skippedValues": { "spokenLanguage": { "fallback": [], "options": [] }, "templates": { "unsupportedSources": [ "standard", "project" ], "standard": { "include": { "regions": [ "ZZ" ], "families": [ "unknown-family" ] } }, "project": { "include": { "ids": [ "not-a-uuid" ] }, "exclude": { "ids": [] } } }, "documents": { "allowedLanguages": [ "oo" ] } } } } ``` # embedded.navigated Source: https://docs.corti.ai/assistant/events/generated/embedded-api/navigated Emitted when the embedded navigate method is successfully called. ## Event Properties | Field | Value | | -------------- | ---------------------- | | `event` | `"embedded.navigated"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------ | -------- | -------------------------------- | | `path` | `string` | Target route path for navigation | #### Example ```json theme={null} { "event": "embedded.navigated", "confidential": false, "payload": { "path": "/session/interaction-123" } } ``` | Field | Type | Description | | ------ | -------- | -------------------------------- | | `path` | `string` | Target route path for navigation | #### Example ```json theme={null} { "event": "embedded.navigated", "confidential": true, "payload": { "path": "/session/interaction-123" } } ``` # embedded.ready Source: https://docs.corti.ai/assistant/events/generated/embedded-api/ready Emitted when the embedded API is ready to receive calls. ## Event Properties | Field | Value | | -------------- | ------------------ | | `event` | `"embedded.ready"` | | `confidential` | `boolean` | | `payload` | `object` | No payload properties. #### Example ```json theme={null} { "event": "embedded.ready", "confidential": false, "payload": {} } ``` No payload properties. #### Example ```json theme={null} { "event": "embedded.ready", "confidential": true, "payload": {} } ``` # embedded.recordingStarted Source: https://docs.corti.ai/assistant/events/generated/embedded-api/recordingStarted Emitted when the embedded startRecording method is successfully called. ## Event Properties | Field | Value | | -------------- | ----------------------------- | | `event` | `"embedded.recordingStarted"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------------ | ------------------ | -------------------------------- | | `interactionId` | `string` | Unique interaction identifier | | `interactionState` | `InteractionState` | Current state of the interaction | #### Example ```json theme={null} { "event": "embedded.recordingStarted", "confidential": false, "payload": { "interactionId": "int_123", "interactionState": "ongoing" } } ``` | Field | Type | Description | | ------------- | --------------------- | --------------------------------------------------------------- | | `interaction` | `ExternalInteraction` | Interaction context including transcripts, documents, and facts | | Field | Type | Description | | ----------------- | ------------------------ | --------------------------------------------------- | | `id` | `string` | Unique interaction identifier | | `title` | `string \| null` | Interaction title | | `state` | `InteractionState` | Current state of the interaction | | `startedAt` | `string (ISO 8601 date)` | When the interaction started (ISO 8601 date string) | | `transcriptCount` | `number` | Number of transcripts in the interaction | | `documentCount` | `number` | Number of documents in the interaction | | `facts` | `Fact[]` | Facts extracted during the interaction | **Fact properties:** | Field | Type | Description | | ------------- | ------------------------------ | -------------------------------------------------------- | | `id` | `string` | Unique fact identifier | | `text` | `string` | Fact text content | | `group` | `string` | Fact group/category name | | `source` | `"core" \| "system" \| "user"` | Source of the fact: core (AI extracted), system, or user | | `isDiscarded` | `boolean` | Whether the fact has been discarded | #### Example ```json theme={null} { "event": "embedded.recordingStarted", "confidential": true, "payload": { "interaction": { "id": "int_123", "title": "Visit", "state": "ongoing", "startedAt": "2024-01-01T00:00:00.000+00:00", "transcriptCount": 0, "documentCount": 0, "facts": [] } } } ``` # embedded.recordingStopped Source: https://docs.corti.ai/assistant/events/generated/embedded-api/recordingStopped Emitted when the embedded stopRecording method is successfully called. ## Event Properties | Field | Value | | -------------- | ----------------------------- | | `event` | `"embedded.recordingStopped"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------------ | ------------------ | -------------------------------- | | `interactionId` | `string` | Unique interaction identifier | | `interactionState` | `InteractionState` | Current state of the interaction | #### Example ```json theme={null} { "event": "embedded.recordingStopped", "confidential": false, "payload": { "interactionId": "int_123", "interactionState": "ongoing" } } ``` | Field | Type | Description | | ------------- | --------------------- | --------------------------------------------------------------- | | `interaction` | `ExternalInteraction` | Interaction context including transcripts, documents, and facts | | Field | Type | Description | | ----------------- | ------------------------ | --------------------------------------------------- | | `id` | `string` | Unique interaction identifier | | `title` | `string \| null` | Interaction title | | `state` | `InteractionState` | Current state of the interaction | | `startedAt` | `string (ISO 8601 date)` | When the interaction started (ISO 8601 date string) | | `transcriptCount` | `number` | Number of transcripts in the interaction | | `documentCount` | `number` | Number of documents in the interaction | | `facts` | `Fact[]` | Facts extracted during the interaction | **Fact properties:** | Field | Type | Description | | ------------- | ------------------------------ | -------------------------------------------------------- | | `id` | `string` | Unique fact identifier | | `text` | `string` | Fact text content | | `group` | `string` | Fact group/category name | | `source` | `"core" \| "system" \| "user"` | Source of the fact: core (AI extracted), system, or user | | `isDiscarded` | `boolean` | Whether the fact has been discarded | #### Example ```json theme={null} { "event": "embedded.recordingStopped", "confidential": true, "payload": { "interaction": { "id": "int_123", "title": "Visit", "state": "ongoing", "startedAt": "2024-01-01T00:00:00.000+00:00", "transcriptCount": 0, "documentCount": 0, "facts": [] } } } ``` # embedded.sessionConfigured Source: https://docs.corti.ai/assistant/events/generated/embedded-api/sessionConfigured Emitted when the embedded configureSession method is successfully called. ## Event Properties | Field | Value | | -------------- | ------------------------------ | | `event` | `"embedded.sessionConfigured"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ----------------------- | --------------------------------------- | ---------------------------------------- | | `defaultLanguage` | `string \| undefined` | Default language for transcription | | `defaultMode` | `"virtual" \| "in-person" \| undefined` | Default recording mode | | `defaultOutputLanguage` | `string \| undefined` | Default language for document generation | | `defaultTemplateKey` | `string \| undefined` | Default template to use for documents | #### Example ```json theme={null} { "event": "embedded.sessionConfigured", "confidential": false, "payload": { "defaultLanguage": "en", "defaultMode": "virtual", "defaultOutputLanguage": "en", "defaultTemplateKey": "soap" } } ``` | Field | Type | Description | | ----------------------- | --------------------------------------- | ---------------------------------------- | | `defaultLanguage` | `string \| undefined` | Default language for transcription | | `defaultMode` | `"virtual" \| "in-person" \| undefined` | Default recording mode | | `defaultOutputLanguage` | `string \| undefined` | Default language for document generation | | `defaultTemplateKey` | `string \| undefined` | Default template to use for documents | #### Example ```json theme={null} { "event": "embedded.sessionConfigured", "confidential": true, "payload": { "defaultLanguage": "en", "defaultMode": "virtual", "defaultOutputLanguage": "en", "defaultTemplateKey": "soap" } } ``` # embedded.statusReturned Source: https://docs.corti.ai/assistant/events/generated/embedded-api/statusReturned Emitted when the embedded getStatus method is successfully called. ## Event Properties | Field | Value | | -------------- | --------------------------- | | `event` | `"embedded.statusReturned"` | | `confidential` | `boolean` | | `payload` | `object` | No payload properties. #### Example ```json theme={null} { "event": "embedded.statusReturned", "confidential": false, "payload": {} } ``` No payload properties. #### Example ```json theme={null} { "event": "embedded.statusReturned", "confidential": true, "payload": {} } ``` # embedded.templatesReturned Source: https://docs.corti.ai/assistant/events/generated/embedded-api/templatesReturned Emitted when the embedded getTemplates method is successfully called. ## Event Properties | Field | Value | | -------------- | ------------------------------ | | `event` | `"embedded.templatesReturned"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------- | -------- | ---------------------------- | | `count` | `number` | Number of templates returned | #### Example ```json theme={null} { "event": "embedded.templatesReturned", "confidential": false, "payload": { "count": 5 } } ``` | Field | Type | Description | | ------- | -------- | ---------------------------- | | `count` | `number` | Number of templates returned | #### Example ```json theme={null} { "event": "embedded.templatesReturned", "confidential": true, "payload": { "count": 5 } } ``` # error.featureFlagDiscrepancyDetected Source: https://docs.corti.ai/assistant/events/generated/errors/featureFlagDiscrepancyDetected Emitted when feature flag providers return different client-side values. ## Event Properties | Field | Value | | -------------- | ---------------------------------------- | | `event` | `"error.featureFlagDiscrepancyDetected"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ----------------------- | ------------------------------------ | ----------- | | `featureKey` | `string` | - | | `severity` | `"warning" \| "error"` | - | | `reason` | `"flag_unknown" \| "value_mismatch"` | - | | `activeProvider` | `string` | - | | `activeProviderLabel` | `string` | - | | `activeValue` | `unknown` | - | | `comparedProvider` | `string` | - | | `comparedProviderLabel` | `string` | - | | `comparedValue` | `unknown` | - | #### Example ```json theme={null} { "event": "error.featureFlagDiscrepancyDetected", "confidential": false, "payload": { "featureKey": "example-feature", "severity": "error", "reason": "value_mismatch", "activeProvider": "growthbook", "activeProviderLabel": "GrowthBook", "activeValue": true, "comparedProvider": "posthog", "comparedProviderLabel": "PostHog", "comparedValue": false } } ``` | Field | Type | Description | | ----------------------- | ------------------------------------ | ----------- | | `featureKey` | `string` | - | | `severity` | `"warning" \| "error"` | - | | `reason` | `"flag_unknown" \| "value_mismatch"` | - | | `activeProvider` | `string` | - | | `activeProviderLabel` | `string` | - | | `activeValue` | `unknown` | - | | `comparedProvider` | `string` | - | | `comparedProviderLabel` | `string` | - | | `comparedValue` | `unknown` | - | #### Example ```json theme={null} { "event": "error.featureFlagDiscrepancyDetected", "confidential": true, "payload": { "featureKey": "example-feature", "severity": "error", "reason": "value_mismatch", "activeProvider": "growthbook", "activeProviderLabel": "GrowthBook", "activeValue": true, "comparedProvider": "posthog", "comparedProviderLabel": "PostHog", "comparedValue": false } } ``` # error.triggered Source: https://docs.corti.ai/assistant/events/generated/errors/triggered Emitted when an error occurs. ## Event Properties | Field | Value | | -------------- | ------------------- | | `event` | `"error.triggered"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------------ | ------------------ | --------------------------------- | | `message` | `string` | Human-readable error description | | `code` | `string` | Machine-readable error identifier | | `interactionId` | `string` | Unique interaction identifier | | `interactionState` | `InteractionState` | Current state of the interaction | #### Example ```json theme={null} { "event": "error.triggered", "confidential": false, "payload": { "message": "Network error", "code": "network_error", "interactionId": "int_123", "interactionState": "ongoing" } } ``` | Field | Type | Description | | ------------- | --------------------- | --------------------------------------------------------------- | | `message` | `string` | Human-readable error description | | `code` | `string` | Machine-readable error identifier | | `interaction` | `ExternalInteraction` | Interaction context including transcripts, documents, and facts | | Field | Type | Description | | ----------------- | ------------------------ | --------------------------------------------------- | | `id` | `string` | Unique interaction identifier | | `title` | `string \| null` | Interaction title | | `state` | `InteractionState` | Current state of the interaction | | `startedAt` | `string (ISO 8601 date)` | When the interaction started (ISO 8601 date string) | | `transcriptCount` | `number` | Number of transcripts in the interaction | | `documentCount` | `number` | Number of documents in the interaction | | `facts` | `Fact[]` | Facts extracted during the interaction | **Fact properties:** | Field | Type | Description | | ------------- | ------------------------------ | -------------------------------------------------------- | | `id` | `string` | Unique fact identifier | | `text` | `string` | Fact text content | | `group` | `string` | Fact group/category name | | `source` | `"core" \| "system" \| "user"` | Source of the fact: core (AI extracted), system, or user | | `isDiscarded` | `boolean` | Whether the fact has been discarded | #### Example ```json theme={null} { "event": "error.triggered", "confidential": true, "payload": { "message": "Network error", "code": "network_error", "interaction": { "id": "int_123", "title": "Visit", "state": "ongoing", "startedAt": "2024-01-01T00:00:00.000+00:00", "transcriptCount": 0, "documentCount": 0, "facts": [] } } } ``` # interaction.archived Source: https://docs.corti.ai/assistant/events/generated/interaction/archived Emitted when an interaction is archived. ## Event Properties | Field | Value | | -------------- | ------------------------ | | `event` | `"interaction.archived"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | --------------- | -------- | ------------------------------ | | `interactionId` | `string` | ID of the archived interaction | #### Example ```json theme={null} { "event": "interaction.archived", "confidential": false, "payload": { "interactionId": "interaction_12345" } } ``` | Field | Type | Description | | --------------- | -------- | ------------------------------ | | `interactionId` | `string` | ID of the archived interaction | #### Example ```json theme={null} { "event": "interaction.archived", "confidential": true, "payload": { "interactionId": "interaction_12345" } } ``` # interaction.created Source: https://docs.corti.ai/assistant/events/generated/interaction/created Emitted when an interaction is created. ## Event Properties | Field | Value | | -------------- | ----------------------- | | `event` | `"interaction.created"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------------ | ------------------ | -------------------------------- | | `interactionId` | `string` | Unique interaction identifier | | `interactionState` | `InteractionState` | Current state of the interaction | #### Example ```json theme={null} { "event": "interaction.created", "confidential": false, "payload": { "interactionId": "int_123", "interactionState": "ongoing" } } ``` | Field | Type | Description | | ------------- | --------------------- | --------------------------------------------------------------- | | `interaction` | `ExternalInteraction` | Interaction context including transcripts, documents, and facts | | Field | Type | Description | | ----------------- | ------------------------ | --------------------------------------------------- | | `id` | `string` | Unique interaction identifier | | `title` | `string \| null` | Interaction title | | `state` | `InteractionState` | Current state of the interaction | | `startedAt` | `string (ISO 8601 date)` | When the interaction started (ISO 8601 date string) | | `transcriptCount` | `number` | Number of transcripts in the interaction | | `documentCount` | `number` | Number of documents in the interaction | | `facts` | `Fact[]` | Facts extracted during the interaction | **Fact properties:** | Field | Type | Description | | ------------- | ------------------------------ | -------------------------------------------------------- | | `id` | `string` | Unique fact identifier | | `text` | `string` | Fact text content | | `group` | `string` | Fact group/category name | | `source` | `"core" \| "system" \| "user"` | Source of the fact: core (AI extracted), system, or user | | `isDiscarded` | `boolean` | Whether the fact has been discarded | #### Example ```json theme={null} { "event": "interaction.created", "confidential": true, "payload": { "interaction": { "id": "int_123", "title": "Visit", "state": "ongoing", "startedAt": "2024-01-01T00:00:00.000+00:00", "transcriptCount": 0, "documentCount": 0, "facts": [] } } } ``` # interaction.deleted Source: https://docs.corti.ai/assistant/events/generated/interaction/deleted Emitted when an interaction is permanently deleted. ## Event Properties | Field | Value | | -------------- | ----------------------- | | `event` | `"interaction.deleted"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | --------------- | -------- | ----------------------------------------- | | `interactionId` | `string` | ID of the permanently deleted interaction | #### Example ```json theme={null} { "event": "interaction.deleted", "confidential": false, "payload": { "interactionId": "interaction_12345" } } ``` | Field | Type | Description | | --------------- | -------- | ----------------------------------------- | | `interactionId` | `string` | ID of the permanently deleted interaction | #### Example ```json theme={null} { "event": "interaction.deleted", "confidential": true, "payload": { "interactionId": "interaction_12345" } } ``` # interaction.loaded Source: https://docs.corti.ai/assistant/events/generated/interaction/loaded Emitted when an interaction is loaded. ## Event Properties | Field | Value | | -------------- | ---------------------- | | `event` | `"interaction.loaded"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------------ | ------------------ | -------------------------------- | | `interactionId` | `string` | Unique interaction identifier | | `interactionState` | `InteractionState` | Current state of the interaction | #### Example ```json theme={null} { "event": "interaction.loaded", "confidential": false, "payload": { "interactionId": "int_123", "interactionState": "ongoing" } } ``` | Field | Type | Description | | ------------- | --------------------- | --------------------------------------------------------------- | | `interaction` | `ExternalInteraction` | Interaction context including transcripts, documents, and facts | | Field | Type | Description | | ----------------- | ------------------------ | --------------------------------------------------- | | `id` | `string` | Unique interaction identifier | | `title` | `string \| null` | Interaction title | | `state` | `InteractionState` | Current state of the interaction | | `startedAt` | `string (ISO 8601 date)` | When the interaction started (ISO 8601 date string) | | `transcriptCount` | `number` | Number of transcripts in the interaction | | `documentCount` | `number` | Number of documents in the interaction | | `facts` | `Fact[]` | Facts extracted during the interaction | **Fact properties:** | Field | Type | Description | | ------------- | ------------------------------ | -------------------------------------------------------- | | `id` | `string` | Unique fact identifier | | `text` | `string` | Fact text content | | `group` | `string` | Fact group/category name | | `source` | `"core" \| "system" \| "user"` | Source of the fact: core (AI extracted), system, or user | | `isDiscarded` | `boolean` | Whether the fact has been discarded | #### Example ```json theme={null} { "event": "interaction.loaded", "confidential": true, "payload": { "interaction": { "id": "int_123", "title": "Visit", "state": "ongoing", "startedAt": "2024-01-01T00:00:00.000+00:00", "transcriptCount": 0, "documentCount": 0, "facts": [] } } } ``` # interaction.renamed Source: https://docs.corti.ai/assistant/events/generated/interaction/renamed Emitted when an interaction is renamed. ## Event Properties | Field | Value | | -------------- | ----------------------- | | `event` | `"interaction.renamed"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | --------------- | -------- | ----------------------------- | | `interactionId` | `string` | ID of the renamed interaction | #### Example ```json theme={null} { "event": "interaction.renamed", "confidential": false, "payload": { "interactionId": "interaction_12345" } } ``` | Field | Type | Description | | --------------- | -------- | ----------------------------- | | `interactionId` | `string` | ID of the renamed interaction | | `title` | `string` | New title for the interaction | #### Example ```json theme={null} { "event": "interaction.renamed", "confidential": true, "payload": { "interactionId": "interaction_12345", "title": "Follow-up visit" } } ``` # interaction.restored Source: https://docs.corti.ai/assistant/events/generated/interaction/restored Emitted when an interaction is restored from archive. ## Event Properties | Field | Value | | -------------- | ------------------------ | | `event` | `"interaction.restored"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | --------------- | -------- | ------------------------------ | | `interactionId` | `string` | ID of the restored interaction | #### Example ```json theme={null} { "event": "interaction.restored", "confidential": false, "payload": { "interactionId": "interaction_12345" } } ``` | Field | Type | Description | | --------------- | -------- | ------------------------------ | | `interactionId` | `string` | ID of the restored interaction | #### Example ```json theme={null} { "event": "interaction.restored", "confidential": true, "payload": { "interactionId": "interaction_12345" } } ``` # interaction.virtualAudioDisconnected Source: https://docs.corti.ai/assistant/events/generated/interaction/virtualAudioDisconnected Emitted when virtual audio disconnects. ## Event Properties | Field | Value | | -------------- | ---------------------------------------- | | `event` | `"interaction.virtualAudioDisconnected"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------------ | ------------------ | -------------------------------- | | `interactionId` | `string` | Unique interaction identifier | | `interactionState` | `InteractionState` | Current state of the interaction | #### Example ```json theme={null} { "event": "interaction.virtualAudioDisconnected", "confidential": false, "payload": { "interactionId": "int_123", "interactionState": "ongoing" } } ``` | Field | Type | Description | | ------------- | --------------------- | --------------------------------------------------------------- | | `interaction` | `ExternalInteraction` | Interaction context including transcripts, documents, and facts | | Field | Type | Description | | ----------------- | ------------------------ | --------------------------------------------------- | | `id` | `string` | Unique interaction identifier | | `title` | `string \| null` | Interaction title | | `state` | `InteractionState` | Current state of the interaction | | `startedAt` | `string (ISO 8601 date)` | When the interaction started (ISO 8601 date string) | | `transcriptCount` | `number` | Number of transcripts in the interaction | | `documentCount` | `number` | Number of documents in the interaction | | `facts` | `Fact[]` | Facts extracted during the interaction | **Fact properties:** | Field | Type | Description | | ------------- | ------------------------------ | -------------------------------------------------------- | | `id` | `string` | Unique fact identifier | | `text` | `string` | Fact text content | | `group` | `string` | Fact group/category name | | `source` | `"core" \| "system" \| "user"` | Source of the fact: core (AI extracted), system, or user | | `isDiscarded` | `boolean` | Whether the fact has been discarded | #### Example ```json theme={null} { "event": "interaction.virtualAudioDisconnected", "confidential": true, "payload": { "interaction": { "id": "int_123", "title": "Visit", "state": "ongoing", "startedAt": "2024-01-01T00:00:00.000+00:00", "transcriptCount": 0, "documentCount": 0, "facts": [] } } } ``` # microphone.ambientSet Source: https://docs.corti.ai/assistant/events/generated/microphone/ambientSet Emitted when the default ambient microphone changes. ## Event Properties | Field | Value | | -------------- | ------------------------- | | `event` | `"microphone.ambientSet"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------------ | ------------------ | ----------------------------------------------- | | `deviceLabel` | `string` | The label of the new default ambient microphone | | `interactionId` | `string` | Unique interaction identifier | | `interactionState` | `InteractionState` | Current state of the interaction | #### Example ```json theme={null} { "event": "microphone.ambientSet", "confidential": false, "payload": { "deviceLabel": "Jabra Evolve", "interactionId": "int_123", "interactionState": "ongoing" } } ``` | Field | Type | Description | | ------------- | --------------------- | --------------------------------------------------------------- | | `deviceLabel` | `string` | The label of the new default ambient microphone | | `interaction` | `ExternalInteraction` | Interaction context including transcripts, documents, and facts | | Field | Type | Description | | ----------------- | ------------------------ | --------------------------------------------------- | | `id` | `string` | Unique interaction identifier | | `title` | `string \| null` | Interaction title | | `state` | `InteractionState` | Current state of the interaction | | `startedAt` | `string (ISO 8601 date)` | When the interaction started (ISO 8601 date string) | | `transcriptCount` | `number` | Number of transcripts in the interaction | | `documentCount` | `number` | Number of documents in the interaction | | `facts` | `Fact[]` | Facts extracted during the interaction | **Fact properties:** | Field | Type | Description | | ------------- | ------------------------------ | -------------------------------------------------------- | | `id` | `string` | Unique fact identifier | | `text` | `string` | Fact text content | | `group` | `string` | Fact group/category name | | `source` | `"core" \| "system" \| "user"` | Source of the fact: core (AI extracted), system, or user | | `isDiscarded` | `boolean` | Whether the fact has been discarded | #### Example ```json theme={null} { "event": "microphone.ambientSet", "confidential": true, "payload": { "deviceLabel": "Jabra Evolve", "interaction": { "id": "int_123", "title": "Visit", "state": "ongoing", "startedAt": "2024-01-01T00:00:00.000+00:00", "transcriptCount": 0, "documentCount": 0, "facts": [] } } } ``` # microphone.dictationSet Source: https://docs.corti.ai/assistant/events/generated/microphone/dictationSet Emitted when the default dictation microphone changes. ## Event Properties | Field | Value | | -------------- | --------------------------- | | `event` | `"microphone.dictationSet"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------------ | ------------------ | ------------------------------------------------- | | `deviceLabel` | `string` | The label of the new default dictation microphone | | `interactionId` | `string` | Unique interaction identifier | | `interactionState` | `InteractionState` | Current state of the interaction | #### Example ```json theme={null} { "event": "microphone.dictationSet", "confidential": false, "payload": { "deviceLabel": "Jabra Evolve", "interactionId": "int_123", "interactionState": "ongoing" } } ``` | Field | Type | Description | | ------------- | --------------------- | --------------------------------------------------------------- | | `deviceLabel` | `string` | The label of the new default dictation microphone | | `interaction` | `ExternalInteraction` | Interaction context including transcripts, documents, and facts | | Field | Type | Description | | ----------------- | ------------------------ | --------------------------------------------------- | | `id` | `string` | Unique interaction identifier | | `title` | `string \| null` | Interaction title | | `state` | `InteractionState` | Current state of the interaction | | `startedAt` | `string (ISO 8601 date)` | When the interaction started (ISO 8601 date string) | | `transcriptCount` | `number` | Number of transcripts in the interaction | | `documentCount` | `number` | Number of documents in the interaction | | `facts` | `Fact[]` | Facts extracted during the interaction | **Fact properties:** | Field | Type | Description | | ------------- | ------------------------------ | -------------------------------------------------------- | | `id` | `string` | Unique fact identifier | | `text` | `string` | Fact text content | | `group` | `string` | Fact group/category name | | `source` | `"core" \| "system" \| "user"` | Source of the fact: core (AI extracted), system, or user | | `isDiscarded` | `boolean` | Whether the fact has been discarded | #### Example ```json theme={null} { "event": "microphone.dictationSet", "confidential": true, "payload": { "deviceLabel": "Jabra Evolve", "interaction": { "id": "int_123", "title": "Visit", "state": "ongoing", "startedAt": "2024-01-01T00:00:00.000+00:00", "transcriptCount": 0, "documentCount": 0, "facts": [] } } } ``` # microphone.qualityLearnMoreClicked Source: https://docs.corti.ai/assistant/events/generated/microphone/qualityLearnMoreClicked Emitted when the user clicks 'What is a good ambient mic?' in the quality callout. ## Event Properties | Field | Value | | -------------- | -------------------------------------- | | `event` | `"microphone.qualityLearnMoreClicked"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------------ | ------------------ | -------------------------------- | | `interactionId` | `string` | Unique interaction identifier | | `interactionState` | `InteractionState` | Current state of the interaction | #### Example ```json theme={null} { "event": "microphone.qualityLearnMoreClicked", "confidential": false, "payload": { "interactionId": "int_123", "interactionState": "ongoing" } } ``` | Field | Type | Description | | ------------- | --------------------- | --------------------------------------------------------------- | | `interaction` | `ExternalInteraction` | Interaction context including transcripts, documents, and facts | | Field | Type | Description | | ----------------- | ------------------------ | --------------------------------------------------- | | `id` | `string` | Unique interaction identifier | | `title` | `string \| null` | Interaction title | | `state` | `InteractionState` | Current state of the interaction | | `startedAt` | `string (ISO 8601 date)` | When the interaction started (ISO 8601 date string) | | `transcriptCount` | `number` | Number of transcripts in the interaction | | `documentCount` | `number` | Number of documents in the interaction | | `facts` | `Fact[]` | Facts extracted during the interaction | **Fact properties:** | Field | Type | Description | | ------------- | ------------------------------ | -------------------------------------------------------- | | `id` | `string` | Unique fact identifier | | `text` | `string` | Fact text content | | `group` | `string` | Fact group/category name | | `source` | `"core" \| "system" \| "user"` | Source of the fact: core (AI extracted), system, or user | | `isDiscarded` | `boolean` | Whether the fact has been discarded | #### Example ```json theme={null} { "event": "microphone.qualityLearnMoreClicked", "confidential": true, "payload": { "interaction": { "id": "int_123", "title": "Visit", "state": "ongoing", "startedAt": "2024-01-01T00:00:00.000+00:00", "transcriptCount": 0, "documentCount": 0, "facts": [] } } } ``` # microphone.qualityOnboardingDismissed Source: https://docs.corti.ai/assistant/events/generated/microphone/qualityOnboardingDismissed Emitted when the user completes the mic quality onboarding modal. ## Event Properties | Field | Value | | -------------- | ----------------------------------------- | | `event` | `"microphone.qualityOnboardingDismissed"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------------ | ------------------ | -------------------------------- | | `interactionId` | `string` | Unique interaction identifier | | `interactionState` | `InteractionState` | Current state of the interaction | #### Example ```json theme={null} { "event": "microphone.qualityOnboardingDismissed", "confidential": false, "payload": { "interactionId": "int_123", "interactionState": "ongoing" } } ``` | Field | Type | Description | | ------------- | --------------------- | --------------------------------------------------------------- | | `interaction` | `ExternalInteraction` | Interaction context including transcripts, documents, and facts | | Field | Type | Description | | ----------------- | ------------------------ | --------------------------------------------------- | | `id` | `string` | Unique interaction identifier | | `title` | `string \| null` | Interaction title | | `state` | `InteractionState` | Current state of the interaction | | `startedAt` | `string (ISO 8601 date)` | When the interaction started (ISO 8601 date string) | | `transcriptCount` | `number` | Number of transcripts in the interaction | | `documentCount` | `number` | Number of documents in the interaction | | `facts` | `Fact[]` | Facts extracted during the interaction | **Fact properties:** | Field | Type | Description | | ------------- | ------------------------------ | -------------------------------------------------------- | | `id` | `string` | Unique fact identifier | | `text` | `string` | Fact text content | | `group` | `string` | Fact group/category name | | `source` | `"core" \| "system" \| "user"` | Source of the fact: core (AI extracted), system, or user | | `isDiscarded` | `boolean` | Whether the fact has been discarded | #### Example ```json theme={null} { "event": "microphone.qualityOnboardingDismissed", "confidential": true, "payload": { "interaction": { "id": "int_123", "title": "Visit", "state": "ongoing", "startedAt": "2024-01-01T00:00:00.000+00:00", "transcriptCount": 0, "documentCount": 0, "facts": [] } } } ``` # microphone.qualityOnboardingShown Source: https://docs.corti.ai/assistant/events/generated/microphone/qualityOnboardingShown Emitted when the mic quality onboarding modal is shown to the user. ## Event Properties | Field | Value | | -------------- | ------------------------------------- | | `event` | `"microphone.qualityOnboardingShown"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------------ | ------------------ | -------------------------------- | | `interactionId` | `string` | Unique interaction identifier | | `interactionState` | `InteractionState` | Current state of the interaction | #### Example ```json theme={null} { "event": "microphone.qualityOnboardingShown", "confidential": false, "payload": { "interactionId": "int_123", "interactionState": "ongoing" } } ``` | Field | Type | Description | | ------------- | --------------------- | --------------------------------------------------------------- | | `interaction` | `ExternalInteraction` | Interaction context including transcripts, documents, and facts | | Field | Type | Description | | ----------------- | ------------------------ | --------------------------------------------------- | | `id` | `string` | Unique interaction identifier | | `title` | `string \| null` | Interaction title | | `state` | `InteractionState` | Current state of the interaction | | `startedAt` | `string (ISO 8601 date)` | When the interaction started (ISO 8601 date string) | | `transcriptCount` | `number` | Number of transcripts in the interaction | | `documentCount` | `number` | Number of documents in the interaction | | `facts` | `Fact[]` | Facts extracted during the interaction | **Fact properties:** | Field | Type | Description | | ------------- | ------------------------------ | -------------------------------------------------------- | | `id` | `string` | Unique fact identifier | | `text` | `string` | Fact text content | | `group` | `string` | Fact group/category name | | `source` | `"core" \| "system" \| "user"` | Source of the fact: core (AI extracted), system, or user | | `isDiscarded` | `boolean` | Whether the fact has been discarded | #### Example ```json theme={null} { "event": "microphone.qualityOnboardingShown", "confidential": true, "payload": { "interaction": { "id": "int_123", "title": "Visit", "state": "ongoing", "startedAt": "2024-01-01T00:00:00.000+00:00", "transcriptCount": 0, "documentCount": 0, "facts": [] } } } ``` # microphone.selected Source: https://docs.corti.ai/assistant/events/generated/microphone/selected Emitted when microphone for the session changes. ## Event Properties | Field | Value | | -------------- | ----------------------- | | `event` | `"microphone.selected"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------------ | ---------------------------------------------------- | ------------------------------------------- | | `deviceLabel` | `string` | The label of the newly selected microphone | | `tier` | `"unknown" \| "good" \| "acceptable" \| "not-ideal"` | The quality tier of the selected microphone | | `interactionId` | `string` | Unique interaction identifier | | `interactionState` | `InteractionState` | Current state of the interaction | #### Example ```json theme={null} { "event": "microphone.selected", "confidential": false, "payload": { "deviceLabel": "Jabra Evolve", "tier": "good", "interactionId": "int_123", "interactionState": "ongoing" } } ``` | Field | Type | Description | | ------------- | ---------------------------------------------------- | --------------------------------------------------------------- | | `deviceLabel` | `string` | The label of the newly selected microphone | | `tier` | `"unknown" \| "good" \| "acceptable" \| "not-ideal"` | The quality tier of the selected microphone | | `interaction` | `ExternalInteraction` | Interaction context including transcripts, documents, and facts | | Field | Type | Description | | ----------------- | ------------------------ | --------------------------------------------------- | | `id` | `string` | Unique interaction identifier | | `title` | `string \| null` | Interaction title | | `state` | `InteractionState` | Current state of the interaction | | `startedAt` | `string (ISO 8601 date)` | When the interaction started (ISO 8601 date string) | | `transcriptCount` | `number` | Number of transcripts in the interaction | | `documentCount` | `number` | Number of documents in the interaction | | `facts` | `Fact[]` | Facts extracted during the interaction | **Fact properties:** | Field | Type | Description | | ------------- | ------------------------------ | -------------------------------------------------------- | | `id` | `string` | Unique fact identifier | | `text` | `string` | Fact text content | | `group` | `string` | Fact group/category name | | `source` | `"core" \| "system" \| "user"` | Source of the fact: core (AI extracted), system, or user | | `isDiscarded` | `boolean` | Whether the fact has been discarded | #### Example ```json theme={null} { "event": "microphone.selected", "confidential": true, "payload": { "deviceLabel": "Jabra Evolve", "tier": "good", "interaction": { "id": "int_123", "title": "Visit", "state": "ongoing", "startedAt": "2024-01-01T00:00:00.000+00:00", "transcriptCount": 0, "documentCount": 0, "facts": [] } } } ``` # microphone.selectorOpened Source: https://docs.corti.ai/assistant/events/generated/microphone/selectorOpened Emitted when the microphone selector opens. ## Event Properties | Field | Value | | -------------- | ----------------------------- | | `event` | `"microphone.selectorOpened"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------------ | ------------------ | -------------------------------- | | `interactionId` | `string` | Unique interaction identifier | | `interactionState` | `InteractionState` | Current state of the interaction | #### Example ```json theme={null} { "event": "microphone.selectorOpened", "confidential": false, "payload": { "interactionId": "int_123", "interactionState": "ongoing" } } ``` | Field | Type | Description | | ------------- | --------------------- | --------------------------------------------------------------- | | `interaction` | `ExternalInteraction` | Interaction context including transcripts, documents, and facts | | Field | Type | Description | | ----------------- | ------------------------ | --------------------------------------------------- | | `id` | `string` | Unique interaction identifier | | `title` | `string \| null` | Interaction title | | `state` | `InteractionState` | Current state of the interaction | | `startedAt` | `string (ISO 8601 date)` | When the interaction started (ISO 8601 date string) | | `transcriptCount` | `number` | Number of transcripts in the interaction | | `documentCount` | `number` | Number of documents in the interaction | | `facts` | `Fact[]` | Facts extracted during the interaction | **Fact properties:** | Field | Type | Description | | ------------- | ------------------------------ | -------------------------------------------------------- | | `id` | `string` | Unique fact identifier | | `text` | `string` | Fact text content | | `group` | `string` | Fact group/category name | | `source` | `"core" \| "system" \| "user"` | Source of the fact: core (AI extracted), system, or user | | `isDiscarded` | `boolean` | Whether the fact has been discarded | #### Example ```json theme={null} { "event": "microphone.selectorOpened", "confidential": true, "payload": { "interaction": { "id": "int_123", "title": "Visit", "state": "ongoing", "startedAt": "2024-01-01T00:00:00.000+00:00", "transcriptCount": 0, "documentCount": 0, "facts": [] } } } ``` # microphone.warningTriggered Source: https://docs.corti.ai/assistant/events/generated/microphone/warningTriggered Emitted when a microphone warning is triggered. ## Event Properties | Field | Value | | -------------- | ------------------------------- | | `event` | `"microphone.warningTriggered"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------------ | ----------------------------------------------------------------------------------- | ------------------------------------------------- | | `warningType` | `"no_available_devices" \| "default_device_disconnected" \| "default_device_unset"` | The type of microphone warning that was triggered | | `interactionId` | `string` | Unique interaction identifier | | `interactionState` | `InteractionState` | Current state of the interaction | #### Example ```json theme={null} { "event": "microphone.warningTriggered", "confidential": false, "payload": { "warningType": "no_available_devices", "interactionId": "int_123", "interactionState": "ongoing" } } ``` | Field | Type | Description | | ------------- | ----------------------------------------------------------------------------------- | --------------------------------------------------------------- | | `warningType` | `"no_available_devices" \| "default_device_disconnected" \| "default_device_unset"` | The type of microphone warning that was triggered | | `interaction` | `ExternalInteraction` | Interaction context including transcripts, documents, and facts | | Field | Type | Description | | ----------------- | ------------------------ | --------------------------------------------------- | | `id` | `string` | Unique interaction identifier | | `title` | `string \| null` | Interaction title | | `state` | `InteractionState` | Current state of the interaction | | `startedAt` | `string (ISO 8601 date)` | When the interaction started (ISO 8601 date string) | | `transcriptCount` | `number` | Number of transcripts in the interaction | | `documentCount` | `number` | Number of documents in the interaction | | `facts` | `Fact[]` | Facts extracted during the interaction | **Fact properties:** | Field | Type | Description | | ------------- | ------------------------------ | -------------------------------------------------------- | | `id` | `string` | Unique fact identifier | | `text` | `string` | Fact text content | | `group` | `string` | Fact group/category name | | `source` | `"core" \| "system" \| "user"` | Source of the fact: core (AI extracted), system, or user | | `isDiscarded` | `boolean` | Whether the fact has been discarded | #### Example ```json theme={null} { "event": "microphone.warningTriggered", "confidential": true, "payload": { "warningType": "no_available_devices", "interaction": { "id": "int_123", "title": "Visit", "state": "ongoing", "startedAt": "2024-01-01T00:00:00.000+00:00", "transcriptCount": 0, "documentCount": 0, "facts": [] } } } ``` # note.added Source: https://docs.corti.ai/assistant/events/generated/note/added Emitted when a note is added. ## Event Properties | Field | Value | | -------------- | -------------- | | `event` | `"note.added"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------------ | ------------------ | -------------------------------- | | `noteId` | `string` | Unique identifier for the note | | `group` | `string` | Category the note belongs to | | `interactionId` | `string` | Unique interaction identifier | | `interactionState` | `InteractionState` | Current state of the interaction | #### Example ```json theme={null} { "event": "note.added", "confidential": false, "payload": { "noteId": "note_2", "group": "summary", "interactionId": "int_123", "interactionState": "ongoing" } } ``` | Field | Type | Description | | ------------- | --------------------- | --------------------------------------------------------------- | | `noteId` | `string` | Unique identifier for the note | | `group` | `string` | Category the note belongs to | | `text` | `string` | Content of the added note | | `interaction` | `ExternalInteraction` | Interaction context including transcripts, documents, and facts | | Field | Type | Description | | ----------------- | ------------------------ | --------------------------------------------------- | | `id` | `string` | Unique interaction identifier | | `title` | `string \| null` | Interaction title | | `state` | `InteractionState` | Current state of the interaction | | `startedAt` | `string (ISO 8601 date)` | When the interaction started (ISO 8601 date string) | | `transcriptCount` | `number` | Number of transcripts in the interaction | | `documentCount` | `number` | Number of documents in the interaction | | `facts` | `Fact[]` | Facts extracted during the interaction | **Fact properties:** | Field | Type | Description | | ------------- | ------------------------------ | -------------------------------------------------------- | | `id` | `string` | Unique fact identifier | | `text` | `string` | Fact text content | | `group` | `string` | Fact group/category name | | `source` | `"core" \| "system" \| "user"` | Source of the fact: core (AI extracted), system, or user | | `isDiscarded` | `boolean` | Whether the fact has been discarded | #### Example ```json theme={null} { "event": "note.added", "confidential": true, "payload": { "noteId": "note_2", "group": "summary", "text": "...", "interaction": { "id": "int_123", "title": "Visit", "state": "ongoing", "startedAt": "2024-01-01T00:00:00.000+00:00", "transcriptCount": 0, "documentCount": 0, "facts": [] } } } ``` # note.dictationStarted Source: https://docs.corti.ai/assistant/events/generated/note/dictationStarted Emitted when note dictation starts. ## Event Properties | Field | Value | | -------------- | ------------------------- | | `event` | `"note.dictationStarted"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------------ | ------------------ | --------------------------------- | | `noteId` | `string` | Which note dictation is targeting | | `group` | `string` | Category the note belongs to | | `interactionId` | `string` | Unique interaction identifier | | `interactionState` | `InteractionState` | Current state of the interaction | #### Example ```json theme={null} { "event": "note.dictationStarted", "confidential": false, "payload": { "noteId": "note_7", "group": "summary", "interactionId": "int_123", "interactionState": "ongoing" } } ``` | Field | Type | Description | | ------------- | --------------------- | --------------------------------------------------------------- | | `noteId` | `string` | Which note dictation is targeting | | `group` | `string` | Category the note belongs to | | `interaction` | `ExternalInteraction` | Interaction context including transcripts, documents, and facts | | Field | Type | Description | | ----------------- | ------------------------ | --------------------------------------------------- | | `id` | `string` | Unique interaction identifier | | `title` | `string \| null` | Interaction title | | `state` | `InteractionState` | Current state of the interaction | | `startedAt` | `string (ISO 8601 date)` | When the interaction started (ISO 8601 date string) | | `transcriptCount` | `number` | Number of transcripts in the interaction | | `documentCount` | `number` | Number of documents in the interaction | | `facts` | `Fact[]` | Facts extracted during the interaction | **Fact properties:** | Field | Type | Description | | ------------- | ------------------------------ | -------------------------------------------------------- | | `id` | `string` | Unique fact identifier | | `text` | `string` | Fact text content | | `group` | `string` | Fact group/category name | | `source` | `"core" \| "system" \| "user"` | Source of the fact: core (AI extracted), system, or user | | `isDiscarded` | `boolean` | Whether the fact has been discarded | #### Example ```json theme={null} { "event": "note.dictationStarted", "confidential": true, "payload": { "noteId": "note_7", "group": "summary", "interaction": { "id": "int_123", "title": "Visit", "state": "ongoing", "startedAt": "2024-01-01T00:00:00.000+00:00", "transcriptCount": 0, "documentCount": 0, "facts": [] } } } ``` # note.dictationStopped Source: https://docs.corti.ai/assistant/events/generated/note/dictationStopped Emitted when note dictation stops. ## Event Properties | Field | Value | | -------------- | ------------------------- | | `event` | `"note.dictationStopped"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------------ | ------------------ | -------------------------------------------- | | `noteId` | `string` | Which note was being dictated into | | `group` | `string` | Category the note belongs to | | `wordsDictated` | `number` | Number of words captured during this session | | `interactionId` | `string` | Unique interaction identifier | | `interactionState` | `InteractionState` | Current state of the interaction | #### Example ```json theme={null} { "event": "note.dictationStopped", "confidential": false, "payload": { "noteId": "note_8", "group": "summary", "wordsDictated": 32, "interactionId": "int_123", "interactionState": "ongoing" } } ``` | Field | Type | Description | | --------------- | --------------------- | --------------------------------------------------------------- | | `noteId` | `string` | Which note was being dictated into | | `group` | `string` | Category the note belongs to | | `text` | `string` | Updated content after dictation | | `wordsDictated` | `number` | Number of words captured during this session | | `interaction` | `ExternalInteraction` | Interaction context including transcripts, documents, and facts | | Field | Type | Description | | ----------------- | ------------------------ | --------------------------------------------------- | | `id` | `string` | Unique interaction identifier | | `title` | `string \| null` | Interaction title | | `state` | `InteractionState` | Current state of the interaction | | `startedAt` | `string (ISO 8601 date)` | When the interaction started (ISO 8601 date string) | | `transcriptCount` | `number` | Number of transcripts in the interaction | | `documentCount` | `number` | Number of documents in the interaction | | `facts` | `Fact[]` | Facts extracted during the interaction | **Fact properties:** | Field | Type | Description | | ------------- | ------------------------------ | -------------------------------------------------------- | | `id` | `string` | Unique fact identifier | | `text` | `string` | Fact text content | | `group` | `string` | Fact group/category name | | `source` | `"core" \| "system" \| "user"` | Source of the fact: core (AI extracted), system, or user | | `isDiscarded` | `boolean` | Whether the fact has been discarded | #### Example ```json theme={null} { "event": "note.dictationStopped", "confidential": true, "payload": { "noteId": "note_8", "group": "summary", "text": "...", "wordsDictated": 32, "interaction": { "id": "int_123", "title": "Visit", "state": "ongoing", "startedAt": "2024-01-01T00:00:00.000+00:00", "transcriptCount": 0, "documentCount": 0, "facts": [] } } } ``` # note.discarded Source: https://docs.corti.ai/assistant/events/generated/note/discarded Emitted when a note is discarded. ## Event Properties | Field | Value | | -------------- | ------------------ | | `event` | `"note.discarded"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------------ | ------------------ | -------------------------------- | | `noteId` | `string` | Unique identifier for the note | | `group` | `string` | Category the note belongs to | | `interactionId` | `string` | Unique interaction identifier | | `interactionState` | `InteractionState` | Current state of the interaction | #### Example ```json theme={null} { "event": "note.discarded", "confidential": false, "payload": { "noteId": "note_5", "group": "summary", "interactionId": "int_123", "interactionState": "ongoing" } } ``` | Field | Type | Description | | ------------- | --------------------- | --------------------------------------------------------------- | | `noteId` | `string` | Unique identifier for the note | | `group` | `string` | Category the note belongs to | | `text` | `string` | Content of the discarded note | | `interaction` | `ExternalInteraction` | Interaction context including transcripts, documents, and facts | | Field | Type | Description | | ----------------- | ------------------------ | --------------------------------------------------- | | `id` | `string` | Unique interaction identifier | | `title` | `string \| null` | Interaction title | | `state` | `InteractionState` | Current state of the interaction | | `startedAt` | `string (ISO 8601 date)` | When the interaction started (ISO 8601 date string) | | `transcriptCount` | `number` | Number of transcripts in the interaction | | `documentCount` | `number` | Number of documents in the interaction | | `facts` | `Fact[]` | Facts extracted during the interaction | **Fact properties:** | Field | Type | Description | | ------------- | ------------------------------ | -------------------------------------------------------- | | `id` | `string` | Unique fact identifier | | `text` | `string` | Fact text content | | `group` | `string` | Fact group/category name | | `source` | `"core" \| "system" \| "user"` | Source of the fact: core (AI extracted), system, or user | | `isDiscarded` | `boolean` | Whether the fact has been discarded | #### Example ```json theme={null} { "event": "note.discarded", "confidential": true, "payload": { "noteId": "note_5", "group": "summary", "text": "...", "interaction": { "id": "int_123", "title": "Visit", "state": "ongoing", "startedAt": "2024-01-01T00:00:00.000+00:00", "transcriptCount": 0, "documentCount": 0, "facts": [] } } } ``` # note.edited Source: https://docs.corti.ai/assistant/events/generated/note/edited Emitted when a note is edited. ## Event Properties | Field | Value | | -------------- | --------------- | | `event` | `"note.edited"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------------ | ------------------ | -------------------------------- | | `noteId` | `string` | Unique identifier for the note | | `group` | `string` | Category the note belongs to | | `interactionId` | `string` | Unique interaction identifier | | `interactionState` | `InteractionState` | Current state of the interaction | #### Example ```json theme={null} { "event": "note.edited", "confidential": false, "payload": { "noteId": "note_3", "group": "summary", "interactionId": "int_123", "interactionState": "ongoing" } } ``` | Field | Type | Description | | ------------- | --------------------- | --------------------------------------------------------------- | | `noteId` | `string` | Unique identifier for the note | | `group` | `string` | Category the note belongs to | | `text` | `string` | Updated content after editing | | `interaction` | `ExternalInteraction` | Interaction context including transcripts, documents, and facts | | Field | Type | Description | | ----------------- | ------------------------ | --------------------------------------------------- | | `id` | `string` | Unique interaction identifier | | `title` | `string \| null` | Interaction title | | `state` | `InteractionState` | Current state of the interaction | | `startedAt` | `string (ISO 8601 date)` | When the interaction started (ISO 8601 date string) | | `transcriptCount` | `number` | Number of transcripts in the interaction | | `documentCount` | `number` | Number of documents in the interaction | | `facts` | `Fact[]` | Facts extracted during the interaction | **Fact properties:** | Field | Type | Description | | ------------- | ------------------------------ | -------------------------------------------------------- | | `id` | `string` | Unique fact identifier | | `text` | `string` | Fact text content | | `group` | `string` | Fact group/category name | | `source` | `"core" \| "system" \| "user"` | Source of the fact: core (AI extracted), system, or user | | `isDiscarded` | `boolean` | Whether the fact has been discarded | #### Example ```json theme={null} { "event": "note.edited", "confidential": true, "payload": { "noteId": "note_3", "group": "summary", "text": "...", "interaction": { "id": "int_123", "title": "Visit", "state": "ongoing", "startedAt": "2024-01-01T00:00:00.000+00:00", "transcriptCount": 0, "documentCount": 0, "facts": [] } } } ``` # note.generated Source: https://docs.corti.ai/assistant/events/generated/note/generated Emitted when a new note is generated during stream. ## Event Properties | Field | Value | | -------------- | ------------------ | | `event` | `"note.generated"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------------ | ------------------ | -------------------------------- | | `noteId` | `string` | Unique identifier for the note | | `group` | `string` | Category the note belongs to | | `interactionId` | `string` | Unique interaction identifier | | `interactionState` | `InteractionState` | Current state of the interaction | #### Example ```json theme={null} { "event": "note.generated", "confidential": false, "payload": { "noteId": "note_1", "group": "summary", "interactionId": "int_123", "interactionState": "ongoing" } } ``` | Field | Type | Description | | ------------- | --------------------- | --------------------------------------------------------------- | | `noteId` | `string` | Unique identifier for the note | | `group` | `string` | Category the note belongs to | | `text` | `string` | Content of the generated note | | `interaction` | `ExternalInteraction` | Interaction context including transcripts, documents, and facts | | Field | Type | Description | | ----------------- | ------------------------ | --------------------------------------------------- | | `id` | `string` | Unique interaction identifier | | `title` | `string \| null` | Interaction title | | `state` | `InteractionState` | Current state of the interaction | | `startedAt` | `string (ISO 8601 date)` | When the interaction started (ISO 8601 date string) | | `transcriptCount` | `number` | Number of transcripts in the interaction | | `documentCount` | `number` | Number of documents in the interaction | | `facts` | `Fact[]` | Facts extracted during the interaction | **Fact properties:** | Field | Type | Description | | ------------- | ------------------------------ | -------------------------------------------------------- | | `id` | `string` | Unique fact identifier | | `text` | `string` | Fact text content | | `group` | `string` | Fact group/category name | | `source` | `"core" \| "system" \| "user"` | Source of the fact: core (AI extracted), system, or user | | `isDiscarded` | `boolean` | Whether the fact has been discarded | #### Example ```json theme={null} { "event": "note.generated", "confidential": true, "payload": { "noteId": "note_1", "group": "summary", "text": "...", "interaction": { "id": "int_123", "title": "Visit", "state": "ongoing", "startedAt": "2024-01-01T00:00:00.000+00:00", "transcriptCount": 0, "documentCount": 0, "facts": [] } } } ``` # note.moved Source: https://docs.corti.ai/assistant/events/generated/note/moved Emitted when a note is moved to a different group. ## Event Properties | Field | Value | | -------------- | -------------- | | `event` | `"note.moved"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------------ | ------------------ | -------------------------------- | | `noteId` | `string` | Unique identifier for the note | | `oldGroup` | `string` | Previous category before move | | `newGroup` | `string` | New category after move | | `interactionId` | `string` | Unique interaction identifier | | `interactionState` | `InteractionState` | Current state of the interaction | #### Example ```json theme={null} { "event": "note.moved", "confidential": false, "payload": { "noteId": "note_9", "oldGroup": "summary", "newGroup": "details", "interactionId": "int_123", "interactionState": "ongoing" } } ``` | Field | Type | Description | | ------------- | --------------------- | --------------------------------------------------------------- | | `noteId` | `string` | Unique identifier for the note | | `oldGroup` | `string` | Previous category before move | | `newGroup` | `string` | New category after move | | `text` | `string` | Content of the moved note | | `interaction` | `ExternalInteraction` | Interaction context including transcripts, documents, and facts | | Field | Type | Description | | ----------------- | ------------------------ | --------------------------------------------------- | | `id` | `string` | Unique interaction identifier | | `title` | `string \| null` | Interaction title | | `state` | `InteractionState` | Current state of the interaction | | `startedAt` | `string (ISO 8601 date)` | When the interaction started (ISO 8601 date string) | | `transcriptCount` | `number` | Number of transcripts in the interaction | | `documentCount` | `number` | Number of documents in the interaction | | `facts` | `Fact[]` | Facts extracted during the interaction | **Fact properties:** | Field | Type | Description | | ------------- | ------------------------------ | -------------------------------------------------------- | | `id` | `string` | Unique fact identifier | | `text` | `string` | Fact text content | | `group` | `string` | Fact group/category name | | `source` | `"core" \| "system" \| "user"` | Source of the fact: core (AI extracted), system, or user | | `isDiscarded` | `boolean` | Whether the fact has been discarded | #### Example ```json theme={null} { "event": "note.moved", "confidential": true, "payload": { "noteId": "note_9", "oldGroup": "summary", "newGroup": "details", "text": "The patient reported mild headaches.", "interaction": { "id": "int_123", "title": "Visit", "state": "ongoing", "startedAt": "2024-01-01T00:00:00.000+00:00", "transcriptCount": 0, "documentCount": 0, "facts": [] } } } ``` # note.restored Source: https://docs.corti.ai/assistant/events/generated/note/restored Emitted when a note is restored. ## Event Properties | Field | Value | | -------------- | ----------------- | | `event` | `"note.restored"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------------ | ------------------ | -------------------------------- | | `noteId` | `string` | Unique identifier for the note | | `group` | `string` | Category the note belongs to | | `interactionId` | `string` | Unique interaction identifier | | `interactionState` | `InteractionState` | Current state of the interaction | #### Example ```json theme={null} { "event": "note.restored", "confidential": false, "payload": { "noteId": "note_6", "group": "summary", "interactionId": "int_123", "interactionState": "ongoing" } } ``` | Field | Type | Description | | ------------- | --------------------- | --------------------------------------------------------------- | | `noteId` | `string` | Unique identifier for the note | | `group` | `string` | Category the note belongs to | | `text` | `string` | Content of the restored note | | `interaction` | `ExternalInteraction` | Interaction context including transcripts, documents, and facts | | Field | Type | Description | | ----------------- | ------------------------ | --------------------------------------------------- | | `id` | `string` | Unique interaction identifier | | `title` | `string \| null` | Interaction title | | `state` | `InteractionState` | Current state of the interaction | | `startedAt` | `string (ISO 8601 date)` | When the interaction started (ISO 8601 date string) | | `transcriptCount` | `number` | Number of transcripts in the interaction | | `documentCount` | `number` | Number of documents in the interaction | | `facts` | `Fact[]` | Facts extracted during the interaction | **Fact properties:** | Field | Type | Description | | ------------- | ------------------------------ | -------------------------------------------------------- | | `id` | `string` | Unique fact identifier | | `text` | `string` | Fact text content | | `group` | `string` | Fact group/category name | | `source` | `"core" \| "system" \| "user"` | Source of the fact: core (AI extracted), system, or user | | `isDiscarded` | `boolean` | Whether the fact has been discarded | #### Example ```json theme={null} { "event": "note.restored", "confidential": true, "payload": { "noteId": "note_6", "group": "summary", "text": "...", "interaction": { "id": "int_123", "title": "Visit", "state": "ongoing", "startedAt": "2024-01-01T00:00:00.000+00:00", "transcriptCount": 0, "documentCount": 0, "facts": [] } } } ``` # settings.onboardingCompleted Source: https://docs.corti.ai/assistant/events/generated/onboarding-and-preferences/onboardingCompleted Emitted when onboarding completes. ## Event Properties | Field | Value | | -------------- | -------------------------------- | | `event` | `"settings.onboardingCompleted"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | --------------- | -------- | --------------------------------- | | `countryCode` | `string` | User's selected country | | `languageCode` | `string` | User's selected language | | `specialtyName` | `string` | User's selected medical specialty | #### Example ```json theme={null} { "event": "settings.onboardingCompleted", "confidential": false, "payload": { "countryCode": "DK", "languageCode": "en", "specialtyName": "cardiology" } } ``` | Field | Type | Description | | --------------- | -------- | --------------------------------- | | `countryCode` | `string` | User's selected country | | `languageCode` | `string` | User's selected language | | `specialtyName` | `string` | User's selected medical specialty | #### Example ```json theme={null} { "event": "settings.onboardingCompleted", "confidential": true, "payload": { "countryCode": "DK", "languageCode": "en", "specialtyName": "cardiology" } } ``` # settings.onboardingContinued Source: https://docs.corti.ai/assistant/events/generated/onboarding-and-preferences/onboardingContinued Emitted when onboarding continues. ## Event Properties | Field | Value | | -------------- | -------------------------------- | | `event` | `"settings.onboardingContinued"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------ | ------------------------------------------ | ------------------------------- | | `step` | `"country" \| "specialty" \| "microphone"` | Which step the user advanced to | #### Example ```json theme={null} { "event": "settings.onboardingContinued", "confidential": false, "payload": { "step": "microphone" } } ``` | Field | Type | Description | | ------ | ------------------------------------------ | ------------------------------- | | `step` | `"country" \| "specialty" \| "microphone"` | Which step the user advanced to | #### Example ```json theme={null} { "event": "settings.onboardingContinued", "confidential": true, "payload": { "step": "microphone" } } ``` # settings.onboardingStarted Source: https://docs.corti.ai/assistant/events/generated/onboarding-and-preferences/onboardingStarted Emitted when onboarding starts. ## Event Properties | Field | Value | | -------------- | ------------------------------ | | `event` | `"settings.onboardingStarted"` | | `confidential` | `boolean` | | `payload` | `object` | No payload properties. #### Example ```json theme={null} { "event": "settings.onboardingStarted", "confidential": false, "payload": {} } ``` No payload properties. #### Example ```json theme={null} { "event": "settings.onboardingStarted", "confidential": true, "payload": {} } ``` # settings.onboardingValueChanged Source: https://docs.corti.ai/assistant/events/generated/onboarding-and-preferences/onboardingValueChanged Emitted when an onboarding value changes. ## Event Properties | Field | Value | | -------------- | ----------------------------------- | | `event` | `"settings.onboardingValueChanged"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | -------------- | -------- | -------------------------- | | `settingName` | `string` | Which setting was modified | | `settingValue` | `string` | New value for the setting | #### Example ```json theme={null} { "event": "settings.onboardingValueChanged", "confidential": false, "payload": { "settingName": "language", "settingValue": "en" } } ``` | Field | Type | Description | | -------------- | -------- | -------------------------- | | `settingName` | `string` | Which setting was modified | | `settingValue` | `string` | New value for the setting | #### Example ```json theme={null} { "event": "settings.onboardingValueChanged", "confidential": true, "payload": { "settingName": "language", "settingValue": "en" } } ``` # settings.pwaInstalled Source: https://docs.corti.ai/assistant/events/generated/onboarding-and-preferences/pwaInstalled Emitted when the PWA is installed. ## Event Properties | Field | Value | | -------------- | ------------------------- | | `event` | `"settings.pwaInstalled"` | | `confidential` | `boolean` | | `payload` | `object` | No payload properties. #### Example ```json theme={null} { "event": "settings.pwaInstalled", "confidential": false, "payload": {} } ``` No payload properties. #### Example ```json theme={null} { "event": "settings.pwaInstalled", "confidential": true, "payload": {} } ``` # settings.reset Source: https://docs.corti.ai/assistant/events/generated/onboarding-and-preferences/reset Emitted when settings are reset. ## Event Properties | Field | Value | | -------------- | ------------------ | | `event` | `"settings.reset"` | | `confidential` | `boolean` | | `payload` | `object` | No payload properties. #### Example ```json theme={null} { "event": "settings.reset", "confidential": false, "payload": {} } ``` No payload properties. #### Example ```json theme={null} { "event": "settings.reset", "confidential": true, "payload": {} } ``` # settings.userSettingsValueChanged Source: https://docs.corti.ai/assistant/events/generated/onboarding-and-preferences/userSettingsValueChanged Emitted when a user setting value changes. ## Event Properties | Field | Value | | -------------- | ------------------------------------- | | `event` | `"settings.userSettingsValueChanged"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | -------------- | -------- | -------------------------- | | `settingName` | `string` | Which setting was modified | | `settingValue` | `string` | New value for the setting | #### Example ```json theme={null} { "event": "settings.userSettingsValueChanged", "confidential": false, "payload": { "settingName": "language", "settingValue": "en" } } ``` | Field | Type | Description | | -------------- | -------- | -------------------------- | | `settingName` | `string` | Which setting was modified | | `settingValue` | `string` | New value for the setting | #### Example ```json theme={null} { "event": "settings.userSettingsValueChanged", "confidential": true, "payload": { "settingName": "language", "settingValue": "en" } } ``` # recording.audioHealthAlertDismissed Source: https://docs.corti.ai/assistant/events/generated/recording/audioHealthAlertDismissed Emitted when the user dismisses an audio health alert toast. ## Event Properties | Field | Value | | -------------- | --------------------------------------- | | `event` | `"recording.audioHealthAlertDismissed"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------------ | ------------------------------------------------------------------------------- | ------------------------------------- | | `alert` | `"speechQuality" \| "silence" \| "silenceCh0" \| "silenceCh1" \| "silenceBoth"` | The alert category that was dismissed | | `action` | `"dismissForNow" \| "muteForSession"` | How the user dismissed the alert | | `interactionId` | `string` | Unique interaction identifier | | `interactionState` | `InteractionState` | Current state of the interaction | #### Example ```json theme={null} { "event": "recording.audioHealthAlertDismissed", "confidential": false, "payload": { "alert": "silence", "action": "muteForSession", "interactionId": "int_123", "interactionState": "ongoing" } } ``` | Field | Type | Description | | ------------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------- | | `alert` | `"speechQuality" \| "silence" \| "silenceCh0" \| "silenceCh1" \| "silenceBoth"` | The alert category that was dismissed | | `action` | `"dismissForNow" \| "muteForSession"` | How the user dismissed the alert | | `interaction` | `ExternalInteraction` | Interaction context including transcripts, documents, and facts | | Field | Type | Description | | ----------------- | ------------------------ | --------------------------------------------------- | | `id` | `string` | Unique interaction identifier | | `title` | `string \| null` | Interaction title | | `state` | `InteractionState` | Current state of the interaction | | `startedAt` | `string (ISO 8601 date)` | When the interaction started (ISO 8601 date string) | | `transcriptCount` | `number` | Number of transcripts in the interaction | | `documentCount` | `number` | Number of documents in the interaction | | `facts` | `Fact[]` | Facts extracted during the interaction | **Fact properties:** | Field | Type | Description | | ------------- | ------------------------------ | -------------------------------------------------------- | | `id` | `string` | Unique fact identifier | | `text` | `string` | Fact text content | | `group` | `string` | Fact group/category name | | `source` | `"core" \| "system" \| "user"` | Source of the fact: core (AI extracted), system, or user | | `isDiscarded` | `boolean` | Whether the fact has been discarded | #### Example ```json theme={null} { "event": "recording.audioHealthAlertDismissed", "confidential": true, "payload": { "alert": "silence", "action": "muteForSession", "interaction": { "id": "int_123", "title": "Visit", "state": "ongoing", "startedAt": "2024-01-01T00:00:00.000+00:00", "transcriptCount": 0, "documentCount": 0, "facts": [] } } } ``` # recording.audioHealthAlertRecovered Source: https://docs.corti.ai/assistant/events/generated/recording/audioHealthAlertRecovered Emitted when an audio health issue recovers and the recovery toast is shown. ## Event Properties | Field | Value | | -------------- | --------------------------------------- | | `event` | `"recording.audioHealthAlertRecovered"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------------ | --------------------------------------------------------- | ------------------------------------------------------- | | `event` | `"speechQualityIssueRecovered" \| "longSilenceRecovered"` | The audio health recovery event | | `channel` | `number` | Audio channel number | | `isMultichannel` | `boolean` | Whether the recording is in multichannel (virtual) mode | | `interactionId` | `string` | Unique interaction identifier | | `interactionState` | `InteractionState` | Current state of the interaction | #### Example ```json theme={null} { "event": "recording.audioHealthAlertRecovered", "confidential": false, "payload": { "event": "speechQualityIssueRecovered", "channel": 0, "isMultichannel": false, "interactionId": "int_123", "interactionState": "ongoing" } } ``` | Field | Type | Description | | ---------------- | --------------------------------------------------------- | --------------------------------------------------------------- | | `event` | `"speechQualityIssueRecovered" \| "longSilenceRecovered"` | The audio health recovery event | | `channel` | `number` | Audio channel number | | `isMultichannel` | `boolean` | Whether the recording is in multichannel (virtual) mode | | `interaction` | `ExternalInteraction` | Interaction context including transcripts, documents, and facts | | Field | Type | Description | | ----------------- | ------------------------ | --------------------------------------------------- | | `id` | `string` | Unique interaction identifier | | `title` | `string \| null` | Interaction title | | `state` | `InteractionState` | Current state of the interaction | | `startedAt` | `string (ISO 8601 date)` | When the interaction started (ISO 8601 date string) | | `transcriptCount` | `number` | Number of transcripts in the interaction | | `documentCount` | `number` | Number of documents in the interaction | | `facts` | `Fact[]` | Facts extracted during the interaction | **Fact properties:** | Field | Type | Description | | ------------- | ------------------------------ | -------------------------------------------------------- | | `id` | `string` | Unique fact identifier | | `text` | `string` | Fact text content | | `group` | `string` | Fact group/category name | | `source` | `"core" \| "system" \| "user"` | Source of the fact: core (AI extracted), system, or user | | `isDiscarded` | `boolean` | Whether the fact has been discarded | #### Example ```json theme={null} { "event": "recording.audioHealthAlertRecovered", "confidential": true, "payload": { "event": "speechQualityIssueRecovered", "channel": 0, "isMultichannel": false, "interaction": { "id": "int_123", "title": "Visit", "state": "ongoing", "startedAt": "2024-01-01T00:00:00.000+00:00", "transcriptCount": 0, "documentCount": 0, "facts": [] } } } ``` # recording.audioHealthAlertTriggered Source: https://docs.corti.ai/assistant/events/generated/recording/audioHealthAlertTriggered Emitted for every audio health detection event, including those suppressed by session mute. ## Event Properties | Field | Value | | -------------- | --------------------------------------- | | `event` | `"recording.audioHealthAlertTriggered"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------------ | ------------------------------------------------------- | ------------------------------------------------------------------- | | `event` | `"speechQualityIssueDetected" \| "longSilenceDetected"` | The audio health event that was detected | | `channel` | `number` | Audio channel number | | `isMultichannel` | `boolean` | Whether the recording is in multichannel (virtual) mode | | `deviceLabel` | `string \| undefined` | Label of the active microphone device (only included for channel 0) | | `interactionId` | `string` | Unique interaction identifier | | `interactionState` | `InteractionState` | Current state of the interaction | #### Example ```json theme={null} { "event": "recording.audioHealthAlertTriggered", "confidential": false, "payload": { "event": "longSilenceDetected", "channel": 0, "isMultichannel": true, "deviceLabel": "Jabra Evolve", "interactionId": "int_123", "interactionState": "ongoing" } } ``` | Field | Type | Description | | ---------------- | ------------------------------------------------------- | ------------------------------------------------------------------- | | `event` | `"speechQualityIssueDetected" \| "longSilenceDetected"` | The audio health event that was detected | | `channel` | `number` | Audio channel number | | `isMultichannel` | `boolean` | Whether the recording is in multichannel (virtual) mode | | `deviceLabel` | `string \| undefined` | Label of the active microphone device (only included for channel 0) | | `interaction` | `ExternalInteraction` | Interaction context including transcripts, documents, and facts | | Field | Type | Description | | ----------------- | ------------------------ | --------------------------------------------------- | | `id` | `string` | Unique interaction identifier | | `title` | `string \| null` | Interaction title | | `state` | `InteractionState` | Current state of the interaction | | `startedAt` | `string (ISO 8601 date)` | When the interaction started (ISO 8601 date string) | | `transcriptCount` | `number` | Number of transcripts in the interaction | | `documentCount` | `number` | Number of documents in the interaction | | `facts` | `Fact[]` | Facts extracted during the interaction | **Fact properties:** | Field | Type | Description | | ------------- | ------------------------------ | -------------------------------------------------------- | | `id` | `string` | Unique fact identifier | | `text` | `string` | Fact text content | | `group` | `string` | Fact group/category name | | `source` | `"core" \| "system" \| "user"` | Source of the fact: core (AI extracted), system, or user | | `isDiscarded` | `boolean` | Whether the fact has been discarded | #### Example ```json theme={null} { "event": "recording.audioHealthAlertTriggered", "confidential": true, "payload": { "event": "longSilenceDetected", "channel": 0, "isMultichannel": true, "deviceLabel": "Jabra Evolve", "interaction": { "id": "int_123", "title": "Visit", "state": "ongoing", "startedAt": "2024-01-01T00:00:00.000+00:00", "transcriptCount": 0, "documentCount": 0, "facts": [] } } } ``` # recording.languageChanged Source: https://docs.corti.ai/assistant/events/generated/recording/languageChanged Emitted when recording language changes. ## Event Properties | Field | Value | | -------------- | ----------------------------- | | `event` | `"recording.languageChanged"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------------ | ------------------ | --------------------------------------- | | `language` | `string` | New language selected for transcription | | `interactionId` | `string` | Unique interaction identifier | | `interactionState` | `InteractionState` | Current state of the interaction | #### Example ```json theme={null} { "event": "recording.languageChanged", "confidential": false, "payload": { "language": "en", "interactionId": "int_123", "interactionState": "ongoing" } } ``` | Field | Type | Description | | ------------- | --------------------- | --------------------------------------------------------------- | | `language` | `string` | New language selected for transcription | | `interaction` | `ExternalInteraction` | Interaction context including transcripts, documents, and facts | | Field | Type | Description | | ----------------- | ------------------------ | --------------------------------------------------- | | `id` | `string` | Unique interaction identifier | | `title` | `string \| null` | Interaction title | | `state` | `InteractionState` | Current state of the interaction | | `startedAt` | `string (ISO 8601 date)` | When the interaction started (ISO 8601 date string) | | `transcriptCount` | `number` | Number of transcripts in the interaction | | `documentCount` | `number` | Number of documents in the interaction | | `facts` | `Fact[]` | Facts extracted during the interaction | **Fact properties:** | Field | Type | Description | | ------------- | ------------------------------ | -------------------------------------------------------- | | `id` | `string` | Unique fact identifier | | `text` | `string` | Fact text content | | `group` | `string` | Fact group/category name | | `source` | `"core" \| "system" \| "user"` | Source of the fact: core (AI extracted), system, or user | | `isDiscarded` | `boolean` | Whether the fact has been discarded | #### Example ```json theme={null} { "event": "recording.languageChanged", "confidential": true, "payload": { "language": "en", "interaction": { "id": "int_123", "title": "Visit", "state": "ongoing", "startedAt": "2024-01-01T00:00:00.000+00:00", "transcriptCount": 0, "documentCount": 0, "facts": [] } } } ``` # recording.modeChanged Source: https://docs.corti.ai/assistant/events/generated/recording/modeChanged Emitted when recording mode changes. ## Event Properties | Field | Value | | -------------- | ------------------------- | | `event` | `"recording.modeChanged"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------------ | -------------------------- | -------------------------------- | | `mode` | `"virtual" \| "in-person"` | New recording mode selected | | `interactionId` | `string` | Unique interaction identifier | | `interactionState` | `InteractionState` | Current state of the interaction | #### Example ```json theme={null} { "event": "recording.modeChanged", "confidential": false, "payload": { "mode": "in-person", "interactionId": "int_123", "interactionState": "ongoing" } } ``` | Field | Type | Description | | ------------- | -------------------------- | --------------------------------------------------------------- | | `mode` | `"virtual" \| "in-person"` | New recording mode selected | | `interaction` | `ExternalInteraction` | Interaction context including transcripts, documents, and facts | | Field | Type | Description | | ----------------- | ------------------------ | --------------------------------------------------- | | `id` | `string` | Unique interaction identifier | | `title` | `string \| null` | Interaction title | | `state` | `InteractionState` | Current state of the interaction | | `startedAt` | `string (ISO 8601 date)` | When the interaction started (ISO 8601 date string) | | `transcriptCount` | `number` | Number of transcripts in the interaction | | `documentCount` | `number` | Number of documents in the interaction | | `facts` | `Fact[]` | Facts extracted during the interaction | **Fact properties:** | Field | Type | Description | | ------------- | ------------------------------ | -------------------------------------------------------- | | `id` | `string` | Unique fact identifier | | `text` | `string` | Fact text content | | `group` | `string` | Fact group/category name | | `source` | `"core" \| "system" \| "user"` | Source of the fact: core (AI extracted), system, or user | | `isDiscarded` | `boolean` | Whether the fact has been discarded | #### Example ```json theme={null} { "event": "recording.modeChanged", "confidential": true, "payload": { "mode": "in-person", "interaction": { "id": "int_123", "title": "Visit", "state": "ongoing", "startedAt": "2024-01-01T00:00:00.000+00:00", "transcriptCount": 0, "documentCount": 0, "facts": [] } } } ``` # recording.started Source: https://docs.corti.ai/assistant/events/generated/recording/started Emitted when recording starts. ## Event Properties | Field | Value | | -------------- | --------------------- | | `event` | `"recording.started"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------------ | -------------------------- | -------------------------------- | | `mode` | `"virtual" \| "in-person"` | Recording mode at start | | `language` | `string` | Language used for transcription | | `deviceLabel` | `string` | Microphone used for recording | | `interactionId` | `string` | Unique interaction identifier | | `interactionState` | `InteractionState` | Current state of the interaction | #### Example ```json theme={null} { "event": "recording.started", "confidential": false, "payload": { "mode": "virtual", "language": "en", "deviceLabel": "Jabra Evolve", "interactionId": "int_123", "interactionState": "ongoing" } } ``` | Field | Type | Description | | ------------- | -------------------------- | --------------------------------------------------------------- | | `mode` | `"virtual" \| "in-person"` | Recording mode at start | | `language` | `string` | Language used for transcription | | `deviceLabel` | `string` | Microphone used for recording | | `interaction` | `ExternalInteraction` | Interaction context including transcripts, documents, and facts | | Field | Type | Description | | ----------------- | ------------------------ | --------------------------------------------------- | | `id` | `string` | Unique interaction identifier | | `title` | `string \| null` | Interaction title | | `state` | `InteractionState` | Current state of the interaction | | `startedAt` | `string (ISO 8601 date)` | When the interaction started (ISO 8601 date string) | | `transcriptCount` | `number` | Number of transcripts in the interaction | | `documentCount` | `number` | Number of documents in the interaction | | `facts` | `Fact[]` | Facts extracted during the interaction | **Fact properties:** | Field | Type | Description | | ------------- | ------------------------------ | -------------------------------------------------------- | | `id` | `string` | Unique fact identifier | | `text` | `string` | Fact text content | | `group` | `string` | Fact group/category name | | `source` | `"core" \| "system" \| "user"` | Source of the fact: core (AI extracted), system, or user | | `isDiscarded` | `boolean` | Whether the fact has been discarded | #### Example ```json theme={null} { "event": "recording.started", "confidential": true, "payload": { "mode": "virtual", "language": "en", "deviceLabel": "Jabra Evolve", "interaction": { "id": "int_123", "title": "Visit", "state": "ongoing", "startedAt": "2024-01-01T00:00:00.000+00:00", "transcriptCount": 0, "documentCount": 0, "facts": [] } } } ``` # recording.stopped Source: https://docs.corti.ai/assistant/events/generated/recording/stopped Emitted when recording stops. ## Event Properties | Field | Value | | -------------- | --------------------- | | `event` | `"recording.stopped"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------------ | ------------------ | ---------------------------------------------- | | `duration` | `number` | Duration of the recording session before pause | | `interactionId` | `string` | Unique interaction identifier | | `interactionState` | `InteractionState` | Current state of the interaction | #### Example ```json theme={null} { "event": "recording.stopped", "confidential": false, "payload": { "duration": 12, "interactionId": "int_123", "interactionState": "ongoing" } } ``` | Field | Type | Description | | ------------- | --------------------- | --------------------------------------------------------------- | | `duration` | `number` | Duration of the recording session before pause | | `interaction` | `ExternalInteraction` | Interaction context including transcripts, documents, and facts | | Field | Type | Description | | ----------------- | ------------------------ | --------------------------------------------------- | | `id` | `string` | Unique interaction identifier | | `title` | `string \| null` | Interaction title | | `state` | `InteractionState` | Current state of the interaction | | `startedAt` | `string (ISO 8601 date)` | When the interaction started (ISO 8601 date string) | | `transcriptCount` | `number` | Number of transcripts in the interaction | | `documentCount` | `number` | Number of documents in the interaction | | `facts` | `Fact[]` | Facts extracted during the interaction | **Fact properties:** | Field | Type | Description | | ------------- | ------------------------------ | -------------------------------------------------------- | | `id` | `string` | Unique fact identifier | | `text` | `string` | Fact text content | | `group` | `string` | Fact group/category name | | `source` | `"core" \| "system" \| "user"` | Source of the fact: core (AI extracted), system, or user | | `isDiscarded` | `boolean` | Whether the fact has been discarded | #### Example ```json theme={null} { "event": "recording.stopped", "confidential": true, "payload": { "duration": 12, "interaction": { "id": "int_123", "title": "Visit", "state": "ongoing", "startedAt": "2024-01-01T00:00:00.000+00:00", "transcriptCount": 0, "documentCount": 0, "facts": [] } } } ``` # recording.transcriptReceived Source: https://docs.corti.ai/assistant/events/generated/recording/transcriptReceived Emitted when a transcript is received. ## Event Properties | Field | Value | | -------------- | -------------------------------- | | `event` | `"recording.transcriptReceived"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------------ | ------------------ | --------------------------------- | | `wordCount` | `number` | Number of words in the transcript | | `interactionId` | `string` | Unique interaction identifier | | `interactionState` | `InteractionState` | Current state of the interaction | #### Example ```json theme={null} { "event": "recording.transcriptReceived", "confidential": false, "payload": { "wordCount": 42, "interactionId": "int_123", "interactionState": "ongoing" } } ``` | Field | Type | Description | | ------------- | --------------------- | --------------------------------------------------------------- | | `transcript` | `string` | Transcribed text from audio | | `wordCount` | `number` | Number of words in the transcript | | `interaction` | `ExternalInteraction` | Interaction context including transcripts, documents, and facts | | Field | Type | Description | | ----------------- | ------------------------ | --------------------------------------------------- | | `id` | `string` | Unique interaction identifier | | `title` | `string \| null` | Interaction title | | `state` | `InteractionState` | Current state of the interaction | | `startedAt` | `string (ISO 8601 date)` | When the interaction started (ISO 8601 date string) | | `transcriptCount` | `number` | Number of transcripts in the interaction | | `documentCount` | `number` | Number of documents in the interaction | | `facts` | `Fact[]` | Facts extracted during the interaction | **Fact properties:** | Field | Type | Description | | ------------- | ------------------------------ | -------------------------------------------------------- | | `id` | `string` | Unique fact identifier | | `text` | `string` | Fact text content | | `group` | `string` | Fact group/category name | | `source` | `"core" \| "system" \| "user"` | Source of the fact: core (AI extracted), system, or user | | `isDiscarded` | `boolean` | Whether the fact has been discarded | #### Example ```json theme={null} { "event": "recording.transcriptReceived", "confidential": true, "payload": { "transcript": "...", "wordCount": 42, "interaction": { "id": "int_123", "title": "Visit", "state": "ongoing", "startedAt": "2024-01-01T00:00:00.000+00:00", "transcriptCount": 0, "documentCount": 0, "facts": [] } } } ``` # template.copied Source: https://docs.corti.ai/assistant/events/generated/templates/copied Emitted when a template is copied. ## Event Properties | Field | Value | | -------------- | ------------------- | | `event` | `"template.copied"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------------------ | ---------------------------------------------------- | ------------------------------------------------ | | `id` | `string` | Identifier of the copied template | | `title` | `string` | Name of the copied template | | `language` | `string` | Language of the copied template | | `templateType` | `"personal" \| undefined` | Whether the copy is a personal template | | `source` | `"template_assembler" \| undefined` | Source of the copy action | | `sourceTemplateType` | `"standard" \| "project" \| "personal" \| undefined` | Type of the source template being copied from | | `reusedSectionCount` | `number \| undefined` | Number of sections reused unchanged by reference | | `customisedSectionCount` | `number \| undefined` | Number of sections created with customizations | #### Example ```json theme={null} { "event": "template.copied", "confidential": false, "payload": { "id": "template_2", "title": "SOAP Copy", "language": "en" } } ``` | Field | Type | Description | | ---------- | -------- | --------------------------------- | | `id` | `string` | Identifier of the copied template | | `title` | `string` | Name of the copied template | | `language` | `string` | Language of the copied template | #### Example ```json theme={null} { "event": "template.copied", "confidential": true, "payload": { "id": "template_2", "title": "SOAP Copy", "language": "en" } } ``` # template.created Source: https://docs.corti.ai/assistant/events/generated/templates/created Emitted when a template is created. ## Event Properties | Field | Value | | -------------- | -------------------- | | `event` | `"template.created"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------------------ | ------------------------------------- | ---------------------------------------------------------------------------------- | | `title` | `string` | Name given to the new template | | `language` | `string` | Language the template is written in | | `numSections` | `number` | Count of sections in the template | | `templateType` | `"legacy" \| "personal" \| undefined` | Type of template: "legacy" for custom templates, "personal" for personal templates | | `source` | `"template_assembler" \| undefined` | Source of creation | | `reusedSectionCount` | `number \| undefined` | Number of sections reused unchanged by reference | | `customisedSectionCount` | `number \| undefined` | Number of sections created with customizations | #### Example ```json theme={null} { "event": "template.created", "confidential": false, "payload": { "title": "SOAP", "language": "en", "numSections": 6 } } ``` | Field | Type | Description | | ------------- | ------------------------ | ------------------------------------------ | | `title` | `string` | Name given to the new template | | `language` | `string` | Language the template is written in | | `numSections` | `number` | Count of sections in the template | | `template` | `TemplateCreatedPayload` | Complete template object with all sections | | Field | Type | Description | | ---------- | ----------- | ----------- | | `id` | `string` | - | | `name` | `string` | - | | `language` | `string` | - | | `sections` | `Section[]` | - | **Section properties:** | Field | Type | Description | | ------- | -------- | ----------- | | `id` | `string` | - | | `title` | `string` | - | #### Example ```json theme={null} { "event": "template.created", "confidential": true, "payload": { "title": "SOAP", "language": "en", "numSections": 6, "template": { "id": "template_1", "name": "SOAP", "language": "en", "sections": [ { "id": "section_1", "title": "HPI" } ] } } } ``` # template.defaultTemplateUpdated Source: https://docs.corti.ai/assistant/events/generated/templates/defaultTemplateUpdated Emitted when the default template is updated. ## Event Properties | Field | Value | | -------------- | ----------------------------------- | | `event` | `"template.defaultTemplateUpdated"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | -------------------------- | ----------------------------- | ------------------------------------ | | `defaultTemplateId` | `string` | Previous default template identifier | | `defaultTemplateType` | `"legacy" \| "schema-driven"` | Previous default template type | | `defaultTemplateLanguage` | `string` | Previous default template language | | `selectedTemplateId` | `string` | New default template identifier | | `selectedTemplateType` | `"legacy" \| "schema-driven"` | New default template type | | `selectedTemplateLanguage` | `string` | New default template language | #### Example ```json theme={null} { "event": "template.defaultTemplateUpdated", "confidential": false, "payload": { "defaultTemplateId": "template_default", "defaultTemplateType": "legacy", "defaultTemplateLanguage": "en", "selectedTemplateId": "template_selected", "selectedTemplateType": "legacy", "selectedTemplateLanguage": "en" } } ``` | Field | Type | Description | | -------------------------- | ----------------------------- | ------------------------------------ | | `defaultTemplateId` | `string` | Previous default template identifier | | `defaultTemplateType` | `"legacy" \| "schema-driven"` | Previous default template type | | `defaultTemplateLanguage` | `string` | Previous default template language | | `selectedTemplateId` | `string` | New default template identifier | | `selectedTemplateType` | `"legacy" \| "schema-driven"` | New default template type | | `selectedTemplateLanguage` | `string` | New default template language | #### Example ```json theme={null} { "event": "template.defaultTemplateUpdated", "confidential": true, "payload": { "defaultTemplateId": "template_default", "defaultTemplateType": "legacy", "defaultTemplateLanguage": "en", "selectedTemplateId": "template_selected", "selectedTemplateType": "legacy", "selectedTemplateLanguage": "en" } } ``` # template.deleteConfirmationDismissed Source: https://docs.corti.ai/assistant/events/generated/templates/deleteConfirmationDismissed Emitted when the user dismisses the template delete dialog. ## Event Properties | Field | Value | | -------------- | ---------------------------------------- | | `event` | `"template.deleteConfirmationDismissed"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | -------------- | ------------ | ----------- | | `templateId` | `string` | - | | `templateType` | `"personal"` | - | #### Example ```json theme={null} { "event": "template.deleteConfirmationDismissed", "confidential": false, "payload": { "templateId": "template_personal_1", "templateType": "personal" } } ``` | Field | Type | Description | | -------------- | ------------ | ----------- | | `templateId` | `string` | - | | `templateType` | `"personal"` | - | #### Example ```json theme={null} { "event": "template.deleteConfirmationDismissed", "confidential": true, "payload": { "templateId": "template_personal_1", "templateType": "personal" } } ``` # template.deleteConfirmationOpened Source: https://docs.corti.ai/assistant/events/generated/templates/deleteConfirmationOpened Emitted when the user opens the template delete dialog. ## Event Properties | Field | Value | | -------------- | ------------------------------------- | | `event` | `"template.deleteConfirmationOpened"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | -------------- | ------------ | ----------- | | `templateId` | `string` | - | | `templateType` | `"personal"` | - | #### Example ```json theme={null} { "event": "template.deleteConfirmationOpened", "confidential": false, "payload": { "templateId": "template_personal_1", "templateType": "personal" } } ``` | Field | Type | Description | | -------------- | ------------ | ----------- | | `templateId` | `string` | - | | `templateType` | `"personal"` | - | #### Example ```json theme={null} { "event": "template.deleteConfirmationOpened", "confidential": true, "payload": { "templateId": "template_personal_1", "templateType": "personal" } } ``` # template.deleted Source: https://docs.corti.ai/assistant/events/generated/templates/deleted Emitted when a template is deleted. ## Event Properties | Field | Value | | -------------- | -------------------- | | `event` | `"template.deleted"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ----------------------- | ------------------------------------- | ------------------------------------------------------------------------------------------ | | `id` | `string` | Identifier of the deleted template | | `templateType` | `"legacy" \| "personal" \| undefined` | Type of template: "legacy" for old custom templates, "personal" for new personal templates | | `source` | `"template_detail" \| undefined` | - | | `copiedFromTemplateId` | `string \| undefined` | - | | `hadCustomisedSections` | `boolean \| undefined` | - | #### Example ```json theme={null} { "event": "template.deleted", "confidential": false, "payload": { "id": "template_3", "templateType": "legacy" } } ``` | Field | Type | Description | | -------------- | ------------------------------------- | ------------------------------------------------------------------------------------------ | | `id` | `string` | Identifier of the deleted template | | `templateType` | `"legacy" \| "personal" \| undefined` | Type of template: "legacy" for old custom templates, "personal" for new personal templates | #### Example ```json theme={null} { "event": "template.deleted", "confidential": true, "payload": { "id": "template_3", "templateType": "legacy" } } ``` # template.metadataEditDismissed Source: https://docs.corti.ai/assistant/events/generated/templates/metadataEditDismissed Emitted when the user dismisses the template metadata editor without saving. ## Event Properties | Field | Value | | -------------- | ---------------------------------- | | `event` | `"template.metadataEditDismissed"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ------------------- | ------------------- | --------------------------------------------------- | | `templateId` | `string` | Identifier of the template whose edit was dismissed | | `templateType` | `"personal"` | Type of template | | `source` | `"template_detail"` | Where the dismiss occurred | | `hasUnsavedChanges` | `boolean` | Whether the user had unsaved changes | #### Example ```json theme={null} { "event": "template.metadataEditDismissed", "confidential": false, "payload": { "templateId": "template_1", "templateType": "personal", "source": "template_detail", "hasUnsavedChanges": true } } ``` | Field | Type | Description | | ------------------- | ------------------- | --------------------------------------------------- | | `templateId` | `string` | Identifier of the template whose edit was dismissed | | `templateType` | `"personal"` | Type of template | | `source` | `"template_detail"` | Where the dismiss occurred | | `hasUnsavedChanges` | `boolean` | Whether the user had unsaved changes | #### Example ```json theme={null} { "event": "template.metadataEditDismissed", "confidential": true, "payload": { "templateId": "template_1", "templateType": "personal", "source": "template_detail", "hasUnsavedChanges": true } } ``` # template.metadataEditOpened Source: https://docs.corti.ai/assistant/events/generated/templates/metadataEditOpened Emitted when the user opens the template metadata editor. ## Event Properties | Field | Value | | -------------- | ------------------------------- | | `event` | `"template.metadataEditOpened"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | -------------- | ------------------- | --------------------------------------- | | `templateId` | `string` | Identifier of the template being edited | | `templateType` | `"personal"` | Type of template | | `source` | `"template_detail"` | Where the edit was initiated | #### Example ```json theme={null} { "event": "template.metadataEditOpened", "confidential": false, "payload": { "templateId": "template_1", "templateType": "personal", "source": "template_detail" } } ``` | Field | Type | Description | | -------------- | ------------------- | --------------------------------------- | | `templateId` | `string` | Identifier of the template being edited | | `templateType` | `"personal"` | Type of template | | `source` | `"template_detail"` | Where the edit was initiated | #### Example ```json theme={null} { "event": "template.metadataEditOpened", "confidential": true, "payload": { "templateId": "template_1", "templateType": "personal", "source": "template_detail" } } ``` # template.metadataUpdated Source: https://docs.corti.ai/assistant/events/generated/templates/metadataUpdated Emitted when template metadata is successfully updated. ## Event Properties | Field | Value | | -------------- | ---------------------------- | | `event` | `"template.metadataUpdated"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ---------------- | ------------------- | ---------------------------------------- | | `templateId` | `string` | Identifier of the template being updated | | `templateType` | `"personal"` | Type of template | | `source` | `"template_detail"` | Where the update was initiated | | `changedFields` | `string[]` | Which fields were changed | | `versionChanged` | `false` | Whether the template version changed | #### Example ```json theme={null} { "event": "template.metadataUpdated", "confidential": false, "payload": { "templateId": "template_1", "templateType": "personal", "source": "template_detail", "changedFields": [ "name", "description" ], "versionChanged": false } } ``` | Field | Type | Description | | ---------------- | ------------------- | ---------------------------------------- | | `templateId` | `string` | Identifier of the template being updated | | `templateType` | `"personal"` | Type of template | | `source` | `"template_detail"` | Where the update was initiated | | `changedFields` | `string[]` | Which fields were changed | | `versionChanged` | `false` | Whether the template version changed | #### Example ```json theme={null} { "event": "template.metadataUpdated", "confidential": true, "payload": { "templateId": "template_1", "templateType": "personal", "source": "template_detail", "changedFields": [ "name", "description" ], "versionChanged": false } } ``` # template.pickerOpened Source: https://docs.corti.ai/assistant/events/generated/templates/pickerOpened Emitted when the template picker opens. ## Event Properties | Field | Value | | -------------- | ------------------------- | | `event` | `"template.pickerOpened"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | ----------------------- | ----------------------------- | ------------------------------------------------------------ | | `location` | `"settings" \| "interaction"` | Where in the UI the picker was opened from | | `personalTemplateCount` | `number \| undefined` | Number of personal templates available when the picker opens | #### Example ```json theme={null} { "event": "template.pickerOpened", "confidential": false, "payload": { "location": "settings", "personalTemplateCount": 3 } } ``` | Field | Type | Description | | ----------------------- | ----------------------------- | ------------------------------------------------------------ | | `location` | `"settings" \| "interaction"` | Where in the UI the picker was opened from | | `personalTemplateCount` | `number \| undefined` | Number of personal templates available when the picker opens | #### Example ```json theme={null} { "event": "template.pickerOpened", "confidential": true, "payload": { "location": "settings", "personalTemplateCount": 3 } } ``` # template.selected Source: https://docs.corti.ai/assistant/events/generated/templates/selected Emitted when a template is selected. ## Event Properties | Field | Value | | -------------- | --------------------- | | `event` | `"template.selected"` | | `confidential` | `boolean` | | `payload` | `object` | | Field | Type | Description | | -------------------------- | ----------------------------- | --------------------------------- | | `selectedTemplateId` | `string` | Identifier of the chosen template | | `selectedTemplateType` | `"legacy" \| "schema-driven"` | Type of the chosen template | | `selectedTemplateLanguage` | `string` | Language of the chosen template | #### Example ```json theme={null} { "event": "template.selected", "confidential": false, "payload": { "selectedTemplateId": "template_selected", "selectedTemplateType": "legacy", "selectedTemplateLanguage": "en" } } ``` | Field | Type | Description | | -------------------------- | ----------------------------- | --------------------------------- | | `selectedTemplateId` | `string` | Identifier of the chosen template | | `selectedTemplateType` | `"legacy" \| "schema-driven"` | Type of the chosen template | | `selectedTemplateLanguage` | `string` | Language of the chosen template | #### Example ```json theme={null} { "event": "template.selected", "confidential": true, "payload": { "selectedTemplateId": "template_selected", "selectedTemplateType": "legacy", "selectedTemplateLanguage": "en" } } ``` # Corti Assistant Events Overview Source: https://docs.corti.ai/assistant/events/index How Corti Assistant dispatches events ## Overview Corti Assistant uses a **real-time event system** to communicate state changes and important updates from the embedded application to your integration. Events enable you to build responsive integrations that react to user actions, recording states, and document lifecycle changes without polling. ## Event structure All Corti Assistant events follow a consistent schema: ```typescript theme={null} { event: string, // Event name (e.g., 'recording.started', 'document.generated') confidential: boolean, // Whether event contains sensitive data payload: object // Event-specific data } ``` ### Structure components * **`event`**: A dot-notation string identifier for the specific event type (e.g., `'recording.started'`, `'document.generated'`, `'interaction.loaded'`) * **`confidential`**: A boolean indicating whether the payload contains sensitive patient or user data * **`payload`**: An object containing event-specific data. The structure varies by event type and confidentiality level. ### Confidential vs Public events Events can contain two types of payloads: * **Public payload**: Contains metadata and identifiers (IDs, states, durations) without sensitive information * **Confidential payload**: Contains full interaction context including transcripts, documents, facts, and other protected data The `confidential` field indicates which payload type is included. ## Integration transports Events are delivered through different transport mechanisms depending on your integration method. For details on how to receive and handle events in your specific integration: * [PostMessage API](/assistant/postmessage-api) - For iframe/WebView integrations * [Window API](/assistant/window-api) - For same-origin integrations * [Web Component API](/assistant/web-component-api) - For the recommended component-based integration ## Detailed event reference For complete documentation of each event, including full payload schemas, confidential fields, and usage examples, see the individual event pages in this section. # Events Migration Guide Source: https://docs.corti.ai/assistant/events/legacy-events Move from deprecated legacy events to the current Embedded API event format. Use this guide to move from the legacy event system to the current Embedded API event format. Due by 2026-08-20. Legacy events are still dispatched for backward compatibility, but support ends on this date. Migrate to the [new event format](/assistant/events) now and track timing in [Scheduled Deprecations](/assistant/deprecation-timeline). ## Overview The legacy event system uses the `CORTI_EMBEDDED_EVENT` wrapper format with camelCase event names. These events are still sent alongside the new dot-notation events, but support will be removed in a future release. ## Reference ### Event wrapper differences The wrapper type stays the same, but the event name and payload contract change. | Legacy field | Current field | Notes | | ------------------------------ | ------------------------------ | ------------------------------------------------------------ | | `type: "CORTI_EMBEDDED_EVENT"` | `type: "CORTI_EMBEDDED_EVENT"` | The outer wrapper does not change | | `event: "recordingStarted"` | `event: "recording.started"` | Event names move from camelCase to dot notation | | `deprecated: true` | Not present | Remove logic that depends on this field | | `payload` | `payload` | Payloads still exist, but individual event shapes may differ | | Not present | `confidential: boolean` | Current events include confidentiality metadata | ### Legacy event structure All legacy events follow this structure: ```typescript theme={null} { type: 'CORTI_EMBEDDED_EVENT', // Always this value for legacy events event: string, // camelCase event name (e.g., 'recordingStarted') deprecated: true, // Always true for legacy events payload?: object // Optional event-specific data } ``` ### New event structure The new event format uses dot-notation event names and includes a `confidential` field: ```typescript theme={null} { type: 'CORTI_EMBEDDED_EVENT', // Same wrapper as legacy format event: string, // Dot-notation event name (e.g., 'recording.started') confidential: boolean, // Indicates if payload contains sensitive data payload: object // Event-specific data with new structure } ``` ### Mapping from legacy event names | Legacy event | Current event | | ------------------- | ------------------------- | | `ready` | `embedded.ready` | | `loaded` | `interaction.loaded` | | `recordingStarted` | `recording.started` | | `recordingStopped` | `recording.stopped` | | `documentGenerated` | `document.generated` | | `documentUpdated` | `document.updated` | | `documentSynced` | `document.synced` | | `usage` | `account.creditsConsumed` | ## Migration path Update your event subscriptions from camelCase names to the current dot-notation names, for example `recordingStarted` to `recording.started`. Handle the `confidential` field and update any payload parsing to match the current event reference pages. Remove logic that depends on `deprecated: true` and stop relying on the legacy event variants during the migration window. The `CORTI_EMBEDDED_EVENT` type wrapper remains the same in both legacy and new formats. You still check for `event.data?.type === 'CORTI_EMBEDDED_EVENT'`. Only the event names and payload structures have changed. You can use the `deprecated: true` field to programmatically detect and log warnings for legacy events in your integration, helping you track migration progress. ## Before and after examples ### ready Emitted when the embedded app is loaded and ready to receive messages. ```typescript theme={null} { type: 'CORTI_EMBEDDED_EVENT', event: 'ready', deprecated: true, payload?: {} } ``` **Replacement:** Use [`embedded.ready`](/assistant/events/generated/embedded-api/ready) instead.
**Before** ```javascript theme={null} const corti = document.querySelector("corti-embedded"); corti.addEventListener("ready", ...); ```
**After** ```javascript theme={null} const corti = document.querySelector("corti-embedded"); corti.addEventListener("embedded.ready", ...); ```
*** ### loaded Emitted when navigation to a specific path has completed. ```typescript theme={null} { type: 'CORTI_EMBEDDED_EVENT', event: 'loaded', deprecated: true, payload: { path: string } } ``` **Replacement:** Use [`interaction.loaded`](/assistant/events/generated/interaction/loaded) instead.
**Before** ```javascript theme={null} const corti = document.querySelector("corti-embedded"); corti.addEventListener("loaded", ...); ```
**After** ```javascript theme={null} const corti = document.querySelector("corti-embedded"); corti.addEventListener("interaction.loaded", ...); ```
*** ### recordingStarted Emitted when recording has started. ```typescript theme={null} { type: 'CORTI_EMBEDDED_EVENT', event: 'recordingStarted', deprecated: true, payload?: {} } ``` **Replacement:** Use [`recording.started`](/assistant/events/generated/recording/started) instead.
**Before** ```javascript theme={null} const corti = document.querySelector("corti-embedded"); corti.addEventListener("recordingStarted", ...); ```
**After** ```javascript theme={null} const corti = document.querySelector("corti-embedded"); corti.addEventListener("recording.started", ...); ```
*** ### recordingStopped Emitted when recording has stopped. ```typescript theme={null} { type: 'CORTI_EMBEDDED_EVENT', event: 'recordingStopped', deprecated: true, payload?: {} } ``` **Replacement:** Use [`recording.stopped`](/assistant/events/generated/recording/stopped) instead.
**Before** ```javascript theme={null} const corti = document.querySelector("corti-embedded"); corti.addEventListener("recordingStopped", ...); ```
**After** ```javascript theme={null} const corti = document.querySelector("corti-embedded"); corti.addEventListener("recording.stopped", ...); ```
*** ### documentGenerated Emitted when a document has been generated. ```typescript theme={null} { type: 'CORTI_EMBEDDED_EVENT', event: 'documentGenerated', deprecated: true, payload: { document: { id: string, name: string, templateRef: string, // ... (see getStatus response for full document structure) } } } ``` **Replacement:** Use [`document.generated`](/assistant/events/generated/document/generated) instead.
**Before** ```javascript theme={null} const corti = document.querySelector("corti-embedded"); corti.addEventListener("documentGenerated", ...); ```
**After** ```javascript theme={null} const corti = document.querySelector("corti-embedded"); corti.addEventListener("document.generated", ...); ```
*** ### documentUpdated Emitted when a document has been updated. ```typescript theme={null} { type: 'CORTI_EMBEDDED_EVENT', event: 'documentUpdated', deprecated: true, payload: { document: { id: string, name: string, templateRef: string, // ... (see getStatus response for full document structure) } } } ``` **Replacement:** Use [`document.updated`](/assistant/events/generated/document/updated) instead.
**Before** ```javascript theme={null} const corti = document.querySelector("corti-embedded"); corti.addEventListener("documentUpdated", ...); ```
**After** ```javascript theme={null} const corti = document.querySelector("corti-embedded"); corti.addEventListener("document.updated", ...); ```
*** ### documentSynced Emitted when a document has been synced to EHR. ```typescript theme={null} { type: 'CORTI_EMBEDDED_EVENT', event: 'documentSynced', deprecated: true, payload: { document: { id: string, name: string, templateRef: string, // ... (see getStatus response for full document structure) } } } ``` **Replacement:** Use [`document.synced`](/assistant/events/generated/document/synced) instead.
**Before** ```javascript theme={null} const corti = document.querySelector("corti-embedded"); corti.addEventListener("documentSynced", ...); ```
**After** ```javascript theme={null} const corti = document.querySelector("corti-embedded"); corti.addEventListener("document.synced", ...); ```
*** ### usage Emitted when credits have been consumed because of either of these triggers: * **Ending/pausing a recording**: credits consumed for transcription and fact extraction * **Ending dictation**: credits consumed for transcription * **Generating a document**: credits consumed for text generation ```typescript theme={null} { type: 'CORTI_EMBEDDED_EVENT', event: 'usage', payload: { creditsConsumed: 0.13, } } ``` This value is not accumulative and only refers to the latest trigger. **Replacement:** Use [`account.creditsConsumed`](/assistant/events/generated/account/creditsConsumed) instead.
**Before** ```javascript theme={null} const corti = document.querySelector("corti-embedded"); corti.addEventListener("usage", ...); ```
**After** ```javascript theme={null} const corti = document.querySelector("corti-embedded"); corti.addEventListener("account.creditsConsumed", ...); ```
*** ### Listening for legacy events ```javascript theme={null} window.addEventListener("message", (event) => { if (event.data?.type === "CORTI_EMBEDDED_EVENT") { switch (event.data.event) { case "ready": console.log("Embedded app ready"); break; case "loaded": console.log("Navigation completed:", event.data.payload.path); break; case "documentGenerated": console.log("Document generated:", event.data.payload.document); break; case "documentUpdated": console.log("Document updated:", event.data.payload.document); break; case "documentSynced": console.log("Document synced:", event.data.payload.document); break; case "recordingStarted": console.log("Recording started"); break; case "recordingStopped": console.log("Recording stopped"); break; default: console.log("Unknown event:", event.data.event); } } }); ``` ## Timeline * **Current**: Both legacy and new events are dispatched during the migration window * **Future**: Legacy event support ends on 2026-08-20 * **Action required**: Migrate to the new event format before the deadline ## Related pages Review the current Embedded API event names, payloads, and generated reference pages. Check rollout timing and upcoming shutdown dates for embedded changes. Follow release announcements related to Embedded API changes and migrations. # Embedding Assistant in React Web App Source: https://docs.corti.ai/assistant/guides/react-integration Complete guide for integrating Corti Assistant into a React web application This guide walks you through integrating Corti Assistant into a React web application. You'll learn the key concepts, architecture patterns, and implementation approach using the Web Component API. **Complete working example available**: This guide references the [basic React example](https://github.com/corticph/corti-examples/tree/main/embedded-assistant/react/basic-example) in our examples repository. Clone it to see a fully functional implementation, then follow this guide to understand how it works and adapt it to your needs. This guide uses ROPC (Resource Owner Password Credentials) authentication, matching the React basic example. *** ## What you'll learn By following this guide, you'll understand: * How to architect a secure client-server integration * Why ROPC authentication works well for embedded EHR scenarios * How to use the Web Component API with React * How to manage the interaction lifecycle programmatically * Key customization points and production considerations ## Prerequisites Before starting, ensure you have: * **Node.js 18+** installed * **Corti API access** with valid credentials * **OAuth client** configured for ROPC (Resource Owner Password Credentials) flow * **User account** created in Corti Console for authentication * Basic familiarity with React and Express Don't have credentials yet? Visit [Corti Console](https://console.corti.app) to create a user and OAuth client. Ensure your OAuth client has "Direct Access Grants" enabled for ROPC flow. *** ## Architecture overview The integration follows a client-server architecture: **Backend (Express Server)**: * Handles OAuth authentication using ROPC flow via `@corti/sdk` * Provides configuration endpoint for environment-specific settings * Keeps sensitive credentials server-side * Runs on port 8013 by default **Frontend (React + Vite)**: * Fetches auth tokens and configuration from backend * Embeds Corti Assistant using `@corti/embedded-web` Web Component * Manages interaction lifecycle (create, navigate, handle events) * Uses React 19's Suspense for async data loading * Runs on port 8015, with `/api` proxied to the backend **Why this architecture?** * **Security**: Credentials stay on the server, never exposed to the browser * **Separation of concerns**: Auth logic separate from UI logic * **Development speed**: Simple to run and test locally *** ## Key concepts ### Authentication flow 1. **Backend authenticates** with Corti using stored credentials (ROPC) 2. **Frontend requests** tokens from backend `/api/auth` endpoint 3. **Web Component** receives tokens and authenticates the embedded session 4. **No user interaction** required - seamless experience **Why ROPC?** Users are already logged into your application. Forcing them to re-authenticate with Corti via browser popup would be confusing and disruptive. ROPC allows seamless embedded authentication. ### Web Component integration The integration uses ``, a React wrapper around the standard Web Component: ```tsx theme={null} import { CortiEmbeddedReact, useCortiEmbeddedApi, } from "@corti/embedded-web/react"; // In your component: ; ``` The basic React example keeps the embedded Assistant hidden during navigation, then sets `visibility` to `"visible"` after you receive `embedded.navigated` or `interaction.loaded` (interaction ready). For timeout and retry guidance around this pattern, see [Embedded Reliability, Timeouts, and Recovery](/assistant/reliability-timeouts). **Key benefits**: * **Type-safe**: Full TypeScript support with IDE autocomplete * **React-friendly**: Hooks-based API (`useCortiEmbeddedApi`) * **Event handling**: Native React event props for Assistant callbacks * **Lifecycle management**: Automatic cleanup and ref management ### Interaction lifecycle Every clinical encounter in Corti Assistant is called an "interaction": 1. **Create interaction** - Define encounter metadata (type, identifier, timestamps) 2. **Navigate to session** - Load the session view with recording interface 3. **User records** - Clinician documents the encounter 4. **Handle events** - React to document generation, errors, state changes **Why create interactions programmatically?** This allows you to: * Link Corti sessions to your EHR's encounter IDs * Pre-fill encounter metadata from your system * Control when and how sessions start * Track interactions in your database *** ## Implementation guide ### Project structure ```text theme={null} your-react-app/ ├── server.ts # Backend: Auth + config endpoints ├── src/ │ ├── App.tsx # Root: Suspense boundary + data fetching │ ├── components/ │ │ └── EmbeddedAssistant.tsx # Main integration component │ ├── lib/ │ │ └── auth.ts # API calls to backend │ └── types.ts # TypeScript definitions ├── .env # Credentials (never commit!) └── package.json ``` ### Step 1: Backend setup **Purpose**: Securely authenticate with Corti and provide tokens to the frontend. **Key file**: `server.ts` **What it does**: * `/api/auth` - Uses `@corti/sdk` to perform ROPC authentication * `/api/config` - Returns base URL based on environment (EU/US) * `/api/health` - Provides a simple backend health check * CORS configured for the local Vite dev server origin * Backend port defaults to 8013; frontend origin defaults to `http://localhost:8015` **Implementation approach**: ```typescript theme={null} // Uses Corti SDK for authentication const auth = new CortiAuth({ environment: process.env.CORTI_ENVIRONMENT, tenantName: process.env.CORTI_TENANT_NAME, }); // ROPC flow - exchanges username/password for tokens const tokenResponse = await auth.getRopcFlowToken({ clientId: process.env.CORTI_CLIENT_ID, username: process.env.CORTI_USER_EMAIL, password: process.env.CORTI_USER_PASSWORD, }); // Return credentials in the shape expected by api.auth() res.json({ access_token: tokenResponse.accessToken, refresh_token: tokenResponse.refreshToken, id_token: "", token_type: tokenResponse.tokenType || "Bearer", expires_in: tokenResponse.expiresIn, mode: "stateful", }); ``` **See the full implementation**: [server.ts in example repo](https://github.com/corticph/corti-examples/blob/main/embedded-assistant/react/basic-example/server.ts) ### Step 2: Frontend data fetching **Purpose**: Fetch auth tokens and config before rendering the Assistant. **Key file**: `src/lib/auth.ts` **What it does**: * Promise-based API calls to backend * Simple in-memory caching to prevent duplicate requests * Separates data fetching logic from UI components **Why separate?** Keeps components focused on rendering and keeps backend communication in one place. **See the implementation**: [auth.ts in example repo](https://github.com/corticph/corti-examples/blob/main/embedded-assistant/react/basic-example/src/lib/auth.ts) ### Step 3: App root with suspense **Purpose**: Coordinate parallel data fetching and handle loading states. **Key file**: `src/App.tsx` **Key pattern**: React 19's `use()` hook with Suspense for async data ```tsx theme={null} function AppWithData({ configPromise, authPromise }) { const config = use(configPromise); // Suspends until resolved const authData = use(authPromise); // Fetched in parallel if (authData.error) { return ; } return ; } ``` **Why this pattern?** * **Parallel fetching**: Config and auth load simultaneously * **Declarative loading**: Suspense fallback handles loading UI automatically * **Error boundaries**: Easy to add error handling at the boundary level **See the implementation**: [App.tsx in example repo](https://github.com/corticph/corti-examples/blob/main/embedded-assistant/react/basic-example/src/App.tsx) ### Step 4: Embedded Assistant component **Purpose**: Render the Web Component and manage the interaction lifecycle. **Key file**: `src/components/EmbeddedAssistant.tsx` **Core responsibilities**: 1. Render `` with configuration 2. Use `useCortiEmbeddedApi` hook to access API methods 3. Handle `onReady` event to authenticate and configure the embedded app 4. Set interaction-level options before creating the interaction 5. Create the interaction, navigate to the session route, and show the embed after the interaction is ready 6. Manage status display, retry, and error states **Critical pattern - preventing double initialization**: ```tsx theme={null} const hasInitialized = useRef(false); const handleReady = async () => { if (hasInitialized.current) return; // Guard against React StrictMode hasInitialized.current = true; // Safe to authenticate and create interaction }; ``` **Why needed?** React 19's StrictMode intentionally double-invokes effects in development. Without this guard, you'd create two interactions. **Event handling strategy**: * `onReady` - Authenticate, configure app UI, set interaction options, create interaction, and navigate to session * `onEvent` - Receive Assistant events for logging or workflow integration * `onError` - Display errors to user The example also adds lifecycle timeouts and a retry path around the critical events. See [Embedded Reliability, Timeouts, and Recovery](/assistant/reliability-timeouts) for the recommended behavior, and see the full implementation for details. **API sequence**: ```tsx theme={null} await api.auth(authData); await api.configureApp({ ui: { interactionTitle: true, aiChat: true, documentFeedback: true, navigation: true, }, }); await api.setInteractionOptions({ mode: { fallback: "in-person", options: ["in-person", "virtual"], }, documents: { actions: { sync: true, }, }, }); const interaction = await api.createInteraction({ assignedUserId: null, encounter: { identifier: `encounter-${Date.now()}`, status: "planned", type: "first_consultation", period: { startedAt: new Date().toISOString() }, }, }); await api.navigate({ path: `/session/${interaction.id}` }); ``` **See the implementation**: [EmbeddedAssistant.tsx in example repo](https://github.com/corticph/corti-examples/blob/main/embedded-assistant/react/basic-example/src/components/EmbeddedAssistant.tsx) *** ## Running the example Clone and run the complete example: ```bash theme={null} git clone https://github.com/corticph/corti-examples.git cd corti-examples/embedded-assistant/react/basic-example npm install cp .env.example .env # Edit .env with your credentials npm run dev ``` Open [http://localhost:8015](http://localhost:8015) to see the integration in action. The backend runs at [http://localhost:8013](http://localhost:8013), and Vite proxies `/api` requests to that backend. *** ## Customization points ### Interaction creation The example creates an interaction in `EmbeddedAssistant.tsx` after authentication and configuration: ```tsx theme={null} const interaction = await api.createInteraction({ assignedUserId: null, encounter: { identifier: `encounter-${Date.now()}`, status: "planned", type: "first_consultation", period: { startedAt: new Date().toISOString(), }, }, }); ``` ### Assistant configuration The example calls `api.configureApp()` after authentication for app-level UI settings: ```tsx theme={null} await api.configureApp({ ui: { interactionTitle: true, aiChat: true, documentFeedback: true, navigation: true, }, }); ``` The example calls `api.setInteractionOptions()` for interaction-level options: ```tsx theme={null} await api.setInteractionOptions({ mode: { fallback: "in-person", options: ["in-person", "virtual"], }, documents: { actions: { sync: true, }, }, }); ``` See [Configuration Reference](/assistant/configuration) for all options. ### Event and error handling The example wires `onEvent` and `onError` callbacks on ``: ```tsx theme={null} const handleEvent = () => { // Events are logged internally by the component }; const handleError = (event: CustomEvent) => { setStatus({ message: `Error: ${event.detail?.message || "Unknown error"}`, type: "error", }); }; ``` See [Events Reference](/assistant/events/index) for all available events. *** ## Production considerations ### Environment variables Never commit credentials to version control: ```bash theme={null} # Use environment-specific .env files .env.development # Local dev credentials .env.production # Production credentials (injected via CI/CD) # Add to .gitignore echo ".env*" >> .gitignore echo "!.env.example" >> .gitignore ``` ### CORS configuration Update CORS for production domains: ```typescript theme={null} app.use( cors({ origin: [ "https://your-production-domain.com", process.env.CLIENT_ORIGIN || (process.env.NODE_ENV === "development" ? "http://localhost:8015" : null), ].filter(Boolean), credentials: true, }), ); ``` *** ## Next steps Build on the React basic example with the current Embedded API: * **Document export** - Handle document events and save generated content to your host application * **Session management** - Store created interaction IDs and navigate back with `api.navigate({ path: "/session/" })` * **App configuration** - Use `api.configureApp()` for UI, appearance, locale, and network settings * **Interaction options** - Use `api.setInteractionOptions()` for mode, document actions, spoken language, and templates * **Event monitoring** - Use `onEvent` and `onError` to track Assistant activity in your application *** ## Troubleshooting ### CORS errors If you see CORS errors, verify Vite's dev server origin matches the `CLIENT_ORIGIN` used by `server.ts`. The example uses strict port 8015 for Vite and port 8013 for the backend. **Fix**: Set `CLIENT_ORIGIN` to match your frontend URL: ```bash theme={null} CLIENT_ORIGIN=http://localhost:8015 ``` ### Authentication fails * Verify credentials in `.env` match your Corti Console setup * Ensure OAuth client has "Direct Access Grants" enabled for ROPC * Check user account exists and is active in your Corti tenant * Verify `CORTI_ENVIRONMENT` matches your tenant region (eu/us) ### Web Component not loading * Check browser console for import errors * Verify `@corti/embedded-web` package is installed correctly * Ensure `baseURL` matches your region (`https://assistant.eu.corti.app`) * Confirm microphone permissions are granted (required for recording) ### Double initialization in Dev Mode This is expected with React 19 StrictMode. The `hasInitialized` ref guard prevents issues. In production builds, StrictMode is disabled and this won't occur. *** ## Related documentation * [Web Component API Reference](/assistant/web-component-api) * [Authentication Guide](/assistant/authentication) * [Configuration Reference](/assistant/configuration) * [API Reference](/assistant/api-reference) * [Events Reference](/assistant/events/index) * [Reliability Guide](/assistant/reliability-timeouts) * [Example Repository](https://github.com/corticph/corti-examples/tree/main/embedded-assistant) # Choose Your Integration Source: https://docs.corti.ai/assistant/integrations-overview Choose the right integration method for embedding Corti Assistant into your application Corti Assistant is a full-featured clinical AI application that can be embedded directly into your healthcare application. It provides ambient documentation, dictation, clinical decision support, and more—all accessible through a programmable interface. **Most common integration pattern:** C# desktop application → WebView → localhost HTML page → Web Component. This approach is used by most integrators and provides full control from your native application while leveraging web technologies for the UI layer. **Ready to start building?** Check out the [Web Component examples on GitHub](https://github.com/corticph/corti-examples/tree/main/embedded-assistant) to see complete React and vanilla TypeScript implementations. The Web Component is the recommended integration method and provides the best developer experience. **Production reliability:** Add timeout and retry handling for lifecycle events such as `interaction.loaded`. See [Embedded Reliability, Timeouts, and Recovery](/assistant/reliability-timeouts). *** ## Integration architecture When you embed Corti Assistant, you're integrating a complete application into your own. This embedded Assistant communicates with your application through one of three integration methods: ```mermaid theme={null} graph LR A[Your Application] -->|Integration Layer| B[Corti Assistant] B -->|Clinical Data| C[Corti Platform] classDef nodeNeutral fill:#f9f9f9,stroke:#333,stroke-width:2px classDef nodeBlue fill:#e8f4f8,stroke:#0066cc,stroke-width:2px classDef nodeGray fill:#f0f0f0,stroke:#666,stroke-width:2px class A nodeNeutral class B nodeBlue class C nodeGray ``` Your integration layer handles: * **Authentication** - Managing user sessions with OAuth2 tokens * **API Communication** - Creating interactions, generating documents, controlling recording * **Event Handling** - Receiving updates about interactions, documents, and user actions * **UI Embedding** - Displaying the Assistant interface in your application *** ## Three integration methods Corti provides three ways to embed the Assistant, each optimized for different architectural needs: ### Quick decision guide ```mermaid theme={null} graph TD Start[Choose Integration Method] --> Q1{Starting a
new integration?} Q1 -->|Yes| WebComp[✅ Web Component
Modern, recommended approach] Q1 -->|No| Q2{Already using
postMessage?} Q2 -->|Yes| PostMsg[PostMessage API
Continue with existing implementation] Q2 -->|No| Q3{Same origin
scenario?} Q3 -->|Yes| WindowAPI[Window API
Direct API access] Q3 -->|No| WebComp2[✅ Web Component
Cross-origin support] classDef nodeGreen fill:#d4edda,stroke:#28a745,stroke-width:2px classDef nodeYellow fill:#fff3cd,stroke:#ffc107,stroke-width:2px classDef nodeCyan fill:#d1ecf1,stroke:#17a2b8,stroke-width:2px class WebComp,WebComp2 nodeGreen class PostMsg nodeYellow class WindowAPI nodeCyan ``` ### Comparison table Choose your integration method based on your architectural requirements: | | Web Component | PostMessage | Window API | | :----------------------- | :------------------------------------: | :----------------------------------: | :--------------------------------: | | **Recommended For** | Native apps (C#) + WebView, web apps | Existing postMessage implementations | Same-origin / direct URL embedding | | **Native App Support** | ✅ Yes (C#, .NET, Electron via WebView) | ✅ Yes (via WebView) | ⚠️ Same-origin only | | **Framework Support** | Universal (React, Vue, Angular...) | Universal | Universal (same-origin only) | | **Cross-Origin Support** | ✅ Yes | ✅ Yes | ❌ No (same-origin only) | | **Type Safety** | Full TypeScript | Manual typing required | Full TypeScript | | **API Style** | Promise-based methods | postMessage request/response | Promise-based methods | | **Event Handling** | Native addEventListener | Manual postMessage parsing | Native addEventListener | | **Ease of Use** | 5/5 | 3/5 | 4/5 | | **React Support** | Built-in hooks and components | Manual implementation | Manual implementation | | **Setup Complexity** | Install NPM package | Create iframe + message handlers | Load script + direct API access | | **iframe Management** | Automatic | Manual | Manual (if using iframe) | *** ### Web Component (recommended) ```mermaid theme={null} graph TB subgraph "Path 1: Web Applications" WA[React / Vue / Angular
or any web framework] end subgraph "Path 2: Native Applications" NA[C# / .NET / Electron
Desktop Application] NW[WebView Control
loads HTML page] NA -->|hosts| NW end subgraph "Integration Layer" B["<corti-embedded>
Web Component"] C[Event Listeners] end subgraph "Corti Assistant" D[Assistant Interface] E[Corti Platform APIs] end WA -->|import and render| B NW -->|import and render| B B <-->|cross-origin iframe| D D <--> E B -->|events| C classDef nodeBlue fill:#e8f4f8,stroke:#0066cc,stroke-width:2px classDef nodePurple fill:#e8d4f8,stroke:#8b5cf6,stroke-width:2px classDef nodeLightPurple fill:#f3e8ff,stroke:#8b5cf6,stroke-width:1px classDef nodeGreen fill:#d4edda,stroke:#28a745,stroke-width:2px class WA,D nodeBlue class NA nodePurple class NW nodeLightPurple class B nodeGreen ``` A modern, framework-agnostic custom HTML element that wraps the Assistant in a cross-origin iframe. The Web Component handles all postMessage complexity internally and provides a clean TypeScript API. **Works for both:** * **Web applications** - React, Vue, Angular, or any framework (including server-rendered pages) * **Native applications** - C#, .NET, Electron with WebView controls loading a local HTML page **Best for:** * Native desktop applications (C#, .NET, Electron) with WebView controls * Web applications using React, Vue, Angular, or any modern framework * Mobile applications with WebView components * New integrations starting from scratch * Teams that want TypeScript support and native event handling * Cross-origin scenarios (different domains) **Architecture highlights:** * Standard Web Component that works everywhere * Built-in React hooks and components for optimal DX * Automatic iframe communication handling * Promise-based API with TypeScript definitions Learn how to integrate using the Web Component API *** ### PostMessage API ```mermaid theme={null} graph TB subgraph "Your Application" A[Parent Window/Frame] B[Manual iframe Management] C[Custom postMessage Handler] end subgraph "Corti Assistant" D[Assistant Interface] E[Corti Platform APIs] end A -->|Create and Mount| B B <-->|postMessage Protocol| D D <--> E D -->|postMessage Events| C classDef nodeYellow fill:#fff3cd,stroke:#ffc107,stroke-width:2px classDef nodeBlue fill:#e8f4f8,stroke:#0066cc,stroke-width:2px class B nodeYellow class D nodeBlue ``` Direct iframe integration where you manually manage the iframe element and communicate via browser's postMessage API. This is the low-level protocol that the Web Component abstracts. **Best for:** * Existing implementations already using postMessage * Teams with specific iframe management requirements * Legacy applications where adding new dependencies is difficult **Architecture highlights:** * Full control over iframe lifecycle and positioning * Direct access to postMessage protocol * Works cross-origin (different domains) * Requires manual event parsing and error handling Learn how to integrate using the PostMessage API *** ### Window API ```mermaid theme={null} graph TB subgraph "Your Application (same origin)" A[Parent Window] B[Direct Window Reference] end subgraph "Corti Assistant (same origin)" D[Assistant Interface] E[window.CortiEmbedded] F[Corti Platform APIs] end A -->|window.open or iframe| B B -->|Direct API Access| E E <--> D D <--> F classDef nodeCyan fill:#d1ecf1,stroke:#17a2b8,stroke-width:2px classDef nodeBlue fill:#e8f4f8,stroke:#0066cc,stroke-width:2px class B nodeCyan class E nodeBlue ``` Same-origin integration where your application and Corti Assistant run on the same domain, allowing direct JavaScript API access through the global `window.CortiEmbedded` object. **Best for:** * Same-origin scenarios (both apps on same domain) * Direct URL embedding in webviews or popup windows * Mobile webview integrations where same-origin is guaranteed * Scenarios requiring the simplest possible integration **Architecture highlights:** * No iframe or postMessage complexity * Direct function calls to JavaScript API * Only works when same-origin policy is satisfied * Promise-based API with full TypeScript support Learn how to integrate using the Window API *** ## Common integration flow Regardless of which method you choose, the integration follows the same high-level flow: Add the Corti Assistant interface to your application using your chosen integration method (Web Component, iframe, or window reference). Implement OAuth2 authentication to obtain user tokens. All methods require user-based authentication with OAuth2 tokens (access, refresh, and ID tokens). Learn how to set up OAuth2 authentication for your users Pass authentication tokens to the Assistant and configure the interface (features, appearance, locale). Use the API to create interactions that represent clinical encounters and manage their lifecycle. Listen for events from the Assistant (document generated, recording started, errors, etc.) and update your application UI accordingly. *** ## Authentication requirements All three integration methods require OAuth2 user authentication. See the [Authentication Guide](/assistant/authentication) for complete setup instructions. All Embedded Assistant integrations require user-based OAuth2 authentication with `access_token`, `refresh_token`, and `id_token`. Client credentials and machine-to-machine flows are not supported. *** ## API capabilities Once embedded and authenticated, all three integration methods provide access to the same comprehensive API: * Authenticate users with OAuth2 tokens * Refresh authentication tokens * Configure interface features and appearance * Set language and locale preferences * Create new clinical interactions * Manage interaction lifecycle * Navigate between sessions * Start and stop audio recording * Control recording state * Monitor recording status * Handle recording events * Add clinical facts to interactions * Generate documents from captured data * Retrieve available templates * Configure document generation behavior * Listen for interaction events * Monitor document generation status * Track recording state changes * Handle errors and warnings Full documentation of all API methods, parameters, and return types *** ## Next steps Modern, recommended approach with framework support Direct iframe control with manual postMessage handling Same-origin integration with direct API access Complete TypeScript and React examples on GitHub # Introduction to Embedded Assistant API Source: https://docs.corti.ai/assistant/introduction Access an API for embedding Corti Assistant in your workflow The Corti Embedded Assistant API enables seamless integration of Corti Assistant into host applications, such as Electronic Health Record (EHR) systems, web-based clinical portals, or native applications using embedded WebViews. The implementation provides a robust, consistent, and secure interface for parent applications to control and interact with embedded Corti Assistant. The details outlined below are for you to embed the Corti Assistant "AI scribe solution" natively within your application. To learn more about the full Corti API, please see more [here](/api-reference/welcome) *** ## Quick start guides Get started quickly with our platform-specific integration guides: Complete guide for React applications with the Web Component API **C#/.NET Desktop Guide Coming Soon** A complete integration guide for WPF, WinForms, and .NET MAUI applications using WebView2 is coming in Q1 2026. In the meantime, the [Web Component API](/assistant/web-component-api) works with any WebView-based application. *** ## Overview The Embedded Assistant API is a communication interface that allows your application to embed and control Corti Assistant within your own application interface. It provides programmatic control over authentication, session management, interaction creation, document generation, and more. The API enables two-way communication between your application and the embedded Corti Assistant, allowing you to: * Authenticate users and manage sessions * Create and manage clinical interactions * Configure the Assistant interface and appearance * Control recording functionality * Receive real-time events and updates * Access generated documents and transcripts *** ## Requirements Before getting started, ensure you have: * **Created an OAuth Client for Corti Assistant**: You'll need to [create a Corti Assistant specific client](mailto:help@corti.aien/articles/11400088-creating-an-api-client#h_239039d8fe) from the [Developer Console](https://console.corti.app). * **Modern browser or WebView**: For web applications, use a modern browser (Chrome, Firefox, Safari, or Edge). For native apps, use a modern WebView (WebView2, WKWebView, or Chromium-based WebView) * **HTTPS**: The embedded Assistant must be loaded over HTTPS (required for microphone access) * **Microphone permissions**: Your application must request and handle microphone permissions appropriately * **OAuth2 client**: You'll need an OAuth2 client configured for user-based authentication ## Available regions * **EU**: [https://assistant.eu.corti.app](https://assistant.eu.corti.app) * **EU MD**: [https://assistantmd.eu.corti.app](https://assistantmd.eu.corti.app) (medical device compliant) * **US**: [https://assistant.us.corti.app](https://assistant.us.corti.app) ## Integration methods The Embedded Assistant API offers three integration methods. For most use cases, we recommend the **Web Component API** for its simplicity and broad compatibility. For detailed comparison and guidance, see the [Integration Method Comparison Guide](/assistant/integrations-overview). ### Web Component API (recommended) Works for all scenarios: iframe, WebView, same-origin, and cross-origin. Framework-agnostic with built-in React support and full TypeScript definitions. [**Web Component API Documentation**](/assistant/web-component-api) | [**Full Examples**](https://github.com/corticph/corti-examples/tree/main/embedded-assistant) ### Alternative methods * [**PostMessage API**](/assistant/postmessage-api) - Lower-level iframe communication (not recommended for new integrations) * [**Window API**](/assistant/window-api) - Same-origin direct access for specific use cases ## Documentation * [**Web Component API**](/assistant/web-component-api) (Recommended) - Complete guide with vanilla TypeScript and React examples * [**PostMessage API**](/assistant/postmessage-api) - Lower-level API for specific use cases * [**Window API**](/assistant/window-api) - Same-origin direct API access * [**Integration Method Comparison**](/assistant/integrations-overview) - Detailed comparison and selection guide * [**API Reference**](/assistant/api-reference) - Complete reference for all methods, events, and integration patterns * [**Embedded Reliability**](/assistant/reliability-timeouts) - Timeout and retry guidance for critical lifecycle events * [**OAuth Authentication**](/assistant/authentication) - Guide for implementing OAuth2 authentication flows * [**Examples Repository**](https://github.com/corticph/corti-examples) - Full working examples ## Next steps 1. Follow a [platform-specific guide](#quick-start-guides) to get started quickly 2. Review the [OAuth Authentication Guide](/assistant/authentication) to set up user authentication 3. Choose your [integration method](/assistant/integrations-overview) based on your use case 4. Add [timeout and retry handling](/assistant/reliability-timeouts) for lifecycle events such as `interaction.loaded` 5. Consult the [API Reference](/assistant/api-reference) for all available methods and events Please [contact us](mailto:help@corti.ai) for help or questions. # PostMessage API Source: https://docs.corti.ai/assistant/postmessage-api Use the PostMessage API to integrate Corti Assistant via iframe or WebView The PostMessage API enables secure cross-origin communication between your application and the embedded Corti Assistant. This method is suitable for iframe or WebView integrations requiring fine-grained control over the iframe lifecycle. **Web Component API is recommended.** The [Web Component API](/assistant/web-component-api) provides the same functionality with a simpler interface. PostMessage API remains fully supported but involves unnecessary complexity. It is not deprecated. **Web Component has full working examples.** Complete, runnable examples are available for the [Web Component integration method](https://github.com/corticph/corti-examples/tree/main/embedded-assistant), which is the recommended approach for most integrations. PostMessage API examples demonstrating the lower-level protocol will be added to the repository in the future. ## Overview The PostMessage API uses the browser's `postMessage` mechanism to enable secure communication between your application and the embedded Corti Assistant, even when they're served from different origins. This makes it ideal for embedding Corti Assistant in iframes or WebViews. ## Requirements Before getting started, ensure you have: * **Access to Corti Assistant**: You'll need credentials and access to one of the available regions * **HTTPS**: The embedded Assistant must be loaded over HTTPS (required for microphone access) * **Microphone permissions**: Your application must request and handle microphone permissions appropriately * **OAuth2 client**: You'll need an OAuth2 client configured for user-based authentication * **Modern browser or WebView**: For web applications, use a modern browser. For native apps, use a modern WebView ## Recommendations * **Validate message origins** to ensure security * **Use specific target origins** instead of `'*'` when possible * **Implement proper error handling** for all API calls * **Handle authentication token refresh** to maintain user sessions * **Request microphone permissions** before initializing the embedded Assistant ## Available regions * **US**: [https://assistant.us.corti.app](https://assistant.us.corti.app) * **EU**: [https://assistant.eu.corti.app](https://assistant.eu.corti.app) * **EU MD**: [https://assistantmd.eu.corti.app](https://assistantmd.eu.corti.app) (medical device compliant) ## Features * Secure cross-origin communication * Works with any iframe or WebView implementation * Fully asynchronous with request/response pattern ## Quick start ### Step 1: Set up authentication Before using the PostMessage API, authenticate your users using OAuth2. See the [Authentication Guide](/assistant/authentication) for complete setup instructions including Authorization Code Flow with PKCE (recommended), obtaining tokens, and handling token refresh. All Embedded Assistant integrations require user-based OAuth2 authentication. Client credentials and machine-to-machine flows are not supported. * Handle token refresh to maintain sessions * Never expose client secrets in client-side code ### Step 2: Load the Embedded Assistant Load the Corti Assistant in an iframe or WebView: #### Required iframe `allow` permissions When you embed Corti Assistant in an iframe, you must delegate the browser permissions that the embedded app needs. * `microphone` is required for recording and dictation * `display-capture` is required if you use virtual recording to capture audio from another tab, window, or screen * `clipboard-write` is recommended so users can copy generated content reliably across browsers Use the `allow` attribute on the iframe: ```html theme={null} ``` These examples omit `*` intentionally. In an iframe `allow` attribute, each feature defaults to the iframe `src` origin, which is the safer default for Corti Assistant embeds. Use `feature *` only if you explicitly want to grant that permission to any origin the iframe might later navigate to. If you omit `allow="microphone"` on the iframe, the embedded Assistant cannot access the user's microphone even if the site itself has microphone permission. If you support virtual mode, include `display-capture` so the embedded Assistant can request browser-managed capture of remote audio streams. ```html European Region expandable theme={null} ``` ```html European Region (Medical Device) expandable theme={null} ``` ```html Americas Region expandable theme={null} ``` ## Message format All messages sent to the embedded app follow this structure: ```typescript theme={null} { type: 'CORTI_EMBEDDED', version: 'v1', action: string, requestId?: string, payload?: object } ``` ### Message properties * `type`: Always `'CORTI_EMBEDDED'` * `version`: API version (currently `'v1'`) * `action`: The method to invoke (see [API Reference](/assistant/api-reference) for all methods) * `requestId`: Optional unique identifier for tracking responses * `payload`: Optional data specific to the action ### Same API as Web Component The PostMessage API provides access to the same methods documented in the [API Reference](/assistant/api-reference), including [auth()](/assistant/api/auth), [configureApp()](/assistant/api/configure-app), [setInteractionOptions()](/assistant/api/set-interaction-options), and [createInteraction()](/assistant/api/create-interaction), with identical parameters and behavior. **Key difference:** Instead of direct method calls that return Promises, you communicate through messages: 1. **Send a message** with `action` field matching the method name 2. **Receive the response** asynchronously via a separate `CORTI_EMBEDDED_RESPONSE` message Example for `auth`: ```javascript theme={null} const assistantOrigin = new URL(iframe.src).origin; // Web Component: Direct call with Promise const user = await api.auth({ access_token, refresh_token, id_token, token_type, }); // PostMessage: Send message, listen for response iframe.contentWindow.postMessage( { type: "CORTI_EMBEDDED", version: "v1", action: "auth", // Method name goes here requestId: "auth-1", payload: { access_token, refresh_token, id_token, token_type }, }, assistantOrigin, ); // Later, in your message listener: window.addEventListener("message", (event) => { if (event.origin !== assistantOrigin) { return; } if ( event.data.type === "CORTI_EMBEDDED_RESPONSE" && event.data.requestId === "auth-1" ) { const user = event.data.payload; // Same result as Web Component } }); ``` **Same functionality, different invocation pattern.** ## Response handling Responses from the embedded app are sent via `postMessage` and can be identified by checking the message type: ```javascript theme={null} window.addEventListener("message", (event) => { // Handle responses if (event.data?.type === "CORTI_EMBEDDED_RESPONSE") { const { requestId, success, payload, error } = event.data; // Handle response } // Handle events if (event.data?.type === "CORTI_EMBEDDED_EVENT") { const { event: eventType, payload } = event.data; // Handle event } }); ``` ## Events Corti Assistant dispatches events to notify your application of state changes and important updates. When using the PostMessage API, these events are wrapped in the `CORTI_EMBEDDED_EVENT` message type. ### Event format translation Core events documented in the [Events Reference](/assistant/events) are wrapped for postMessage delivery: **Core Event Structure:** ```json theme={null} { "event": "recording.started", "confidential": false, "payload": { "mode": "dictation", "language": "en", "interactionId": "int_123" } } ``` **PostMessage wrapper:** ```json theme={null} { "type": "CORTI_EMBEDDED_EVENT", "event": "recording.started", "confidential": false, "payload": { "mode": "dictation", "language": "en", "interactionId": "int_123" } } ``` ### Listening for events Set up a message listener to receive events from the embedded Assistant: ```javascript Listening for Events expandable theme={null} const ALLOWED_ORIGINS = [ "https://assistant.eu.corti.app", "https://assistantmd.eu.corti.app", "https://assistant.us.corti.app", ]; window.addEventListener("message", (event) => { // Validate origin for security if (!ALLOWED_ORIGINS.includes(event.origin)) { return; } // Check for Corti events if (event.data?.type === "CORTI_EMBEDDED_EVENT") { const { event: eventName, confidential, payload } = event.data; // Handle different event types switch (eventName) { case "recording.started": console.log("Recording started:", payload); break; case "recording.paused": console.log("Recording paused:", payload); break; case "document.generated": console.log("Document generated:", payload); handleDocumentGenerated(payload); break; case "error.triggered": console.error("Error occurred:", payload); break; default: console.log("Unknown event:", eventName, payload); } } }); function handleDocumentGenerated(payload) { const { documentId, documentName, interactionId } = payload; // Update your UI, sync to backend, etc. } ``` ### Available events For a complete list of events and their payload structures, see the [Events Overview](/assistant/events). Common events include: * `recording.started` - Recording has started * `recording.paused` - Recording has paused * `document.generated` - Document has been generated * `document.updated` - Document has been edited * `document.synced` - Document synced to external system * `error.triggered` - An error occurred ### Legacy events The embedded Assistant also dispatches [legacy events](/assistant/events/legacy-events) using camelCase names (e.g., `recordingStarted`, `documentGenerated`). These are deprecated and will be removed in a future version. ## Error handling Always handle errors when making requests: ```javascript Error Handling expandable theme={null} try { const result = await sendMessage("auth", { accessToken: "your-access-token", refreshToken: "your-refresh-token", id_token: "your-id-token", token_type: "Bearer", }); console.log("Authentication successful:", result); } catch (error) { console.error("Authentication failed:", error.message); // Handle authentication failure } ``` ## Security considerations When using `postMessage`, always: 1. **Validate message origin**: Check `event.origin` to ensure messages come from trusted sources 2. **Use specific target origins**: Replace `'*'` with the specific origin when possible 3. **Sanitize data**: Never trust data from postMessage without validation ```javascript Security Best Practices expandable theme={null} const ALLOWED_ORIGINS = [ "https://assistant.eu.corti.app", "https://assistantmd.eu.corti.app", "https://assistant.us.corti.app", ]; window.addEventListener("message", (event) => { // Validate origin if (!ALLOWED_ORIGINS.includes(event.origin)) { console.warn("Message from untrusted origin:", event.origin); return; } // Process message if (event.data?.type === "CORTI_EMBEDDED_EVENT") { // Handle event } }); // Send messages with specific origin iframe.contentWindow.postMessage( { type: "CORTI_EMBEDDED", version: "v1", action: "auth", payload: { /* ... */ }, }, "https://assistant.eu.corti.app", ); // Specific origin instead of '*' ``` ## Next steps * Review the [OAuth Authentication Guide](/assistant/authentication) to set up user authentication * See the [API Reference](/assistant/api-reference) for all available methods and their parameters * Learn about [events](/assistant/events) that the embedded app can send * Check out the [Window API](/assistant/window-api) for same-origin integrations Please [contact us](mailto:help@corti.ai) for help or questions. # Release Notes Source: https://docs.corti.ai/assistant/release-notes Updates and improvements to Corti Assistant and Embedded API integrations Corti Assistant release notes moved to [Release Notes](/release-notes/corti-assistant). # Embedded Reliability, Timeouts, and Recovery Source: https://docs.corti.ai/assistant/reliability-timeouts Recommended timeout and retry patterns for Embedded Assistant lifecycle events. Use this guidance to keep the host application responsive when an embedded lifecycle event does not arrive. The most important event to guard is [`interaction.loaded`](/assistant/events/generated/interaction/loaded), which confirms that the requested interaction is ready to display after navigation. Load the Assistant with `visibility="hidden"`, navigate to the interaction, and show the component after you receive [`embedded.navigated`](/assistant/events/generated/embedded-api/navigated) or [`interaction.loaded`](/assistant/events/generated/interaction/loaded). This prevents users from seeing an incomplete embedded state. ## Recommended timeout Start a timeout whenever your integration initiates navigation that depends on `interaction.loaded`. Use a default timeout between 10 and 30 seconds. Corti recommends starting with 20 seconds, then tuning based on your own network and WebView conditions. When the timeout expires: 1. Stop waiting for the stale event listener. 2. Show a user-visible recovery state. 3. Provide a retry action that reinitializes the embed, remounts the Web Component, or reloads the iframe. 4. Log the timeout in your own monitoring so your team can separate integration, network, and service issues. Do not leave users on an indefinite spinner. A clear recovery state is safer than a silent loading screen. ## User-facing recovery copy Use short copy that explains the state without exposing implementation details: ```text theme={null} Assistant is taking longer than expected to load. Check your connection, then try again. ``` Use an action label such as `Retry` or `Reload Assistant`. When the user retries, create a fresh embedded instance or reload the iframe before repeating authentication, configuration, interaction creation, and navigation. ## Minimal implementation The following example starts a timer before navigation, clears it when `interaction.loaded` arrives, and exposes a retry path that remounts the Web Component. ```ts title="TypeScript" theme={null} const INTERACTION_LOADED_TIMEOUT_MS = 20000; type EmbedState = "loading" | "ready" | "recoverable-error"; let interactionLoadedTimer: ReturnType | undefined; function setEmbedState(state: EmbedState) { // Render your own loading, ready, or retry UI here. console.log("Embedded Assistant state:", state); } function clearInteractionLoadedTimer() { if (interactionLoadedTimer) clearTimeout(interactionLoadedTimer); interactionLoadedTimer = undefined; } function waitForInteractionLoaded( corti: HTMLElement, startEmbed: (corti: HTMLElement) => Promise, ) { clearInteractionLoadedTimer(); setEmbedState("loading"); const handleLoaded = () => { clearInteractionLoadedTimer(); setEmbedState("ready"); }; corti.addEventListener("interaction.loaded", handleLoaded, { once: true }); interactionLoadedTimer = setTimeout(() => { corti.removeEventListener("interaction.loaded", handleLoaded); setEmbedState("recoverable-error"); }, INTERACTION_LOADED_TIMEOUT_MS); return startEmbed(corti); } async function retryEmbed( corti: HTMLElement, startEmbed: (corti: HTMLElement) => Promise, ) { clearInteractionLoadedTimer(); const freshCorti = corti.cloneNode(false) as HTMLElement; corti.replaceWith(freshCorti); await waitForInteractionLoaded(freshCorti, startEmbed); } ``` ## What to monitor Track these signals in your host application: * Timeout count and timeout rate for `interaction.loaded` * Browser or WebView family and version * Assistant region and `baseURL` * Whether the retry succeeds * The last Embedded API method called before the timeout Do not log OAuth tokens, request bodies, transcripts, generated documents, or other sensitive clinical data. ## Related pages Use the recommended integration method for new embedded deployments. Review lifecycle events emitted by the Embedded Assistant. # Web Component API Source: https://docs.corti.ai/assistant/web-component-api Integrate Corti Assistant using the modern Web Component approach The Web Component API is the **recommended approach** for embedding Corti Assistant into your application. It provides a modern, framework-agnostic integration method using standard Web Components that work with any framework. This approach works for **both native applications with WebView controls** (C#, .NET, Electron) and **web applications** built with any modern framework. We provide dedicated React wrappers for enhanced developer experience, while other frameworks (Vue, Angular, Svelte, etc.) can use the raw HTML element directly or with small framework-specific wrappers. **Want to skip to code?** Full working examples are available in the [Corti Examples Repository](https://github.com/corticph/corti-examples/tree/main/embedded-assistant) for both [vanilla TypeScript](https://github.com/corticph/corti-examples/tree/main/embedded-assistant/vanilla-ts/basic-example) and [React](https://github.com/corticph/corti-examples/tree/main/embedded-assistant/react/basic-example). *** ## Installation Install the Corti Embedded Web package: ```bash npm theme={null} npm install @corti/embedded-web ``` ```bash yarn theme={null} yarn add @corti/embedded-web ``` ```bash pnpm theme={null} pnpm add @corti/embedded-web ``` ## Authentication Before using the Web Component API, authenticate your users using OAuth2. See the [Authentication Guide](/assistant/authentication) for complete setup instructions including Authorization Code Flow with PKCE (recommended), obtaining tokens, and handling token refresh. All Embedded Assistant integrations require user-based OAuth2 authentication. Client credentials and machine-to-machine flows are not supported. *** ## Usage The Web Component works with any modern framework (React, Vue, Angular, Svelte, etc.) since it's based on standard Web Components. We provide dedicated React wrappers for enhanced developer experience, while other frameworks can use the raw HTML element with small framework-specific wrappers if needed. ### Vanilla TypeScript/JavaScript This approach works universally - use it directly or wrap it in framework-specific components for Vue, Angular, or other frameworks. **Full working example**: See the complete [vanilla TypeScript example](https://github.com/corticph/corti-examples/tree/main/embedded-assistant/vanilla-ts/basic-example) with authentication, interaction creation, and event handling. Import the web component and use it as a custom HTML element: ```typescript theme={null} import "@corti/embedded-web"; ``` Add the custom element to your HTML: ```html theme={null} ``` Access the API via the element reference (which should be automatically typed if retrieved via `querySelector`): ```typescript theme={null} async function initializeAssistant() { const corti = document.querySelector("corti-embedded"); // Wait for the custom element to be defined await customElements.whenDefined("corti-embedded"); // Wait for the embedded assistant to be ready await new Promise((resolve) => { corti.addEventListener("embedded.ready", () => resolve(), { once: true }); }); // Authenticate await corti.auth({ access_token: "your-access-token", refresh_token: "your-refresh-token", id_token: "your-id-token", token_type: "Bearer", }); // Create interaction const interaction = await corti.createInteraction({ assignedUserId: null, encounter: { identifier: `encounter-${Date.now()}`, status: "planned", type: "first_consultation", period: { startedAt: new Date().toISOString() }, }, }); // Navigate to session (object form supported in @corti/embedded-web@0.3.0+) await corti.navigate({ path: `/session/${interaction.id}` }); // Handle errors corti.addEventListener("error", (event: CustomEvent) => { console.error("Error:", event.detail?.message); }); // Optional: Listen to all events for debugging and monitoring corti.addEventListener( "event", (event: CustomEvent<{ name: string; payload: unknown }>) => { console.log("Event:", event.detail.name, event.detail.payload); }, ); } // Initialize when the DOM is ready initializeAssistant().catch(console.error); ``` ### React (built-in support) For React, we provide dedicated components and hooks for the best developer experience. For other frameworks like Vue or Angular, use the vanilla approach above and create small framework-specific wrappers as needed. **Full working example**: See the complete [React example](https://github.com/corticph/corti-examples/tree/main/embedded-assistant/react/basic-example) with authentication, interaction creation, and event handling. In React, use the `baseURL` prop. In plain HTML, use the `baseurl` attribute. Both map to the same underlying component property. Import the React component and hook: ```typescript theme={null} import { CortiEmbeddedReact, type CortiEmbeddedReactRef, useCortiEmbeddedApi, } from "@corti/embedded-web/react"; ``` Use the component in your React application: ```tsx theme={null} import { useRef } from "react"; import { CortiEmbeddedReact, type CortiEmbeddedReactRef, useCortiEmbeddedApi, } from "@corti/embedded-web/react"; function EmbeddedAssistant({ baseURL, authData }) { const cortiRef = useRef(null); const api = useCortiEmbeddedApi(cortiRef); const hasInitialized = useRef(false); const handleReady = async () => { // Guard against React StrictMode double-invocation if (hasInitialized.current) return; hasInitialized.current = true; try { // Authenticate await api.auth(authData); // Create interaction const interaction = await api.createInteraction({ assignedUserId: null, encounter: { identifier: `encounter-${Date.now()}`, status: "planned", type: "first_consultation", period: { startedAt: new Date().toISOString() }, }, }); // Navigate to session (object form supported in @corti/embedded-web@0.3.0+) await api.navigate({ path: `/session/${interaction.id}` }); } catch (error) { console.error("Error:", error); } }; const handleError = (event: CustomEvent) => { console.error("Error:", event.detail?.message); }; const handleEvent = (event: CustomEvent) => { console.log("Event:", event.detail.name, event.detail.payload); }; return ( ); } ``` *** ## Component properties ### `baseURL` (required) The base URL of the Corti Assistant instance. Choose the appropriate region: * **EU**: `https://assistant.eu.corti.app` * **EU MD**: `https://assistantmd.eu.corti.app` (medical device compliant) * **US**: `https://assistant.us.corti.app` ### `visibility` (optional) Controls the initial visibility of the embedded assistant: * `"visible"` (default): Assistant is visible on mount * `"hidden"`: Assistant is hidden on mount Load the assistant with `visibility="hidden"` and listen for the [`embedded.navigated`](/assistant/events/generated/embedded-api/navigated) event before showing it. This event confirms the interaction is ready to display, preventing users from seeing a loading or incomplete state. For production integrations, combine hidden loading with a timeout and retry flow for [`interaction.loaded`](/assistant/events/generated/interaction/loaded). See [Embedded Reliability, Timeouts, and Recovery](/assistant/reliability-timeouts). *** ## API methods All API methods are available through the element reference (vanilla) or via the `useCortiEmbeddedApi` hook (React). **Shared API surface** The Web Component provides the same operations documented in the [API Reference](/assistant/api-reference). Web Component, Window API, and PostMessage expose the same capabilities, but their invocation shapes differ. This page focuses on the package API exposed by the Web Component and React wrapper. For Window API and PostMessage-specific call shapes, see the [Window API](/assistant/window-api) and [PostMessage API](/assistant/postmessage-api) pages. Use the [API Reference](/assistant/api-reference) for shared method semantics and payload details. ### Core methods * **`auth(credentials)`** - Authenticate the user session * **`configureApp(config)`** - Configure app-level UI, appearance, locale, and network settings * **`createInteraction(interaction)`** - Create a new clinical interaction * **`navigate(pathOrPayload)`** - Navigate to a specific route * **`startRecording()`** - Start audio recording * **`stopRecording()`** - Stop audio recording * **`getStatus()`** - Get current session status * **`setInteractionOptions(config)`** - Configure interaction-level defaults and options * **`getTemplates()`** - Retrieve available document templates * **`addFacts(factsArray)`** - Add clinical facts to the current interaction * **`setCredentials(credentials)`** - Update authentication credentials * **`showDeviceLinkQR(tokenResponse)`** - Display the QR pairing flow for the Corti mobile companion app `navigate()` compatibility in Web Component / React: * Versions earlier than `@corti/embedded-web@0.3.0`: pass a path string, for example `navigate("/session/")`. * `@corti/embedded-web@0.3.0` and later: both `navigate("/session/")` and `navigate({ path: "/session/" })` are accepted. For consistency with Window API and PostMessage, object form is recommended when you use `@corti/embedded-web@0.3.0` or later. ### Deprecated methods * **`configure(config)` (Deprecated)** - Configure interface features and appearance using the legacy configuration structure * **`configureSession(config)` (Deprecated)** - Configure session-specific settings using the legacy configuration structure See the [API Reference](/assistant/api-reference) for detailed documentation of each method. *** ## Events The Web Component emits events for all Assistant activities. Listen for events using standard event listeners. ### Ready event Emitted when the Assistant is fully loaded and ready to receive API calls: ```typescript theme={null} corti.addEventListener("embedded.ready", (event) => { console.log("Assistant is ready", event.detail); }); ``` ### Other named events You can listen for all other named events directly by listening for them as above. For example, the `recording.started` event (as detailed [here](/assistant/events/generated/recording/started)): ```typescript theme={null} corti.addEventListener("recording.started", (event) => { console.log("Recording has started", event.detail); }); ``` For a complete list of events and their payloads, see the [Events Reference](/assistant/events/index). ### Error event Emitted when an API call fails or an error occurs: ```typescript theme={null} corti.addEventListener("error", (event: CustomEvent) => { console.error("Error:", event.detail); // event.detail contains: { message: string, code?: string, details?: unknown } }); ``` ### Generic event You can also listen for all events, regardless of type, by simply listening for `"event"` as per below: ```typescript theme={null} corti.addEventListener( "event", (event: CustomEvent<{ name: string; payload: unknown }>) => { console.log("Event name:", event.detail.name); console.log("Event payload:", event.detail.payload); }, ); ``` This is useful for debugging and logging purposes, but be aware that there are *many* events with lots of data, so be mindful about putting this into production. *** ## End-user authentication Before using most API methods, you must authenticate with valid OAuth2 tokens. The Web Component expects tokens from an OAuth2 authentication flow. See the [Authentication Guide](/assistant/authentication) for detailed information on obtaining tokens. ```typescript theme={null} await api.auth({ access_token: "your-access-token", refresh_token: "your-refresh-token", id_token: "your-id-token", token_type: "Bearer", }); ``` *** ## Configuration Configure the Assistant interface after authentication: Use `configureApp()` for app-level settings and `setInteractionOptions()` for interaction-level defaults. ```typescript theme={null} await api.configureApp({ ui: { interactionTitle: true, aiChat: true, documentFeedback: false, navigation: true, }, appearance: { primaryColor: "#00a6ff", }, locale: { interfaceLanguage: "en", dictationLanguage: "en", }, }); await api.setInteractionOptions({ mode: { options: ["in-person", "virtual"], }, templates: { sources: { personal: { enabled: true, }, }, }, documents: { actions: { sync: false, }, }, }); ``` See [Configuration Scenarios](/assistant/configuration-scenarios) for practical examples and [Supported Values](/assistant/configuration-values) for the current configuration options. *** ## Full working examples Complete, runnable examples are available in the [Corti Examples Repository](https://github.com/corticph/corti-examples): Demonstrates `` web component usage with vanilla TypeScript, including authentication, interaction creation, navigation, and event handling. Demonstrates `CortiEmbeddedReact` component and `useCortiEmbeddedApi` hook usage with React, including authentication, interaction creation, navigation, and event handling. Add a timeout and retry path for critical lifecycle events such as `interaction.loaded`. See [Embedded Reliability, Timeouts, and Recovery](/assistant/reliability-timeouts) for a copyable pattern. *** ## TypeScript support The package includes full TypeScript definitions: ```typescript theme={null} import type { CortiEmbeddedAPI, CreateInteractionPayload, ConfigureAppPayload, KeycloakTokenResponse, } from "@corti/embedded-web"; import type { CortiEmbeddedReactRef } from "@corti/embedded-web/react"; ``` *** ## Next steps Compare all three integration methods and choose the right one for your architecture Set up OAuth2 authentication for your users Complete API documentation for all methods Customize the Assistant interface and behavior # Overview of the Corti Embedded Assistant Source: https://docs.corti.ai/assistant/welcome Embed a first-class ambient scribing experience into your healthcare application in minutes. ## Build ambient scribing directly into your application Corti Assistant is an embeddable ambient scribing application for software teams building EHRs and healthcare platforms. It gives you a production-ready, healthcare-grade assistant that listens to clinical conversations, structures medical information, and generates EHR-ready documentation. You embed it directly into your product so it feels near-native to clinicians, not like a separate tool they have to manage. This documentation is for developers who want to ship ambient scribing quickly, without building speech recognition, clinical NLP, document generation, and compliance infrastructure from scratch. Corti Assistant can run as a standalone app, but it is designed first and foremost to be embedded into existing healthcare software. *** ## Why embed an assistant instead of building one? Embedding Corti Assistant lets you focus on your core product while offering a full ambient scribing experience that fits naturally into your workflow. Instead of stitching together speech-to-text, clinical extraction, templates, and exports yourself, you integrate a single embedded surface that already works end to end. When embedded, the assistant: * Lives inside your EHR or healthcare application * Uses your existing authentication and session model * Appears exactly where clinicians already work * Shares context like patient, encounter, and workflow state The result is a seamless experience where documentation happens as part of the product, not alongside it. From a development perspective, this means weeks of integration instead of months or years of AI development, model tuning, and compliance work. You also inherit enterprise-grade security and certifications out of the box, including HIPAA, GDPR, SOC 2 Type 2, ISO 27001, WCAG 2.2 AA, and more. *** ## From conversation to structured clinical documentation At its core, Corti Embedded Assistant turns spoken consultations into structured, editable documentation. The workflow is simple and predictable: **Conversation → Transcript → Clinical facts → EHR-ready documents** During a consultation, the assistant listens continuously. Speech is transcribed using healthcare-optimized recognition, then analyzed to extract clinically relevant facts such as symptoms, history, vitals, and plans. These facts are presented as structured, editable items that clinicians can review, adjust, or reorganize before generating one or more documents. From a single conversation, you can generate multiple outputs such as SOAP notes, H\&P, emergency notes, referrals, discharge summaries, or patient summaries, all based on the same curated fact set. *** ## Embedded by design Corti Assistant is delivered as an embeddable application with APIs that give you control over how it behaves inside your product. You decide: * When sessions start and stop * How recording is controlled * How documents are generated and exported * How authentication and identity are handled Three integration methods are available: * [**Web Component API**](/assistant/web-component-api)\*\* (Recommended)\*\* - Modern, framework-agnostic integration using standard Web Components with full TypeScript support. Works seamlessly in vanilla JavaScript, React, Vue, Angular, and other frameworks for both iframe and same-origin scenarios. [Full examples available](https://github.com/corticph/corti-examples/tree/main/embedded-assistant). * [**PostMessage API**](/assistant/postmessage-api) - Lower-level iframe communication API. Fully supported but not recommended due to added complexity compared to the Web Component API. * [**Window API**](/assistant/window-api) - Same-origin direct API access. Useful for specific scenarios like direct URL embedding in webviews/iframes. The Web Component approach is recommended for most integrations. PostMessage API adds unnecessary complexity, while Window API serves specific use cases. *** ## What you get out of the box The embedded assistant is not just transcription. It is a complete documentation surface that includes: * Real-time AI chat for editing documents, asking clinical questions, and referencing guidelines * Familiar clinical templates like SOAP, H\&P, and custom formats * Multi-document generation from a single encounter * Drag-and-drop, editable clinical facts * One-click export to your EHR or downstream systems The UI can be customized to match your application, including branding, feature visibility, language support, and terminology overrides. *** ## Built for healthcare, operated at scale Corti Assistant runs on the same healthcare-specific platform used by large health systems. The underlying Corti API handles: * Medical speech recognition * Clinical fact extraction * Language models tuned for healthcare * Document generation * Security, privacy, and compliance For organisations with advanced needs, enterprise capabilities are available through the same embedded interface, including EHR connectivity, medical coding, standardized documentation, custom guidelines, and priority support. # Window API Source: https://docs.corti.ai/assistant/window-api Use the Window API for direct integration with Corti Assistant The Window API provides a direct, Promise-based TypeScript API exposed on `window.CortiEmbedded` for integration with Corti Assistant. This method is suitable for same-origin integrations requiring direct JavaScript access. **Web Component API is recommended for most cases.** The [Web Component API](/assistant/web-component-api) is recommended when you embed the Assistant via a local host page. The Window API remains useful for specific scenarios, such as embedding via direct URL in webviews/iframes. It is fully supported and not deprecated. **Web Component has full working examples.** Complete, runnable examples are available for the [Web Component integration method](https://github.com/corticph/corti-examples/tree/main/embedded-assistant), which is the recommended approach for most integrations. Window API examples for same-origin scenarios will be added to the repository in the future. ## Overview The Window API offers a Promise-based, TypeScript-friendly interface for integrating Corti Assistant into your application. It provides direct access to `window.CortiEmbedded.v1`, making it feel like a traditional JavaScript SDK. ## Requirements ### Implementation requirements To use the Window API, you'll need to implement a WebView or similar browser component within your native application. The embedded Corti Assistant runs as a web application and requires a modern browser environment to function properly. ### Minimum requirements * **Modern WebView**: Use a modern WebView implementation that supports: * **WebView2** (Windows) - Recommended for Windows applications * **WKWebView** (iOS/macOS) - Recommended for Apple platforms * **WebView** (Android) - Use the latest Chromium-based WebView * **Electron WebView** - For Electron-based applications * **Browser compatibility**: The WebView must support: * ES6+ JavaScript features * Modern Web APIs (WebRTC, MediaDevices API) * PostMessage API * Local Storage and Session Storage * **Microphone permissions**: Your application must request and handle microphone permissions: * Request microphone access before initializing the embedded Assistant * Handle permission denial gracefully * Provide clear messaging to users about why microphone access is needed * Ensure permissions are granted at the OS level (not just browser level) ### Platform-specific considerations **Windows (WebView2)** * Ensure WebView2 Runtime is installed or bundled with your application * Request microphone permissions in your application manifest * Handle permission prompts appropriately **iOS/macOS (WKWebView)** * Add `NSMicrophoneUsageDescription` to your Info.plist * Request microphone permissions using `AVAudioSession` or similar APIs * Ensure permissions are granted before loading the embedded Assistant **Android (WebView)** * Request `RECORD_AUDIO` permission in your AndroidManifest.xml * Request runtime permissions using `ActivityCompat.requestPermissions()` * Handle permission callbacks appropriately ## Recommendations * **Use TypeScript** for better type safety and developer experience * **Implement proper error handling** for all API calls * **Handle token refresh** to maintain user sessions * **Request microphone permissions early** in your application flow * **Test on target platforms** to ensure WebView compatibility ## Quick Start ### Step 1: Set up authentication Before using the Window API, authenticate your users using OAuth2. See the [Authentication Guide](/assistant/authentication) for complete setup instructions including Authorization Code Flow with PKCE (recommended), obtaining tokens, and handling token refresh. All Embedded Assistant integrations require user-based OAuth2 authentication. Client credentials and machine-to-machine flows are not supported. * Handle token refresh to maintain sessions * Never expose client secrets in client-side code ### Step 2: Wait for the Embedded App to be ready The embedded Corti Assistant will send an `embedded.ready` event when it's loaded and ready to receive API calls: ```javascript Basic Setup expandable theme={null} window.addEventListener("message", async (event) => { if ( event.data?.type === "CORTI_EMBEDDED_EVENT" && event.data.event === "embedded.ready" ) { // The API is now available const api = window.CortiEmbedded.v1; console.log("Corti Assistant is ready"); } }); ``` ### Step 3: Authenticate the user Once the API is ready, authenticate the user with their OAuth2 tokens: ```javascript Authentication expandable theme={null} const api = window.CortiEmbedded.v1; const user = await api.auth({ access_token: "your-access-token", // From OAuth2 flow refresh_token: "your-refresh-token", // From OAuth2 flow id_token: "your-id-token", // From OAuth2 flow token_type: "Bearer", }); console.log("Authenticated user:", user); ``` ### Step 4: Configure and use After authentication, you can configure the interface and start using the Assistant: ```javascript Configure and Use expandable theme={null} // Configure app-level settings await api.configureApp({ ui: { interactionTitle: false, aiChat: false, navigation: true, }, appearance: { primaryColor: "#00a6ff", }, locale: { interfaceLanguage: "en", dictationLanguage: "en", }, }); // Configure interaction defaults await api.setInteractionOptions({ mode: { options: ["in-person", "virtual"], }, }); // Create an interaction const interaction = await api.createInteraction({ assignedUserId: null, encounter: { identifier: `encounter-${Date.now()}`, status: "planned", type: "first_consultation", period: { startedAt: new Date().toISOString(), }, title: "Initial Consultation", }, }); // Navigate to the interaction await api.navigate({ path: `/session/${interaction.id}`, }); ``` ## API structure The API is available at `window.CortiEmbedded.v1` and provides the following methods: ```typescript theme={null} window.CortiEmbedded.v1 = { auth: (payload) => Promise, configureApp: (payload) => Promise, createInteraction: (payload) => Promise, addFacts: (payload) => Promise, setInteractionOptions: (payload) => Promise, navigate: (payload) => Promise, setCredentials: (payload) => Promise, startRecording: () => Promise, stopRecording: () => Promise, getStatus: () => Promise, }; ``` ### Same API as Web Component The Window API provides the exact same methods as described in the [API Reference](/assistant/api-reference). The only difference is the invocation style - with Window API, you call methods directly via `window.CortiEmbedded.v1.methodName()` instead of through a Web Component. Example: * Web Component: `await api.auth({ ... })` * Window API: `await window.CortiEmbedded.v1.auth({ ... })` Same method, same parameters, same return values - just different access patterns. ## Events Corti Assistant dispatches events to notify your application of user activity, state change, data updates, and many more interactions. When using the Window API, events are delivered through the same `postMessage` mechanism. ### Event format translation Core events documented in the [Events Reference](/assistant/events) are wrapped in the `CORTI_EMBEDDED_EVENT` message type: **Core event structure:** ```json theme={null} { "event": "event-name", "confidential": true, "payload": { "various": "properties" } } ``` **Window API delivery:** ```json theme={null} { "type": "CORTI_EMBEDDED_EVENT", "event": "recording.started", "confidential": false, "payload": { "mode": "virtual", "language": "en", "interactionId": "int_123", "interactionState": "ongoing" } } ``` ### Listening for events Even when using the Window API for method calls, events are delivered via `postMessage`. Set up a listener: ```javascript Listening for Events expandable theme={null} window.addEventListener("message", (event) => { // Check for Corti events if (event.data?.type === "CORTI_EMBEDDED_EVENT") { const { event: eventName, confidential, payload } = event.data; // Handle different event types switch (eventName) { case "recording.started": console.log("Recording started:", payload); updateRecordingState(true); break; case "recording.paused": console.log("Recording paused:", payload); updateRecordingState(false); break; case "document.generated": console.log("Document generated:", payload); handleNewDocument(payload); break; case "error.triggered": console.error("Error occurred:", payload); showErrorNotification(payload); break; default: console.log("Unknown event:", eventName); } } }); function updateRecordingState(isRecording) { // Update your UI to reflect recording state } function handleNewDocument(payload) { const { documentId, documentName, templateId } = payload; // Process the new document } ``` ### Combining API calls and events Use the Window API for actions and events for state updates: ```javascript Combined Usage expandable theme={null} const api = window.CortiEmbedded.v1; // Set up event listener window.addEventListener("message", (event) => { if (event.data?.type === "CORTI_EMBEDDED_EVENT") { const { event: eventName, payload } = event.data; if (eventName === "recording.started") { console.log("Recording started successfully"); } } }); // Trigger action via Window API try { await api.startRecording(); // Event will be received via message listener above } catch (error) { console.error("Failed to start recording:", error); } ``` ### Available events For a complete list of events and their payload structures, see the [Events Overview](/assistant/events). Common events include: * `recording.started` - Recording has started * `recording.paused` - Recording has paused * `document.generated` - Document has been generated * `document.updated` - Document has been edited * `interaction.loaded` - Interaction has been loaded * `error.triggered` - An error occurred ### Legacy events The embedded Assistant also dispatches [legacy events](/assistant/events/legacy-events) using camelCase names (e.g., `recordingStarted`, `documentGenerated`). These are deprecated and will be removed in a future version. ## Error handling All API methods return Promises and can throw errors. Always wrap calls in try-catch blocks: ```javascript Error Handling expandable theme={null} try { const api = window.CortiEmbedded.v1; const user = await api.auth({ access_token: "your-access-token", refresh_token: "your-refresh-token", id_token: "your-id-token", // From OAuth2 flow token_type: "Bearer", }); console.log("Authentication successful:", user); } catch (error) { console.error("Authentication failed:", error.message); // Handle authentication failure } ``` ## TypeScript support If you're using TypeScript, you can extend the Window interface to get type safety: ```typescript TypeScript Definitions expandable theme={null} interface CortiEmbeddedAPI { auth: (payload: AuthPayload) => Promise; configureApp: (payload: ConfigureAppPayload) => Promise; createInteraction: ( payload: CreateInteractionPayload, ) => Promise; addFacts: (payload: AddFactsPayload) => Promise; setInteractionOptions: (payload: SetInteractionOptionsPayload) => Promise; navigate: (payload: NavigatePayload) => Promise; setCredentials: (payload: SetCredentialsPayload) => Promise; startRecording: () => Promise; stopRecording: () => Promise; getStatus: () => Promise; } interface Window { CortiEmbedded: { v1: CortiEmbeddedAPI; }; } ``` ## Helper function You can create a helper function to ensure the API is ready: ```javascript Helper Function expandable theme={null} function waitForCortiAPI() { return new Promise((resolve) => { if (window.CortiEmbedded?.v1) { resolve(window.CortiEmbedded.v1); return; } const listener = (event) => { if ( event.data?.type === "CORTI_EMBEDDED_EVENT" && event.data.event === "embedded.ready" ) { window.removeEventListener("message", listener); resolve(window.CortiEmbedded.v1); } }; window.addEventListener("message", listener); }); } // Usage async function useAPI() { const api = await waitForCortiAPI(); const user = await api.auth({ access_token: "your-access-token", refresh_token: "your-refresh-token", id_token: "your-id-token", token_type: "Bearer", }); } ``` ## Next steps * Review the [OAuth Authentication Guide](/assistant/authentication) to set up user authentication * See the [API Reference](/assistant/api-reference) for all available methods and their parameters * Learn about [events](/assistant/events) that the embedded app can send * Check out the [PostMessage API](/assistant/postmessage-api) for cross-origin integrations Please [contact us](mailto:help@corti.ai) for help or questions. # Create an API client Source: https://docs.corti.ai/authentication/creating_clients Quick steps to creating your first client on the Corti Console.