Skip to main content
Follow these steps to get access and start building:
1

Create an account in the self-service API Console

The Corti API Console is where you can create an account to access Corti AI   Create an account
   Create a project
   Create API clients
   Use the client ID and client secret in your app
   Develop against the API and collaborate with your project team
   Monitor usage, manage billing and more

2

Authenticate and test the API

Authentication to the API on all environments is governed by OAuth 2.0. This authentication protocol offers enhanced security measures, ensuring that access to patient data and medical documentation is securely managed and compliant with healthcare regulations.
By default, you will receive a client-id and client-secret to authenticate via client credentials grant type.
1

Request an access token

To acquire an access token, make a request to the authURL provided to you:
auth URL
https://auth.{environment}.corti.app/realms/{tenant-name}/protocol/openid-connect/token
Create your account and client credentials in the API Console - in addition to client ID and client secret you’ll see the tenant name and environment.

The full request body that needs to be of Content-Type: "application/x-www-form-urlencoded" looks like this:
Client Credentials request body
grant_type: "client_credentials"
scope: "openid"
client_id: "<the-provided-client-id>"
client_secret: "********"
2

Receive an access token

It will return you an access_token:
Access token
{"access_token":"ey...","expires_in":300,"refresh_expires_in":0,"token_type":"Bearer","id_token":"e...","not-before-policy":0,"scope":"openid email profile"}
As you can see, the access token expires after 300 seconds (5 minutes). By default as per oAuth standards, no refresh token is used in this flow. There are many available modules to manage monitoring expiry and acquiring a new access token. However, a refresh token can be enabled if needed.
Example authentication code snippets for the Corti API in Python, JavaScript, and .NET:
import requests

CLIENT_ID, CLIENT_SECRET, ENV, TENANT = (
    "<your client id>",
    "<your client secret>",
    "<your environment>",
    "<your tenant name>",
)

URL = (
    f"https://auth.{ENV}.corti.app"
    f"/realms/{TENANT}/protocol/openid-connect/token"
)

def get_access_token():
    r = requests.post(
        URL,
        data={
            "client_id": CLIENT_ID,
            "client_secret": CLIENT_SECRET,
            "grant_type": "client_credentials",
            "scope": "openid"
        },
    )
    r.raise_for_status()
    return r.json()["access_token"]

if __name__ == "__main__":
    token = get_access_token()
    print(token)

Once you’re authenticated, make requests against the API. Below is a basic example for creating an Interaction. See all API requests and specifications here.
1

Call the base URL

Subsequently you use the access_token to authenticate any API request. The baseURL is dependent on the environment:
baseURL API
api.{environment}.corti.app/v2
Create your account and client credentials in the API Console - in addition to client ID and client secret you’ll see the tenant name and environment.

If, for example, you are on the eu environment and want to create an interaction as the starting point for any other workflow operations your URL will look like this:
URL to create an interaction
POST https://api.eu.corti.app/v2/interactions/
2

Pass the auth token

For REST API requests, your access_token should be passed as part of the Request Header. Additionally you need to include the Tenant-Name parameter:
API call request header
Tenant-Name: <tenantname>
Authorization: Bearer <access_token>
Create your account and client credentials in the API Console - in addition to client ID and client secret you’ll see the tenant name and environment.

For WebSocket connections, the access_token should be passed in as URL parameter. The Tenant-Name is already part of the WebSocket url returned with the create interaction request:
wss url with access_token appended
wss://{stream-url}&token=Bearer {access_token}
Find the full specification for create interaction request in the API Reference

3

Building a complete workflow

Goal: Integrate with Corti SDK to enable an ambient documentation, AI scribe workflow application
DecisionIntegration Planning
1How will audio be processed - real-time web socket connection, via /stream API (recommended) or asynchronous processing, via /recordings and `/transcripts APIs?To use the /stream API, follow the path for real-time ambient documentation; to use the /recordings and /transcripts APIs, follow the path of asynchronous ambient documentation.
2What template(s) will be used in document generation workflow?Use the GET/templates request to see available templates. In addition to accessing Corti-default templates, you can add the ?org= filter to find templates available to your specific organization.
3Will document be generated based on Facts (recommended), transcript, or string?Designing around Facts, transcript, or string will determine how to use the context variable in the Generate Document request.
4Will EHR context or other data be used as Facts in document generation?If yes, then use the Add Facts request to support the context updates.
5How will the output be integrated into EHR or other target systems?Document output will be a json object that can be saved as a full note, or parsed by section so that text can be directed to appropriate fields in the target system.
The first step will be to create an interaction, as shown above. Once you’ve created your interaction, the interaction ID can be used for the steps that follow below.
See more details about this workflow here and the full API specifications here.
1

Upload a recording

Here is where you use the interaction ID.
upload recording
await client.recordings.upload(file, "INTERACTION ID");
Create a helper function to upload a recording:
full function
import { createReadStream } from "fs";

async function uploadRecording(client, interactionId, filePath){
    const file = createReadStream(filePath, {autoClose: true});
    return client.recordings.upload(file, interactionId);
}
The createReadStream import is used to create a readable stream from a file path.Find the full specification for uploading a recording in the API reference.
2

Generate a transcript

Here you’ll need the interactionId as well as the recordingId returned from the upload response.
Generate a transcript
const { recordingId } = await uploadRecording(client, interactionId, "sample.mp3") 
const transcript = await client.transcripts.create(interactionId, {
    recordingId: recordingId,
    primaryLanguage: "en"
});
Find the full specification for generating a transcript in the API reference
3

Generating a document

Here you need the interactionId and transcript text (transcript) from the previous step.
Generate a Document
const document = await client.documents.create(interactionId, {
    context: [{
        type: "string",
        data: transcript.transcripts.map(t => t.text).join(" "),
    }],
    templateKey: "soap",
    outputLanguage: "en",
});
Find the full specification for generating a document in the API reference
4

Retrieving the document

If you need to retrieve a generated document again or at a later time (since the create call already returns the document), you can do so using the interactionId and documentId from the previous step.
Get a Document
await client.documents.get("INTERACTION ID", "DOCUMENT ID");
Find the full specification for retrieving a document in the API reference here
4

Putting it all together

import { CortiEnvironment, CortiClient } from "@corti/sdk";
import dotenv from "dotenv";
import fs from "fs";

dotenv.config();

const { CLIENT_ID, CLIENT_SECRET, TENANT_NAME: TENANT } = process.env;

const client = new CortiClient({
    environment: CortiEnvironment.Eu,
    auth: { clientId: CLIENT_ID, clientSecret: CLIENT_SECRET },
    tenantName: TENANT,
});

const { interactionId } = await client.interactions.create({
    encounter: {
        identifier: "GUID OF YOUR CHOOSING",
        status: "planned",
        type: "first_consultation",
    },
});

const { recordingId } = await client.recordings.upload(
    fs.createReadStream("sample.mp3", { autoClose: true }),
    interactionId
);

const transcript = await client.transcripts.create(interactionId, {
    recordingId,
    primaryLanguage: "en"
});

const document = await client.documents.create(interactionId, {
    context: [{ 
        type: "string",
        data: transcript.transcripts.map(t => t.text).join(" "),
    }],
    templateKey: "soap",
    outputLanguage: "en",
});

console.log(document.name);
The dotenv import loads environment variables from a .env file. This keeps sensitive values like your client ID and client secret out of your source code, which is considered best practice.