Skip to main content
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 lean more about the full Corti API, please see more here

Overview

This implementation provides a robust, consistent, and secure interface for parent applications to control and interact with the embedded Corti Assistant. The API supports both asynchronous (postMessage) and synchronous (window object) integration modes.

Integration Modes

1. PostMessage API

This method is recommended for iFrame/WebView integration
  • Secure cross-origin communication
  • Works with any iframe or WebView implementation
  • Fully asynchronous with request/response pattern
PostMessage Example
<!-- Load the embedded Corti iframe -->
<iframe id="corti-iframe" src="/embedded" width="100%" height="600px"></iframe>

<script>
const iframe = document.getElementById('corti-iframe');
let isReady = false;

// Listen for the ready event
window.addEventListener('message', async (event) => {
  if (event.data?.type === 'CORTI_EMBEDDED_EVENT' && event.data.event === 'ready') {
    isReady = true;
    console.log('Corti embedded app is ready');

    // Start the integration flow
    await authenticateUser();
  }
});

async function authenticateUser() {
  // Send authentication request
  iframe.contentWindow.postMessage({
    type: 'CORTI_EMBEDDED',
    version: 'v1',
    action: 'auth',
    requestId: 'auth-1',
    payload: {
      mode: 'stateful',
      accessToken: 'your-access-token',
      refreshToken: 'your-refresh-token'
    }
  }, '*');
}
</script>

2. Window API

This method is recommended for direct integration
  • Synchronous typescript API via window.CortiEmbedded
  • Promise-based methods
  • Ideal for same-origin integrations
Windows API Example
// Wait for the embedded app to be ready
window.addEventListener("message", async (event) => {
  if (
    event.data?.type === "CORTI_EMBEDDED_EVENT" &&
    event.data.event === "ready"
  ) {
    // Use the window API directly
    const api = window.CortiEmbedded.v1;
    const user = await api.auth({
      mode: "stateful",
      accessToken: "your-access-token",
      refreshToken: "your-refresh-token",
    });

    console.log("Authenticated user:", user);
  }
});

Authentication

Authenticate the user session with the embedded app:
iframe.contentWindow.postMessage({
  type: 'CORTI_EMBEDDED',
  version: 'v1',
  action: 'auth',
  requestId: 'unique-id',
  payload: {
    mode: 'stateless' | 'stateful', // we currently do not take this value into account and will always refresh the token internally
    access_token: string,
    refresh_token?: string,
    id_token?: string,
    expires_in?: number,
    token_type?: string
  }
}, '*');

Configure interface (limited access)

This method is in Limited Access for now - contact us for more information.
The configure command allows you to configure the Assistant interface for the current session, include toggling which UI features are visible, the visual appearance of assistant, and locale settings.
iframe.contentWindow.postMessage(
  {
    type: "CORTI_EMBEDDED",
    version: "v1",
    action: "configure",
    payload: {
      ui: {
        title: false,
        chat: false,
        feedback: false,
        navigation: true,
      },
      appearance: {
        primaryColor: "#00a6ff",
      },
      locale: {
        interfaceLanguage: "de-DE",
      },
    },
  },
  "*"
);
The defaults are as follows:
  • ui.title: true
  • ui.chat: true
  • ui.feedback: true
  • ui.navigation: false
  • appearance.primaryColor: null (uses built-in styles, which is blue-ish)
  • locale.interfaceLanguage: null (uses the current user’s specified default language or determined by browser setting if not specified)
The configure command can be invoked at any time and will take effect instantly. The command can be invoked with a partial object, and only the specified properties will take effect. The command returns the full currently applied settings object. Note that if appearance.primaryColor has not been set, it will always return as null indicating default colors will be used, unlike locale.interfaceLanguage which will return whatever actual language is currently used for the interface.

Available UI languages

Updated as per November 2025
Language code Language
enEnglish
de-DEGerman
fr-FRFrench
it-ITItalian
sv-SESwedish
da-DKDanish

Create interaction

iframe.contentWindow.postMessage({
  type: 'CORTI_EMBEDDED',
  version: 'v1',
  action: 'createInteraction',
  payload: {
    assignedUserId: null,
    encounter: {
      identifier: `encounter-${Date.now()}`,
      status: "planned",
      type: "first_consultation",
      period: {
        startedAt: new Date().toISOString(),
      },
      title: "Initial Consultation",
    },
    patient: {
      identifier: "brief-clinical-note-en",
    },
}, '*');

Add Facts

Add contextual facts to the current interaction:
iframe.contentWindow.postMessage({
  type: 'CORTI_EMBEDDED',
  version: 'v1',
  action: 'addFacts',
  requestId: 'unique-id',
  payload: {
    facts: [
      { text: "Chest pain", group: "other" },
      { text: "Shortness of breath", group: "other" },
      { text: "Fatigue", group: "other" },
      { text: "Dizziness", group: "other" },
      { text: "Nausea", group: "other" },
    ]
  }
}, '*');

Configure Session

Set session-level defaults and preferences:
iframe.contentWindow.postMessage({
  type: 'CORTI_EMBEDDED',
  version: 'v1',
  action: 'configureSession',
  requestId: 'unique-id',
  payload: {
    defaultLanguage: 'en',
    defaultOutputLanguage: 'en',
    defaultTemplateKey: 'soap_note',
    defaultMode: 'virtual'
  }
}, '*');
Navigate to a specific path within the embedded app:
iframe.contentWindow.postMessage({
  type: 'CORTI_EMBEDDED',
  version: 'v1',
  action: 'navigate',
  requestId: 'unique-id',
  payload: {
    path: '/session/interaction-123'
  }
}, '*');

Set credentials (limited access)

This method is in Limited Access for now - contact us for more information.
Change the credentials of the currently authenticated user. This can be used both to set the 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. The current password policy must be followed:
  • At least 1 uppercase, 1 lowercase, 1 numerical and 1 special character
  • At least 8 characters long
iframe.contentWindow.postMessage({
  type: 'CORTI_EMBEDDED',
  version: 'v1',
  action: 'setCredentials',
  requestId: 'unique-id',
  payload: { password: 'new-password' }
}, '*');

Recording controls

Start and stop recording within the embedded session:
// Start recording
iframe.contentWindow.postMessage({
  type: 'CORTI_EMBEDDED',
  version: 'v1',
  action: 'startRecording',
  requestId: 'unique-id'
}, '*');

// Stop recording
iframe.contentWindow.postMessage({
type: 'CORTI_EMBEDDED',
version: 'v1',
action: 'stopRecording',
requestId: 'unique-id'
}, '\*');

Events

The embedded app sends events to notify the parent application of important state changes:

Event Types

readyEmbedded app is loaded and ready
loadedNavigation to a specific path completed
recordingStartedRecording has started
recordingStoppedRecording has stopped
documentGeneratedA document has been generated
documentUpdated A document has been updated
documentSyncedA document has been synced to EHR

Listening for Events

Listening for Events
window.addEventListener("message", (event) => {
  if (event.data?.type === "CORTI_EMBEDDED_EVENT") {
    switch (event.data.event) {
      case "ready":
        console.log("Embedded app ready");
        break;
      case "documentGenerated":
        console.log("Document generated:", event.data.payload.document);
        break;
      case "recordingStarted":
        console.log("Recording started");
        break;
      // ... handle other events
    }
  }
});

Complete Integration Flow

Here’s a complete example showing the recommended integration steps:
Example Embedded Integration
// State management
let iframe = null;
let isReady = false;
let currentInteractionId = null;
let pendingRequests = new Map();

// Initialize the integration
function initializeCortiEmbeddedIntegration(iframeElement) {
  iframe = iframeElement;
  isReady = false;
  currentInteractionId = null;

  setupEventListeners();
}

function setupEventListeners() {
  window.addEventListener('message', (event) => {
    if (event.data?.type === 'CORTI_EMBEDDED_EVENT') {
      handleEvent(event.data);
    }
  });
}

function handleEvent(eventData) {
  switch (eventData.event) {
    case 'ready':
      isReady = true;
      startIntegrationFlow();
      break;
    case 'documentGenerated':
      onDocumentGenerated(eventData.payload.document);
      break;
    // ... handle other events
  }
}

async function startIntegrationFlow() {
  try {
    // 1. Authenticate
    await authenticate();

    // 2. Configure session
    await configureSession();

    // 3. Create interaction
    const interaction = await createInteraction();

    // 4. Add relevant facts
    await addFacts();

    // 5. Navigate to interaction UI
    await navigateToSession(interaction.id);

    console.log('Integration flow completed successfully');
  } catch (error) {
    console.error('Integration flow failed:', error);
  }
}

async function authenticate() {
  return new Promise((resolve, reject) => {
    const requestId = generateRequestId();

    pendingRequests.set(requestId, { resolve, reject });

    iframe.contentWindow.postMessage({
      type: 'CORTI_EMBEDDED',
      version: 'v1',
      action: 'auth',
      requestId,
      payload: {
        mode: 'stateful',
        accessToken: 'your-accesstoken',
        refreshToken: 'your-refreshtoken',
        ...
      }
    }, '*');
  });
}

// Usage example:
const iframeElement = document.getElementById('corti-iframe');
initializeCortiEmbeddedIntegration(iframeElement);

Error Handling

All API methods can throw errors. Always wrap calls in try-catch blocks:
Error Handling
try {
  const api = window.CortiEmbedded.v1;
  const user = await api.auth(authPayload);
  console.log("Authentication successful:", user);
} catch (error) {
  console.error("Authentication failed:", error.message);
  // Handle authentication failure
}

Please contact us for help or questions.