This page provides a complete reference for all API actions, events, message types, and return values available in the Corti Embedded Assistant API.
Message Types
Outgoing Messages (Parent → Embedded)
All messages sent to the embedded app use this structure:
{
type: 'CORTI_EMBEDDED',
version: 'v1',
action: string,
requestId?: string, // Optional but recommended (see below)
payload?: object
}
requestId is optional but strongly recommendedWhile the requestId field is currently optional for backwards compatibility, omitting it is deprecated and will become required in a future API version. Always include a unique requestId to enable proper request tracking, error correlation, and response matching.
Incoming Messages (Embedded → Parent)
Responses
{
type: 'CORTI_EMBEDDED_RESPONSE',
requestId: string,
success: boolean,
data?: any,
error?: {
message: string,
code?: string
}
}
Events
{
type: 'CORTI_EMBEDDED_EVENT',
event: string,
payload?: object
}
Request IDs
The requestId field is used to correlate API requests with their corresponding responses. While currently optional, omitting requestId is deprecated and it will become required in a future API version.
Why Use Request IDs?
- Response Matching: Correlate responses with their originating requests, especially important when multiple requests are in flight
- Error Tracking: Identify which specific request failed when debugging issues
- Request Tracing: Track the full lifecycle of a request through logs and monitoring
- Timeout Handling: Implement proper timeout logic by tracking pending requests
Best Practices
- Always include a
requestId: Even though it’s optional now, always provide a unique identifier
- Use unique values: Generate a new unique ID for each request (e.g., UUID, timestamp-based, or incremental)
- Keep them short but unique: UUIDs work well, or use a pattern like
req-${Date.now()}-${counter}
- Store for debugging: Log request IDs to help troubleshoot issues in production
Example
// Good: Include requestId
iframe.contentWindow.postMessage(
{
type: "CORTI_EMBEDDED",
version: "v1",
action: "auth",
requestId: `req-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
payload: {
/* ... */
},
},
"*",
);
// Deprecated: Omitting requestId (will log a warning)
iframe.contentWindow.postMessage(
{
type: "CORTI_EMBEDDED",
version: "v1",
action: "auth",
// requestId missing - this is deprecated!
payload: {
/* ... */
},
},
"*",
);
When requestId is omitted, the embedded app will log a console warning and
use an empty string internally. This behavior is provided for backwards
compatibility only and should not be relied upon.
Error Codes
All API actions may return errors with the following structure:
{
message: string,
code: 'UNAUTHORIZED' | 'NOT_READY' | 'NOT_FOUND' | 'INVALID_PAYLOAD' | 'INTERNAL_ERROR',
details?: unknown
}
| Code | Description | Common Causes |
|---|
UNAUTHORIZED | User is not authenticated or session expired | Must call auth first, or session has expired |
NOT_READY | Required precondition not met | Missing interaction, not in recording session, etc. |
NOT_FOUND | Requested resource does not exist | Invalid interaction ID, user not found |
INVALID_PAYLOAD | Request payload validation failed | Missing required fields, invalid formats, constraint violations |
INTERNAL_ERROR | Unexpected server or client error | Retry the request or contact support |
Actions
auth
Authenticate the user session with the embedded app. All payload properties are required and can be found on the response from authentication service. For convenience, you can simply set the authentication response as the payload directly.
PostMessage:
iframe.contentWindow.postMessage(
{
type: "CORTI_EMBEDDED",
version: "v1",
action: "auth",
requestId: "unique-id",
payload: {
access_token: string,
refresh_token: string,
id_token: string,
token_type: string,
},
},
"*",
);
Window API:
const api = window.CortiEmbedded.v1;
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:
{
id: string,
email: string
}
Configure the Assistant interface for the current session, including toggling UI features, visual appearance, and locale settings.
PostMessage:
iframe.contentWindow.postMessage({
type: "CORTI_EMBEDDED",
version: "v1",
action: "configure",
payload: {
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<string, string>,
},
},
}, "*");
Window API:
const api = window.CortiEmbedded.v1;
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<string, string>,
},
});
Prerequisites: None (can be called anytime)
Input Validation:
appearance.primaryColor: Must be valid CSS color (hex, rgb, hsl) or null
locale.interfaceLanguage: Must be one of the supported interface languages (see Configuration Reference) or null
locale.dictationLanguage: Must be one of the supported dictation languages (see Configuration Reference)
locale.overrides: Keys must match known override strings (see Configuration Reference)
features.*: Must be boolean values
network.apiUrl: Must be valid HTTPS URL if provided
Possible Errors:
INVALID_PAYLOAD: Invalid color format, unsupported language code, non-boolean feature flags
INTERNAL_ERROR: Failed to apply configuration
Returns:
{
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<string, string>,
}
}
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 (uses built-in styles)
locale.interfaceLanguage: null (uses user’s default or browser setting)
locale.dictationLanguage: "en"
locale.overrides: {}
Note: The command can be invoked with a partial object, and only the specified properties will take effect. Returns the full currently applied configuration object.
createInteraction
Create a new interaction session.
PostMessage:
iframe.contentWindow.postMessage(
{
type: "CORTI_EMBEDDED",
version: "v1",
action: "createInteraction",
requestId: "unique-id",
payload: {
assignedUserId: string | null,
encounter: {
identifier: string,
status: string,
type: string,
period: {
startedAt: string,
},
title: string,
},
},
},
"*",
);
Window API:
const api = window.CortiEmbedded.v1;
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 (auth must be called first)
Input Validation:
encounter.identifier: Required, non-empty string
encounter.status: Must be one of: "planned", "in-progress", "completed", "cancelled"
encounter.type: Must be one of: "ambulatory", "emergency", "field", "first_consultation", "home_health", "inpatient_encounter", "short_stay", "virtual"
encounter.period.startedAt: Required, must be valid ISO 8601 datetime string
encounter.title: Optional string
assignedUserId: Optional string or null
patient: Optional object with patient demographic information
patient.identifier: Optional string (patient identifier from external system)
patient.name: Optional string (patient’s full name)
patient.gender: Optional, must be one of: "male", "female", "other", "unknown"
patient.birthDate: Optional string or null (ISO 8601 date format)
patient.pronouns: Optional string (preferred pronouns)
Possible Errors:
UNAUTHORIZED: User not authenticated, call auth first
INVALID_PAYLOAD: Missing required fields, invalid encounter status/type, invalid date format
INTERNAL_ERROR: Failed to create interaction
Returns:
{
id: string,
createdAt: string,
status?: string
}
addFacts
Add contextual facts to the current interaction.
PostMessage:
iframe.contentWindow.postMessage({
type: 'CORTI_EMBEDDED',
version: 'v1',
action: 'addFacts',
requestId: 'unique-id',
payload: {
facts: Array<{
text: string,
group: string
}>
}
}, '*');
Window API:
const api = window.CortiEmbedded.v1;
await api.addFacts({
facts: [
{ text: "Chest pain", group: "other" },
{ text: "Shortness of breath", group: "other" },
{ text: "Fatigue", group: "other" },
],
});
Prerequisites:
- User must be authenticated
- An interaction must exist (call
createInteraction first)
Input Validation:
facts: Required, must be non-empty array
facts[].text: Required, non-empty string
facts[].group: Optional string, defaults to "other" if not provided
facts[].source: Optional, defaults to "user"
Possible Errors:
NOT_READY: No active interaction, call createInteraction first
UNAUTHORIZED: User not authenticated
INVALID_PAYLOAD: Empty facts array, missing text field
INTERNAL_ERROR: Failed to save facts
Returns: void
Set session-level defaults and preferences. This method will not overwrite defaults set by the user.
defaultTemplateKey and defaultOutputLanguage must be provided together. If
one is missing, the method returns INVALID_PAYLOAD.
defaultOutputLanguage has no effect on its own. It is only used together
with defaultTemplateKey to resolve the template.
getTemplates returns language-specific IDs (for example, "corti-soap-en").
For configureSession, split this into: defaultTemplateKey: "corti-soap"
and defaultOutputLanguage: "en".
PostMessage:
iframe.contentWindow.postMessage(
{
type: "CORTI_EMBEDDED",
version: "v1",
action: "configureSession",
requestId: "unique-id",
payload: {
defaultLanguage: string,
defaultOutputLanguage: string,
defaultTemplateKey: string,
defaultMode: "virtual" | "in-person",
},
},
"*",
);
Window API:
const api = window.CortiEmbedded.v1;
await api.configureSession({
defaultLanguage: "en",
defaultOutputLanguage: "en",
defaultTemplateKey: "corti-soap",
defaultMode: "virtual",
});
// If getTemplates().templates[i].id is "corti-soap-en"
await api.configureSession({
defaultTemplateKey: "corti-soap",
defaultOutputLanguage: "en",
});
Prerequisites: User must be authenticated
Input Validation:
defaultLanguage: Must be valid language code (e.g., "en-US", "da-DK")
defaultOutputLanguage: Must be valid language code
defaultTemplateKey: Must be a language-agnostic template identifier
defaultMode: Must be either "virtual" or "in-person"
- All fields are optional, but if either
defaultTemplateKey or defaultOutputLanguage is provided, both must be provided together
defaultTemplateKey and defaultOutputLanguage must be set 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 + defaultOutputLanguage. Call getTemplates first to verify the template exists for the selected language.
INTERNAL_ERROR: Failed to update session settings
Returns: void
navigate
Navigate to a specific path within the embedded app.
PostMessage:
iframe.contentWindow.postMessage(
{
type: "CORTI_EMBEDDED",
version: "v1",
action: "navigate",
requestId: "unique-id",
payload: {
path: string,
},
},
"*",
);
Window API:
const api = window.CortiEmbedded.v1;
await api.navigate({
path: "/session/interaction-123",
});
Prerequisites: None
Input Validation:
path: Required, must start with /
path: Cannot be a full URL (no http://, https://, or // protocol)
path: Must be a relative path within the application
Valid Path Patterns:
/ – Root/home
/session/<id> – Specific session
/templates – Template browser
/settings/* – Settings pages
Possible Errors:
INVALID_PAYLOAD:
- Path does not start with
/ → "Path must be a relative path starting with '/'"
- Path is a full URL →
"Path must be a relative path, not a full URL"
INTERNAL_ERROR: Navigation failed
Returns: void
Navigable URLs:
/ – start a new session
/session/<id> - go to an existing session identified by <id>
/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 (only relevant if navigation is visible)
setCredentials
Change the credentials of the currently authenticated user. Can be used to set credentials for a user without a password (if only authenticated via identity provider) or to change the password of a user with an existing password.
Password Policy:
- At least 1 uppercase, 1 lowercase, 1 numerical and 1 special character
- At least 8 characters long
PostMessage:
iframe.contentWindow.postMessage(
{
type: "CORTI_EMBEDDED",
version: "v1",
action: "setCredentials",
requestId: "unique-id",
payload: {
password: string,
},
},
"*",
);
Window API:
const api = window.CortiEmbedded.v1;
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 (A-Z)
- At least 1 lowercase letter (a-z)
- At least 1 number (0-9)
- At least 1 special character
Possible Errors:
UNAUTHORIZED: User not authenticated
INVALID_PAYLOAD:
- Password too short →
"Password does not meet requirements"
- Missing required character types →
"Password does not meet requirements"
INTERNAL_ERROR: Failed to update password
Returns: void
startRecording
Start recording within the embedded session.
PostMessage:
iframe.contentWindow.postMessage(
{
type: "CORTI_EMBEDDED",
version: "v1",
action: "startRecording",
requestId: "unique-id",
},
"*",
);
Window API:
const api = window.CortiEmbedded.v1;
await api.startRecording();
Prerequisites:
- User must be authenticated (call
auth)
- An interaction must exist (call
createInteraction)
- Application must be in an interview/session context
Input Validation: None (no payload required)
Possible Errors:
NOT_READY:
"Must be in an interview to start recording" – Not in interview context
"No interaction ID found. Call createInteraction first" – No active interaction
UNAUTHORIZED:
"Not authenticated. Call auth first" – No valid session
NOT_FOUND: "Interaction not found" – Interaction ID is invalid
INTERNAL_ERROR: Failed to connect to recording service (WebSocket error)
Returns: void
stopRecording
Stop recording within the embedded session.
PostMessage:
iframe.contentWindow.postMessage(
{
type: "CORTI_EMBEDDED",
version: "v1",
action: "stopRecording",
requestId: "unique-id",
},
"*",
);
Window API:
const api = window.CortiEmbedded.v1;
await api.stopRecording();
Prerequisites: Must be actively recording (call startRecording first)
Input Validation: None (no payload required)
Possible Errors:
NOT_READY: "Must be in an interview to stop recording" – Not in active recording session
INTERNAL_ERROR: Failed to disconnect from recording service
Returns: void
getStatus
Request information about the current state of the application, including authentication status, current user, current URL and interaction details.
PostMessage:
iframe.contentWindow.postMessage(
{
type: "CORTI_EMBEDDED",
version: "v1",
action: "getStatus",
requestId: "unique-id",
},
"*",
);
Window API:
const api = window.CortiEmbedded.v1;
const status = await api.getStatus();
Prerequisites: None (always available)
Input Validation: None (no payload required)
Notes:
- This action does not throw structured error codes
- Very rare, typically always succeeds
- May return partial data if specific queries fail (e.g.,
interaction will be null if fetch fails)
Returns:
{
auth: {
isAuthenticated: boolean,
user: {
id: string,
email: string
} | null
},
currentUrl: string,
interaction: {
id: string,
title: string,
state: "planned" | "ongoing" | "paused" | "disconnected" | "ending" | "parsing" | "ended",
startedAt: string,
endedAt: string | null,
endsAt: string | null,
transcripts: Array<{
utterances: Array<{
id: string,
start: number,
duration: number,
text: string,
isFinal: boolean,
participantId: string | undefined,
}>,
participants: Array<{
id: string,
channel: number,
role: "agent" | "patient" | "other" | "multiple"
}>,
isMultiChannel: boolean
}>,
documents: Array<{
id: string,
name: string,
templateRef: string,
isStream: boolean,
sections: Array<{
key: string,
name: string,
text: string,
sort: number,
createdAt: string,
updatedAt: string,
markdown: string | undefined | null,
htmlText: string | undefined,
plainText: string | undefined,
}>,
outputLanguage: string,
}>,
facts: Array<{
id: string,
text: string,
group: string,
isDiscarded: boolean,
source: "core" | "system" | "user",
createdAt: string | undefined,
updatedAt: string,
isNew: boolean,
isDraft: boolean | undefined,
}>,
websocketUrl: string
} | null
}
getTemplates
Retrieve all document templates available to the authenticated user. This includes both built-in templates provided by Corti and custom templates created by the user themself.
Use this method before configureSession to verify the
template and language combination you want to set as default.
PostMessage:
iframe.contentWindow.postMessage(
{
type: "CORTI_EMBEDDED",
version: "v1",
action: "getTemplates",
requestId: "unique-id",
},
"*",
);
Window API:
const api = window.CortiEmbedded.v1;
const response = await api.getTemplates();
console.log(response.templates); // Array of available templates
Prerequisites: User must be authenticated (auth must be called first)
Input Validation: None (no payload required)
Possible Errors:
UNAUTHORIZED: User not authenticated, call auth first
INTERNAL_ERROR: Failed to fetch templates from server
Returns:
{
templates: Array<{
id: string; // Language-specific ID (for example, "corti-soap-en")
name: string;
description?: string;
language: {
code: string;
name: string;
locale?: string;
};
sections: Array<{
id: string;
title: string;
}>;
isCustom: boolean;
}>;
}
Example Response:
{
"templates": [
{
"id": "corti-soap-en",
"name": "SOAP Note",
"description": "Standard SOAP format for clinical documentation",
"language": {
"code": "en",
"name": "English",
"locale": "en-US"
},
"sections": [
{ "id": "subjective", "title": "Subjective" },
{ "id": "objective", "title": "Objective" },
{ "id": "assessment", "title": "Assessment" },
{ "id": "plan", "title": "Plan" }
],
"isCustom": false
},
{
"id": "custom-template-123",
"name": "Emergency Department Note",
"description": "Custom template for ED visits",
"language": {
"code": "en",
"name": "English"
},
"sections": [
{ "id": "chief-complaint", "title": "Chief Complaint" },
{ "id": "hpi", "title": "History of Present Illness" },
{ "id": "treatment", "title": "Treatment & Disposition" }
],
"isCustom": true
}
]
}
Use Cases:
- Display a template picker UI for users to select their preferred documentation template
- Filter templates by language to match the current session settings
- Distinguish between built-in and custom templates using the
isCustom flag
- Pre-populate template selection based on user preferences or defaults
Common Workflows
Initialize and Start Recording
const api = window.CortiEmbedded.v1;
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 (optional)
await api.configure({
features: { virtualMode: true },
locale: { dictationLanguage: "en-US" },
});
// 3. Create interaction
const interaction = await api.createInteraction({
encounter: {
identifier: "enc-123",
status: "planned",
type: "ambulatory",
period: { startedAt: new Date().toISOString() },
title: "Patient Visit",
},
});
console.log("Created interaction:", interaction.id);
// 4. Navigate to session page
await api.navigate({
path: `/session/${interaction.id}`,
});
console.log("Navigated to session");
// 5. Start recording
await api.startRecording();
console.log("Recording started");
} catch (error) {
console.error("Workflow failed:", error.message, error.code);
}
Error Handling Best Practices
const api = window.CortiEmbedded.v1;
async function startRecordingWithRetry() {
try {
await api.startRecording();
} catch (error) {
switch (error.code) {
case "UNAUTHORIZED":
// Re-authenticate user
console.log("Session expired, re-authenticating...");
await reauthenticate();
await api.startRecording(); // Retry
break;
case "NOT_READY":
// Ensure prerequisites are met
if (error.message.includes("createInteraction")) {
console.log("Creating interaction first...");
await api.createInteraction({
encounter: {
identifier: `enc-${Date.now()}`,
status: "planned",
type: "ambulatory",
period: { startedAt: new Date().toISOString() },
},
});
await api.startRecording(); // Retry
} else if (error.message.includes("interview")) {
console.error("Must be in interview context");
// Navigate user to session page or show error
}
break;
case "INTERNAL_ERROR":
// Retry once after delay
console.log("Internal error, retrying...");
await new Promise((resolve) => setTimeout(resolve, 2000));
try {
await api.startRecording();
} catch (retryError) {
console.error("Retry failed:", retryError.message);
// Show error to user
}
break;
default:
console.error("Unexpected error:", error);
}
}
}
Complete Session Lifecycle
const api = window.CortiEmbedded.v1;
let currentInteractionId = null;
async function initializeSession() {
// Authenticate
await api.auth({
/* tokens */
});
// Configure
await api.configure({
features: {
virtualMode: true,
aiChat: true,
},
});
// Set defaults
await api.configureSession({
defaultLanguage: "en-US",
defaultMode: "virtual",
});
}
async function startNewInteraction(encounterData) {
const interaction = await api.createInteraction({
encounter: encounterData,
});
currentInteractionId = interaction.id;
// Add initial context
await api.addFacts({
facts: [{ text: "Chief complaint: Headache", group: "symptoms" }],
});
await api.startRecording();
return interaction;
}
async function endInteraction() {
await api.stopRecording();
// Get final status
const status = await api.getStatus();
return status.interaction;
}
// Usage
await initializeSession();
const interaction = await startNewInteraction({
identifier: "enc-001",
status: "planned",
type: "ambulatory",
period: { startedAt: new Date().toISOString() },
title: "Follow-up Visit",
});
// ... conduct session ...
const finalInteraction = await endInteraction();
console.log("Session ended:", finalInteraction);
Configuration Reference
Available Interface Languages
Updated as per 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 as per February 2026
| Language code | Language |
|---|
en | English |
en-GB | British English |
de | German |
fr | French |
sv | Swedish |
da | Danish |
nl | Dutch |
no | Norwegian |
| Language code | Language |
|---|
en | English |
Known Strings to Override
Currently, only 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. |
Appearance Configuration
Disclaimer - always ensure WCAG 2.2 AA conformance
Corti Assistant’s default theme has been evaluated against WCAG 2.2 Level AA and meets applicable success criteria in our supported browsers.
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 (including color contrast and focus visibility).
When supplying a custom accent or theme, Customers must ensure WCAG 2.2 AA conformance, including:
- 1.4.3 Contrast (Minimum): normal text ≥ 4.5:1; large text ≥ 3:1
- 1.4.11 Non-text Contrast: UI boundaries, focus rings, and selected states ≥ 3:1
- 2.4.11 Focus Not Obscured (Minimum): focus indicators remain visible and unobstructed
Corti provides accessible defaults. If you override them, verify contrast for all states (default, hover, active, disabled, focus) and on all backgrounds you use.