This guide takes you from an empty page to a working consultation. By the end you’ll have TORTUS embedded in your app, a consultation running, and structured results flowing back to your code.
Prerequisites
Before you begin, make sure you have:
A publishable key (pk_...) and a client ID + client secret from TORTUS. Contact us if you need them.
A modern browser target (ES2020+) and access to install from GitHub Packages.
A secure backend you control. Launch tokens must never be minted in the browser.
Steps
Install the SDK
Configure the @tortus-ai scope to use GitHub Packages, then install the package. npm install @tortus-ai/embed-client
See Installation for the full registry and authentication setup.
Mint a launch token on your backend
The SDK authenticates with a short-lived launch token that you fetch from your own server, so your client secret never reaches the browser. // server-side route, e.g. GET /api/tortus/client-secrets
const credentials = Buffer . from ( ` ${ CLIENT_ID } : ${ CLIENT_SECRET } ` ). toString ( "base64" );
const response = await fetch ( "https://api.tortus.ai/v1/oauth/launch" , {
method: "POST" ,
headers: {
Authorization: `Basic ${ credentials } ` ,
"Content-Type" : "application/json" ,
},
body: JSON . stringify ({
externalUserId: "your-internal-user-id" ,
userPayload: { email: "clinician@example.com" , firstName: "Ada" , lastName: "Lovelace" },
}),
});
const { launch_token } = await response . json ();
// return { launchToken: launch_token } to your frontend
Read the full flow in Authentication .
Add a container to your page
TORTUS renders into an element you provide. Give it a width and height. < div id = "tortus-container" style = "width: 100%; height: 600px;" ></ div >
Load TORTUS
Initialise the client. It starts in standby mode , waiting for instructions. import { loadTortus } from "@tortus-ai/embed-client" ;
const client = await loadTortus ({
publishableKey: "pk_your_key_here" ,
container: "#tortus-container" ,
environment: "sandbox" , // use "production" when you go live
fetchClientSecrets : async () => {
const res = await fetch ( "/api/tortus/client-secrets" );
const data = await res . json ();
return { launchToken: data . launchToken };
},
});
Handle the result
Subscribe to consultation:completed and acknowledge receipt with finish(). client . on ( "consultation:completed" , async ({ consultation , result , finish }) => {
const { medicalNotes , letters , medicalCodes , transcriptions } = result . artifacts ;
// Filter by contentType, never rely on array order
const note = medicalNotes . find (( n ) => n . contentType === "text/html" );
try {
await saveToEhr ({ reference: consultation . reference , note: note ?. content , codes: medicalCodes });
await finish ({ status: "success" });
} catch {
await finish ({ status: "failed" }); // lets the user retry in the TORTUS UI
}
});
Start a consultation
Kick off a face-to-face consultation. TORTUS takes over the embedded view from here. await client . consultations . start (
{
mode: "FACE_TO_FACE" ,
patient: { name: "Jane Smith" , dateOfBirth: "1985-03-15" },
integration: { system: "EMIS" },
},
{ returnTo: "home" },
);
That’s the full loop: load → start → receive results → acknowledge. Everything else builds on
these steps.
Next steps
Consultations Explore audio, face-to-face, and live recording modes plus EHR integrations.
Handling events & results Learn the full event lifecycle and how to read artifacts.
Configuration Every option loadTortus() accepts.
How it works The mental model behind the embedded client.