# Claude Code guidelines for this docs repo Source: https://developer.suki.ai/CLAUDE Claude Code guidelines for this docs repo Use this file when editing Suki developer documentation with Claude Code. For repo layout, navigation IA, release notes, and SDK product boundaries, also read [AGENTS.md](AGENTS.md). For writing rules, follow [STYLE\_GUIDE.md](STYLE_GUIDE.md). ## Audience Primary readers are partner engineers integrating Suki APIs and SDKs into healthcare applications. They know REST, JSON, and OAuth/JWT basics. Prefer task-ready guidance over explaining those primitives unless the page is explicitly introductory. ## Terminology Use published product names and casing from the style guide and Vale: * **Form filling**, **Form filling SDK**, **Form filling API(s)** in body prose; **Form Filling** / **Form Filling SDK** / **Form Filling API(s)** in Card / Tab / nav / frontmatter titles * Mid-sentence **ambient** as a common adjective (`ambient session`); **Ambient** as product list / sentence-start / UI chrome; **Ambient API(s)** as the product name * **Dictation**, **Patient Summary**, **Partner ID**, **Partner Token**, **Suki Token**, **WebSocket** * Keep code identifiers exact: `InitOptions`, `SukiAuthManager`, `PlatformClient`, `DictationClient` Do not invent alternate product names (for example “organization” for partner, or “token” when the docs say Partner Token / Suki Token). ## Content types Identify the page type before editing. Prefer one type per page: | Type | Does | Does not | | --------------------- | ------------------------------------ | -------------------------------- | | Concept / explanation | What and when | Full implementation walkthroughs | | How-to | One job, steps a developer can run | Long conceptual essays | | Tutorial | End-to-end lesson | Exhaustive API surface coverage | | Reference | Contracts, payloads, frames, options | Narrative onboarding | If a how-to needs a concept, link to the concept page instead of restating it. ## Style * Sentence case for H2/H3; no trailing `?` on How/What/Why headings * Oxford comma; no em dashes; active voice; present tense * Sentence case for Next steps and body link text; preserve protected product tokens * Do not restate the title in the first sentence * Do not invent product behavior; ask when ground truth is missing * Do only what was asked; no drive-by refactors ## Documentation access Published docs serve Markdown at the same path with `.md` appended. Prefer those when fetching live pages: Example: `https://developer.suki.ai/documentation/get-started/overview.md` Agent indexes and skills: * `https://developer.suki.ai/llms.txt` (curated page index) * `https://developer.suki.ai/.well-known/agent-skills/index.json` * `https://developer.suki.ai/mcp` ## Git Do not commit or push unless the user explicitly asks. # A Source: https://developer.suki.ai/Glossary/a Glossary terms starting with A
Welcome to the Suki Developer Platform Glossary. If you are new here and not sure what a term means? Search and filter below, or open A-Z letter pages under Documentation > Resources > Glossary to learn more about each term.
Suki provides partner REST, WebSocket, and webhook APIs so you can embed Ambient Clinical Intelligence (ACI) into healthcare applications.
Suki APIs help you build clinical AI into healthcare software for providers, nurses, and other care staff. Ambient documentation, Form filling, Dictation, and Patient summary are the core workflows you can build from your backend. Your application owns the UI and how results move into the EHR.
Use REST for sessions and results, WebSockets for live audio, and webhooks for completion events. Responses use standard HTTP status codes and JSON.
Explore and test the APIs with the OpenAPI bundle. Access requires partner onboarding.
Each API follows the same integration pattern:
API access is available only to approved Suki partners. Complete partner onboarding, then configure authentication for both your application and clinicians. Your application authenticates with a JWT (partner\_token) issued by your identity provider.
After a clinician signs in, Suki issues a Suki access token that authorizes REST API and WebSocket requests on the clinician's behalf.
If you need staging access, production credentials, or rollout assistance, contact your Suki partnership representative or Suki Support.
[https://sdp.suki.ai](https://sdp.suki.ai)
[https://sdp.suki.ai/api/v1](https://sdp.suki.ai/api/v1)
Choose your API Workflow
Pick the APIs for your use case and workflow and start building your integration.
Learn about the latest updates to the Ambient, Form filling, Dictation, and Patient Summary APIs.
`.
HCC codes are returned in structured data output only. Do not send them in session context.
For a comparison of ICD-10 and HCC codes and how Suki returns them, refer to [Diagnosis codes FAQs](/api-reference/faqs/diagnosis-codes#what-is-the-difference-between-icd-10-and-hcc-codes).
## Code examples
**Language tabs (agents):** Equivalent code samples are available in: Python, TypeScript. Humans see one language at a time. Use the variant that matches the user's stack; behavior is the same across tabs.
```python theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
import requests
encounter_id = "123dfg-456dfg-789dfg-012dfg"
url = f"https://sdp.suki-stage.com/api/v1/ambient/encounter/{encounter_id}/structured-data"
headers = {
"sdp_suki_token": "",
"sdp_provider_id": ""
}
response = requests.get(url, headers=headers)
if response.status_code == 200:
structured_data = response.json()
print("Encounter Structured Data:")
if "structured_data" in structured_data:
diagnoses = structured_data["structured_data"].get("diagnoses", {})
if "values" in diagnoses:
for diagnosis in diagnoses["values"]:
print(f"Diagnosis Note: {diagnosis.get('diagnosis_note')}")
# Additional diagnosis fields
if diagnosis.get('laterality_indicator') is not None:
print(f"Laterality Indicator: {diagnosis.get('laterality_indicator')}")
if diagnosis.get('post_coord_lex_flag') is not None:
print(f"Post-coordination Lexical Flag: {diagnosis.get('post_coord_lex_flag')}")
# Diagnosis codes (ICD10, IMO, SNOMED, HCC)
for code in diagnosis.get("codes", []):
print(f" Code: {code.get('code')}")
print(f" Description: {code.get('description')}")
print(f" Type: {code.get('type')}")
hcc_codes = [c for c in diagnosis.get("codes", []) if c.get("type") == "HCC"]
if hcc_codes:
print(f" HCC categories: {', '.join(c['code'] for c in hcc_codes)}")
print("---")
orders = structured_data["structured_data"].get("orders", {})
med_orders = orders.get("medication_orders") or {}
for order in med_orders.get("values") or []:
print(f"Order (submittable): {order.get('drug_name')} - {order.get('status')}")
for order in med_orders.get("partial_values") or []:
print(f"Order (partial): {order.get('drug_name')} - {order.get('status')}")
else:
print(f"Failed to get encounter structured data: {response.status_code}")
print(response.json())
```
```typescript theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
const encounterId = '123dfg-456dfg-789dfg-012dfg';
const response = await fetch(
`https://sdp.suki-stage.com/api/v1/ambient/encounter/${encounterId}/structured-data`,
{
headers: {
'sdp_suki_token': '',
'sdp_provider_id': ''
}
}
);
if (response.ok) {
const structuredData = await response.json();
console.log('Encounter Structured Data:');
if (structuredData.structured_data) {
const diagnoses = structuredData.structured_data.diagnoses || {};
if (diagnoses.values) {
diagnoses.values.forEach((diagnosis: any) => {
console.log(`Diagnosis Note: ${diagnosis.diagnosis_note}`);
// Additional diagnosis fields
if (diagnosis.laterality_indicator !== undefined && diagnosis.laterality_indicator !== null) {
console.log(`Laterality Indicator: ${diagnosis.laterality_indicator}`);
}
if (diagnosis.post_coord_lex_flag !== undefined && diagnosis.post_coord_lex_flag !== null) {
console.log(`Post-coordination Lexical Flag: ${diagnosis.post_coord_lex_flag}`);
}
// Diagnosis codes (ICD10, IMO, SNOMED, HCC)
diagnosis.codes?.forEach((code: any) => {
console.log(` Code: ${code.code}`);
console.log(` Description: ${code.description}`);
console.log(` Type: ${code.type}`);
});
const hccCodes = diagnosis.codes?.filter((code: any) => code.type === 'HCC') ?? [];
if (hccCodes.length > 0) {
console.log(` HCC categories: ${hccCodes.map((c: any) => c.code).join(', ')}`);
}
console.log('---');
});
}
const orders = structuredData.structured_data.orders || {};
const medOrders = orders.medication_orders || {};
(medOrders.values || []).forEach((order: any) => {
console.log(`Order (submittable): ${order.drug_name} - ${order.status}`);
});
(medOrders.partial_values || []).forEach((order: any) => {
console.log(`Order (partial): ${order.drug_name} - ${order.status}`);
});
}
} else {
const error = await response.json();
console.error(`Failed to get encounter structured data: ${response.status}`, error);
}
```
# List Encounter Notes
Source: https://developer.suki.ai/api-reference/ambient-content/list-encounter-notes
GET /api/v1/ambient/encounter/{emr_encounter_id}/notes
List Ambient notes linked to an EMR encounter for cross-modality workflows
Use this endpoint to list all finished and unfinished notes tied to an `emr_encounter_id` .
Pass the same `emr_encounter_id` you sent when creating interoperable ambient sessions. Use each returned note `id` as `note_id` with the note-level content, context, and structured data endpoints.
Cross-modality ambient workflows require `emr_encounter_id` on session create. Without it, notes are not interoperable across modalities. Refer to [Ambient interoperability](/documentation/concepts/ambient-clinical-notes/ambient-interoperability) for more details.
## Code examples
**Language tabs (agents):** Equivalent code samples are available in: Python, TypeScript. Humans see one language at a time. Use the variant that matches the user's stack; behavior is the same across tabs.
```python expandable theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
import json
import requests
BASE_URL = "https://sdp.suki.ai"
# Same emr_encounter_id you passed on Create Ambient Session
emr_encounter_id = ""
# Get sdp_suki_token from Login: POST /api/v1/auth/login
sdp_suki_token = ""
# Required for single_auth partners
sdp_provider_id = ""
url = f"{BASE_URL}/api/v1/ambient/encounter/{emr_encounter_id}/notes"
headers = {
"sdp_suki_token": sdp_suki_token,
"sdp_provider_id": sdp_provider_id,
}
response = requests.get(url, headers=headers, timeout=60)
print("HTTP status:", response.status_code)
try:
response_body = response.json()
except ValueError:
print("Response was not JSON:")
print(response.text)
raise SystemExit(1)
print("Response body:")
print(json.dumps(response_body, indent=2))
if response.status_code == 200:
notes = response_body.get("notes") or []
print(f"Notes found: {len(notes)}")
for note in notes:
note_id = note.get("id")
created_at = note.get("created_at")
updated_at = note.get("updated_at")
print("note_id:", note_id)
print("created_at:", created_at)
print("updated_at:", updated_at)
print(
"Use note_id with Get Note Content, Get Note Context, "
"and Get Note Structured Data."
)
else:
print("List Encounter Notes failed.")
if isinstance(response_body, dict):
print("code:", response_body.get("code"))
print("message:", response_body.get("message"))
```
```typescript expandable theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
const BASE_URL = "https://sdp.suki.ai";
// Same emr_encounter_id you passed on Create Ambient Session
const emrEncounterId = "";
// Get sdp_suki_token from Login: POST /api/v1/auth/login
const sdpSukiToken = "";
// Required for single_auth partners
const sdpProviderId = "";
type EncounterNote = {
id?: string;
created_at?: string;
updated_at?: string;
};
type ListEncounterNotesResponse = {
notes?: EncounterNote[];
};
type ApiErrorResponse = {
code?: number;
message?: string;
};
const response = await fetch(
`${BASE_URL}/api/v1/ambient/encounter/${emrEncounterId}/notes`,
{
method: "GET",
headers: {
sdp_suki_token: sdpSukiToken,
sdp_provider_id: sdpProviderId,
},
}
);
const responseText = await response.text();
let responseBody: ListEncounterNotesResponse | ApiErrorResponse | unknown;
try {
responseBody = responseText ? JSON.parse(responseText) : {};
} catch {
console.error("Response was not JSON:");
console.error(responseText);
throw new Error("List Encounter Notes returned non-JSON response");
}
console.log("HTTP status:", response.status);
console.log("Response body:", JSON.stringify(responseBody, null, 2));
if (response.status === 200) {
const payload = responseBody as ListEncounterNotesResponse;
const notes = payload.notes || [];
console.log(`Notes found: ${notes.length}`);
for (const note of notes) {
console.log("note_id:", note.id);
console.log("created_at:", note.created_at);
console.log("updated_at:", note.updated_at);
console.log(
"Use note_id with Get Note Content, Get Note Context, and Get Note Structured Data."
);
}
} else {
const error = responseBody as ApiErrorResponse;
console.error("List Encounter Notes failed.");
console.error("code:", error.code);
console.error("message:", error.message);
}
```
# Get Note Content
Source: https://developer.suki.ai/api-reference/ambient-content/note-content
GET /api/v1/ambient/note/{note_id}/content
Retrieve accumulated Ambient note section content, including the latest edits
Use this endpoint to get accumulated section content across ambient sessions in a note.
If clinicians edit sections in a headed product such as the Web SDK, the response returns the **latest edited** section content. Use `composition_id` from [Create ambient session](/api-reference/ambient-sessions/create) as `note_id`.
Prefer this endpoint when clinicians edit the note in the other headed products and you need the latest section content in your integration. Session-scoped content endpoints return content for a single ambient session only.
## Code examples
**Language tabs (agents):** Equivalent code samples are available in: Python, TypeScript. Humans see one language at a time. Use the variant that matches the user's stack; behavior is the same across tabs.
```python expandable theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
import json
import requests
BASE_URL = "https://sdp.suki.ai"
# Use composition_id from Create Ambient Session as note_id
note_id = ""
# Get sdp_suki_token from Login: POST /api/v1/auth/login
sdp_suki_token = ""
# Required for single_auth partners
sdp_provider_id = ""
url = f"{BASE_URL}/api/v1/ambient/note/{note_id}/content"
headers = {
"sdp_suki_token": sdp_suki_token,
"sdp_provider_id": sdp_provider_id,
}
response = requests.get(url, headers=headers, timeout=60)
print("HTTP status:", response.status_code)
try:
response_body = response.json()
except ValueError:
print("Response was not JSON:")
print(response.text)
raise SystemExit(1)
print("Response body:")
print(json.dumps(response_body, indent=2))
if response.status_code == 200:
summary = response_body.get("summary") or []
print(f"Sections found: {len(summary)}")
for section in summary:
print("title:", section.get("title"))
print("loinc_code:", section.get("loinc_code"))
print("content:", section.get("content"))
print("source_transcripts:", section.get("source_transcripts"))
else:
print("Get Note Content failed.")
if isinstance(response_body, dict):
print("code:", response_body.get("code"))
print("message:", response_body.get("message"))
```
```typescript expandable theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
const BASE_URL = "https://sdp.suki.ai";
// Use composition_id from Create Ambient Session as note_id
const noteId = "";
// Get sdp_suki_token from Login: POST /api/v1/auth/login
const sdpSukiToken = "";
// Required for single_auth partners
const sdpProviderId = "";
type ContentBlock = {
title?: string;
loinc_code?: string;
content?: string;
source_transcripts?: string[];
};
type GetNoteContentResponse = {
summary?: ContentBlock[];
};
type ApiErrorResponse = {
code?: number;
message?: string;
};
const response = await fetch(
`${BASE_URL}/api/v1/ambient/note/${noteId}/content`,
{
method: "GET",
headers: {
sdp_suki_token: sdpSukiToken,
sdp_provider_id: sdpProviderId,
},
}
);
const responseText = await response.text();
let responseBody: GetNoteContentResponse | ApiErrorResponse | unknown;
try {
responseBody = responseText ? JSON.parse(responseText) : {};
} catch {
console.error("Response was not JSON:");
console.error(responseText);
throw new Error("Get Note Content returned non-JSON response");
}
console.log("HTTP status:", response.status);
console.log("Response body:", JSON.stringify(responseBody, null, 2));
if (response.status === 200) {
const payload = responseBody as GetNoteContentResponse;
const summary = payload.summary || [];
console.log(`Sections found: ${summary.length}`);
for (const section of summary) {
console.log("title:", section.title);
console.log("loinc_code:", section.loinc_code);
console.log("content:", section.content);
console.log("source_transcripts:", section.source_transcripts);
}
} else {
const error = responseBody as ApiErrorResponse;
console.error("Get Note Content failed.");
console.error("code:", error.code);
console.error("message:", error.message);
}
```
# Get Note Context
Source: https://developer.suki.ai/api-reference/ambient-content/note-context
GET /api/v1/ambient/note/{note_id}/context
Retrieve Ambient note context aggregated across Ambient sessions
Use this endpoint to get ambient note context aggregated across ambient sessions.
The response `context` object can include fields related to the visit context aggregated across sessions in the note.
To know the supported visit type, encounter type, and provider role values, refer to the ambient [Info](/api-reference/info/information) endpoint.
Use `composition_id` from [Create ambient session](/api-reference/ambient-sessions/create) as `note_id`.
## Code examples
**Language tabs (agents):** Equivalent code samples are available in: Python, TypeScript. Humans see one language at a time. Use the variant that matches the user's stack; behavior is the same across tabs.
```python expandable theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
import json
import requests
BASE_URL = "https://sdp.suki.ai"
# Use composition_id from Create Ambient Session as note_id
note_id = ""
# Get sdp_suki_token from Login: POST /api/v1/auth/login
sdp_suki_token = ""
# Required for single_auth partners
sdp_provider_id = ""
url = f"{BASE_URL}/api/v1/ambient/note/{note_id}/context"
headers = {
"sdp_suki_token": sdp_suki_token,
"sdp_provider_id": sdp_provider_id,
}
response = requests.get(url, headers=headers, timeout=60)
print("HTTP status:", response.status_code)
try:
response_body = response.json()
except ValueError:
print("Response was not JSON:")
print(response.text)
raise SystemExit(1)
print("Response body:")
print(json.dumps(response_body, indent=2))
if response.status_code == 200:
context = response_body.get("context") or {}
print("visit_type:", context.get("visit_type"))
print("encounter_type:", context.get("encounter_type"))
print("provider_role:", context.get("provider_role"))
print("reason_for_visit:", context.get("reason_for_visit"))
print("chief_complaint:", context.get("chief_complaint"))
else:
print("Get Note Context failed.")
if isinstance(response_body, dict):
print("code:", response_body.get("code"))
print("message:", response_body.get("message"))
```
```typescript expandable theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
const BASE_URL = "https://sdp.suki.ai";
// Use composition_id from Create Ambient Session as note_id
const noteId = "";
// Get sdp_suki_token from Login: POST /api/v1/auth/login
const sdpSukiToken = "";
// Required for single_auth partners
const sdpProviderId = "";
type NoteContext = {
visit_type?: string;
encounter_type?: string;
provider_role?: string;
reason_for_visit?: string;
chief_complaint?: string;
};
type GetNoteContextResponse = {
context?: NoteContext;
};
type ApiErrorResponse = {
code?: number;
message?: string;
};
const response = await fetch(
`${BASE_URL}/api/v1/ambient/note/${noteId}/context`,
{
method: "GET",
headers: {
sdp_suki_token: sdpSukiToken,
sdp_provider_id: sdpProviderId,
},
}
);
const responseText = await response.text();
let responseBody: GetNoteContextResponse | ApiErrorResponse | unknown;
try {
responseBody = responseText ? JSON.parse(responseText) : {};
} catch {
console.error("Response was not JSON:");
console.error(responseText);
throw new Error("Get Note Context returned non-JSON response");
}
console.log("HTTP status:", response.status);
console.log("Response body:", JSON.stringify(responseBody, null, 2));
if (response.status === 200) {
const payload = responseBody as GetNoteContextResponse;
const context = payload.context || {};
console.log("visit_type:", context.visit_type);
console.log("encounter_type:", context.encounter_type);
console.log("provider_role:", context.provider_role);
console.log("reason_for_visit:", context.reason_for_visit);
console.log("chief_complaint:", context.chief_complaint);
} else {
const error = responseBody as ApiErrorResponse;
console.error("Get Note Context failed.");
console.error("code:", error.code);
console.error("message:", error.message);
}
```
# Get Note Structured Data
Source: https://developer.suki.ai/api-reference/ambient-content/note-structured-data
GET /api/v1/ambient/note/{note_id}/structured-data
Retrieve accumulated diagnoses and orders across Ambient sessions in a note
Use this endpoint to get accumulated structured data (diagnoses and orders) across sessions in a note.
Use `composition_id` from [Create ambient session](/api-reference/ambient-sessions/create) as `note_id`.
This note-level endpoint accumulates diagnoses and orders across ambient sessions in the note.
If you need the latest diagnoses and orders from the most recent session for that encounter ID, use the [Get ambient Encounter Structured Data](/api-reference/ambient-content/encounter-structured-data) endpoint.
## Code examples
**Language tabs (agents):** Equivalent code samples are available in: Python, TypeScript. Humans see one language at a time. Use the variant that matches the user's stack; behavior is the same across tabs.
```python expandable theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
import json
import requests
BASE_URL = "https://sdp.suki.ai"
# Use composition_id from Create Ambient Session as note_id
note_id = ""
# Get sdp_suki_token from Login: POST /api/v1/auth/login
sdp_suki_token = ""
# Required for single_auth partners
sdp_provider_id = ""
url = f"{BASE_URL}/api/v1/ambient/note/{note_id}/structured-data"
headers = {
"sdp_suki_token": sdp_suki_token,
"sdp_provider_id": sdp_provider_id,
}
response = requests.get(url, headers=headers, timeout=60)
print("HTTP status:", response.status_code)
try:
response_body = response.json()
except ValueError:
print("Response was not JSON:")
print(response.text)
raise SystemExit(1)
print("Response body:")
print(json.dumps(response_body, indent=2))
if response.status_code == 200:
structured_data = response_body.get("structured_data") or {}
diagnoses = (structured_data.get("diagnoses") or {}).get("values") or []
print(f"Diagnoses found: {len(diagnoses)}")
for diagnosis in diagnoses:
print("diagnosis_note:", diagnosis.get("diagnosis_note"))
print("laterality_indicator:", diagnosis.get("laterality_indicator"))
print("post_coord_lex_flag:", diagnosis.get("post_coord_lex_flag"))
for code in diagnosis.get("codes") or []:
print(" code:", code.get("code"))
print(" description:", code.get("description"))
print(" type:", code.get("type"))
print("---")
medication_orders = (
(structured_data.get("orders") or {}).get("medication_orders") or {}
)
submittable_orders = medication_orders.get("values") or []
partial_orders = medication_orders.get("partial_values") or []
print(f"Submittable medication orders: {len(submittable_orders)}")
for order in submittable_orders:
medication_code = order.get("medication_code") or {}
print("drug_name:", order.get("drug_name"))
print("status:", order.get("status"))
print("medication_code:", medication_code.get("code"))
print("medication_code_type:", medication_code.get("type"))
print("---")
print(f"Partial medication orders: {len(partial_orders)}")
for order in partial_orders:
medication_code = order.get("medication_code") or {}
print("drug_name:", order.get("drug_name"))
print("status:", order.get("status"))
print("medication_code:", medication_code.get("code"))
print("medication_code_type:", medication_code.get("type"))
print("---")
else:
print("Get Note Structured Data failed.")
if isinstance(response_body, dict):
print("code:", response_body.get("code"))
print("message:", response_body.get("message"))
```
```typescript expandable theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
const BASE_URL = "https://sdp.suki.ai";
// Use composition_id from Create Ambient Session as note_id
const noteId = "";
// Get sdp_suki_token from Login: POST /api/v1/auth/login
const sdpSukiToken = "";
// Required for single_auth partners
const sdpProviderId = "";
type Code = {
code?: string;
description?: string;
type?: string;
};
type Diagnosis = {
diagnosis_note?: string;
laterality_indicator?: number;
post_coord_lex_flag?: number;
codes?: Code[];
};
type MedicationCode = {
code?: string;
type?: string;
};
type MedicationOrder = {
drug_name?: string;
status?: string;
medication_code?: MedicationCode;
};
type GetNoteStructuredDataResponse = {
structured_data?: {
diagnoses?: {
values?: Diagnosis[];
};
orders?: {
medication_orders?: {
values?: MedicationOrder[];
partial_values?: MedicationOrder[];
};
};
};
};
type ApiErrorResponse = {
code?: number;
message?: string;
};
const response = await fetch(
`${BASE_URL}/api/v1/ambient/note/${noteId}/structured-data`,
{
method: "GET",
headers: {
sdp_suki_token: sdpSukiToken,
sdp_provider_id: sdpProviderId,
},
}
);
const responseText = await response.text();
let responseBody: GetNoteStructuredDataResponse | ApiErrorResponse | unknown;
try {
responseBody = responseText ? JSON.parse(responseText) : {};
} catch {
console.error("Response was not JSON:");
console.error(responseText);
throw new Error("Get Note Structured Data returned non-JSON response");
}
console.log("HTTP status:", response.status);
console.log("Response body:", JSON.stringify(responseBody, null, 2));
if (response.status === 200) {
const payload = responseBody as GetNoteStructuredDataResponse;
const structuredData = payload.structured_data || {};
const diagnoses = structuredData.diagnoses?.values || [];
console.log(`Diagnoses found: ${diagnoses.length}`);
for (const diagnosis of diagnoses) {
console.log("diagnosis_note:", diagnosis.diagnosis_note);
console.log("laterality_indicator:", diagnosis.laterality_indicator);
console.log("post_coord_lex_flag:", diagnosis.post_coord_lex_flag);
for (const code of diagnosis.codes || []) {
console.log(" code:", code.code);
console.log(" description:", code.description);
console.log(" type:", code.type);
}
console.log("---");
}
const medicationOrders = structuredData.orders?.medication_orders || {};
const submittableOrders = medicationOrders.values || [];
const partialOrders = medicationOrders.partial_values || [];
console.log(`Submittable medication orders: ${submittableOrders.length}`);
for (const order of submittableOrders) {
console.log("drug_name:", order.drug_name);
console.log("status:", order.status);
console.log("medication_code:", order.medication_code?.code);
console.log("medication_code_type:", order.medication_code?.type);
console.log("---");
}
console.log(`Partial medication orders: ${partialOrders.length}`);
for (const order of partialOrders) {
console.log("drug_name:", order.drug_name);
console.log("status:", order.status);
console.log("medication_code:", order.medication_code?.code);
console.log("medication_code_type:", order.medication_code?.type);
console.log("---");
}
} else {
const error = responseBody as ApiErrorResponse;
console.error("Get Note Structured Data failed.");
console.error("code:", error.code);
console.error("message:", error.message);
}
```
## Related guides
Convert medication instructions from ambient encounters into structured Medication order data
# Get Ambient Session Recording
Source: https://developer.suki.ai/api-reference/ambient-content/recording
GET /api/v1/ambient/session/{ambient_session_id}/recording
Get presigned URLs for Ambient session recordings for streaming or download
Use this endpoint to get presigned URLs for all recordings for the ambient session . Refer to the [Download Ambient Recordings](/documentation/how-to/audio-streaming/audio-streaming-download) guide for more details.
Use the `download` query parameter to control the URL type:
* If `download=true`, the URL is for downloading the **full** file.
* If `download=false` or omitted, the URL supports streaming with **range** requests.
## Code examples
**Language tabs (agents):** Equivalent code samples are available in: Python, TypeScript. Humans see one language at a time. Use the variant that matches the user's stack; behavior is the same across tabs.
```python theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
import requests
ambient_session_id = "123dfg-456dfg-789dfg-012dfg" # this will be the Ambient session id you get from the create Ambient session API
url = f"https://sdp.suki-stage.com/api/v1/ambient/session/{ambient_session_id}/recording"
headers = {
"sdp_suki_token": "",
"sdp_provider_id": ""
}
# For streaming URLs (default)
response = requests.get(url, headers=headers)
# For download-only URLs
# response = requests.get(url, headers=headers, params={"download": True})
if response.status_code == 200:
data = response.json()
print(f"Streamable: {data.get('is_streamable')}")
for rec in data.get("recordings", []):
print(f"Recording ID: {rec.get('recording_id')}")
print(f"Presigned URL: {rec.get('presigned_url')}")
print(f"Expires at: {rec.get('expires_at')}")
print(f"Sequence: {rec.get('sequence_number')}")
print("---")
else:
print(f"Failed to get recording URLs: {response.status_code}")
print(response.json())
```
```typescript theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
const ambientSessionId = '123dfg-456dfg-789dfg-012dfg'; // this will be the Ambient session id you get from the create Ambient session API
// For streaming URLs (default)
const response = await fetch(
`https://sdp.suki-stage.com/api/v1/ambient/session/${ambientSessionId}/recording`,
{
headers: {
'sdp_suki_token': '',
'sdp_provider_id': ''
}
}
);
// For download-only URLs, append ?download=true
// const response = await fetch(
// `https://sdp.suki-stage.com/api/v1/ambient/session/${ambientSessionId}/recording?download=true`,
// { headers: { 'sdp_suki_token': '', 'sdp_provider_id': '' } }
// );
if (response.ok) {
const data = await response.json();
console.log('Streamable:', data.is_streamable);
data.recordings?.forEach((rec: any) => {
console.log('Recording ID:', rec.recording_id);
console.log('Presigned URL:', rec.presigned_url);
console.log('Expires at:', rec.expires_at);
console.log('Sequence:', rec.sequence_number);
console.log('---');
});
} else {
const error = await response.json();
console.error(`Failed to get recording URLs: ${response.status}`, error);
}
```
# Get Ambient Session Status
Source: https://developer.suki.ai/api-reference/ambient-content/status
GET /api/v1/ambient/session/{ambient_session_id}/status
Check current status and processing state of Ambient session
Use this endpoint to get the **current status** of an ambient session . Use it to track the session's progress, for example, to see if it is ready to receive audio, still processing, or has completed.
## Session status values
Use the following status values to track the session's progress:
* **created**: The ambient session has been created but has not yet started.
* **ready**: The ambient session has started and is ready for audio streaming.
* **running**: The ambient session is actively processing audio and generating content.
* **aborted**: The ambient session has been cancelled by the user or client.
* **skipped**: The ambient session was skipped because not enough audio was received or the transcript was empty.
* **failed**: The ambient session failed due to an error during processing.
* **completed**: The ambient session completed successfully and generated the final content.
**paused** status is no longer supported.
Upon reaching **completed** state, the session is ready to return the content, transcripts, or other structured data .
## Code examples
**Language tabs (agents):** Equivalent code samples are available in: Python, TypeScript. Humans see one language at a time. Use the variant that matches the user's stack; behavior is the same across tabs.
```python theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
import requests
ambient_session_id = "123dfg-456dfg-789dfg-012dfg"
url = f"https://sdp.suki-stage.com/api/v1/ambient/session/{ambient_session_id}/status"
headers = {
"sdp_suki_token": "",
"sdp_provider_id": ""
}
response = requests.get(url, headers=headers)
if response.status_code == 200:
status_data = response.json()
status = status_data.get("status")
print(f"Session status: {status}")
if status == "completed":
print("Session completed successfully. Content is ready.")
elif status == "failed":
print("Session failed during processing.")
elif status == "skipped":
print("Session was skipped (empty transcript or too short).")
else:
print(f"Failed to get status: {response.status_code}")
print(response.json())
```
```typescript theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
const ambientSessionId = '123dfg-456dfg-789dfg-012dfg';
const response = await fetch(
`https://sdp.suki-stage.com/api/v1/ambient/session/${ambientSessionId}/status`,
{
headers: {
'sdp_suki_token': '',
'sdp_provider_id': ''
}
}
);
if (response.ok) {
const statusData = await response.json();
const status = statusData.status;
console.log(`Session status: ${status}`);
if (status === 'completed') {
console.log('Session completed successfully. Content is ready.');
} else if (status === 'failed') {
console.log('Session failed during processing.');
} else if (status === 'skipped') {
console.log('Session was skipped (empty transcript or too short).');
}
} else {
const error = await response.json();
console.error(`Failed to get status: ${response.status}`, error);
}
```
# Get Ambient Session Structured Data
Source: https://developer.suki.ai/api-reference/ambient-content/structured-data
GET /api/v1/ambient/session/{ambient_session_id}/structured-data
Retrieve structured clinical data from completed Ambient session
**Updated:**
* Diagnosis output now includes [HCC codes](https://www.aapc.com/resources/what-is-hierarchical-condition-category?srsltid=AfmBOopcl-dIWrRrQFq58LGS72p58BakTdoWdEHv0P9z89c3XBXuJAOY) alongside ICD10, IMO, and SNOMED.
* You now get **Medication orders** in the structured data output for an ambient session.
Use this endpoint to get the cumulative structured data associated with the specified ambient session .
## Diagnosis codes in structured data
When Problem-Based Charting (PBC) is enabled, each diagnosis in `structured_data.diagnoses.values` can include multiple codes in the `codes` array:
| `type` | Description |
| -------- | ------------------------------------------------------- |
| `ICD10` | ICD-10-CM diagnosis code |
| `IMO` | IMO term code |
| `SNOMED` | SNOMED CT code (when available) |
| `HCC` | CMS-HCC model category derived from the ICD-10-CM code. |
HCC entries use `description` in the format `CMS-HCC model category ` (for example, `CMS-HCC model category 65`). HCC codes are returned in structured data output only. **Do not** send them in session context.
* If an ICD-10-CM HCC diagnosis code does not map to an HCC model category, Suki looks for general HCC codes that match the diagnosis description. If no match is found, the diagnosis is returned without an HCC code.
* For how ICD-10 and HCC codes differ and how Suki returns them, refer to [Diagnosis codes FAQs](/api-reference/faqs/diagnosis-codes#what-is-the-difference-between-icd-10-and-hcc-codes).
## Code examples
**Language tabs (agents):** Equivalent code samples are available in: Python, TypeScript. Humans see one language at a time. Use the variant that matches the user's stack; behavior is the same across tabs.
```python theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
import requests
ambient_session_id = "123dfg-456dfg-789dfg-012dfg"
url = f"https://sdp.suki-stage.com/api/v1/ambient/session/{ambient_session_id}/structured-data"
headers = {
"sdp_suki_token": "",
"sdp_provider_id": ""
}
response = requests.get(url, headers=headers)
if response.status_code == 200:
structured_data = response.json()
print("Structured Data:")
if "structured_data" in structured_data:
diagnoses = structured_data["structured_data"].get("diagnoses", {})
if "values" in diagnoses:
for diagnosis in diagnoses["values"]:
print(f"Diagnosis Note: {diagnosis.get('diagnosis_note')}")
# Additional diagnosis fields
if diagnosis.get('laterality_indicator') is not None:
print(f"Laterality Indicator: {diagnosis.get('laterality_indicator')}")
if diagnosis.get('post_coord_lex_flag') is not None:
print(f"Post-coordination Lexical Flag: {diagnosis.get('post_coord_lex_flag')}")
# Diagnosis codes (ICD10, IMO, SNOMED, HCC)
for code in diagnosis.get("codes", []):
print(f" Code: {code.get('code')}")
print(f" Description: {code.get('description')}")
print(f" Type: {code.get('type')}")
hcc_codes = [c for c in diagnosis.get("codes", []) if c.get("type") == "HCC"]
if hcc_codes:
print(f" HCC categories: {', '.join(c['code'] for c in hcc_codes)}")
print("---")
orders = structured_data["structured_data"].get("orders", {})
med_orders = orders.get("medication_orders") or {}
for order in med_orders.get("values") or []:
med_code = order.get("medication_code") or {}
print(f"Order (submittable): {order.get('drug_name')} - {order.get('status')}")
print(f" Medication code: {med_code.get('code')} ({med_code.get('type')})")
print("---")
for order in med_orders.get("partial_values") or []:
med_code = order.get("medication_code") or {}
print(f"Order (partial): {order.get('drug_name')} - {order.get('status')}")
print(f" Medication code: {med_code.get('code')} ({med_code.get('type')})")
print("---")
else:
print(f"Failed to get structured data: {response.status_code}")
print(response.json())
```
```typescript theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
const ambientSessionId = '123dfg-456dfg-789dfg-012dfg';
const response = await fetch(
`https://sdp.suki-stage.com/api/v1/ambient/session/${ambientSessionId}/structured-data`,
{
headers: {
'sdp_suki_token': '',
'sdp_provider_id': ''
}
}
);
if (response.ok) {
const structuredData = await response.json();
console.log('Structured Data:');
if (structuredData.structured_data) {
const diagnoses = structuredData.structured_data.diagnoses || {};
if (diagnoses.values) {
diagnoses.values.forEach((diagnosis: any) => {
console.log(`Diagnosis Note: ${diagnosis.diagnosis_note}`);
// Additional diagnosis fields
if (diagnosis.laterality_indicator !== undefined && diagnosis.laterality_indicator !== null) {
console.log(`Laterality Indicator: ${diagnosis.laterality_indicator}`);
}
if (diagnosis.post_coord_lex_flag !== undefined && diagnosis.post_coord_lex_flag !== null) {
console.log(`Post-coordination Lexical Flag: ${diagnosis.post_coord_lex_flag}`);
}
// Diagnosis codes (ICD10, IMO, SNOMED, HCC)
diagnosis.codes?.forEach((code: any) => {
console.log(` Code: ${code.code}`);
console.log(` Description: ${code.description}`);
console.log(` Type: ${code.type}`);
});
const hccCodes = diagnosis.codes?.filter((code: any) => code.type === 'HCC') ?? [];
if (hccCodes.length > 0) {
console.log(` HCC categories: ${hccCodes.map((c: any) => c.code).join(', ')}`);
}
console.log('---');
});
}
const orders = structuredData.structured_data.orders || {};
const medOrders = orders.medication_orders || {};
(medOrders.values || []).forEach((order: any) => {
const medCode = order.medication_code || {};
console.log(`Order (submittable): ${order.drug_name} - ${order.status}`);
console.log(` Medication code: ${medCode.code} (${medCode.type})`);
console.log('---');
});
(medOrders.partial_values || []).forEach((order: any) => {
const medCode = order.medication_code || {};
console.log(`Order (partial): ${order.drug_name} - ${order.status}`);
console.log(` Medication code: ${medCode.code} (${medCode.type})`);
console.log('---');
});
}
} else {
const error = await response.json();
console.error(`Failed to get structured data: ${response.status}`, error);
}
```
# Get Session Transcript
Source: https://developer.suki.ai/api-reference/ambient-content/transcript
GET /api/v1/ambient/session/{ambient_session_id}/transcript
Retrieve conversation transcript from completed Ambient session
Use this endpoint to get the full transcript for a specified ambient session after it has completed.
**Updated:**
The response will now include the new `lang_id` field within the payload. The `lang_id` field indicates the language in which the transcript was sent.
For a full list of language codes and their corresponding languages, refer to the [Language code reference](/api-reference/capabilities/multilingual#language-code-reference) section.
## Code examples
**Language tabs (agents):** Equivalent code samples are available in: Python, TypeScript. Humans see one language at a time. Use the variant that matches the user's stack; behavior is the same across tabs.
```python theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
import requests
ambient_session_id = "123dfg-456dfg-789dfg-012dfg"
url = f"https://sdp.suki-stage.com/api/v1/ambient/session/{ambient_session_id}/transcript"
headers = {
"sdp_suki_token": "",
"sdp_provider_id": ""
}
response = requests.get(url, headers=headers)
if response.status_code == 200:
transcript_data = response.json()
print("Transcript:")
for transcript in transcript_data.get("final_transcript", []):
print(f"Transcript ID: {transcript.get('transcript_id')}")
print(f"Recording ID: {transcript.get('recording_id')}")
print(f"Language: {transcript.get('lang_id')}")
print(f"Transcript: {transcript.get('transcript')}")
print(f"Start Time: {transcript.get('start_time')}")
print(f"End Time: {transcript.get('end_time')}")
# Start offset (relative to beginning of audio)
start_offset = transcript.get('start_offset', {})
if start_offset:
print(f"Start Offset: {start_offset.get('hours')}h {start_offset.get('minutes')}m {start_offset.get('seconds')}s")
# End offset (relative to beginning of audio)
end_offset = transcript.get('end_offset', {})
if end_offset:
print(f"End Offset: {end_offset.get('hours')}h {end_offset.get('minutes')}m {end_offset.get('seconds')}s")
print("---")
else:
print(f"Failed to get transcript: {response.status_code}")
print(response.json())
```
```typescript theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
const ambientSessionId = '123dfg-456dfg-789dfg-012dfg';
const response = await fetch(
`https://sdp.suki-stage.com/api/v1/ambient/session/${ambientSessionId}/transcript`,
{
headers: {
'sdp_suki_token': '',
'sdp_provider_id': ''
}
}
);
if (response.ok) {
const transcriptData = await response.json();
console.log('Transcript:');
transcriptData.final_transcript?.forEach((transcript: any) => {
console.log(`Transcript ID: ${transcript.transcript_id}`);
console.log(`Recording ID: ${transcript.recording_id}`);
console.log(`Language: ${transcript.lang_id}`);
console.log(`Transcript: ${transcript.transcript}`);
console.log(`Start Time: ${transcript.start_time}`);
console.log(`End Time: ${transcript.end_time}`);
// Start offset (relative to beginning of audio)
if (transcript.start_offset) {
const { hours, minutes, seconds } = transcript.start_offset;
console.log(`Start Offset: ${hours}h ${minutes}m ${seconds}s`);
}
// End offset (relative to beginning of audio)
if (transcript.end_offset) {
const { hours, minutes, seconds } = transcript.end_offset;
console.log(`End Offset: ${hours}h ${minutes}m ${seconds}s`);
}
console.log('---');
});
} else {
const error = await response.json();
console.error(`Failed to get transcript: ${response.status}`, error);
}
```
# Dictation APIs
Source: https://developer.suki.ai/api-reference/ambient-dictation
Create Dictation sessions, stream audio for transcription, and end sessions
The Dictation APIs enable partners to run speech-to-text without the full ambient clinical note flow. You create a transcription session, open a WebSocket to stream audio, then end the session when capture is finished so resources close cleanly.
These APIs are called from your servers and authenticated with a Suki Token (`sdp_suki_token`). Critically, Dictation returns transcript text for the session. It does not replace Ambient Session Management when you need a generated clinical note.
## Available endpoints
Create a Dictation session and receive a transcription session ID
Stream audio over WebSocket and receive transcript frames
End the Dictation session when capture is finished
## Related guides
Learn how to stream Dictation audio and receive transcript frames
Learn how Dictation works and the different modes it supports
Review outbound audio message framing for Dictation WebSockets
Learn how to parse inbound transcript frames from the Dictation WebSocket
## Common use cases
Capture speech-to-text for the focused field or scratchpad without running the full ambient clinical note pipeline.
Let clinicians fix wording in a generated note with Dictation, then save the edited content in your existing chart workflow.
Own audio capture on a backend or device gateway while your UI renders live transcript results for the clinician.
Keep one Dictation session available across multiple speech bursts until the clinician finishes the documentation task.
# Ambient Session Management APIs
Source: https://developer.suki.ai/api-reference/ambient-session-management
Create and manage Ambient sessions, stream audio, attach metadata, and end sessions
The Ambient Session Management APIs enable partners to run the ambient session lifecycle for a patient encounter . An encounter is the visit. An ambient session is one recording for that visit. One encounter can include one or more ambient sessions.
For each session, you create the session, seed or update visit context, optionally attach metadata, stream visit audio over WebSocket, then end the session when that recording is finished. Ending a session closes that recording so Suki can process it. It does not mean the encounter is closed if another ambient session will follow.
These APIs are called from your servers and authenticated with a Suki Token (`sdp_suki_token`). Critically, ending the session is what closes capture cleanly so Suki can finish processing. Streaming alone does not replace the End Ambient Session call.
## Available endpoints
Create a new ambient session for a patient encounter
Seed patient, provider, and encounter context for the session
Update context during an active ambient session
Attach or update metadata for an ambient session
Stream visit audio over WebSocket to the Suki backend
End the ambient session when recording is finished
## Related guides
Learn how to stream ambient audio for a live session
Learn how the Ambient streaming architecture works
Learn how ambient sessions turn conversation into clinical notes
## Common use cases
Capture the visit conversation in your product so Suki can draft the clinical note while clinicians stay in your EHR workflow.
Provide diagnoses, note sections, specialty, and EMR encounter details so generated notes match the visit your system already knows.
Share an EMR encounter ID so clinicians can start capture on one modality and finish review on another without losing the note.
End the ambient session when that recording is finished so Suki can process it for review, retrieval, or webhooks. Start another session on the same encounter when the visit needs more capture.
# Audio Streaming
Source: https://developer.suki.ai/api-reference/ambient-sessions/audio-stream
GET /ws/stream
WebSocket endpoint for real-time audio streaming during Ambient sessions
Use this API to stream audio to the speech service over a WebSocket connection for ambient and Form filling sessions.
## Related guides
## Prerequisites
Complete these steps **before** opening the WebSocket.
Opening `/ws/stream` before the session and context are ready often leads to handshake failures or a broken stream.
* **Authenticate** and obtain `sdp_suki_token`.
* **Create an ambient session** with POST [`/api/v1/ambient/session/create`](/api-reference/ambient-sessions/create). A successful create returns **201 Created**; keep the `ambient_session_id` you used or received.
* **Seed session context** with POST [`/api/v1/ambient/session/{ambient_session_id}/context`](/api-reference/ambient-sessions/context). Send the JSON body your integration requires (see that endpoint for the full schema).
* **Authenticate and open the WebSocket** on `wss://sdp.suki-stage.com/ws/stream`. To stream audio, you must first establish an **authenticated** WebSocket connection. The authentication method you use depends on your client type: browser or non-browser.
## Browser clients
When connecting from a browser, include the `Sec-WebSocket-Protocol` header as part of the WebSocket handshake.
Set the header value as a single comma-separated string. The order must be:
* Subprotocol name.
* Token - Your `sdp_suki_token`.
* Ambient session ID - Your ambient session ID.
Avoid adding spaces between values unless your client library requires it.
For example:
```bash theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
Sec-WebSocket-Protocol: SukiAmbientAuth,,
```
The server negotiates this subprotocol to establish the connection.
## Non-browser clients
For non-browser clients such as mobile apps, backend services, or testing tools, pass authentication details as separate HTTP headers in the WebSocket upgrade request. Do not use the `Sec-WebSocket-Protocol` header.
Include the following headers:
* `sdp_suki_token` - Session token from login.
* `sdp_provider_id` - Provider identifier. Optional for standard partners; **Required** for Single Auth Token authentication.
* `ambient_session_id` - The ID for the current ambient session .
If you push non-JSON payloads where the server expects JSON, you can see parse errors (for example invalid character or null byte errors).
## Full code examples
For end-to-end ambient and Form filling streaming examples, start with these tutorials:
# Seed Ambient Session Context
Source: https://developer.suki.ai/api-reference/ambient-sessions/context
POST /api/v1/ambient/session/{ambient_session_id}/context
Provide clinical context and patient information for Ambient session
**Updated:**
You can now provide **EMR context** and **Medication orders** details in the Ambient Session Context API.
Use this endpoint to provide or update the session context for an ambient session . Providing detailed context helps Suki generate a more accurate and relevant clinical note .
For ambient notes that open in Web SDK, seed patient details in this context call: `patient_id`, `name`, `dob`, and `sex`. Web SDK needs these fields to show the patient profile. Refer to [Use ambient across modalities](/documentation/how-to/ambient-clinical-notes/use-ambient-across-modalities) for more details.
For example, you can provide:
* Provider details (e.g., specialty and role).
* Patient and visit information.
* A list of LOINC codes for the clinical sections you want to generate.
* Existing diagnoses with their associated medical codes.
* EMR context, including target EMR, when you need EMR-specific behavior (for example order submission rules).
* Orders context (medication orders), including structured medication orders for active medications and related metadata.
For more information about the context, refer to the [PBC](/api-reference/capabilities/problem-based-charting) and [Specialty](/documentation/concepts/ambient-clinical-notes/specialties) section.
For more information about Medication orders, refer to the [Medication orders](/documentation/concepts/ambient-clinical-notes/medication-orders) guide.
Use the `Codes` section to provide additional context for a session, such as **medical codes** for a **diagnosis**.
To ensure your requests succeed, follow these validation rules when sending data to the API:
### Field validation and constraints
* **Character Limits**: Ensure the **`chief_complaint`** and **`reason_for_visit`** fields do not exceed **255 characters**.
* **Enumerated Values**: Use only the predefined string values for **`visit_type`**, **`encounter_type`**, and **`provider_role`**.
* **EMR Selection**: You must set **`emr.target_emr`** to one of the following: **`ATHENA`**, **`EPIC`**, or **`CERNER`**.
### Medication order requirements
When you send **`orders.medication_orders.values`**, include the following required properties for each object:
* **Drug Information**: Provide both the **`drug_name`** and a **`medication_code`**.
* **Coding Systems**: For **`medication_code`**, specify the **`code`** and set the **`type`** to **`RXCUI`** or **`NDC`**.
* **Order Status**: Set the **`status`** to **`ACTIVE`**, **`DISCONTINUED`**, or **`REFILLED`**.
* **Metadata**: Include the **`metadata`** object with a required **`origin`** of **`EMR`** or **`SUKI_AMBIENT`**.
If you set **`origin`** to **`EMR`**, you must also set **`metadata.encounter_relation`** to either **`CURRENT_ENCOUNTER`** or **`PRIOR_ENCOUNTER`**.
**Diagnosis links**:
To link a diagnosis to an order, ensure the codes in **`linked_diagnosis_codes`** match a diagnosis already provided in the **`diagnoses`** section.
Use the same coding system for both to allow the service to validate the link.
## Code examples
**Language tabs (agents):** Equivalent code samples are available in: Python, TypeScript. Humans see one language at a time. Use the variant that matches the user's stack; behavior is the same across tabs.
```python expandable theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
import json
import requests
BASE_URL = "https://sdp.suki.ai"
# From Create Ambient Session response
ambient_session_id = ""
# Get sdp_suki_token from Login: POST /api/v1/auth/login
sdp_suki_token = ""
# Required for single_auth partners
sdp_provider_id = ""
# Set these from your system
patient_id = ""
patient_given_name = ""
patient_family_name = ""
patient_dob = "" # YYYY-MM-DD
patient_sex = "" # male | female | other | unknown
provider_specialty = ""
provider_role = ""
chief_complaint = ""
reason_for_visit = ""
encounter_type = ""
visit_type = ""
section_loinc_codes = [""]
diagnosis_code = ""
diagnosis_code_type = "" # for example ICD10
diagnosis_description = ""
diagnosis_note = ""
target_emr = "" # ATHENA | EPIC | CERNER
drug_name = ""
medication_code = ""
medication_code_type = "" # RXCUI | NDC
medication_status = "" # ACTIVE | DISCONTINUED | REFILLED
medication_origin = "" # EMR | SUKI_AMBIENT
medication_instructions = ""
url = f"{BASE_URL}/api/v1/ambient/session/{ambient_session_id}/context"
headers = {
"sdp_suki_token": sdp_suki_token,
"sdp_provider_id": sdp_provider_id,
"Content-Type": "application/json",
}
payload = {
"provider": {
"specialty": provider_specialty,
"provider_role": provider_role,
},
"patient": {
"patient_id": patient_id,
"name": {
"given": [patient_given_name],
"family": patient_family_name,
},
"dob": patient_dob,
"sex": patient_sex,
},
"visit": {
"chief_complaint": chief_complaint,
"encounter_type": encounter_type,
"reason_for_visit": reason_for_visit,
"visit_type": visit_type,
},
"sections": [{"loinc": loinc} for loinc in section_loinc_codes],
"diagnoses": {
"values": [
{
"codes": [
{
"code": diagnosis_code,
"description": diagnosis_description,
"type": diagnosis_code_type,
}
],
"diagnosis_note": diagnosis_note,
}
]
},
"emr": {
"target_emr": target_emr,
},
"orders": {
"medication_orders": {
"values": [
{
"drug_name": drug_name,
"medication_code": {
"code": medication_code,
"type": medication_code_type,
},
"linked_diagnosis_codes": [
{
"code": diagnosis_code,
"type": diagnosis_code_type,
}
],
"metadata": {
"origin": medication_origin,
},
"status": medication_status,
"instructions": medication_instructions,
}
]
}
},
}
response = requests.post(url, headers=headers, json=payload, timeout=60)
print("HTTP status:", response.status_code)
try:
response_body = response.json()
except ValueError:
print("Response was not JSON:")
print(response.text)
raise SystemExit(1)
print("Response body:")
print(json.dumps(response_body, indent=2))
if response.status_code == 200:
print("Context seeded successfully.")
else:
print("Seed Ambient session context failed.")
if isinstance(response_body, dict):
print("code:", response_body.get("code"))
print("message:", response_body.get("message"))
```
```typescript expandable theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
const BASE_URL = "https://sdp.suki.ai";
// From Create Ambient Session response
const ambientSessionId = "";
// Get sdp_suki_token from Login: POST /api/v1/auth/login
const sdpSukiToken = "";
// Required for single_auth partners
const sdpProviderId = "";
// Set these from your system
const patientId = "";
const patientGivenName = "";
const patientFamilyName = "";
const patientDob = ""; // YYYY-MM-DD
const patientSex = ""; // male | female | other | unknown
const providerSpecialty = "";
const providerRole = "";
const chiefComplaint = "";
const reasonForVisit = "";
const encounterType = "";
const visitType = "";
const sectionLoincCodes = [""];
const diagnosisCode = "";
const diagnosisCodeType = ""; // for example ICD10
const diagnosisDescription = "";
const diagnosisNote = "";
const targetEmr = ""; // ATHENA | EPIC | CERNER
const drugName = "";
const medicationCode = "";
const medicationCodeType = ""; // RXCUI | NDC
const medicationStatus = ""; // ACTIVE | DISCONTINUED | REFILLED
const medicationOrigin = ""; // EMR | SUKI_AMBIENT
const medicationInstructions = "";
type ApiErrorResponse = {
code?: number;
message?: string;
};
const payload = {
provider: {
specialty: providerSpecialty,
provider_role: providerRole,
},
patient: {
patient_id: patientId,
name: {
given: [patientGivenName],
family: patientFamilyName,
},
dob: patientDob,
sex: patientSex,
},
visit: {
chief_complaint: chiefComplaint,
encounter_type: encounterType,
reason_for_visit: reasonForVisit,
visit_type: visitType,
},
sections: sectionLoincCodes.map((loinc) => ({ loinc })),
diagnoses: {
values: [
{
codes: [
{
code: diagnosisCode,
description: diagnosisDescription,
type: diagnosisCodeType,
},
],
diagnosis_note: diagnosisNote,
},
],
},
emr: {
target_emr: targetEmr,
},
orders: {
medication_orders: {
values: [
{
drug_name: drugName,
medication_code: {
code: medicationCode,
type: medicationCodeType,
},
linked_diagnosis_codes: [
{
code: diagnosisCode,
type: diagnosisCodeType,
},
],
metadata: {
origin: medicationOrigin,
},
status: medicationStatus,
instructions: medicationInstructions,
},
],
},
},
};
const response = await fetch(
`${BASE_URL}/api/v1/ambient/session/${ambientSessionId}/context`,
{
method: "POST",
headers: {
sdp_suki_token: sdpSukiToken,
sdp_provider_id: sdpProviderId,
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
}
);
const responseText = await response.text();
let responseBody: ApiErrorResponse | Record | unknown;
try {
responseBody = responseText ? JSON.parse(responseText) : {};
} catch {
console.error("Response was not JSON:");
console.error(responseText);
throw new Error("Seed Ambient session context returned non-JSON response");
}
console.log("HTTP status:", response.status);
console.log("Response body:", JSON.stringify(responseBody, null, 2));
if (response.status === 200) {
console.log("Context seeded successfully.");
} else {
const error = responseBody as ApiErrorResponse;
console.error("Seed Ambient session context failed.");
console.error("code:", error.code);
console.error("message:", error.message);
}
```
# Create Ambient Session
Source: https://developer.suki.ai/api-reference/ambient-sessions/create
POST /api/v1/ambient/session/create
Initialize a new Ambient session for patient encounter documentation
**Updated**
* Pass **`emr_encounter_id`** to enable cross-modality ambient interoperability.
* The response now includes **`composition_id`**. Use it as `note_id` with the note-level Ambient APIs.
* The `multilingual` parameter is deprecated. Multilingual support is enabled by default for all ambient sessions.
Use this endpoint to create an ambient session . An ambient session is one recording for a patient encounter (visit). One encounter can include one or more ambient sessions.
Suki returns an `ambient_session_id` and a `composition_id` .
Use **`ambient_session_id`** for session-scoped operations such as context, streaming, status, and session content.
Store the **`composition_id`** from the response. You will pass this value as the `note_id` when you call the following note-level Ambient APIs:
To learn how to use ambient across modalities, refer to the [Ambient interoperability](/documentation/concepts/ambient-clinical-notes/ambient-interoperability) guide.
### Request body fields
All fields in the request body are **optional** for a standalone session. Pass `emr_encounter_id` to make the note interoperable. Pass `encounter_id` for a re-ambient workflow, then reuse the same value for every re-ambient session on that note.
| Field | Type | Description |
| -------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `ambient_session_id` | string (UUID) | Session ID (Ambient APIs) . If omitted, Suki generates one and returns it in the response. |
| `emr_encounter_id` | string (UUID) | EMR Encounter ID . Identifies the patient visit, which can contain multiple notes. Without this field, the note is not interoperable across modalities. |
| `encounter_id` | string | Encounter ID . Required for re-ambient workflows. Store and reuse the same value for every re-ambient session on that note. Up to **255** characters. The create response does not include `encounter_id`. |
**Important**:
* We recommend that recordings are at least **1 minute** long. Short recordings may not contain enough information for note generation.
* If the recording is too short, note generation may be **skipped**.
* For interoperable workflows, pass a valid UUID for `emr_encounter_id`.
* To continue a note on another modality, pass the existing `emr_encounter_id`.
* Do not create sessions for the same `emr_encounter_id` at the same time. Wait at least **1 second** between create requests for that encounter. Faster back-to-back creates can return a conflict.
## Code examples
**Language tabs (agents):** Equivalent code samples are available in: Python, TypeScript. Humans see one language at a time. Use the variant that matches the user's stack; behavior is the same across tabs.
```python expandable theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
import json
import requests
BASE_URL = "https://sdp.suki.ai"
CREATE_SESSION_URL = f"{BASE_URL}/api/v1/ambient/session/create"
# Get sdp_suki_token from Login: POST /api/v1/auth/login
sdp_suki_token = ""
# Required for single_auth partners
sdp_provider_id = ""
# Set these from your system. Omit a field by leaving the value as None.
# All request body fields are optional. Suki generates values you omit.
# Pass emr_encounter_id for cross-modality Ambient interoperability.
ambient_session_id = None # Optional UUID for this Ambient session
emr_encounter_id = None # UUID for your EMR encounter
encounter_id = None # Required for re-ambient workflows
headers = {
"sdp_suki_token": sdp_suki_token,
"sdp_provider_id": sdp_provider_id,
"Content-Type": "application/json",
}
payload = {}
if ambient_session_id:
payload["ambient_session_id"] = ambient_session_id
if emr_encounter_id:
payload["emr_encounter_id"] = emr_encounter_id
if encounter_id:
payload["encounter_id"] = encounter_id
response = requests.post(
CREATE_SESSION_URL,
headers=headers,
json=payload,
timeout=60,
)
print("HTTP status:", response.status_code)
try:
response_body = response.json()
except ValueError:
print("Response was not JSON:")
print(response.text)
raise SystemExit(1)
print("Response body:")
print(json.dumps(response_body, indent=2))
if response.status_code == 201:
created_ambient_session_id = response_body["ambient_session_id"]
composition_id = response_body["composition_id"]
print("ambient_session_id:", created_ambient_session_id)
print("composition_id:", composition_id)
print(
"Use ambient_session_id for session APIs "
"(context, stream, status, session content)."
)
print(
"Use composition_id as note_id for note-level Ambient APIs "
"(note content, note context, note structured data)."
)
else:
print("Create Ambient session failed.")
if isinstance(response_body, dict):
print("code:", response_body.get("code"))
print("message:", response_body.get("message"))
```
```typescript expandable theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
const BASE_URL = "https://sdp.suki.ai";
const CREATE_SESSION_URL = `${BASE_URL}/api/v1/ambient/session/create`;
// Get sdp_suki_token from Login: POST /api/v1/auth/login
const sdpSukiToken = "";
// Required for single_auth partners
const sdpProviderId = "";
// Set these from your system. Omit a field by leaving the value undefined.
// All request body fields are optional. Suki generates values you omit.
// Pass emr_encounter_id for cross-modality Ambient interoperability.
const ambientSessionId: string | undefined = undefined; // Optional UUID for this Ambient session
const emrEncounterId: string | undefined = undefined; // UUID for your EMR encounter
const encounterId: string | undefined = undefined; // Required for re-ambient workflows
type CreateAmbientSessionRequest = {
ambient_session_id?: string;
emr_encounter_id?: string;
encounter_id?: string;
};
type CreateAmbientSessionResponse = {
ambient_session_id: string;
composition_id: string;
};
type ApiErrorResponse = {
code?: number;
message?: string;
};
const payload: CreateAmbientSessionRequest = {};
if (ambientSessionId) {
payload.ambient_session_id = ambientSessionId;
}
if (emrEncounterId) {
payload.emr_encounter_id = emrEncounterId;
}
if (encounterId) {
payload.encounter_id = encounterId;
}
const response = await fetch(CREATE_SESSION_URL, {
method: "POST",
headers: {
sdp_suki_token: sdpSukiToken,
sdp_provider_id: sdpProviderId,
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
});
const responseText = await response.text();
let responseBody: CreateAmbientSessionResponse | ApiErrorResponse | unknown;
try {
responseBody = responseText ? JSON.parse(responseText) : {};
} catch {
console.error("Response was not JSON:");
console.error(responseText);
throw new Error("Create Ambient session returned non-JSON response");
}
console.log("HTTP status:", response.status);
console.log("Response body:", JSON.stringify(responseBody, null, 2));
if (response.status === 201) {
const session = responseBody as CreateAmbientSessionResponse;
console.log("ambient_session_id:", session.ambient_session_id);
console.log("composition_id:", session.composition_id);
console.log(
"Use ambient_session_id for session APIs (context, stream, status, session content)."
);
console.log(
"Use composition_id as note_id for note-level Ambient APIs (note content, note context, note structured data)."
);
} else {
const error = responseBody as ApiErrorResponse;
console.error("Create Ambient session failed.");
console.error("code:", error.code);
console.error("message:", error.message);
}
```
# End Ambient Session
Source: https://developer.suki.ai/api-reference/ambient-sessions/end
POST /api/v1/ambient/session/{ambient_session_id}/end
Complete Ambient session and trigger clinical note generation
Use this endpoint to end an ambient session and trigger the clinical note generation process.
Ending an ambient session closes that recording so Suki can process it. It does not close the patient encounter . Create another ambient session for the same encounter when the visit needs more capture.
If you get a `skipped` status, it means that the note was not generated because the conversation transcript was empty or the session was too short.
## Code examples
**Language tabs (agents):** Equivalent code samples are available in: Python, TypeScript. Humans see one language at a time. Use the variant that matches the user's stack; behavior is the same across tabs.
```python theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
import requests
ambient_session_id = "123dfg-456dfg-789dfg-012dfg"
url = f"https://sdp.suki-stage.com/api/v1/ambient/session/{ambient_session_id}/end"
headers = {
"sdp_suki_token": "",
"sdp_provider_id": ""
}
response = requests.post(url, headers=headers)
if response.status_code == 200:
print("Session ended successfully. Clinical note generation triggered.")
else:
print(f"Failed to end session: {response.status_code}")
print(response.json())
```
```typescript theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
const ambientSessionId = '123dfg-456dfg-789dfg-012dfg';
const response = await fetch(
`https://sdp.suki-stage.com/api/v1/ambient/session/${ambientSessionId}/end`,
{
method: 'POST',
headers: {
'sdp_suki_token': '',
'sdp_provider_id': ''
}
}
);
if (response.ok) {
console.log('Session ended successfully. Clinical note generation triggered.');
} else {
const error = await response.json();
console.error(`Failed to end session: ${response.status}`, error);
}
```
# Ambient Session Metadata
Source: https://developer.suki.ai/api-reference/ambient-sessions/metadata
POST /api/v1/ambient/session/{ambient_session_id}/metadata
Add metadata to Ambient session (deprecated; use context endpoint instead)
This endpoint is `deprecated`
We no longer support this endpoint. If you are using it, update your code to use the [Context](/api-reference/ambient-sessions/context) endpoint instead. That endpoint lets you set session context for an ambient session .
# Update Ambient Session Context
Source: https://developer.suki.ai/api-reference/ambient-sessions/update-context
PATCH /api/v1/ambient/session/{ambient_session_id}/context
Update existing session context with new clinical information
**Updated:**
You can now update the ambient session context with new **EMR context** and **Medication orders**. Read more in [Medication orders](/documentation/concepts/ambient-clinical-notes/medication-orders).
Use this endpoint to update the session context for an ambient session . The API applies a field mask so you can send only the parts of the context you want to change.
This is a **PATCH** operation that allows partial updates to the session context.
## Comparison with POST Context
| Feature | POST (Seed) | PATCH (Update) |
| :---------------- | ------------------------------------------------------------------ | ----------------------------------------------------------------- |
| **Purpose** | Set entire context all at once when available | Incrementally build context as information becomes available |
| **Data Approach** | Complete context payload | Partial context updates |
| **Timing** | Before EndSession. When you have all context information together. | Before EndSession. As information becomes available progressively |
| **Behavior** | Replaces entire context | Updates only specified fields |
# API Reference Guidelines
Source: https://developer.suki.ai/api-reference/api-guidelines
Decode API reference tags ([NEW], [DEPRECATED], [EARLY ACCESS]), parameter markers, example conventions, and versioning before you implement endpoints
This guide explains the **tags**, **indicators**, and **standards** we use in our API documentation. Understanding these conventions will help you navigate the reference materials more effectively and build robust integrations.
## API status tags
We use the following tags next to endpoint titles to indicate their status and maturity.
### \[NEW]
This tag indicates that an API endpoint has been recently added to Suki for partners.
* Use this safely in production; it is fully supported and documented.
* The API may receive additional non-breaking features in future updates.
### \[UPDATED]
This tag indicates that an API endpoint has received significant, backward-compatible enhancements or modifications.
* Look for new parameters, response fields, or behavior changes that you can implement.
* Your existing integrations should continue to work without modification.
### \[DEPRECATED]
This tag indicates that an API endpoint is scheduled for removal in a future version.
* You should plan to migrate any existing integrations to the recommended alternatives.
* Check the documentation for the specific migration timeline and replacement APIs. Suki provides migration guides to support your transition.
You should avoid using this endpoint in new integrations.
### \[EARLY ACCESS]
This tag indicates that an API endpoint is available for testing and feedback but may not be fully stable.
* The API's behavior may change based on feedback, and breaking changes are possible before it becomes generally available.
* Access to this API may require special configuration. We encourage your feedback to help us improve it.
Use early access APIs with caution.
The ws and webhook indicators in the API documentation indicate that an API endpoint is a WebSocket or a Webhook endpoint.
## Documentation standards
### Parameters
Each parameter in an API request is clearly marked to ensure you know what is required.
* **Required**: You must include these parameters in all API requests. Requests will fail without them.
* **Optional**: Include these parameters to access enhanced functionality. When applicable, the documentation will note the default value used if you don't provide one.
### Examples
All API endpoints include comprehensive examples to guide your implementation.
* **Response examples**: You will find examples of success responses with realistic data, as well as common error responses. Each field in the response is described.
* **Code examples**: You will find practical code snippets, such as `cURL` commands for direct testing and `JSON` payloads to illustrate the request and response structure.
## Using code examples in your integration
Code snippets in the API reference (Python, TypeScript, cURL, and similar) show real paths, headers, and JSON shapes. To use them in your own systems, do the following:
* **Use real credentials**: Replace placeholders (for example ``, ``, or ``) with values from your partner integration. Complete the [Partner onboarding](/documentation/get-started/partner-onboarding) flow first, then follow [Provider authentication](/api-reference/provider-authentication) for register, login, and how tokens map to API headers. Form filling APIs follow the same authentication model; see [Form filling authentication](/form-filling-api-reference/authentication) for that tab’s layout.
* **Use the right base URL**: Examples may use a staging host (for example `https://sdp.suki-stage.com`). Use the base URL Suki gives you for each environment (staging, production, and so on).
* **Keep secrets server-side**: Do not put partner tokens, JWTs, or `sdp_suki_token` in untrusted clients, public repos, or browser bundles where they can be extracted.
* **Python**: Install any dependency the sample assumes (for example `requests` via `pip install requests`). Run calls from a backend service or trusted script with outbound HTTPS to Suki.
* **TypeScript or JavaScript**: Run samples on the **server** using a runtime that provides **`fetch`** (for example **Node.js 18+**, Deno, or Bun). Older Node versions need another HTTP client or a `fetch` polyfill. Calling Suki **directly from a browser** usually hits **CORS** limits unless your integration is explicitly allowed; typical integrations call Suki from **your backend** or **your proxy**.
* **cURL**: Use a shell with `curl` installed; pass the same headers and JSON bodies as in the docs, with real tokens and URL.
## Version management
### API versioning
Our APIs follow semantic versioning principles to make updates predictable.
* **Major versions (`v1`, `v2`)**: Indicate breaking changes that may require you to update your code.
* **Minor versions (`v1.1`, `v1.2`)**: Introduce new features in a backward-compatible way.
* **Patch versions (`v1.1.1`, `v1.1.2`)**: Include backward-compatible bug fixes and minor improvements.
The following table shows which features are available in each version:
| Feature | v1 |
| ------------------------------- | -- |
| Authentication (Login/Register) | ✓ |
| Ambient session management | ✓ |
| Audio streaming (WebSocket) | ✓ |
| Content retrieval | ✓ |
| Audio transcription | ✓ |
| Multilingual support | ✓ |
| Personalization | ✓ |
| Problem-Based Charting (PBC) | ✓ |
| Webhooks | ✓ |
| User preferences | ✓ |
| Form filling | ✓ |
| Dictation | ✓ |
| Patient summaries | ✓ |
### Backward compatibility
We are committed to making platform updates as smooth as possible.
* We communicate breaking changes well in advance and provide migration guidance.
* When we add new optional parameters, your existing integrations will not break.
* Changes to the format of API responses are additive, meaning we may add new fields, but we will not remove or alter existing ones in a breaking way.
## Best practices
### Choosing the right API
* **For production use**: You should use APIs that are unmarked or are tagged as `[NEW]` or `[UPDATED]`. These are stable, fully supported, and have long-term compatibility guarantees.
* **For development and testing**: Consider using `[EARLY ACCESS]` APIs to preview upcoming features and provide feedback. This can help you plan for future integrations.
### Migration strategy
When an API you are using is marked as `[DEPRECATED]`, we recommend the following process:
1. **Assess impact**: Review your current usage of the deprecated endpoint.
2. **Plan timeline**: Check the deprecation timeline in the documentation and plan your migration.
3. **Test alternatives**: Implement and test the recommended replacement API.
4. **Migrate**: Update your integration in phases to minimize disruption.
### Getting help
For questions about API status, migration timelines, or implementation guidance, please contact our **customer success team**.
Read the [API changelog](/api-reference/product-updates/changelog) section for the latest updates and changes to the APIs. For a searchable list of deprecated endpoints, parameters, and SDK properties, refer to the [Deprecation list](/updates/deprecations).
## Next steps
Use the following APIs to get started with the Suki Platform:
**[Ambient API](/api-reference/overview)** - Learn about the Ambient API.
**[Form filling API](/form-filling-api-reference/overview)** - Learn about the Form filling API.
**[Dictation API](/api-reference/ambient-dictation)** - Learn about the Dictation API.
# Asynchronous Notifications
Source: https://developer.suki.ai/api-reference/asynchronous/webhook
POST /webhooks/notification
Webhook endpoint for receiving asynchronous notifications from Suki platform
* **Notification Webhooks are available through the Partner APIs.** Sessions created with the Web SDK, Mobile SDK, or Headless Web SDK use the same platform Webhook callbacks.
* **Webhooks are available for ambient session and CKG data ingestion events.**.
Use this endpoint to implement an HTTP endpoint in your application that receives Webhook requests from Suki.
During [Partner onboarding](/documentation/get-started/partner-onboarding), provide the endpoint URL to Suki to receive notifications for ambient session and CKG data ingestion events.
Learn more about how Webhooks work and how to implement your own HTTP endpoint to receive notifications in the [Webhook overview](/documentation/webhook/overview) documentation.
## Code examples
**Language tabs (agents):** Equivalent code samples are available in: Python, TypeScript. Humans see one language at a time. Use the variant that matches the user's stack; behavior is the same across tabs.
```python theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/webhooks/notification', methods=['POST'])
def handle_webhook():
"""
Webhook endpoint to receive notifications from Suki platform.
This endpoint should be hosted by your application.
"""
# The payload is received in the request body from Suki
data = request.get_json() # This is the payload sent by Suki
if not data:
return jsonify({"error": "Invalid request"}), 400
status = data.get("status")
if status == "success":
# Handle success notification
session_id = data.get("session_id")
encounter_id = data.get("encounter_id")
sessions = data.get("sessions", [])
additional_info = data.get("additional_info")
print(f"Session {session_id} completed successfully")
print(f"Encounter ID: {encounter_id}")
print(f"Total sessions: {len(sessions)}")
if additional_info:
print(f"Additional info: {additional_info}")
# Access links to retrieve content
if "_links" in data:
links = data["_links"]
print("Available links:")
# contents is an array of Link objects
if "contents" in links:
print(" Session contents:")
for link in links["contents"]:
print(f" {link.get('method')} {link.get('href')} - {link.get('name')}")
# encounter_content is an array of Link objects
if "encounter_content" in links:
print(" Encounter content:")
for link in links["encounter_content"]:
print(f" {link.get('method')} {link.get('href')} - {link.get('name')}")
# transcripts is an array of Link objects
if "transcripts" in links:
print(" Transcripts:")
for link in links["transcripts"]:
print(f" {link.get('method')} {link.get('href')} - {link.get('name')}")
# status is an array of Link objects
if "status" in links:
print(" Status:")
for link in links["status"]:
print(f" {link.get('method')} {link.get('href')} - {link.get('name')}")
return jsonify({"message": "Notification received"}), 200
elif status == "failure":
# Handle failure notification
session_id = data.get("session_id")
encounter_id = data.get("encounter_id")
error_code = data.get("error_code")
error_detail = data.get("error_detail")
print(f"Session {session_id} failed")
print(f"Encounter ID: {encounter_id}")
print(f"Error Code: {error_code}")
print(f"Error Detail: {error_detail}")
return jsonify({"message": "Failure notification received"}), 200
else:
return jsonify({"error": "Unknown status"}), 400
if __name__ == '__main__':
app.run(port=3000)
```
```typescript theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
import express from 'express';
const app = express();
app.use(express.json());
app.post('/webhooks/notification', (req, res) => {
/**
* Webhook endpoint to receive notifications from Suki platform.
* This endpoint should be hosted by your application.
*/
// The payload is received in the request body from Suki
const data = req.body; // This is the payload sent by Suki
if (!data) {
return res.status(400).json({ error: 'Invalid request' });
}
const status = data.status;
if (status === 'success') {
// Handle success notification
const sessionId = data.session_id;
const encounterId = data.encounter_id;
const sessions = data.sessions || [];
const additionalInfo = data.additional_info;
console.log(`Session ${sessionId} completed successfully`);
console.log(`Encounter ID: ${encounterId}`);
console.log(`Total sessions: ${sessions.length}`);
if (additionalInfo) {
console.log(`Additional info:`, additionalInfo);
}
// Access links to retrieve content
if (data._links) {
const links = data._links;
console.log('Available links:');
// contents is an array of Link objects
if (links.contents) {
console.log(' Session contents:');
links.contents.forEach((link: any) => {
console.log(` ${link.method} ${link.href} - ${link.name}`);
});
}
// encounter_content is an array of Link objects
if (links.encounter_content) {
console.log(' Encounter content:');
links.encounter_content.forEach((link: any) => {
console.log(` ${link.method} ${link.href} - ${link.name}`);
});
}
// transcripts is an array of Link objects
if (links.transcripts) {
console.log(' Transcripts:');
links.transcripts.forEach((link: any) => {
console.log(` ${link.method} ${link.href} - ${link.name}`);
});
}
// status is an array of Link objects
if (links.status) {
console.log(' Status:');
links.status.forEach((link: any) => {
console.log(` ${link.method} ${link.href} - ${link.name}`);
});
}
}
return res.status(200).json({ message: 'Notification received' });
} else if (status === 'failure') {
// Handle failure notification
const sessionId = data.session_id;
const encounterId = data.encounter_id;
const errorCode = data.error_code;
const errorDetail = data.error_detail;
console.log(`Session ${sessionId} failed`);
console.log(`Encounter ID: ${encounterId}`);
console.log(`Error Code: ${errorCode}`);
console.log(`Error Detail: ${errorDetail}`);
return res.status(200).json({ message: 'Failure notification received' });
} else {
return res.status(400).json({ error: 'Unknown status' });
}
});
app.listen(3000, () => {
console.log('Webhook server listening on port 3000');
});
```
# Create Dictation Session
Source: https://developer.suki.ai/api-reference/audio-transcription/create-session
POST /api/v1/transcription/session/create
Create a Dictation session to transcribe spoken audio into text in real time
Use this endpoint to create a Dictation session to transcribe spoken audio into text.
Refer to the [Audio Dictation guide](/documentation/concepts/dictation/dictation) for more information.
Returns a `201 Created` status with the `transcription_session_id` that is used to identify the session for transcribing audio and ending the session.
# End Dictation Session
Source: https://developer.suki.ai/api-reference/audio-transcription/end-session
POST /api/v1/transcription/session/{transcription_session_id}/end
End a Dictation session and retrieve the final transcription results
Use this endpoint to end an active Dictation session and retrieve the final transcription results. This endpoint stops the audio streaming and returns the complete transcript along with session metadata.
Returns a `200 OK` status with a success message.
# Dictation Session Streaming
Source: https://developer.suki.ai/api-reference/audio-transcription/stream-transcription
GET /ws/transcribe
Stream audio to an active Dictation session for real-time transcription
Use this WebSocket endpoint to stream audio to an active Dictation session for **real-time transcription**.
## Related guides
## Prerequisites
Complete these steps **before** opening the WebSocket.
Opening `/ws/transcribe` before the Dictation session is **`READY`** or **`IDLE`** often leads to handshake failures. If the session is **`RUNNING`**, **`COMPLETED`**, or in another state, the WebSocket handshake fails with **`FailedPrecondition`** (for example **transcript session is not accepting new speech sessions**).
* **Authenticate** and obtain `sdp_suki_token`.
* **Create a Dictation session** with POST [`/api/v1/transcription/session/create`](/api-reference/audio-transcription/create-session). A successful create returns **201 Created**; keep the `transcription_session_id` from the response.
* **Authenticate and open the WebSocket** on `wss://sdp.suki-stage.com/ws/transcribe`. To stream audio, you must first establish an **authenticated** WebSocket connection. The authentication method you use depends on your client type: browser or non-browser.
- Stream audio in **chunks** for the best latency and throughput.
- For partial and final inbound transcript frames, **`EOF`**, and session state rules, refer to [Dictation transcript frames](/documentation/how-to/audio-streaming/dictation-streaming-transcripts).
## Inbound transcript messages
The server sends **JSON text frames** that include `transcript`, `is_final`, and `transcript_id`. Use `is_final` to identify whether the
transcript is a partial result or a final result. After the audio stream ends, the server sends `{ "transcript": { "transcript": "EOF" } }` and then closes the WebSocket connection.
Refer to [Read Dictation transcript frames](/documentation/how-to/audio-streaming/dictation-streaming-transcripts) for frame examples, **`words`** and speaker IDs on finals, and client-side filtering rules.
## Authentication
Authentication is applied during the WebSocket handshake. The method depends on your client type. Use the `Sec-WebSocket-Protocol` header for browser clients, and `sdp_suki_token` and `transcription_session_id` headers for non-browser clients.
### Browser clients
If you are connecting from a browser, you must use the `Sec-WebSocket-Protocol` header during the WebSocket handshake.
The header must specify the `SukiAmbientAuth` protocol, followed by the **token** and the **transcription session ID** in the following format.
```bash theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
Sec-WebSocket-Protocol: SukiAmbientAuth,,
```
### Non-browser clients
If you are connecting from a non-browser client, such as a mobile or server-side application, you must provide the **token** and **session ID** as separate HTTP headers in the initial WebSocket upgrade request.
* `sdp_suki_token`: Session token from login.
* `sdp_provider_id`: Provider identifier. Optional for standard partners; **Required** for Single Auth Token authentication.
* `transcription_session_id`: The ID for the current session.
Important:
* All messages must be sent as **JSON text** frames over the WebSocket connection.
* Do not send raw binary data or use HTTP endpoints for streaming audio.
## Full code examples
For end-to-end Dictation streaming examples, start with these tutorials:
# Authentication APIs
Source: https://developer.suki.ai/api-reference/authentication
Register providers, exchange Partner Tokens for Suki Tokens, and fetch JWKS keys for Register, Login, and token verification on the Suki platform
The Authentication APIs enable partners to register healthcare providers with Suki and exchange a Partner Token for a Suki access token. Providers typically sign in to your application through your identity provider. Your identity provider issues a Partner Token (JWT), and your servers send that token to Suki on Register and Login. Suki does not store provider passwords or host provider sign-in screens.
Register is a one-time call that creates a provider in Suki or links an existing provider to your partner organization. Login returns a Suki Token (`sdp_suki_token`) that authorizes subsequent REST and WebSocket API calls for that provider. Critically, a Partner Token alone is not enough to call session, streaming, or content APIs. You must exchange it for a Suki Token first.
Register and Login are called from your servers and authenticated with your `partner_id` and Partner Token. Suki Tokens are valid for **1 hour**. Call Login again with a valid Partner Token to refresh. The JWKS endpoint is public and returns Suki's public keys so you can verify Suki-issued tokens such as the Suki Token.
## Available endpoints
Register a new provider or link an existing provider to your partner organization
Exchange your Partner Token for a Suki Token
Return the current JSON Web Key Set for verifying Suki-issued tokens
## Related guides
Learn how to authenticate standard partners using the Suki Token
Learn how to authenticate Single Auth Token partners using the Suki Token and send provider identity on every request
Learn how to authenticate Bearer partners using the Suki Token and send provider identity on Login and Register
## Common use cases
Use your own authentication flow while Suki securely authenticates clinicians in the background. No separate Suki sign-in is required.
Renew authentication before Suki Tokens expire so ambient capture, dictation, and other workflows continue without interruption.
Validate Suki Tokens using the JWKS endpoint before creating sessions, processing webhooks, or authorizing API requests.
Use a shared organization-level Partner Token while identifying each clinician with `provider_id` during authentication.
# JWKS URL
Source: https://developer.suki.ai/api-reference/authentication/jwks
GET /api/auth/.well-known/jwks-pub.json
Public key endpoint for JWT token verification and signature validation
Use this public endpoint to get the JWKS (JSON Web Key Set) containing Suki's public keys. Use these keys to verify the signature of any JWT issued by Suki, such as the `suki_token`.
This endpoint follows the **RFC 7517** standard.
**Authentication**
This is a public endpoint and does not require authentication.
## Common integration patterns and use cases
* **Verify issued tokens:** Fetch the JWKS and use the matching public key to verify the signature of a `suki_token` before trusting its claims.
* **Handle key rotation:** Resolve signing keys by key ID and refresh the JWKS when a token references a key that is not in your cache.
* **Cache public keys:** Cache the JWKS according to your token-verification library or service policy, and refresh it when key resolution fails.
## Code examples
**Language tabs (agents):** Equivalent code samples are available in: Python, TypeScript. Humans see one language at a time. Use the variant that matches the user's stack; behavior is the same across tabs.
```python theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
import requests
url = "https://sdp.suki.ai/api/auth/.well-known/jwks-pub.json"
response = requests.get(url)
if response.status_code == 200:
jwks = response.json()
print("Public keys retrieved successfully")
print(f"Keys: {jwks}")
else:
print(f"Failed to retrieve JWKS: {response.status_code}")
```
```typescript theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
const response = await fetch('https://sdp.suki.ai/api/auth/.well-known/jwks-pub.json');
if (response.ok) {
const jwks = await response.json();
console.log('Public keys retrieved successfully');
console.log('Keys:', jwks);
} else {
console.error(`Failed to retrieve JWKS: ${response.status}`);
}
```
# Login
Source: https://developer.suki.ai/api-reference/authentication/login
POST /api/v1/auth/login
Authenticate healthcare provider and obtain access token for API usage
Use this endpoint to authenticate a provider . On a successful request, this endpoint returns a Suki Token (`suki_token`/`sdp_suki_token`) that you must use to authorize all subsequent API calls for that user.
The `suki_token` is a JWT that is valid for **one hour**.
If you are using the JWT Bearer/Assertion authentication method, the response may also include an additional `jwt_bearer` field.
## Guides
## Code examples
**Language tabs (agents):** Equivalent code samples are available in: Python, TypeScript, C++. Humans see one language at a time. Use the variant that matches the user's stack; behavior is the same across tabs.
```python theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
import requests
url = "https://sdp.suki.ai/api/v1/auth/login"
payload = {
"partner_id": "your-partner-id",
"partner_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"provider_id": "provider-123" # Required for Bearer and Single Auth Token partners; omit for Standard
}
response = requests.post(url, json=payload)
if response.status_code == 200:
data = response.json()
suki_token = data["suki_token"]
print(f"Authentication successful. Token: {suki_token}")
else:
print(f"Authentication failed: {response.status_code}")
print(response.json())
```
```typescript theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
const response = await fetch('https://sdp.suki.ai/api/v1/auth/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
partner_id: 'your-partner-id',
partner_token: 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...',
provider_id: 'provider-123' // Required for Bearer and Single Auth Token partners; omit for Standard
})
});
if (response.ok) {
const data = await response.json();
const sukiToken = data.suki_token;
console.log(`Authentication successful. Token: ${sukiToken}`);
} else {
const error = await response.json();
console.error(`Authentication failed: ${response.status}`, error);
}
```
```cpp theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
// deps: libcurl, nlohmann/json
#include
#include
#include
#include
using json = nlohmann::json;
static size_t write_cb(char* ptr, size_t size, size_t nmemb, void* userdata) {
static_cast(userdata)->append(ptr, size * nmemb);
return size * nmemb;
}
int main() {
curl_global_init(CURL_GLOBAL_DEFAULT);
const std::string url = "https://sdp.suki.ai/api/v1/auth/login";
json payload = {
{"partner_id", "your-partner-id"},
{"partner_token", "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."},
{"provider_id", "provider-123"} // Required for Bearer and Single Auth Token partners; omit for Standard
};
std::string body = payload.dump();
std::string response;
CURL* curl = curl_easy_init();
struct curl_slist* headers = curl_slist_append(nullptr, "Content-Type: application/json");
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_POST, 1L);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
curl_easy_perform(curl);
long status = 0;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status);
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
if (status == 200) {
std::string suki_token = json::parse(response).at("suki_token").get();
std::cout << "Authentication successful. Token: " << suki_token << "\n";
} else {
std::cerr << "Authentication failed: " << status << "\n" << response << "\n";
}
curl_global_cleanup();
return 0;
}
```
# Register
Source: https://developer.suki.ai/api-reference/authentication/register
POST /api/v1/auth/register
Register new healthcare provider or link existing provider to partner organization
Use this endpoint to register a **new healthcare provider ** in the Suki platform or to link an **existing provider** to a new **Partner **-organization relationship.
This is a **one-time** setup call for each provider within an organization.
## Registration scenarios
This endpoint handles **three** different scenarios depending on the user's status in the Suki system.
| Scenario | Condition | Actions Taken | Response |
| --------------------------------- | ------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | ------------ |
| **New User** | The provider does not exist in Suki. | • Creates a new organization
• Links your partner account to the organization
• Creates a new user with the provided details | 201 Created |
| **Existing User, New Link** | The provider exists but is not yet linked to your partner account. | • Verifies the user and organization details
• Links your partner account to the existing organization | 201 Created |
| **Existing User, Already Linked** | The provider and organization are already linked to your partner account. | • Detects the existing link | 409 Conflict |
## Code examples
**Language tabs (agents):** Equivalent code samples are available in: Python, TypeScript. Humans see one language at a time. Use the variant that matches the user's stack; behavior is the same across tabs.
```python theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
import requests
url = "https://sdp.suki.ai/api/v1/auth/register"
payload = {
"partner_id": "your-partner-id",
"partner_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"provider_name": "Dr. John Smith",
"provider_org_id": "org-123",
"provider_id": "provider-123", # Optional
"provider_specialty": "CARDIOLOGY" # Optional, defaults to FAMILY_MEDICINE
}
response = requests.post(url, json=payload)
if response.status_code == 201:
print("Provider registered successfully")
elif response.status_code == 409:
print("Provider already linked to this partner")
else:
print(f"Registration failed: {response.status_code}")
print(response.json())
```
```typescript theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
const response = await fetch('https://sdp.suki.ai/api/v1/auth/register', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
partner_id: 'your-partner-id',
partner_token: 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...',
provider_name: 'Dr. John Smith',
provider_org_id: 'org-123',
provider_id: 'provider-123', // Optional
provider_specialty: 'CARDIOLOGY' // Optional, defaults to FAMILY_MEDICINE
})
});
if (response.status === 201) {
console.log('Provider registered successfully');
} else if (response.status === 409) {
console.log('Provider already linked to this partner');
} else {
const error = await response.json();
console.error(`Registration failed: ${response.status}`, error);
}
```
# Bearer Partner Type Authentication
Source: https://developer.suki.ai/api-reference/bearer-partner-authentication
Learn how Bearer partner authentication works, when to pass provider_id, and how to integrate with REST APIs
Some partners cannot include clinician identity in the OAuth 2.0 token they send to Suki. During onboarding, Suki configures these organizations as **Bearer partners**.
Bearer partners authenticate the same way as other server-to-server integrations by sending `partner_token` in the request body of the [Login](/api-reference/authentication/login) and [Register](/api-reference/authentication/register) APIs.
Because `partner_token` does not identify the clinician, Bearer partners must also include `provider_id` in every Login and Register request. Suki uses `provider_id` to identify the clinician and validates it against the expression configured during onboarding.
**Important**
* Bearer authentication **is not the same as** Single Auth Token authentication.
* A partner cannot use both Bearer authentication and Single Auth Token authentication.
* Suki assigns your authentication type during onboarding. If you are unsure whether your organization is configured as a Bearer partner, contact your Suki partnership team.
## Prerequisites
Before you implement Bearer partner authentication, confirm the following:
* **Bearer partner configuration** - Your Suki partner contact confirms that your organization is configured as a Bearer partner.
* **Provider ID format** - You and Suki agree on a stable provider identifier format, such as an email address or external user ID. Use the same format on every login and register call.
* **HTTPS required** - Send [Login](/api-reference/authentication/login) and [Register](/api-reference/authentication/register) requests as HTTPS POST requests with a JSON body. Do not send credentials in query parameters.
## Common integration patterns and use cases
Use Bearer partner authentication only when Suki configured this partner type during onboarding. In each pattern, your backend supplies the active provider identity separately from the shared token.
Reuse the shared `partner_token` across providers, and send the active provider's stable `provider_id` on every [Login](/api-reference/authentication/login) and [Register](/api-reference/authentication/register) request.
Read the signed-in provider from your application session or identity store, then map that identity to the agreed `provider_id` format in the authentication request body.
Keep your existing shared-token model when your identity provider cannot issue a Suki-compatible per-user token. Use `provider_id` as the separate provider identity.
## How Bearer authentication differs
Bearer partners authenticate the same way as other partners, but clinician identity is provided through `provider_id` instead of being derived from `partner_token`.
| Topic | Standard authentication | Bearer authentication |
| :---------------------------------- | :------------------------------------- | :---------------------------------------------------------- |
| Clinician identity | Derived from `partner_token` | Passed as `provider_id` in the request body |
| `partner_token` | Per-user ID token or user-scoped token | Typically one shared `partner_token` for multiple providers |
| `provider_id` on Login and Register | Optional and ignored | **Required** |
| `provider_id` validation | Not applicable | Must match the expression configured during onboarding |
### Bearer authentication behavior
* Login and Register identify the clinician using `provider_id` instead of per-user claims in `partner_token`.
* Clients, including the Web SDK, pass `provider_id` for Bearer partners. Other clients can do the same.
* `provider_id` is **required** and must follow the format agreed during onboarding.
* Bearer partners cannot have the `READONLY` partner access level.
In Bearer partner authentication, the partner application provides the provider identity on behalf of the signed-in user. This is different from the HTTP `Authorization: Bearer` header format.
You still send `partner_token` in the JSON request body for login and register requests.
**Security considerations for Bearer partners**
Accepting end-user identifiers as API metadata has security implications:
* **Encryption:** Send `provider_id` in the JSON body of HTTPS POST requests over TLS. Do not put credentials in query parameters.
* **Impersonation:** Restrict Login and Register to trusted systems so impostors cannot assert another clinician's `provider_id`.
* **Shared token risk:** Unlike signed OAuth tokens, user metadata in the API body can be misused if someone obtains your shared service account `partner_token`. Monitor for abuse and rotate tokens when your identity provider allows it.
## Authentication workflow
For Bearer partners, Suki validates `partner_id` and `provider_id` on [Login](/api-reference/authentication/login). If the provider is not registered, call [Register](/api-reference/authentication/register), then call Login again.
The flow matches standard partners after registration. Learn more about registration scenarios in [Provider authentication](/api-reference/provider-authentication#registration-scenarios).
## Login and Register request fields
Bearer partners use the same login and register endpoints as standard partners. You must include `provider_id` on every login and register call.
### Login
**Endpoint:** [Login](/api-reference/authentication/login)
**Method:** POST
| Field | Bearer partners |
| :-------------- | :------------------------------------------------------------------------------------------------------------------------------------ |
| `partner_id` | Required . Partner ID from Suki. |
| `partner_token` | Required . Shared `partner_token` your integration uses for authentication. |
| `provider_id` | Required . Stable identifier for the provider in your system. Must match the format agreed with Suki during onboarding. |
#### Example request
```bash cURL theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
curl -X POST https://sdp.suki-stage.com/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{
"partner_id": "your-partner-id",
"partner_token": "your-shared-partner-token",
"provider_id": "provider-123"
}'
```
On success, the API returns `suki_token`. Use it as the `sdp_suki_token` header on later Ambient API calls. The token is valid for **1 hour**. To refresh it, call Login again with a valid `partner_token`.
### Register
Register a provider once before their first login. Refer to [Provider authentication](/api-reference/provider-authentication#registration-scenarios) for new user, existing user, and conflict responses.
**Endpoint:** [Register](/api-reference/authentication/register)
**Method:** POST
| Field | Bearer partners |
| :------------------- | :--------------------------------------------------------------------------------------------------------------------------- |
| `partner_id` | Required . Partner ID from Suki. |
| `partner_token` | Required . Shared `partner_token` your integration uses for authentication. |
| `provider_id` | Required . Stable identifier for the provider in your system. Use this as the user identifier at registration. |
| `provider_name` | Required . Display name for the provider. |
| `provider_org_id` | Required . Organization the provider belongs to. |
| `provider_specialty` | Optional . Defaults to `FAMILY_MEDICINE` if omitted. |
#### Example request
```bash cURL theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
curl -X POST https://sdp.suki-stage.com/api/v1/auth/register \
-H "Content-Type: application/json" \
-d '{
"partner_id": "your-partner-id",
"partner_token": "your-shared-partner-token",
"provider_id": "provider-123",
"provider_name": "Dr. Jane Smith",
"provider_org_id": "org-123"
}'
```
For all Register fields and response codes, refer to the [Register API reference](/api-reference/authentication/register).
# Multilingual Support
Source: https://developer.suki.ai/api-reference/capabilities/multilingual
Multi-language support for Ambient sessions and clinical documentation
Quick summary
Multilingual support lets patients and providers speak in their preferred language during clinical conversations, while Suki automatically generates the final clinical note in English.
Last updated:
August 2026
**Multilingual support is supported by:** Ambient APIs, Mobile SDK, Web SDK (v2.1.1+)
Multilingual support lets patients and providers speak in their preferred language during clinical conversations, while Suki automatically generates the final clinical note in English. This removes the need for manual translation and makes healthcare more accessible to diverse patient populations.
When you enable multilingual support for an ambient session , you get the following benefits:
* **Patient comfort**: Patients can communicate in their native language, leading to more accurate information sharing.
* **Better care quality**: When patients speak in their preferred language, they provide more detailed and accurate information.
* **No translation needed**: Clinicians don't need to translate conversations manually, Suki handles it automatically.
* **EHR compatibility**: All notes are generated in English, ensuring compatibility with standard EHR systems.
* **Wider accessibility**: Support for 80+ languages makes healthcare more inclusive.
## How Multilingual improves clinical documentation
Multilingual support improves note quality by capturing the conversation as the patient actually speaks it, then producing documentation in English that your EHR can use.
| What you configure | How the note gets better |
| ---------------------------------------------- | ----------------------------------------------------------------------------- |
| Multilingual enabled on the ambient session | Patients can speak in their preferred language without losing clinical detail |
| Automatic language detection and transcription | More of the visit content is captured accurately |
| English clinical note generation | Notes stay EHR-ready without a separate translation step |
When patients speak freely in their preferred language, the source conversation is usually richer and more accurate. Suki turns that into an English clinical note, so documentation quality improves without adding translation work for the clinician.
## How Multilingual transcription works
When you enable multilingual support for an ambient session, Suki automatically:
1. **Detects the language** spoken during the conversation.
2. **Transcribes the audio** in the detected language.
3. **Translates and processes** the conversation content.
4. **Generates the clinical note** in English.
The transcript API returns a `lang_id` field that identifies which language was detected for each segment of the conversation. This helps you understand what language was spoken during different parts of the session.
## How to enable multilingual support
Multilingual support lets patients and providers speak in their preferred language. Suki still generates the clinical note in English. How you turn it on depends on the product you use.
Click the tabs below to see the implementation details for your integration path.
**Tabs (agents):** humans see one product tab at a time. Read all three.
* **Ambient APIs:** Multilingual is on by default. Do not send a `multilingual` field (deprecated). Create the session, stream audio, end, then read `lang_id` from the transcript API if needed. Contact Suki support to disable org-wide.
* **Web SDK:** Multilingual is automatic for ambient (`v2.1.1+`). No multilingual flag in `ambientOptions`. Notes return in English. See [Multilingual sessions](/web-sdk/guides/ambient-multilingual).
* **Mobile SDK:** Multilingual is **off by default**. Set `SukiAmbientConstant.kIsMultilingual` to `true` at `createSession` when non-English speech is possible. Cannot change mid-session.
Multilingual support is **enabled by default** for ambient sessions. You do not need to pass a `multilingual` field when you create a session. The old `multilingual` parameter is deprecated.
Call the Create ambient session API without a `multilingual` field. Suki enables multilingual processing for the session automatically.
```python Python theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
import requests
response = requests.post(
"https://sdp.suki.ai/api/v1/ambient/session/create",
json={
# Optional: "ambient_session_id", "encounter_id", "emr_encounter_id"
},
headers={
"sdp_suki_token": "",
"sdp_provider_id": "",
},
)
if response.status_code == 200:
session = response.json()
ambient_session_id = session["ambient_session_id"]
print(f"Session created: {ambient_session_id}")
else:
print(f"Failed to create session: {response.status_code}")
print(response.json())
```
Refer to [Create ambient session](/api-reference/ambient-sessions/create) for the full request and response.
Stream the encounter audio, then end the session so Suki can generate the English clinical note. Multilingual processing applies for the full session and cannot be changed mid-session.
After processing completes, call the transcript API if you need language detection. Each transcript segment can include a `lang_id` for the language Suki detected.
Refer to [Get ambient session Transcript](/api-reference/ambient-content/transcript) and the [Language code reference](/api-reference/capabilities/multilingual#language-code-reference) for more details.
If your organization must turn multilingual support off, contact the Suki support team. Do not rely on the deprecated `multilingual` request field.
Refer to [Deprecations](/updates/deprecations) for the deprecation notice.
The Web SDK enables Multilingual support automatically for ambient sessions. You do not configure a multilingual flag in `ambientOptions`. Requires Web SDK `v2.1.1+`.
Initialize authentication and mount the ambient experience as usual. Multilingual processing is already on for the session.
Refer to [Ambient implementation](/web-sdk/guides/ambient-implementation) for mount options and [Multilingual sessions](/web-sdk/guides/ambient-multilingual) for Web SDK behavior.
Providers and patients can speak in their preferred language during the ambient session. You do not need to select a language in the SDK.
After submit, the Web SDK returns the generated note in English. Handle `onNoteSubmit` (React) or `note-submission:success` (JavaScript and React) as usual.
Refer to [Note management](/web-sdk/guides/note-management) for more details.
On Mobile SDK, Multilingual support is **off by default**. Set `SukiAmbientConstant.kIsMultilingual` to `true` when you create the session if the conversation may include languages other than English. Once set, you cannot change it for that session.
Pass `kIsMultilingual: true` in the session info dictionary when you call `createSession`. Store the returned `sessionId` for recording and session-level content retrieval.
```swift Swift theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
let sessionInfo: [String: AnyHashable] = [
SukiAmbientConstant.kSessionId: encounterId, // Ambient API encounter_id for re-ambient
SukiAmbientConstant.kIsMultilingual: true
]
SukiAmbientCoreManager.shared.createSession(
with: sessionInfo,
onCompletion: { result in
switch result {
case .success(let sessionResponse):
let sessionId = sessionResponse.sessionId
// Store sessionId for recording and content retrieval
print("Session created successfully: \(sessionId)")
case .failure(let error):
print("Error creating session: \(error)")
}
}
)
```
Refer to [Create session](/mobile-sdk/ambient-guides/create-session) for session info parameters.
Call `setSessionContext(with:)` as needed, then start recording. Capture the visit in the patient's preferred language. End the session when capture is complete so note generation can run.
After processing completes, retrieve content with `content(for:)`. The generated clinical note is in English even when the conversation was in another language.
Refer to [Session status and content retrieval](/mobile-sdk/ambient-guides/session-status-and-content-retrieval) for more details.
For Ambient APIs and Web SDK, multilingual support is on by default. For Mobile SDK, set `kIsMultilingual` to `true` when you create the session. In all products, the setting applies to the entire session and cannot be changed mid-session.
## Supported languages for Multilingual support
Suki supports over **80 languages** for Multilingual ambient sessions. Refer to the table below to see the languages currently supported:
| | | | | |
| ------------------- | ------------------ | ----------- | ------------- | --------- |
| Spanish | Norwegian | Macedonian | Kazakh | Yoruba |
| Italian | Finnish | Hungarian | Icelandic | Telugu |
| English | Vietnamese | Tamil | Marathi | Khmer |
| Portuguese | Thai | Hindi | Maori | Malayalam |
| German | Slovak | Estonian | Swahili | Lao |
| Japanese | Greek | Urdu | Armenian | Punjabi |
| Polish | Czech | Latvian | Belarusian | Gujarati |
| Russian | Croatian | Slovenian | Nepali | Somali |
| Dutch | Danish | Azerbaijani | Occitan | Bengali |
| Indonesian | Tagalog | Hebrew | Lingala | Georgian |
| Catalan | Korean | Lithuanian | Maltese | Assamese |
| French | Romanian | Persian | Tajik | Mongolian |
| Turkish | Bulgarian | Welsh | Luxembourgish | Myanmar |
| Swedish | Galician | Serbian | Hausa | Shona |
| Ukrainian | Bosnian | Afrikaans | Uzbek | Amharic |
| Malay | Arabic | Kannada | Pashto | Sindhi |
| Chinese (Cantonese) | Chinese (Mandarin) | | | |
## Language code reference
Pick a **Letter** below to show languages starting with that letter, then use the table to map each `lang_id` to its corresponding language. This helps you interpret the language codes returned by the Transcript API.
We regularly update this list as we add support for new languages. If a language is not included in this table, it is not yet supported.
**A**
| Language | Language ID (`lang_id`) |
| ----------- | ----------------------- |
| afrikaans | af |
| amharic | am |
| arabic | ar |
| armenian | hy |
| assamese | as |
| azerbaijani | az |
**B**
| Language | Language ID (`lang_id`) |
| ---------- | ----------------------- |
| belarusian | be |
| bengali | bn |
| bosnian | bs |
| bulgarian | bg |
**C**
| Language | Language ID (`lang_id`) |
| ------------------ | ----------------------- |
| catalan | ca |
| chinese | zh |
| chinese\_cantonese | yue |
| chinese\_mandarin | cmn |
| croatian | hr |
| czech | cs |
**D**
| Language | Language ID (`lang_id`) |
| -------- | ----------------------- |
| danish | da |
| dutch | nl |
**E**
| Language | Language ID (`lang_id`) |
| -------- | ----------------------- |
| english | en |
| estonian | et |
**F**
| Language | Language ID (`lang_id`) |
| -------- | ----------------------- |
| finnish | fi |
| french | fr |
**G**
| Language | Language ID (`lang_id`) |
| -------- | ----------------------- |
| galician | gl |
| georgian | ka |
| german | de |
| greek | el |
| gujarati | gu |
**H**
| Language | Language ID (`lang_id`) |
| --------- | ----------------------- |
| hausa | ha |
| hebrew | he |
| hindi | hi |
| hungarian | hu |
**I**
| Language | Language ID (`lang_id`) |
| ---------- | ----------------------- |
| icelandic | is |
| indonesian | id |
| italian | it |
**J**
| Language | Language ID (`lang_id`) |
| -------- | ----------------------- |
| japanese | ja |
**K**
| Language | Language ID (`lang_id`) |
| -------- | ----------------------- |
| kannada | kn |
| kazakh | kk |
| khmer | km |
| korean | ko |
**L**
| Language | Language ID (`lang_id`) |
| ------------- | ----------------------- |
| lao | lo |
| latvian | lv |
| lingala | ln |
| lithuanian | lt |
| luxembourgish | lb |
**M**
| Language | Language ID (`lang_id`) |
| ---------- | ----------------------- |
| macedonian | mk |
| malay | ms |
| malayalam | ml |
| maltese | mt |
| maori | mi |
| marathi | mr |
| mongolian | mn |
| myanmar | my |
**N**
| Language | Language ID (`lang_id`) |
| --------- | ----------------------- |
| nepali | ne |
| norwegian | no |
**O**
| Language | Language ID (`lang_id`) |
| -------- | ----------------------- |
| occitan | oc |
**P**
| Language | Language ID (`lang_id`) |
| ---------- | ----------------------- |
| pashto | ps |
| persian | fa |
| polish | pl |
| portuguese | pt |
| punjabi | pa |
**R**
| Language | Language ID (`lang_id`) |
| -------- | ----------------------- |
| romanian | ro |
| russian | ru |
**S**
| Language | Language ID (`lang_id`) |
| --------- | ----------------------- |
| serbian | sr |
| shona | sn |
| sindhi | sd |
| slovak | sk |
| slovenian | sl |
| somali | so |
| spanish | es |
| swahili | sw |
| swedish | sv |
**T**
| Language | Language ID (`lang_id`) |
| -------- | ----------------------- |
| tagalog | tl |
| tajik | tg |
| tamil | ta |
| telugu | te |
| thai | th |
| turkish | tr |
**U**
| Language | Language ID (`lang_id`) |
| --------- | ----------------------- |
| ukrainian | uk |
| urdu | ur |
| uzbek | uz |
**V**
| Language | Language ID (`lang_id`) |
| ---------- | ----------------------- |
| vietnamese | vi |
**W**
| Language | Language ID (`lang_id`) |
| -------- | ----------------------- |
| welsh | cy |
**Y**
| Language | Language ID (`lang_id`) |
| -------- | ----------------------- |
| yoruba | yo |
## Best practices
* **Enable when needed**: Only enable multilingual support when you expect conversations in multiple languages. This optimizes performance.
* **Set patient language preference**: If you know the patient's preferred language, you can display this information in your UI to help providers prepare.
* **Monitor language detection**: Use the `lang_id` from transcripts to understand language usage patterns in your application.
* **Test with your languages**: Verify multilingual support works correctly with the languages your patients commonly use.
* **Note language in UI**: Consider displaying the detected language in your UI so providers know what language was spoken.
## Related APIs
Use these APIs to work with multilingual support:
Retrieve transcripts with language detection information (`lang_id`)
Create ambient sessions. Multilingual support is enabled by default
# Personalization
Source: https://developer.suki.ai/api-reference/capabilities/personalization
Customize clinical note generation based on provider preferences and specialty
Quick summary
Personalization allows you to customize how Suki generates clinical notes based on each provider's preferences. Control how much detail (short and concise, balanced, or very detailed) and how it's formatted (continuous paragraphs or bullet points).
Last updated:
August 2026
**Personalization is supported by:** APIs, Web SDK, Mobile SDK
Personalization allows you to help providers customize how Suki generates their clinical notes. Every provider has different preferences, some want short bullet points, others want detailed paragraphs. Personalization ensures each provider gets notes that match their style.
When you use personalization, you can customize two aspects of note generation:
* **How much detail**: Short and concise, balanced, or very detailed notes.
* **How it's formatted**: Continuous paragraphs (narrative) or bullet points.
Once you set a provider's preferences, Suki automatically applies them to all future notes for that provider. You set it once, and Suki remembers, no need to send preferences with every request.
Using personalization, you get the following benefits:
* **Provider satisfaction**: Notes match each provider's preferred style and level of detail.
* **Consistent documentation**: Once set, preferences apply automatically to all future notes.
* **Efficiency**: Providers don't need to manually adjust notes; they're generated according to their preferences.
* **Flexibility**: Different providers can have different preferences based on their specialty and workflow.
* **Better adoption**: When notes match provider preferences, they're more likely to use and trust the system.
## How Personalization improves clinical documentation
Personalization reduces post-generation editing by aligning note style with how each provider already documents. This helps clinicians trust the generated notes and reduces the need for manual edits before signing.
| What you configure | How the note gets better |
| ------------------------------------------------ | ---------------------------------------------------------------------- |
| Verbosity (`CONCISE`, `BALANCED`, or `DETAILED`) | Notes have the right amount of detail for that provider |
| Section format (`NARRATIVE` or `BULLETED`) | Sections match the reading style the provider expects |
| Persistent provider preferences | Future notes stay consistent without sending settings on every session |
When notes match provider preference, clinicians usually make fewer style edits before sign. That improves trust and adoption, and it shortens the path from generated note to final documentation.
## How Personalization works
Settings are saved at the user level (not per session) and applied to all future note generation for that provider.
**The process:**
1. **Set preferences**: Use the Mobile SDK `setPersonalizationPreferences` method or the User Preferences API to configure a provider's preferences.
2. **Automatic application**: Suki applies these settings to all future notes for that provider.
3. **Update anytime**: Update preferences at any time; the most recent settings always take effect.
Settings are **persistent** and **user-specific**. You don't need to send preferences with every session request.
## How to set Personalization preferences
Personalization is saved for the provider, not for one ambient session. Set preferences before the visit starts. Suki uses the latest saved values for that provider's future notes.
Click the tabs below to see the implementation details for your integration path.
**Tabs (agents):** humans see one product tab at a time. Read all three.
* **Ambient APIs:** `PATCH /api/v1/user/preferences` with `personalization_preference` (verbosity `CONCISE` | `BALANCED` | `DETAILED`; optional section\_format per LOINC). Preferences are per `sdp_provider_id`.
* **Mobile SDK:** Set personalization through Mobile SDK provider preference APIs (see this tab).
* **Web SDK:** Configure personalization in the Web SDK ambient / preferences flow (see this tab). Preferences apply to future notes for that provider.
Save preferences with the [User Preferences API](/api-reference/user-preferences/preferences). Send a `PATCH` to `/api/v1/user/preferences` and set `sdp_provider_id` to the provider whose note style you want to change.
Use that provider's Suki token and provider ID. Preferences are user-specific, so use the correct `sdp_provider_id` for each clinician.
Set verbosity to `CONCISE`, `BALANCED`, or `DETAILED`. Optionally set section format to `NARRATIVE` or `BULLETED` for supported LOINC sections: History of Present Illness (`10164-2`), Assessment and Plan (`51847-2`), Assessment (`51848-0`), and Plan (`18776-5`).
This is a `PATCH` request, so send only the fields you want to change. You can update verbosity, section format, or both.
```python Python theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
import requests
response = requests.patch(
"https://sdp.suki.ai/api/v1/user/preferences",
headers={
"sdp_suki_token": "",
"sdp_provider_id": "",
"Content-Type": "application/json",
},
json={
"personalization_preference": {
"verbosity": "CONCISE",
"section_format": [
{
"loinc": "10164-2",
"style": "NARRATIVE",
}
],
}
},
)
if response.status_code == 200:
print("Preferences updated successfully")
print(response.json())
else:
print(f"Failed to update preferences: {response.status_code}")
print(response.json())
```
Refer to [User Preferences](/api-reference/user-preferences/preferences) for the full request and response schema.
After the update succeeds, create ambient sessions as usual for the same provider. You do not send personalization fields in session context. Suki applies the saved preferences when it generates the note.
When a provider changes their preferred style, call the same `PATCH` endpoint again before the next visit. The most recent values apply to future notes.
Call `setPersonalizationPreferences` after you initialize the Mobile SDK. Preferences are saved for the signed-in provider and apply to future notes. Requires Mobile SDK `v2.3.0+`.
Initialize and authenticate the Mobile SDK for the provider whose preferences you want to update.
Include `verbosity`, `section_format`, or both. For each section format entry, pass a supported LOINC code and either `NARRATIVE` or `BULLETED`.
```swift Swift theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
let preferences: [String: AnyHashable] = [
"verbosity": "CONCISE",
"section_format": [
[
"loinc": "10164-2",
"style": "NARRATIVE"
],
[
"loinc": "51847-2",
"style": "BULLETED"
]
]
]
```
Call `setPersonalizationPreferences` and wait for the completion handler before you start recording.
```swift Swift theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
SukiAmbientCoreManager.shared.setPersonalizationPreferences(preferences) { result in
switch result {
case .success:
print("Preferences updated successfully")
case .failure(let error):
print("Error updating preferences: \(error)")
}
}
```
Do not pass a nil or empty preferences dictionary. The SDK returns `invalidPreferences` for that input.
Create the session, set clinical context, record, and end the session as usual. Do not add personalization fields to `setSessionContext(with:)`. The SDK uses the saved preferences for future note generation.
Call `setPersonalizationPreferences` again when the provider changes verbosity or section style. The most recently saved values apply to future notes.
The Web SDK does not take personalization fields in `ambientOptions`. Save preferences from your backend with the User Preferences API for the same provider, then run the Web SDK ambient workflow as usual.
Use the same provider identity that authenticates the Web SDK. Your backend must update preferences for that provider.
Call `PATCH /api/v1/user/preferences` from your backend with the provider's Suki token and `sdp_provider_id`. Keep API credentials on the server, not in browser code.
```typescript TypeScript theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
const response = await fetch(
"https://sdp.suki.ai/api/v1/user/preferences",
{
method: "PATCH",
headers: {
"Content-Type": "application/json",
"sdp_suki_token": "",
"sdp_provider_id": "",
},
body: JSON.stringify({
personalization_preference: {
verbosity: "CONCISE",
section_format: [
{
loinc: "10164-2",
style: "NARRATIVE",
},
],
},
}),
},
);
if (response.ok) {
const data = await response.json();
console.log("Preferences updated successfully", data);
} else {
const error = await response.json();
console.error(`Failed to update preferences: ${response.status}`, error);
}
```
Refer to [User Preferences](/api-reference/user-preferences/preferences) for the full request and response schema.
After preferences are saved, initialize and mount the Web SDK for that provider. Configure note sections in `ambientOptions.sections` as usual. Do not add verbosity or section-format fields to `ambientOptions`.
Refer to [Ambient implementation](/web-sdk/guides/ambient-implementation) and [AmbientOptions](/web-sdk/api-reference/types/ambient-options) for more details.
Providers capture and submit the session in the Web SDK. Suki applies the latest saved preferences when it generates the note.
When a provider changes their preferred style in your app, call the User Preferences API again from your backend before the next visit.
## Personalization options
With Personalization, you can customize two aspects of note generation: verbosity and section style.
### Verbosity
Verbosity controls how much detail is included in the generated note. This setting applies to **all note sections**.
**Applies to:** All note sections
Generates shorter, to-the-point notes with essential information only.
Provides a moderate level of detail: comprehensive but not excessive.
Generates comprehensive notes with extensive detail and context.
### Section style
Section style lets you control the formatting style for specific clinical sections. Set different styles for different sections as needed.
**Applies to:**
* History of Present Illness **(10164-2)**.
* Assessment and Plan **(51847-2)**.
* Assessment **(51848-0)**.
* Plan **(18776-5)**.
Generates notes in a continuous, story-like format that flows naturally.
Generates notes using bullet points for easy scanning and quick reference.
**Example:**
```json JSON theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
"section_format": [
{
"loinc": "10164-2",
"style": "NARRATIVE"
},
{
"loinc": "51848-0",
"style": "BULLETED"
}
]
```
## Best practices
* **Set defaults early**: Configure preferences when providers are first registered in your system.
* **Offer a UI**: Let providers choose their preferences through your application's settings page.
* **Explain options**: Help providers understand the difference between verbosity levels and style options.
* **Update when requested**: Make it easy for providers to change their preferences as their needs evolve.
* **Test with real notes**: Verify that preferences produce notes that match provider expectations.
## Related APIs
Set and update personalization preferences for providers
# Problem-Based Charting
Source: https://developer.suki.ai/api-reference/capabilities/problem-based-charting
Problem-based clinical documentation approach with diagnosis context integration
**Updated**
* Structured data output now includes **HCC** codes alongside ICD10, IMO, and SNOMED for each suggested diagnosis.
* For how the system reconciles those diagnoses with the conversation (API flow), refer to [Reconciliation](/api-reference/capabilities/problem-based-charting#reconciliation). For Web SDK implementation details, refer to [Existing patient diagnoses](/web-sdk/guides/ambient-problem-based-charting#existing-patient-diagnoses).
Quick summary
Problem-Based Charting (PBC) is a clinical documentation approach that organizes notes by patient problems instead of traditional note sections, and suggests diagnoses for each problem.
Last updated:
August 2026
**Problem-Based Charting is supported by:** Ambient APIs, Web SDK, Mobile SDK
Problem-Based Charting (PBC) organizes clinical notes by patient problems instead of traditional note sections. When you use PBC, Suki generates two things:
1. **Clinical note**: Organized by each problem or diagnosis.
2. **Structured artifacts**: Suggested diagnoses with ICD10, IMO, SNOMED, and HCC codes.
PBC notes are different from traditional notes. In traditional notes, information is organized by sections like "History," "Assessment," and "Plan." In PBC notes, information is organized by each problem, grouping all related information together.
When you enable PBC, providers get a note that has the following benefits:
* **Easier to read**: See everything about each problem in one place.
* **Better continuity**: Includes existing problems from previous visits automatically.
* **Complete picture**: Captures both old and new problems discussed during the visit.
* **Structured data**: Generates standardized codes (ICD10, IMO, SNOMED, and HCC) for EHR integration.
## How Problem-Based Charting improves clinical documentation
Problem-Based Charting improves documentation by organizing the note around clinical problems and returning coded diagnoses that your application can use to send to the EHR.
| What you configure | How the note gets better |
| ----------------------------------------------- | -------------------------------------------------------------------------------- |
| One PBN section (for example Assessment & Plan) | Related history, assessment, and plan content groups under each problem |
| Existing patient diagnoses in session context | Continuity of care is reflected instead of treating every visit as a blank slate |
| Structured diagnosis output after generation | Suggested ICD10, IMO, SNOMED, and HCC codes support coding and EHR workflows |
Traditional section-based notes can scatter details about one problem across the note. PBC keeps problem-specific content together and adds structured diagnosis artifacts, so review, coding, and EHR submission are closer to how clinicians manage care.
## How to use PBC for APIs and SDKs
You enable PBC by doing three things:
* Define which note sections to generate and which one is the PBN.
* Pass the patient's existing diagnoses into session context.
* Read structured diagnosis output after the session finishes.
**Workflow tabs (agents):** (1) Configure sections for PBC / mark one PBN section, (2) Provide existing diagnoses in context, (3) Retrieve diagnosis results after processing. Details differ by Web SDK, Ambient APIs, and Mobile SDK.
* **Web SDK:** In `ambientOptions.sections`, set `isPBNSection: true` on **exactly one** section. Rules for defaults, overrides, and errors are in [Configuration rules](/api-reference/capabilities/problem-based-charting#configuration-rules-for-pbc).
* **Ambient APIs:** Send the LOINC `sections` array in the [Context API](/api-reference/ambient-sessions/context) body so the Suki backend knows which note sections to generate. PBN defaults (for example, when Assessment & Plan, LOINC `51847-2`, is treated as the PBN) follow the same rules as in [Configuration rules](/api-reference/capabilities/problem-based-charting#configuration-rules-for-pbc).
* **Mobile SDK (iOS):** Pass LOINC sections in `kSections` via `setSessionContext`. See the Mobile SDK [Create session](/mobile-sdk/ambient-guides/provide-clinical-context) guide for field names and examples.
* **Ambient APIs:** Include diagnoses when you POST (and, if needed, PATCH) session context.
* **Web SDK:** Use `ambientOptions.diagnoses`. Refer to the [AmbientOptions type](/web-sdk/api-reference/types/ambient-options) for more details.
* **Mobile SDK (iOS):** Use `SukiAmbientConstant.kDiagnosisInfo` in `setSessionContext`. Refer to the Mobile SDK [Create session](/mobile-sdk/ambient-guides/provide-clinical-context) guide for more details.
* **Ambient APIs:** After the session has finished processing, use the [Structured Data](/api-reference/ambient-content/structured-data) or [Encounter Structured Data](/api-reference/ambient-content/encounter-structured-data) APIs and related endpoints to read suggested diagnoses.
* **Mobile SDK (iOS):** After the session completes, call `getStructuredData(for:)`. See [Session status and content retrieval](/mobile-sdk/ambient-guides/session-status-and-content-retrieval#get-structured-data).
* **Web SDK:** After the user submits the ambient session and note generation succeeds, read results from the **`onNoteSubmit`** callback (React) or the **`note-submission:success`** event (JavaScript and React). Refer to [Receiving note content](/web-sdk/guides/note-management#receiving-note-content), [Response structure](/web-sdk/guides/note-management#response-structure), [NoteContent](/web-sdk/api-reference/types/note-content), and [Diagnosis](/web-sdk/api-reference/types/diagnosis) for more details.
## Implementation examples
**Implementation example tabs (agents):** Web SDK (`ambientOptions.diagnoses` / `isPBNSection`), Mobile SDK iOS (`kDiagnosisInfo` / `getStructuredData`), and Ambient APIs (Context API + structured data endpoints). Use the tab that matches your integration.
Use the `diagnoses` block in `ambientOptions` to provide existing patient diagnoses when starting a session.
**Code example:**
```javascript JavaScript theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
sdkClient.mount({
rootElement: document.getElementById("suki-root"),
encounter: encounterDetails,
ambientOptions: {
sections: [
{ loinc: "51847-2", isPBNSection: true }, // Assessment and Plan as PBN
{ loinc: "11450-4" }, // Problem List
{ loinc: "29545-1" }, // Physical Exam
],
diagnoses: { // [!code ++:14] New in v2.1.2
values: [
{
codes: [
{
code: "I10",
description: "Essential hypertension",
type: "ICD10",
},
],
diagnosisNote: "Hypertension",
},
],
},
},
});
```
Use `setSessionContext` to pass LOINC sections and structured diagnosis information. After the session ends, call `getStructuredData(for:)` to retrieve diagnoses and other structured output. Refer to the Mobile SDK [Create session](/mobile-sdk/ambient-guides/provide-clinical-context) guide for more details.
**Code example:**
```swift Swift theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
let context = SukiAmbientContext(
sections: [
SukiAmbientSection(loinc: "51847-2", isPBNSection: true), // Assessment and Plan as PBN
SukiAmbientSection(loinc: "11450-4"), // Problem List
SukiAmbientSection(loinc: "29545-1"), // Physical Exam
],
diagnosisInfo: [
SukiAmbientDiagnosisInfo(
codes: [
SukiAmbientCode(code: "I10", description: "Essential hypertension", type: .icd10),
],
diagnosisNote: "Hypertension",
),
],
)
```
Use the [Context API](/api-reference/ambient-sessions/context) to provide existing patient diagnoses when starting a session.
**Code example:**
```python Python theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
payload = {
"diagnoses": {
"values": [
{
"codes": [
{
"code": "I10",
"description": "Essential hypertension",
"type": "ICD10",
},
],
"diagnosis_note": "Hypertension",
},
],
},
}
```
**APIs to use:**
Provide initial diagnoses when starting a session
Update or add diagnoses during a session
## Configuration rules for PBC
When configuring PBC, follow these rules:
Only one section can have `isPBNSection: true`. If multiple sections are marked as PBN, the ambient session will fail to start.
If no section includes the `isPBNSection` flag and the Assessment & Plan section (51847-2) is present, that section automatically becomes the PBN.
Explicitly set `isPBNSection: false` to prevent automatic PBN assignment.
For a given ambient session, **only one section** can have `isPBNSection: true`. If more than one section is marked as PBN, the ambient session will fail to start.
## Core principles
Understanding these principles helps you use PBC effectively:
The ICD10 code is treated as the **primary identifier** for all clinical problems during processing.
**System uses ICD10 for:**
* Diagnosis identification and matching.
* Clinical problem categorization.
* Cross-session diagnosis continuity.
* Normalization of other code types (IMO , SNOMED).
Non-ICD10 codes are automatically converted to ICD10 equivalents when possible.
All diagnosis generation is done on a **best-effort** basis. The system prioritizes returning partial, useful information over failing an entire request due to an issue with a single data point.
In reambient scenarios, diagnoses from previous sessions are **not automatically carried forward**. You must provide the full context for each new session.
**Your Responsibility**: Include all relevant diagnoses (previous + new + modified) in each session context.
## How PBC works
PBC processes diagnoses in three steps:
You provide existing diagnoses when starting the session using the Context APIs.
Suki's AI analyzes the conversation and identifies new problems or updates to existing diagnoses.
Suki generates a clinical note organized by problem and structured artifacts with standardized codes.
```mermaid actions={false} theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
%%{init: {'theme':'base', 'themeVariables': { 'primaryColor':'#FFE148','primaryTextColor':'#111827','primaryBorderColor':'#FFE148','lineColor':'#FFE148','secondaryColor':'#FFF394','tertiaryColor':'#FFFADE','mainBkg':'#FFF394','secondBkg':'#FFFADE','tertiaryBorderColor':'#FFE148','border1':'#FFE148','border2':'#FFE148','arrowheadColor':'#FFE148','fontFamily':'Inter, system-ui, sans-serif','fontSize':'14px','nodeBorder':'#FFE148','edgeLabelBackground':'#FFE148','clusterBkg':'#FFFADE','clusterBorder':'#FFE148','defaultLinkColor':'#FFE148','titleColor':'#111827','nodeTextColor':'#111827'}}}%%
flowchart TD
A[Session audio/transcript] --> B[ML processing]
B --> C[Suki Backend]
C --> D{Valid ICD10?}
D -->|Yes| E[Case 1: Enrich with IMO
Add description, laterality, IMO code]
D -->|No| F[Case 2: Enrich with IMO
Add ICD10, description, laterality, IMO code]
E --> G[Final Output
Problems with Code, Description, Diagnosis Note]
F --> G
style A fill:#FFF394,stroke:#FFE148,stroke-width:2px,color:#111827
style B fill:#FFF394,stroke:#FFE148,stroke-width:2px,color:#111827
style C fill:#FFF394,stroke:#FFE148,stroke-width:2px,color:#111827
style D fill:#FFE148,stroke:#FFE148,stroke-width:2px,color:#111827
style E fill:#FFF394,stroke:#FFE148,stroke-width:2px,color:#111827
style F fill:#FFF394,stroke:#FFE148,stroke-width:2px,color:#111827
style G fill:#FFF394,stroke:#FFE148,stroke-width:2px,color:#111827
```
Suki backend includes the ML-suggested problem name and returns standardized ICD10 code, description, and IMO equivalent in the final output.
### Validation rules
Each diagnosis in your request must follow these rules:
* **One code per diagnosis**: Each diagnosis must have exactly one code (ICD10 or IMO).
* **No mixed codes**: A single diagnosis cannot contain multiple code types.
* **Code required**: Every diagnosis must include a code.
### Input processing
Suki processes diagnoses through normalization and deduplication:
Normalization does not preserve the original input code. If a code cannot map to an ICD10 equivalent, **Suki skips the entire diagnosis object** and continues processing the remaining input.
### Reconciliation flow for APIs and SDKs
When you send existing diagnoses at session start, Suki reconciles them with what is discussed during the visit. Matching problems are merged into one entry, so the structured output stays clean and free of duplicates.
For Ambient APIs, send diagnoses through the Context API, then retrieve results from the Structured Data API. For Web SDK, pass diagnoses in `ambientOptions.diagnoses`. Refer to [Existing patient diagnoses](/web-sdk/guides/ambient-problem-based-charting#existing-patient-diagnoses) for the Web SDK flow.
**What happens to each diagnosis you send:**
* **Discussed in the session**: The diagnosis is updated and returned in the structured data output.
* **Not discussed in the session**: The diagnosis is not included in the final output.
* **Matches something discussed**: Your diagnosis and the one discussed are merged into a single entry (no duplicate).
The Structured Data API returns only diagnoses that were updated or newly generated.
### How diagnoses are generated
During the session, Suki's AI analyzes the conversation and takes one of three actions on the diagnoses you provided:
1. **Update existing diagnosis**: Modify the content if it was discussed.
2. **Generate new diagnosis**: Create a new diagnosis if a new problem was discussed.
3. **Keep unchanged**: Leave a diagnosis untouched if it wasn't significantly discussed.
Only diagnoses that were **updated or newly generated** are returned in the final output. Untouched diagnoses are not returned. If a diagnosis wasn't discussed, it won't appear in the output.
### Output enrichment
For any diagnosis that was updated or newly generated, Suki enriches it with:
* **ICD10 code**: Standard diagnosis code.
* **IMO code**: Intelligent Medical Objects code.
* **SNOMED code**: SNOMED CT code (when available).
* **HCC code**: CMS-HCC model category derived from the ICD-10-CM code. The description uses the format `CMS-HCC model category `.
* **Laterality**: Left/right/bilateral information when applicable.
* **Post coordination flag**: Indicates if the diagnosis requires additional modifiers.
The IMO code returned in the output may be different from any IMO code that was sent in the input payload. This is normal: Suki uses the most accurate code based on the conversation content.
If enrichment fails, Suki still returns the diagnosis with available information to ensure you don't lose generated content.
### Retrieving generated diagnoses
Refer to the [How to use PBC for APIs and SDKs](/api-reference/capabilities/problem-based-charting#how-to-use-pbc-for-apis-and-sdks) section for more details.
## Handling re-Ambient scenarios
In reambient scenarios, where a single patient encounter involves multiple recording sessions, understanding how context is managed is critical.
Sessions do not **automatically inherit diagnoses** from previous sessions. Each session starts fresh. You must provide all relevant diagnoses for each new session.
**What this means:**
* When you use APIs in your implementation, only diagnoses passed via the Context APIs for the current session are considered.
* Diagnoses from previous sessions are **not automatically carried forward**.
* You must provide the complete and current list of all relevant diagnoses for each new session.
**Your responsibilities:**
Because sessions don't inherit context, you must provide the complete list of all relevant diagnoses for each new session. This includes:
* **Modified diagnoses**: Any diagnoses that were changed by the provider in previous sessions.
* **New diagnoses**: Any newly added diagnoses from previous sessions.
* **Existing diagnoses**: Diagnoses from previous sessions that should be retained.
**Recommended workflow:**
```mermaid actions={false} theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
%%{init: {'theme':'base', 'themeVariables': { 'primaryColor':'#FFE148','primaryTextColor':'#1a1a1a','primaryBorderColor':'#FFE148','lineColor':'#FFE148','secondaryColor':'#FFF394','tertiaryColor':'#FFFADE','mainBkg':'#FFF394','secondBkg':'#FFFADE','border1':'#FFE148','border2':'#FFE148','arrowheadColor':'#FFE148','fontFamily':'Inter, system-ui, sans-serif','fontSize':'14px','nodeBorder':'#FFE148','clusterBkg':'#FFFADE','clusterBorder':'#FFE148','defaultLinkColor':'#FFE148','titleColor':'#1a1a1a','edgeLabelBackground':'#FFE148','nodeTextColor':'#1a1a1a'}}}%%
graph TD
A[Start session] --> B[Context with
existing diagnoses]
B --> C[Conduct encounter]
C --> D[End session]
D --> E[Get generated
diagnoses]
E --> F{Reambient session?}
F -->|Yes| G[Include all
relevant diagnoses]
F -->|No| H[Complete]
G --> B
style A fill:#FFF394,stroke:#FFE148,stroke-width:2px,color:#1a1a1a
style B fill:#FFF394,stroke:#FFE148,stroke-width:2px,color:#1a1a1a
style C fill:#FFF394,stroke:#FFE148,stroke-width:2px,color:#1a1a1a
style D fill:#FFF394,stroke:#FFE148,stroke-width:2px,color:#1a1a1a
style E fill:#FFF394,stroke:#FFE148,stroke-width:2px,color:#1a1a1a
style F fill:#FFE148,stroke:#FFE148,stroke-width:2px,color:#1a1a1a
style G fill:#FFF394,stroke:#FFE148,stroke-width:2px,color:#1a1a1a
style H fill:#FFF394,stroke:#FFE148,stroke-width:2px,color:#1a1a1a
```
After each session, retrieve the generated diagnoses using the Structured Data API, then include all relevant diagnoses (including any modifications) when starting the next session.
## Best practices
* **Provide complete context**: Always include all relevant existing diagnoses when starting a session.
* **Use ICD10 when possible**: ICD10 codes are preferred and processed most reliably.
* **Handle reambient carefully**: Retrieve diagnoses after each session and include them in the next session.
* **Validate codes**: Ensure diagnosis codes are valid before sending them.
* **Monitor output**: Check which diagnoses were updated or generated to understand what was discussed.
* **Update after user edits**: If providers modify diagnoses, include those modifications in subsequent sessions.
## FAQs
In reambient scenarios, the system does not automatically carry forward diagnoses from previous sessions. You must provide the complete and current list of all relevant diagnoses for each new session.
The system uses [IMO APIs](https://www.imohealth.com/) to find the best possible ICD10 code equivalent for non-ICD10 codes. If a code cannot be mapped, that diagnosis is skipped.
The system normalizes all codes to ICD10 before processing. ICD10 is used as the source of truth for all diagnosis operations.
The system deduplicates diagnoses based on the ICD10 code. Diagnoses with the same ICD10 code are merged, and their notes are combined.
Only diagnoses that were updated or newly generated are returned. Diagnoses that weren't discussed during the session are not included in the output.
# Ambient & Dictation Error Messages
Source: https://developer.suki.ai/api-reference/error-messages
Look up Ambient and Dictation API error ids, HTTP mappings, and category filters to debug failed REST calls in your integration
This page lists the error messages returned by the Suki APIs. All error messages are returned in JSON format as shown in the example below.
```json JSON theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
{
"code": 16,
"message": "invalid sdp token",
"details": []
}
```
The `code` field in the JSON body is a numeric status code. Suki maps it to the HTTP status on the response. See [Status code to HTTP mapping](#status-code-to-http-mapping) for the full mapping.
Each table row lists an **error id**: a stable name you can use in client code. In responses, that value appears in the `message` field, either as the full string or as a prefix (for example `invalid_sdp_token` or `invalid_sdp_token: …`). Match on the id exactly or by prefix when you handle errors in your integration.
-
-
-
-
-
-
-
No error messages match this filter or search. Choose another category or clear the search box.
## Common errors
These are some common errors that can occur across all Suki REST APIs.
| Error id | Code | Description |
| :------------------------------- | :--- | :-------------------------------------------------------------- |
| `invalid_sdp_token` | 401 | Missing, expired, or invalid `sdp_suki_token` |
| `permission_denied` | 403 | General authorization failure |
| `insufficient_scope` | 403 | `method not allowed due to insufficient scope. Need: sdp.write` |
| `sbac_denied` | 403 | Organization not accessible to partner (SBAC) |
| `provider_not_registered` | 401 | Clinician not registered via `/auth/register` |
| `provider_user_inactive` | 412 | Provider user is not ACTIVATED/LICENSE\_PENDING |
| `missing_sdp_provider_id_header` | 400 | `sdp_provider_id` header required (single\_auth) |
| `invalid_sdp_provider_id` | 400 | `sdp_provider_id` fails partner regex |
| `request_cancelled` | 499 | Request cancelled by client |
| `request_timeout` | 504 | Request timed out |
| `internal_server_error` | 500 | Unexpected server error |
## Ambient
These are some common errors that can occur while implementing the ambient workflow using the REST APIs.
| Error id | Code | Description |
| :--------------------------------------------- | :--- | :------------------------------------------------------------------- |
| `invalid_ambient_id_format` | 400 | `ambient_session_id` is not a valid UUID on create |
| `invalid_request` | 400 | Request body failed validation |
| `invalid_end_session_request` | 400 | End session request failed validation |
| `invalid_cancel_session_request` | 400 | Cancel session request failed validation |
| `invalid_context_request` | 400 | Context body failed validation (specialty, role, visit type, etc.) |
| `invalid_encryption_hash` | 400 | Encryption hash must be exactly 64 characters |
| `ambient_session_id_required` | 400 | `ambient_session_id` is required (recording) |
| `invalid_ambient_session_id` | 404 | Ambient session ID not found |
| `ambient_session_not_found` | 404 | Ambient session not found on end |
| `session_wrong_state` | 412 | Session is not in CREATED state (includes current status) |
| `session_job_type_unsupported` | 412 | Session job type does not support EndSession |
| `session_in_progress` | 409 | Concurrent session conflict (for example session in progress on WEB) |
| `transcripts_retention_exceeded` | 404 | Transcripts older than 7 days are not available |
| `content_retention_exceeded` | 404 | Content for session older than 7 days is not available |
| `encounter_content_retention_exceeded` | 404 | Content for encounter older than 7 days is not available |
| `structured_data_retention_exceeded` | 404 | Structured data for session older than 7 days is not available |
| `encounter_structured_data_retention_exceeded` | 404 | Structured data for encounter older than 7 days is not available |
| `invalid_encounter_id` | 404 | Encounter ID not found |
| `no_appointment_found` | 404 | No appointment found for `emr_encounter_id` |
| `missing_emr_encounter_id` | 400 | `emr_encounter_id` is required (list encounter notes) |
| `missing_user_id` | 400 | `user_id` missing from token (list encounter notes) |
| `missing_organization_id` | 400 | `organization_id` missing from token (list encounter notes) |
| `error_creating_session` | 500 | Error creating ambient session |
| `error_ending_session` | 500 | Internal error ending session |
| `error_streaming_audio` | 500 | Internal error during `/ws/stream` |
| `client_disconnected` | 499 | WebSocket client disconnected during stream |
| `unknown_request_type` | 400 | Unknown message type on `/ws/stream` |
| `error_extracting_ambient_session_id` | 400 | Ambient session ID missing from WebSocket/metadata |
| `error_adding_context` | 500 | Internal error adding/updating session context |
| `error_processing_context` | 500 | Internal error processing context payload |
| `error_generating_presigned_url` | 500 | Internal error generating offline audio upload URL |
| `error_fetching_recording` | 500 | Internal error fetching session recording |
| `client_metrics_log_failed` | 500 | Client metrics could not be logged |
| `error_resolving_encounter` | 500 | Internal error resolving encounter (list notes) |
| `error_fetching_encounter_notes` | 500 | Internal error fetching encounter notes |
## Dictation
These are some common errors that can occur while implementing the Dictation workflow using the REST APIs.
| Error id | Code | Description |
| :------------------------------------------ | :--- | :------------------------------------------------------------ |
| `invalid_transcription_session_id_format` | 400 | `transcription_session_id` is not a valid UUID |
| `invalid_request` | 400 | Transcription request failed validation |
| `error_extracting_transcription_session_id` | 400 | Transcription session ID missing from WebSocket/metadata |
| `invalid_transcription_session_id` | 404 | Transcription session ID not found |
| `transcription_session_not_found` | 500 | No transcription session found for given ID |
| `session_job_type_unsupported` | 412 | Session job type does not support EndTranscriptionSession |
| `transcription_session_completed` | 412 | Transcription session is already completed |
| `transcription_session_not_accepting` | 412 | Transcription session is not accepting new speech sessions |
| `error_creating_transcription_session` | 500 | Error creating transcription session |
| `error_ending_transcription_session` | 500 | Internal error ending transcription session |
| `error_creating_transcription_stream` | 500 | Internal error creating `/ws/transcribe` stream |
| `error_receiving_audio` | 500 | Internal error receiving audio on transcription stream |
| `error_sending_audio` | 500 | Internal error sending audio on transcription stream |
| `error_ending_transcription` | 500 | Internal error ending transcription stream |
| `error_reading_transcripts` | 500 | Internal error reading transcripts from transcription service |
| `client_disconnected` | 499 | WebSocket client disconnected during transcribe |
| `unknown_request_type` | 400 | Unknown message type on `/ws/transcribe` |
## Feedback
These are some common errors that can occur while implementing the Feedback workflow using the REST APIs.
### Ambient feedback
| Error id | Code | Description |
| :---------------------------- | :--- | :------------------------------------------------------ |
| `invalid_request` | 400 | Feedback request failed validation |
| `ambient_session_id_required` | 400 | `ambient_session_id` is required |
| `invalid_uuid_format` | 400 | `ambient_session_id` is not a valid UUID |
| `feedback_required` | 400 | `feedback` object is required |
| `rating_feedback_required` | 400 | `ratingFeedback` is required |
| `invalid_entity` | 400 | Only `CONTENT` entity is supported for ambient feedback |
| `invalid_rating_range` | 400 | `minRating` must be less than `maxRating` |
| `rating_below_min` | 400 | `rating` must be >= `minRating` |
| `rating_above_max` | 400 | `rating` must be \<= `maxRating` |
| `negative_min_rating` | 400 | `minRating` must be >= 0 |
| `comments_too_long` | 400 | `qualitativeComments` exceeds 2000 characters |
| `failed_get_session` | 500 | Failed to get ambient session for feedback |
| `failed_submit_feedback` | 500 | Failed to submit feedback to feedback service |
## Notification
These are some common errors that can occur while implementing the Notification workflow using the REST APIs.
### Success payload
| Field | Description |
| :------------- | :------------------------------------------------------------- |
| `status` | `"success"` |
| `session_id` | Session that completed |
| `encounter_id` | Parent encounter ID |
| `sessions` | Ordered list of session IDs in encounter |
| `_links` | HATEOAS links to status, transcripts, content, structured-data |
### Failure payload
| Error id | Code | Description |
| :------------------------------------------- | :--- | :------------------------------------------------ |
| `ERROR_CODE_UNSPECIFIED` | — | Failure reason unknown or not classified |
| `ERROR_CODE_NOTIFICATION_GENERATION_FAILURE` | — | Notification payload generation failed |
| `ERROR_CODE_TRANSCRIPTION` | — | **Swagger example only**, not in proto enum today |
| Field | Description |
| :------------- | :--------------------------------------------- |
| `status` | `"failure"` |
| `session_id` | Session that failed |
| `encounter_id` | Parent encounter ID |
| `error_code` | Machine-readable error code (see table above) |
| `error_detail` | Human-readable failure details from job output |
## Preferences
These are some common errors that can occur while implementing the Preferences workflow using the REST APIs.
| Error id | Code | Description |
| :------------------------------------- | :--- | :--------------------------------------------------------------- |
| `invalid_request` | 400 | Preferences request failed validation |
| `update_paths_required` | 400 | `update_paths` field mask is required |
| `preference_required` | 400 | `preference` object is required |
| `personalization_preference_required` | 400 | `personalization_preference` is required |
| `verbosity_or_section_format_required` | 400 | At least one of `verbosity` or `section_format` must be provided |
| `invalid_preferences_format` | 400 | Preferences could not be converted to internal format |
| `invalid_verbosity` | 400 | Invalid verbosity enum value |
| `invalid_section_format` | 400 | Invalid section format enum value |
| `error_updating_preferences` | 500 | Internal error updating user preferences via ms-preferences |
## Status code to HTTP mapping
This table maps the status names returned by the Suki APIs to the corresponding HTTP status codes.
| Status name | HTTP |
| :------------------- | :--- |
| `InvalidArgument` | 400 |
| `Unauthenticated` | 401 |
| `PermissionDenied` | 403 |
| `NotFound` | 404 |
| `AlreadyExists` | 409 |
| `Aborted` | 409 |
| `FailedPrecondition` | 412 |
| `Unimplemented` | 501 |
| `Unavailable` | 503 |
| `Internal` | 500 |
| `Canceled` | 499 |
| `DeadlineExceeded` | 504 |
# Audio Capture & Streaming
Source: https://developer.suki.ai/api-reference/faqs/audio-capture-streaming
Questions about audio capture, streaming, and WebSocket implementation
LINEAR16 (16KHz sampling rate) over the mono channel. Audio should be chunked into 100ms packets for optimal performance. We only support 100ms audio chunks to achieve the right balance between quality, latency, and efficiency.
Network connection speed and consistency are important for Suki to perform well.
**Suki requires:**
* **Upload speed**: 1Mbps.
* **Bitrate**: 768kbps.
* **Ping time**: 150ms.
* **Unloaded latency**: \<50ms.
* **Loaded latency**: \<150ms.
Client should set up a WebSocket Secure (wss\://) request with the Suki endpoint. Refer to the [Audio stream API](/api-reference/ambient-sessions/audio-stream) for implementation details.
For **ambient** WebSocket **`/ws/stream`** message format, send **LINEAR16**, **16 kHz**, **mono** audio, in about **100 ms** chunks (see the [Audio streaming](/api-reference/ambient-sessions/audio-stream) reference and [WebSocket streaming wire format](/documentation/how-to/audio-streaming/websocket-streaming-wire-format-ambient#audio-format-and-chunking)).
### How messages are sent
Each outbound message from the client must be:
* **One WebSocket text frame** (UTF-8) with **one JSON object** inside.
* **One logical send** per frame (do not pack multiple JSON objects in one frame).
On **`/ws/stream`**, do **not** send PCM as **binary** WebSocket frames. Do **not** stream raw audio over HTTP with `Content-Type: application/json`.
**Field names (proto-style JSON)**
* **`START_TIME`** and **`AUDIO`**: use **`type`** and **`data`**.
* **`data`**: standard Base64 ([RFC 4648](https://datatracker.ietf.org/doc/html/rfc4648)) of the **raw bytes** you mean to send (same idea as Go `encoding/json` for `[]byte`). Do not use hex, URL-safe Base64, or raw binary inside the JSON string.
* **`EVENT`**: use **`type`**: `"EVENT"` and the **`event`** field. Do **not** put the action name in **`data`**.
### Message order (each stream segment)
1. Send **`START_TIME`** once: **`data`** is Base64 of a UTF-8 **RFC 3339** timestamp (for example `2026-04-25T12:34:56Z`).
2. Send one or more **`AUDIO`** messages: **`data`** is Base64 of each **raw PCM** chunk.
3. Send a final **`AUDIO`** to end audio: **`data`** is **`RU9G`** (Base64 of ASCII **`EOF`**, bytes `0x45`, `0x4F`, `0x46`). Do not use a separate `end_of_stream` type unless your integration team tells you otherwise.
`EVENT` messages can go **anywhere** in the stream when you need control (pause, resume, keep-alive, cancel, abort).
### EVENT values you can send
Use **`{"type":"EVENT","event":""}`** with one of:
| Value | What it does |
| :-------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------- |
| **PAUSE** | Pause the stream |
| **RESUME** | Resume the stream |
| **CANCEL** | User cancels the stream |
| **ABORT** | Stream is aborted (interruption) |
| **KEEP\_ALIVE** | Keep the connection alive during inactivity. While **paused**, send at least once every **five seconds** so the server does not close the connection. |
### Message examples
```json START_TIME theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
{
"type": "START_TIME",
"data": ""
}
```
```json AUDIO (PCM chunk) theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
{
"type": "AUDIO",
"data": "Base64EncodedPcmBytes"
}
```
```json AUDIO (stream end marker) theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
{
"type": "AUDIO",
"data": "RU9G"
}
```
```json PAUSE Event theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
{
"type": "EVENT",
"event": "PAUSE"
}
```
```json RESUME Event theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
{
"type": "EVENT",
"event": "RESUME"
}
```
```json CANCEL Event theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
{
"type": "EVENT",
"event": "CANCEL"
}
```
```json KEEP_ALIVE Event theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
{
"type": "EVENT",
"event": "KEEP_ALIVE"
}
```
```json ABORT Event theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
{
"type": "EVENT",
"event": "ABORT"
}
```
The stream end marker is an **`AUDIO`** message whose **`data`** is Base64 of the raw bytes **`EOF`**, not a bare JSON string `"EOF"` and not an **`EVENT`** named EOF, unless your stack documentation says otherwise.
# Authentication
Source: https://developer.suki.ai/api-reference/faqs/authentication
Authentication and authorization questions for API access
We support
* OAuth 2.0 ID token for public clients such as browsers, mobile devices etc.
* Access token for confidential clients such as backend systems.
We support [HMAC (Hash-based message authentication
code)](https://www.okta.com/identity-101/hmac/) for webhook authentication
(Details can be shared)
Client systems cannot authenticate with Suki's platform via APIs when a JWT token fails. It's handled with standard HTTPS error codes; refer
API reference
# Diagnosis codes
Source: https://developer.suki.ai/api-reference/faqs/diagnosis-codes
FAQ on diagnosis code types in Ambient API structured data, including ICD-10 and HCC
ICD-10 and HCC codes serve different purposes in clinical documentation:
**ICD-10 (International Classification of Diseases, 10th Revision)**
* A standardized system of codes used to classify and record diagnoses, symptoms, and procedures.
* Used for billing, clinical documentation, and interoperability across healthcare systems.
* Example: `I10` = Essential hypertension.
* ICD-10 is the **primary identifier** Suki uses internally to match, deduplicate, and process diagnoses.
**HCC (Hierarchical Condition Category)**
* A risk-adjustment coding model used primarily in value-based care and Medicare Advantage programs.
* HCC codes are derived from ICD-10 codes: CMS groups related ICD-10 codes into HCC categories to estimate a patient's expected healthcare costs.
* Not every ICD-10 code maps to an HCC category: only those that significantly affect patient risk scores do.
* Example: ICD-10 `I10` (Essential hypertension) maps to HCC 136.
**How they relate in Suki structured data:**
* Suki processes and stores diagnoses using ICD-10 as the source of truth.
* In API structured data, HCC categories appear in the diagnosis `codes` array with `type: "HCC"`.
* In the Web SDK, the `hccCode` field in the [Diagnosis type](/web-sdk/api-reference/types/diagnosis) surfaces the HCC category that corresponds to the ICD-10 code, when one exists.
* You can use HCC output to support risk-adjustment workflows or value-based care reporting in your integration.
| | ICD-10 | HCC |
| ----------- | ------------------------- | ------------------------------------ |
| **Purpose** | Clinical diagnosis coding | Risk adjustment and cost prediction |
| **Scope** | All diagnoses | Subset of high-impact diagnoses |
| **Used by** | All payers, EHRs | Medicare Advantage, value-based care |
| **In Suki** | Primary identifier | Supplemental output field |
See [Ambient session structured data](/api-reference/ambient-content/structured-data) and [Supported Diagnosis Codes](/api-reference/info/diagnosis) for endpoint details.
No. HCC codes are returned in structured data output only. Send ICD10 or IMO codes in session context. Suki derives HCC categories from the ICD-10-CM code when you retrieve structured data.
See [Ambient session structured data](/api-reference/ambient-content/structured-data#diagnosis-codes-in-structured-data).
# Miscellaneous
Source: https://developer.suki.ai/api-reference/faqs/miscellaneous
General API questions and miscellaneous topics
To handle different doctor roles/specialties dynamically, clients use our
context API to seed session context with the provider role (e.g. SPECIALTY).
Yes. For an **ambient** session, ending the session and receiving a successful webhook means note generation completed. Suki still runs the Ambient documentation pipeline in the background, including clinical note and encounter content, even if your application only follows the transcript link afterward.
The success webhook payload identifies the session and encounter and includes `_links` for session content, encounter content, structured data, status, and transcripts. Using only [Get Session Transcript](/api-reference/ambient-content/transcript) does not skip note generation. Session status **`completed`** means the session finished and final content was generated. See [Webhook event types](/documentation/webhook/event-types) and [Webhook payload](/documentation/webhook/payload-and-response).
There is no ambient option today that returns only the conversation transcript without generating the clinical note and related encounter documentation.
# Patient Summary
Source: https://developer.suki.ai/api-reference/faqs/patient-summary
FAQs about the Patient Summary APIs
Use your partner organization ID from your external registration with Suki. Do not use an internal Suki organization ID. Suki maps your partner organization ID to the correct internal organization and Clinical Knowledge Graph (CKG).
Authenticate requests with your Suki Developer Platform (SDP) token. No separate login or token exchange is required.
The pre-signed Google Cloud Storage (GCS) upload URL expires after 15 minutes. If the URL expires before you upload, request a new upload URL.
Each FHIR bundle can be up to 500 MB.
Upload a raw JSON file containing a FHIR Bundle of type `searchset`. The bundle must follow the FHIR specification and include only supported resource types.
No. All resources in a bundle must belong to the same organization.
No. Suki does not enforce referential integrity during ingestion. Ingestion can succeed even if a resource references another resource that has not been ingested yet.
Set the correlation ID to the original tracking identifier when uploading updates for a previous ingestion. This ensures Suki processes related updates in order and helps avoid conflicting changes.
No. A successful `PUT` confirms the file was stored. Suki processes the file asynchronously after upload completes. Poll the ingestion status endpoint or configure webhooks to learn when processing finishes.
# Technical Constraints & Risks
Source: https://developer.suki.ai/api-reference/faqs/technical-constraints-risks
Technical limitations, constraints, and risk considerations for API integration
No, we don't have pre-approved domains.
To register for our webhook , share your callback URL so we can notify you there. This step is currently manual; we plan to upgrade this system in the future.
N/A, we can share the IP of our system which calls callback URL if need be
A POST call will be made to the webhook hosted by the Partner using the HTTPS protocol.
Yes, the Webhook URL should be configured at the Partner level during [partner onboarding](/documentation/get-started/partner-onboarding).
We currently do not support session-based webhook callbacks or similar configurations; webhook support is only for APIs and not for Web SDK.
The client is responsible for creating and managing the `encounter_id`.
We support combining sessions and generating the note based on the provided `encounter_id`. An encounter can include multiple ambient sessions .
1. Obtain a developer platform test license (POC: Commercials team).
.
2. Complete partner onboarding:
.
* Share required details for partner creation in our system → you will receive a Partner ID (`partner_id`).
.
* Let us know the unique identifier you will use in the OAuth 2.0 JWT token to identify the user (provider ).
.
Example: user\_id, email\_id, sid, pid, etc. (need not be actual id from your system, can be hash value as well)
* Provide your JWKS endpoint for accessing your public key.
.
* Test authentication with Suki.
.
Here's a minimal flow for your reference:
`/login → /session/create → /context (optional) → /stream → /end`
This flow automatically triggers note generation.
Either:
1. Use `/status` to poll the note generation status, then call /content to retrieve the note.
2. Receive a notification through our webhook.
As long as the stream is active, there is no upper limit on call duration. The
system allows a maximum pause of 30 minutes during the streaming. If you
get disconnected and then rejoin, or lose the WebSocket connection, Suki stitches
the sessions together using the same session ID.
Although there is no strict lower limit, Suki needs a minimum amount of data to generate recommendations. For calls **shorter than 1 minute**, Suki may not have enough meaningful data/information to provide accurate recommendations and returns empty content. The session will be marked as **skipped**.
# Ambient Session User Feedback
Source: https://developer.suki.ai/api-reference/feedback/feedback
POST /api/v1/ambient/session/{session_id}/{entity}/feedback
Submit user feedback on generated clinical content for continuous improvement
Use the **Feedback API** to collect quantitative (ratings) and qualitative (comments) feedback. Capturing feedback helps drive AI model improvement and increases client confidence.
The API supports the following actions:
* **Quantitative Feedback**: Rate content using a scale with configurable minimum and maximum values.
* **Qualitative Feedback**: Provide optional, free-form comments for detailed insights.
* **Entity-Level Tracking**: Tie feedback to specific entities within sessions.
## Supported entity types
Provide feedback on the following entity types:
| Entity type | Description |
| ----------- | -------------------------- |
| `content` | Generated clinical content |
Within a single ambient session (session\_id), you can provide feedback for each `entity type` only **once**. If you provide feedback for the same `entity type` multiple times, the feedback will not be valid.
Also, a single `session_id` can contain **multiple feedbacks**, provided each entry corresponds to a **different** entity type.
## Rating system
The `ratingFeedback` object provides quantitative feedback. It includes the following fields:
* `min_rating`: The minimum rating value (e.g., 0).
* `max_rating`: The maximum rating value.
* `rating`: The actual rating you provide within the min-max range.
Configure the rating scale by setting the `min_rating` and `max_rating` values. The range is **inclusive**, so both the `min_rating` and `max_rating` values are valid ratings.
For example:
* To create a rating scale of 0 to 5, set `min_rating` to 0 and `max_rating` to 5. A user can provide any integer rating from 0 to 5.
* To create a binary scale, set `min_rating` to 0 and `max_rating` to 1. A user can provide a rating of either 0 or 1.
* Suki suggests using a scale of 1 to 5 for the rating.
## Character limits
* `qualitative_comments`: Maximum **2000** characters.
Your feedback helps Suki to improve the AI-generated content. All feedback is reviewed and used to enhance quality.
## Code examples
**Language tabs (agents):** Equivalent code samples are available in: Python, TypeScript. Humans see one language at a time. Use the variant that matches the user's stack; behavior is the same across tabs.
```python theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
import requests
session_id = "session_abc_123"
entity = "content"
url = f"https://sdp.suki-stage.com/api/v1/ambient/session/{session_id}/{entity}/feedback"
headers = {
"sdp_suki_token": "",
"sdp_provider_id": "",
"Content-Type": "application/json"
}
payload = {
"ratingFeedback": {
"min_rating": 0,
"max_rating": 5,
"rating": 4
},
"qualitative_comments": "The generated content was accurate and helpful. Great job!" # Optional, max 2000 chars
}
response = requests.post(url, json=payload, headers=headers)
if response.status_code == 201:
data = response.json()
feedback_id = data.get("feedback_id")
print(f"Feedback submitted successfully. Feedback ID: {feedback_id}")
else:
print(f"Failed to submit feedback: {response.status_code}")
print(response.json())
```
```typescript theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
const sessionId = 'session_abc_123';
const entity = 'content';
const response = await fetch(
`https://sdp.suki-stage.com/api/v1/ambient/session/${sessionId}/${entity}/feedback`,
{
method: 'POST',
headers: {
'sdp_suki_token': '',
'sdp_provider_id': '',
'Content-Type': 'application/json'
},
body: JSON.stringify({
ratingFeedback: {
min_rating: 0,
max_rating: 5,
rating: 4
},
qualitative_comments: 'The generated content was accurate and helpful. Great job!' // Optional, max 2000 chars
})
}
);
if (response.status === 201) {
const data = await response.json();
const feedbackId = data.feedback_id;
console.log(`Feedback submitted successfully. Feedback ID: ${feedbackId}`);
} else {
const error = await response.json();
console.error(`Failed to submit feedback: ${response.status}`, error);
}
```
# HTTPS Guidelines
Source: https://developer.suki.ai/api-reference/https-guidelines
Map Suki HTTP status codes (2xx, 4xx, 5xx) to request outcomes, including auth failures, validation errors, and server errors
Suki uses **standard HTTP status codes** to indicate the outcome of an API request.
* **`2xx` codes** indicate success.
* **`4xx` codes** indicate a client-side error (e.g., you omitted a required parameter or provided an invalid configuration).
* **`5xx` codes** indicate a server-side error on Suki's end. These are rare.
The following table lists the most common HTTP status codes you may receive.
| Code | Title | Description |
| :----------------------- | :---------------- | :---------------------------------------------------------------------------------------------------------------------------------------- |
| **`200`** | OK | The request was successful. |
| **`201`** | Created | The resource was created successfully. The URL for the new resource is in the `Location` header. |
| **`204`** | No Content | The request was successful, and there is no content to return. |
| **`400`** | Bad Request | The request was unacceptable, often due to malformed syntax or a missing parameter. |
| **`401`** | Unauthorized | The request was not authenticated. This could be due to missing, invalid, or insufficient credentials. |
| **`402`** | Over Quota | The request was valid, but you have exceeded your plan's quota or rate limits. |
| **`403`** | Forbidden | The request was not authenticated. This could be due to missing, invalid, or insufficient credentials. |
| **`404`** | Not Found | The requested resource does not exist. |
| **`409`** | Conflict | The request conflicts with the current state of the resource (e.g., the resource already exists, or the request was based on stale data). |
| **`422`** | Validation Failed | The request was parsed correctly but failed a validation rule. |
| **`429`** | Too Many Requests | You have sent too many requests in a short period. We recommend using an exponential backoff strategy for your requests. |
| **`500, 502, 503, 504`** | Server Errors | An unexpected error occurred on Suki's servers. |
# Info APIs
Source: https://developer.suki.ai/api-reference/info
Reference data for specialties, diagnoses, encounter types, visit types, LOINC codes, and provider roles
The Info APIs enable partners to retrieve the specialties, encounter types, visit types, provider roles, LOINC codes, and diagnosis types Suki accepts when you register providers or create ambient sessions. Partners typically call these endpoints from their backend to populate pickers in their product and validate payloads before session or context requests.
You can fetch a combined catalog from System Information or request each category through its dedicated Info endpoint. Critically, Info endpoints return supported values only. They do not create ambient sessions or generate clinical notes.
Info APIs are called from your servers and authenticated with a Suki Token (`sdp_suki_token`). Response bodies are stable reference data you can cache on a release schedule that fits your product.
## Available endpoints
Retrieve platform system information for your integration
Retrieve supported LOINC codes for note sections and related fields
Retrieve diagnosis code reference data for ambient requests
Retrieve supported medical specialties for provider registration and sessions
Retrieve supported encounter types for ambient sessions
Retrieve supported visit types for ambient sessions
Retrieve supported provider roles for ambient context
## Related guides
Learn how ambient sessions use encounter and specialty context
See how LOINC-coded sections map into generated notes
Review specialty support for clinical notes
## Common use cases
Use supported catalogs so registration and session forms only offer values Suki accepts.
Check note sections and diagnosis payloads against supported codes before you seed ambient context.
Refresh Info catalogs on your release cycle so specialty, encounter, and role pickers stay aligned with Suki.
Apply supported provider roles when building ambient session context for the encounter.
# Supported Diagnosis Codes
Source: https://developer.suki.ai/api-reference/info/diagnosis
GET /api/v1/info/diagnosis
Get list of supported diagnosis codes and medical conditions
**Updated:**
* **HCC** is now included as a supported diagnosis code type for ambient structured data output.
* Send **ICD10** or **IMO** codes in session context. Do not send [HCC](https://www.aapc.com/resources/what-is-hierarchical-condition-category?srsltid=AfmBOopcl-dIWrRrQFq58LGS72p58BakTdoWdEHv0P9z89c3XBXuJAOY) codes as input.
* HCC categories are returned from the [Ambient session structured data API](/api-reference/ambient-content/structured-data) when an ICD-10-CM diagnosis maps to a CMS-HCC model category.
For how ICD-10 and HCC differ, refer to [Diagnosis codes FAQs](/api-reference/faqs/diagnosis-codes#what-is-the-difference-between-icd-10-and-hcc-codes).
Use this endpoint to get the list of supported diagnosis code types, including **HCC** for ambient structured data output.
## Code examples
**Language tabs (agents):** Equivalent code samples are available in: Python, TypeScript. Humans see one language at a time. Use the variant that matches the user's stack; behavior is the same across tabs.
```python theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
import requests
url = "https://sdp.suki-stage.com/api/v1/info/diagnosis"
headers = {
"sdp_suki_token": "",
"sdp_provider_id": ""
}
response = requests.get(url, headers=headers)
if response.status_code == 200:
diagnosis_data = response.json()
print("Supported Diagnosis Code Types:")
for code_type in diagnosis_data.get("diagnosis_code_types", []):
print(f" {code_type.get('code_type')}")
else:
print(f"Failed to get diagnosis codes: {response.status_code}")
print(response.json())
```
```typescript theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
const response = await fetch('https://sdp.suki-stage.com/api/v1/info/diagnosis', {
headers: {
'sdp_suki_token': '',
'sdp_provider_id': ''
}
});
if (response.ok) {
const diagnosisData = await response.json();
console.log('Supported Diagnosis Code Types:');
diagnosisData.diagnosis_code_types?.forEach((codeType: any) => {
console.log(` ${codeType.code_type}`);
});
} else {
const error = await response.json();
console.error(`Failed to get diagnosis codes: ${response.status}`, error);
}
```
# Encounter Types
Source: https://developer.suki.ai/api-reference/info/encounter-types
GET /api/v1/info/encounter-types
Get list of supported clinical encounter types
Use this endpoint to get the list of supported encounter types.
## Code examples
**Language tabs (agents):** Equivalent code samples are available in: Python, TypeScript. Humans see one language at a time. Use the variant that matches the user's stack; behavior is the same across tabs.
```python theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
import requests
url = "https://sdp.suki-stage.com/api/v1/info/encounter-types"
headers = {
"sdp_suki_token": "",
"sdp_provider_id": ""
}
response = requests.get(url, headers=headers)
if response.status_code == 200:
encounter_types_data = response.json()
print("Supported Encounter Types:")
for encounter_type in encounter_types_data.get("encounter_types", []):
print(f" {encounter_type.get('code')}")
else:
print(f"Failed to get encounter types: {response.status_code}")
print(response.json())
```
```typescript theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
const response = await fetch('https://sdp.suki-stage.com/api/v1/info/encounter-types', {
headers: {
'sdp_suki_token': '',
'sdp_provider_id': ''
}
});
if (response.ok) {
const encounterTypesData = await response.json();
console.log('Supported Encounter Types:');
encounterTypesData.encounter_types?.forEach((encounterType: any) => {
console.log(` ${encounterType.code}`);
});
} else {
const error = await response.json();
console.error(`Failed to get encounter types: ${response.status}`, error);
}
```
# System Information
Source: https://developer.suki.ai/api-reference/info/information
GET /api/v1/info
Get system information and supported configuration values
Use this endpoint to get the list of supported LOINC codes, specialties, encounter types, visit types, provider roles, diagnosis code types, and medication order metadata (coding systems, dosage units, frequency types, timings, order statuses, encounter relations, and origins).
You can also fetch each category with dedicated `GET /api/v1/info/...` routes listed under **Info** in the API reference.
## Code examples
**Language tabs (agents):** Equivalent code samples are available in: Python, TypeScript. Humans see one language at a time. Use the variant that matches the user's stack; behavior is the same across tabs.
```python theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
import requests
url = "https://sdp.suki-stage.com/api/v1/info"
headers = {
"sdp_suki_token": "",
"sdp_provider_id": ""
}
response = requests.get(url, headers=headers)
if response.status_code == 200:
info = response.json()
print("System Information:")
print(f"LOINCs: {len(info.get('loincs', []))} codes")
print(f"Specialties: {len(info.get('specialties', []))} specialties")
print(f"Encounter Types: {len(info.get('encounter_types', []))} types")
print(f"Visit Types: {len(info.get('visit_types', []))} types")
print(f"Provider Roles: {len(info.get('provider_roles', []))} roles")
print(f"Diagnosis Code Types: {len(info.get('diagnosis_code_types', []))} types")
print(f"Medication coding systems: {len(info.get('medication_coding_systems', []))}")
print(f"Medication dosage units: {len(info.get('medication_dosage_units', []))}")
print(f"Medication frequency types: {len(info.get('medication_frequency_types', []))}")
print(f"Medication timings: {len(info.get('medication_timings', []))}")
print(f"Medication order statuses: {len(info.get('medication_order_statuses', []))}")
print(f"Order encounter relations: {len(info.get('order_encounter_relations', []))}")
print(f"Order origins: {len(info.get('order_origins', []))}")
else:
print(f"Failed to get system information: {response.status_code}")
print(response.json())
```
```typescript theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
const response = await fetch('https://sdp.suki-stage.com/api/v1/info', {
headers: {
'sdp_suki_token': '',
'sdp_provider_id': ''
}
});
if (response.ok) {
const info = await response.json();
console.log('System Information:');
console.log(`LOINCs: ${info.loincs?.length || 0} codes`);
console.log(`Specialties: ${info.specialties?.length || 0} specialties`);
console.log(`Encounter Types: ${info.encounter_types?.length || 0} types`);
console.log(`Visit Types: ${info.visit_types?.length || 0} types`);
console.log(`Provider Roles: ${info.provider_roles?.length || 0} roles`);
console.log(`Diagnosis Code Types: ${info.diagnosis_code_types?.length || 0} types`);
console.log(`Medication coding systems: ${info.medication_coding_systems?.length ?? 0}`);
console.log(`Medication dosage units: ${info.medication_dosage_units?.length ?? 0}`);
console.log(`Medication frequency types: ${info.medication_frequency_types?.length ?? 0}`);
console.log(`Medication timings: ${info.medication_timings?.length ?? 0}`);
console.log(`Medication order statuses: ${info.medication_order_statuses?.length ?? 0}`);
console.log(`Order encounter relations: ${info.order_encounter_relations?.length ?? 0}`);
console.log(`Order origins: ${info.order_origins?.length ?? 0}`);
} else {
const error = await response.json();
console.error(`Failed to get system information: ${response.status}`, error);
}
```
# Supported LOINCs
Source: https://developer.suki.ai/api-reference/info/loincs
GET /api/v1/info/loincs
Get list of supported LOINC codes for clinical note sections
Use this endpoint to get the list of supported LOINC codes.
For more information about the LOINC codes, refer to the [Note sections](/documentation/concepts/ambient-clinical-notes/note-sections).
## Code examples
**Language tabs (agents):** Equivalent code samples are available in: Python, TypeScript. Humans see one language at a time. Use the variant that matches the user's stack; behavior is the same across tabs.
```python theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
import requests
url = "https://sdp.suki-stage.com/api/v1/info/loincs"
headers = {
"sdp_suki_token": "",
"sdp_provider_id": ""
}
response = requests.get(url, headers=headers)
if response.status_code == 200:
loincs_data = response.json()
print("Supported LOINC Codes:")
for loinc in loincs_data.get("loincs", []):
print(f" {loinc.get('code')}: {loinc.get('common_name')}")
else:
print(f"Failed to get LOINC codes: {response.status_code}")
print(response.json())
```
```typescript theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
const response = await fetch('https://sdp.suki-stage.com/api/v1/info/loincs', {
headers: {
'sdp_suki_token': '',
'sdp_provider_id': ''
}
});
if (response.ok) {
const loincsData = await response.json();
console.log('Supported LOINC Codes:');
loincsData.loincs?.forEach((loinc: any) => {
console.log(` ${loinc.code}: ${loinc.common_name}`);
});
} else {
const error = await response.json();
console.error(`Failed to get LOINC codes: ${response.status}`, error);
}
```
# Medication Order Metadata
Source: https://developer.suki.ai/api-reference/info/orders
GET /api/v1/info/orders
Get all supported medication order metadata in one response
Use this endpoint to get supported metadata for medication orders in one response, including coding systems, dosage units, frequency types, timings, statuses, origins, and encounter relations.
## Code examples
**Language tabs (agents):** Equivalent code samples are available in: Python, TypeScript. Humans see one language at a time. Use the variant that matches the user's stack; behavior is the same across tabs.
```python theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
import requests
url = "https://sdp.suki-stage.com/api/v1/info/orders"
headers = {
"sdp_suki_token": "",
"sdp_provider_id": ""
}
response = requests.get(url, headers=headers)
if response.status_code == 200:
order_info = response.json()
print("Medication order metadata:")
print(f" Coding systems: {len(order_info.get('coding_systems', []))}")
print(f" Dosage units: {len(order_info.get('dosage_units', []))}")
print(f" Encounter relations: {len(order_info.get('encounter_relations', []))}")
print(f" Frequency types: {len(order_info.get('frequency_types', []))}")
print(f" Origins: {len(order_info.get('origins', []))}")
print(f" Statuses: {len(order_info.get('statuses', []))}")
print(f" Timings: {len(order_info.get('timings', []))}")
else:
print(f"Failed to get medication order metadata: {response.status_code}")
print(response.json())
```
```typescript theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
const response = await fetch('https://sdp.suki-stage.com/api/v1/info/orders', {
headers: {
'sdp_suki_token': '',
'sdp_provider_id': ''
}
});
if (response.ok) {
const orderInfo = await response.json();
console.log('Medication order metadata:');
console.log(` Coding systems: ${orderInfo.coding_systems?.length ?? 0}`);
console.log(` Dosage units: ${orderInfo.dosage_units?.length ?? 0}`);
console.log(` Encounter relations: ${orderInfo.encounter_relations?.length ?? 0}`);
console.log(` Frequency types: ${orderInfo.frequency_types?.length ?? 0}`);
console.log(` Origins: ${orderInfo.origins?.length ?? 0}`);
console.log(` Statuses: ${orderInfo.statuses?.length ?? 0}`);
console.log(` Timings: ${orderInfo.timings?.length ?? 0}`);
} else {
const error = await response.json();
console.error(`Failed to get medication order metadata: ${response.status}`, error);
}
```
# Medication Coding Systems
Source: https://developer.suki.ai/api-reference/info/orders-coding-systems
GET /api/v1/info/orders/coding-systems
Get supported medication coding systems
Use this endpoint to get the list of supported medication coding systems.
## Code examples
**Language tabs (agents):** Equivalent code samples are available in: Python, TypeScript. Humans see one language at a time. Use the variant that matches the user's stack; behavior is the same across tabs.
```python theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
import requests
url = "https://sdp.suki-stage.com/api/v1/info/orders/coding-systems"
headers = {
"sdp_suki_token": "",
"sdp_provider_id": ""
}
response = requests.get(url, headers=headers)
if response.status_code == 200:
coding_systems_data = response.json()
print("Supported Medication Coding Systems:")
for item in coding_systems_data.get("coding_systems", []):
print(f" {item.get('code')}")
else:
print(f"Failed to get medication coding systems: {response.status_code}")
print(response.json())
```
```typescript theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
const response = await fetch('https://sdp.suki-stage.com/api/v1/info/orders/coding-systems', {
headers: {
'sdp_suki_token': '',
'sdp_provider_id': ''
}
});
if (response.ok) {
const codingSystemsData = await response.json();
console.log('Supported Medication Coding Systems:');
codingSystemsData.coding_systems?.forEach((item: any) => {
console.log(` ${item.code}`);
});
} else {
const error = await response.json();
console.error(`Failed to get medication coding systems: ${response.status}`, error);
}
```
# Medication Dosage Units
Source: https://developer.suki.ai/api-reference/info/orders-dosage-units
GET /api/v1/info/orders/dosage-units
Get supported medication dosage units
Use this endpoint to get the list of supported medication dosage units.
## Code examples
**Language tabs (agents):** Equivalent code samples are available in: Python, TypeScript. Humans see one language at a time. Use the variant that matches the user's stack; behavior is the same across tabs.
```python theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
import requests
url = "https://sdp.suki-stage.com/api/v1/info/orders/dosage-units"
headers = {
"sdp_suki_token": "",
"sdp_provider_id": ""
}
response = requests.get(url, headers=headers)
if response.status_code == 200:
dosage_units_data = response.json()
print("Supported Medication Dosage Units:")
for item in dosage_units_data.get("dosage_units", []):
print(f" {item.get('code')}")
else:
print(f"Failed to get medication dosage units: {response.status_code}")
print(response.json())
```
```typescript theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
const response = await fetch('https://sdp.suki-stage.com/api/v1/info/orders/dosage-units', {
headers: {
'sdp_suki_token': '',
'sdp_provider_id': ''
}
});
if (response.ok) {
const dosageUnitsData = await response.json();
console.log('Supported Medication Dosage Units:');
dosageUnitsData.dosage_units?.forEach((item: any) => {
console.log(` ${item.code}`);
});
} else {
const error = await response.json();
console.error(`Failed to get medication dosage units: ${response.status}`, error);
}
```
# Order Encounter Relations
Source: https://developer.suki.ai/api-reference/info/orders-encounter-relations
GET /api/v1/info/orders/encounter-relations
Get supported order encounter relations
Use this endpoint to get the list of supported order encounter relations.
## Code examples
**Language tabs (agents):** Equivalent code samples are available in: Python, TypeScript. Humans see one language at a time. Use the variant that matches the user's stack; behavior is the same across tabs.
```python theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
import requests
url = "https://sdp.suki-stage.com/api/v1/info/orders/encounter-relations"
headers = {
"sdp_suki_token": "",
"sdp_provider_id": ""
}
response = requests.get(url, headers=headers)
if response.status_code == 200:
encounter_relations_data = response.json()
print("Supported Order Encounter Relations:")
for item in encounter_relations_data.get("encounter_relations", []):
print(f" {item.get('code')}")
else:
print(f"Failed to get order encounter relations: {response.status_code}")
print(response.json())
```
```typescript theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
const response = await fetch('https://sdp.suki-stage.com/api/v1/info/orders/encounter-relations', {
headers: {
'sdp_suki_token': '',
'sdp_provider_id': ''
}
});
if (response.ok) {
const encounterRelationsData = await response.json();
console.log('Supported Order Encounter Relations:');
encounterRelationsData.encounter_relations?.forEach((item: any) => {
console.log(` ${item.code}`);
});
} else {
const error = await response.json();
console.error(`Failed to get order encounter relations: ${response.status}`, error);
}
```
# Medication Frequency Types
Source: https://developer.suki.ai/api-reference/info/orders-frequencies
GET /api/v1/info/orders/frequencies
Get supported medication frequency types
Use this endpoint to get the list of supported medication frequency types.
## Code examples
**Language tabs (agents):** Equivalent code samples are available in: Python, TypeScript. Humans see one language at a time. Use the variant that matches the user's stack; behavior is the same across tabs.
```python theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
import requests
url = "https://sdp.suki-stage.com/api/v1/info/orders/frequencies"
headers = {
"sdp_suki_token": "",
"sdp_provider_id": ""
}
response = requests.get(url, headers=headers)
if response.status_code == 200:
frequency_types_data = response.json()
print("Supported Medication Frequency Types:")
for item in frequency_types_data.get("frequency_types", []):
print(f" {item.get('code')}")
else:
print(f"Failed to get medication frequency types: {response.status_code}")
print(response.json())
```
```typescript theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
const response = await fetch('https://sdp.suki-stage.com/api/v1/info/orders/frequencies', {
headers: {
'sdp_suki_token': '',
'sdp_provider_id': ''
}
});
if (response.ok) {
const frequencyTypesData = await response.json();
console.log('Supported Medication Frequency Types:');
frequencyTypesData.frequency_types?.forEach((item: any) => {
console.log(` ${item.code}`);
});
} else {
const error = await response.json();
console.error(`Failed to get medication frequency types: ${response.status}`, error);
}
```
# Medication Timings
Source: https://developer.suki.ai/api-reference/info/orders-medication-timings
GET /api/v1/info/orders/medication-timings
Get supported medication timings
Use this endpoint to get the list of supported medication timings.
## Code examples
**Language tabs (agents):** Equivalent code samples are available in: Python, TypeScript. Humans see one language at a time. Use the variant that matches the user's stack; behavior is the same across tabs.
```python theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
import requests
url = "https://sdp.suki-stage.com/api/v1/info/orders/medication-timings"
headers = {
"sdp_suki_token": "",
"sdp_provider_id": ""
}
response = requests.get(url, headers=headers)
if response.status_code == 200:
timings_data = response.json()
print("Supported Medication Timings:")
for item in timings_data.get("timings", []):
print(f" {item.get('code')}")
else:
print(f"Failed to get medication timings: {response.status_code}")
print(response.json())
```
```typescript theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
const response = await fetch('https://sdp.suki-stage.com/api/v1/info/orders/medication-timings', {
headers: {
'sdp_suki_token': '',
'sdp_provider_id': ''
}
});
if (response.ok) {
const timingsData = await response.json();
console.log('Supported Medication Timings:');
timingsData.timings?.forEach((item: any) => {
console.log(` ${item.code}`);
});
} else {
const error = await response.json();
console.error(`Failed to get medication timings: ${response.status}`, error);
}
```
# Order Origins
Source: https://developer.suki.ai/api-reference/info/orders-origins
GET /api/v1/info/orders/origins
Get supported order origins
Use this endpoint to get the list of supported order origins.
## Code examples
**Language tabs (agents):** Equivalent code samples are available in: Python, TypeScript. Humans see one language at a time. Use the variant that matches the user's stack; behavior is the same across tabs.
```python theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
import requests
url = "https://sdp.suki-stage.com/api/v1/info/orders/origins"
headers = {
"sdp_suki_token": "",
"sdp_provider_id": ""
}
response = requests.get(url, headers=headers)
if response.status_code == 200:
origins_data = response.json()
print("Supported Order Origins:")
for item in origins_data.get("origins", []):
print(f" {item.get('code')}")
else:
print(f"Failed to get order origins: {response.status_code}")
print(response.json())
```
```typescript theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
const response = await fetch('https://sdp.suki-stage.com/api/v1/info/orders/origins', {
headers: {
'sdp_suki_token': '',
'sdp_provider_id': ''
}
});
if (response.ok) {
const originsData = await response.json();
console.log('Supported Order Origins:');
originsData.origins?.forEach((item: any) => {
console.log(` ${item.code}`);
});
} else {
const error = await response.json();
console.error(`Failed to get order origins: ${response.status}`, error);
}
```
# Medication Order Statuses
Source: https://developer.suki.ai/api-reference/info/orders-statuses
GET /api/v1/info/orders/statuses
Get supported medication order statuses
Use this endpoint to get the list of supported medication order statuses.
## Code examples
**Language tabs (agents):** Equivalent code samples are available in: Python, TypeScript. Humans see one language at a time. Use the variant that matches the user's stack; behavior is the same across tabs.
```python theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
import requests
url = "https://sdp.suki-stage.com/api/v1/info/orders/statuses"
headers = {
"sdp_suki_token": "",
"sdp_provider_id": ""
}
response = requests.get(url, headers=headers)
if response.status_code == 200:
statuses_data = response.json()
print("Supported Medication Order Statuses:")
for item in statuses_data.get("statuses", []):
print(f" {item.get('code')}")
else:
print(f"Failed to get medication order statuses: {response.status_code}")
print(response.json())
```
```typescript theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
const response = await fetch('https://sdp.suki-stage.com/api/v1/info/orders/statuses', {
headers: {
'sdp_suki_token': '',
'sdp_provider_id': ''
}
});
if (response.ok) {
const statusesData = await response.json();
console.log('Supported Medication Order Statuses:');
statusesData.statuses?.forEach((item: any) => {
console.log(` ${item.code}`);
});
} else {
const error = await response.json();
console.error(`Failed to get medication order statuses: ${response.status}`, error);
}
```
# Provider Roles
Source: https://developer.suki.ai/api-reference/info/provider-roles
GET /api/v1/info/provider-roles
Get list of supported healthcare provider roles
Use this endpoint to get the list of supported provider roles.
## Code examples
**Language tabs (agents):** Equivalent code samples are available in: Python, TypeScript. Humans see one language at a time. Use the variant that matches the user's stack; behavior is the same across tabs.
```python theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
import requests
url = "https://sdp.suki-stage.com/api/v1/info/provider-roles"
headers = {
"sdp_suki_token": "",
"sdp_provider_id": ""
}
response = requests.get(url, headers=headers)
if response.status_code == 200:
provider_roles_data = response.json()
print("Supported Provider Roles:")
for provider_role in provider_roles_data.get("provider_roles", []):
print(f" {provider_role.get('code')}")
else:
print(f"Failed to get provider roles: {response.status_code}")
print(response.json())
```
```typescript theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
const response = await fetch('https://sdp.suki-stage.com/api/v1/info/provider-roles', {
headers: {
'sdp_suki_token': '',
'sdp_provider_id': ''
}
});
if (response.ok) {
const providerRolesData = await response.json();
console.log('Supported Provider Roles:');
providerRolesData.provider_roles?.forEach((providerRole: any) => {
console.log(` ${providerRole.code}`);
});
} else {
const error = await response.json();
console.error(`Failed to get provider roles: ${response.status}`, error);
}
```
# Supported Medical Specialties
Source: https://developer.suki.ai/api-reference/info/specialties
GET /api/v1/info/specialties
Get list of supported medical specialties and their identifiers
Use this endpoint to get the list of supported medical specialties. This list is used to validate the specialty field in the session context when creating or updating an ambient session .
For more information about the specialties, refer to the [Medical specialties section](/documentation/concepts/ambient-clinical-notes/specialties).
## Code examples
**Language tabs (agents):** Equivalent code samples are available in: Python, TypeScript. Humans see one language at a time. Use the variant that matches the user's stack; behavior is the same across tabs.
```python theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
import requests
url = "https://sdp.suki-stage.com/api/v1/info/specialties"
headers = {
"sdp_suki_token": "",
"sdp_provider_id": ""
}
response = requests.get(url, headers=headers)
if response.status_code == 200:
specialties_data = response.json()
print("Supported Medical Specialties:")
for specialty in specialties_data.get("specialties", []):
print(f" {specialty.get('code')}")
else:
print(f"Failed to get specialties: {response.status_code}")
print(response.json())
```
```typescript theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
const response = await fetch('https://sdp.suki-stage.com/api/v1/info/specialties', {
headers: {
'sdp_suki_token': '',
'sdp_provider_id': ''
}
});
if (response.ok) {
const specialtiesData = await response.json();
console.log('Supported Medical Specialties:');
specialtiesData.specialties?.forEach((specialty: any) => {
console.log(` ${specialty.code}`);
});
} else {
const error = await response.json();
console.error(`Failed to get specialties: ${response.status}`, error);
}
```
# Visit Types
Source: https://developer.suki.ai/api-reference/info/visit-types
GET /api/v1/info/visit-types
Get list of supported clinical visit types
Use this endpoint to get the list of supported visit types.
## Code examples
**Language tabs (agents):** Equivalent code samples are available in: Python, TypeScript. Humans see one language at a time. Use the variant that matches the user's stack; behavior is the same across tabs.
```python theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
import requests
url = "https://sdp.suki-stage.com/api/v1/info/visit-types"
headers = {
"sdp_suki_token": "",
"sdp_provider_id": ""
}
response = requests.get(url, headers=headers)
if response.status_code == 200:
visit_types_data = response.json()
print("Supported Visit Types:")
for visit_type in visit_types_data.get("visit_types", []):
print(f" {visit_type.get('code')}")
else:
print(f"Failed to get visit types: {response.status_code}")
print(response.json())
```
```typescript theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
const response = await fetch('https://sdp.suki-stage.com/api/v1/info/visit-types', {
headers: {
'sdp_suki_token': '',
'sdp_provider_id': ''
}
});
if (response.ok) {
const visitTypesData = await response.json();
console.log('Supported Visit Types:');
visitTypesData.visit_types?.forEach((visitType: any) => {
console.log(` ${visitType.code}`);
});
} else {
const error = await response.json();
console.error(`Failed to get visit types: ${response.status}`, error);
}
```
# Medication Orders Info APIs
Source: https://developer.suki.ai/api-reference/medication-orders-info
Get supported medication order metadata in one response
The Medication Orders Info APIs enable partners to retrieve supported metadata for medication orders, including coding systems, dosage units, frequency types, timings, statuses, origins, and encounter relations. Use these catalogs when you build order-related request fields or validate clinician selections.
These APIs are called from your servers and authenticated with a Suki Token (`sdp_suki_token`). Critically, these endpoints return reference metadata. They do not place medication orders in an EHR.
## Available endpoints
Retrieve supported medication order metadata in one response
Retrieve supported coding systems for medication orders
Retrieve supported dosage units for medication orders
Retrieve supported encounter relations for medication orders
Retrieve supported frequency values for medication orders
Retrieve supported medication timing values
Retrieve supported origin values for medication orders
Retrieve supported status values for medication orders
## Related guides
Learn how to convert medication instructions from ambient encounters into structured Medication order data
See how Medication orders fit into the ambient clinical note workflow
## Common use cases
Use supported order metadata so clinicians select units, frequency, timing, and status values that pass EHR validation.
Refresh Medication Orders Info on your release cycle so coding systems and timings stay aligned with Suki.
Check ambient-suggested medication orders against supported catalogs before you persist them in the chart.
Use the same order metadata when clinicians review medication suggestions after capture continues across products.
# Ambient & Dictation APIs Overview
Source: https://developer.suki.ai/api-reference/overview
Plan an Ambient or Dictation integration with REST session APIs, Partner WebSocket audio, webhooks, and endpoints for notes, preferences, feedback, and structured clinical output
**New**
The Ambient APIs are now interoperable with other Suki products that support Ambient workflows. Pass `emr_encounter_id` when you create an ambient session so clinicians can continue the same clinical note across Ambient APIs, Mobile SDK, and Web SDK.
Refer to [Ambient interoperability](/documentation/concepts/ambient-clinical-notes/ambient-interoperability) for more information.
**Are you a Suki partner?**
To use any Suki API or SDK, you must be a Suki partner. Contact the partnership team to begin the onboarding process. They help you set up your authentication system and get started with the Suki APIs and SDKs.
Suki Ambient APIs let you integrate **Ambient clinical documentation** into your application. You can create and manage **ambient sessions**, stream visit audio, and retrieve AI-generated **clinical notes** while maintaining full control over your application's workflows and user experience.
An encounter is the patient visit. An ambient session is one recording for that visit. One encounter can include one or more ambient sessions.
During an ambient session, providers and patients have a real-time conversation while Suki processes the streamed audio and generates a clinical note. Most operations use REST APIs and return standard [HTTP status codes](/api-reference/https-guidelines).
Audio is streamed over the Partner WebSocket (`GET /ws/stream`) using the ambient session ID. When note generation is complete, Suki sends a webhook notification to your application, so you don't need to continuously poll for results.
The same APIs also support **Dictation** when you need transcription without the full clinical note workflow. You create a Dictation session, stream audio, and retrieve the transcript in real time. To learn more, refer to [Audio Dictation](/documentation/concepts/dictation/dictation).
## Available APIs
The following set of APIs are available for the Ambient and Dictation workflows. View each card below to learn more about the endpoints that are available and how to use them.
Endpoints for authentication and authorization.
Endpoints for ambient session management.
Endpoints for Dictation.
Endpoints for content retrieval after the session is completed.
Endpoints for managing the user preferences.
Endpoints for managing user feedback.
Endpoints for managing notifications to your service.
Endpoints for retrieving information about the encounter type, visit type, and provider role.
Endpoints for retrieving information about the medication orders.
## Authentication
For an overview of Suki's supported authentication mechanisms refer to [Authentication mechanisms](/documentation/how-to/partner-authentication). We recommend using OAuth 2.0 with JWT tokens for your authentication system.
If you know what your authentication model is, refer to the following guides to get started:
All Suki API requests must include the following headers:
| Header | Value | Required |
| :-------------- | :----------------------------------- | :---------- |
| `Authorization` | `Bearer ` | Yes |
| `partner_token` | JWT issued by your identity provider | Yes |
| `Content-Type` | `application/json` | Yes |
| `Accept` | `application/json` | Recommended |
If you're using a Suki SDK, the SDK manages authentication and sends the required headers automatically after authentication is configured.
## Key capabilities
The Ambient APIs provide the following capabilities, so you can control audio capture, note quality, and how generated outputs reach your product and downstream systems.
Create ambient sessions, stream visit audio over the Partner WebSocket, and pause, resume, or end each session when that recording is finished.
Turn provider-patient conversations into structured clinical notes and full transcripts, then retrieve draft content and metadata after processing finishes.
Let patients speak in 80+ languages while Suki generates English notes and transcripts that fit standard EHR workflows.
Set provider-level verbosity and section formats through the User Preferences API so generated notes match how each clinician documents care.
Organize documentation by patient problems, merge existing diagnoses from context, and retrieve ICD10, IMO, SNOMED, and HCC structured output for EHR integration.
Convert medication instructions from ambient encounters into structured Medication order data for APIs, Mobile SDK, and Web SDK.
Extract diagnoses, medications, and other encounter-level artifacts from the conversation for charting, orders, and analytics in your application.
Configure which LOINC-based sections appear in generated notes so output aligns with your specialty templates and compliance requirements.
Run speech-to-text sessions when you need transcription without the full ambient clinical note workflow.
Receive webhook callbacks when processing completes, and submit feedback on transcripts or generated content to track quality over time.
## Requirements
Before you can use the Ambient and Dictation APIs, meet these requirements:
* You must be a Suki partner. Learn more about how to become a Suki partner in the [Partner onboarding](/documentation/get-started/partner-onboarding) documentation.
* A standards-based authentication system (for example OAuth 2.0 or OpenID Connect with JWTs).
* JWT tokens with consistent user identifiers (for example `sub`, `email`, or `userId`).
* A publicly accessible JWKS endpoint for token verification.
## Common integration patterns and use cases
The Ambient APIs provide REST and WebSocket endpoints to create ambient sessions, stream audio, complete sessions, and retrieve clinical outputs. Your application owns the capture UI, session orchestration, and how notes are reviewed or persisted.
The following examples show common ways to integrate the Ambient APIs into your application.
Create the session through REST, stream visit audio over the Partner WebSocket, and expose recording controls in your own interface.
End the session, receive a completion webhook or poll status, then retrieve the generated clinical note and related outputs when processing completes.
Map LOINC-based note sections, transcripts, and available structured data into your review and EHR persistence workflow after the session completes.
Use Dictation APIs when your application needs real-time transcription without running the ambient note-generation workflow.
## API versioning
All endpoints use the `/api/v1/` prefix. **v1** is the stable version. Non-breaking changes may ship without a major version jump. For policies and migration, refer to [API guidelines](/api-reference/api-guidelines#version-management).
These APIs may include Early Access features. If you are unsure what is enabled for your account, contact your Suki representative.
## Suki Ambient APIs workflow
To integrate with the Suki Ambient API , you follow a session-based workflow.
```mermaid actions={false} theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
flowchart TD
Start([Start Session]) --> Auth[Authentication]
Auth --> Check{User registered?}
Check -->|No| Register[Register new user]
Register --> Auth
Check -->|Yes| Token[Get sdp_suki_token]
Token --> Create[Create session]
Create --> SessionID[Get ambient_session_id]
SessionID --> Context{Seed Context?}
Context -->|Yes| AddContext[Add additional context]
Context -->|No| Stream
AddContext --> Stream[Stream audio chunks]
Stream --> Control[PAUSE/RESUME/KEEP_ALIVE]
Control --> Done{Session Complete?}
Done -->|No| Stream
Done -->|Yes| End[Generate note]
End --> Wait[AI processing]
Wait --> Notify{Webhook configured?}
Notify -->|Yes| Hook[Receive notification]
Notify -->|No| Poll[Poll status]
Hook --> Retrieve
Poll --> Retrieve[Retrieve note]
Retrieve --> Complete([Session Complete])
classDef authStyle fill:#FFE148,stroke:#D4A017,stroke-width:2px,color:#000000
classDef registerStyle fill:#FFF394,stroke:#FFE148,stroke-width:2px,color:#000000
classDef highlightStyle fill:#FFD700,stroke:#D4A017,stroke-width:3px,color:#000000
class Auth,Create,Stream,End authStyle
class Register registerStyle
class Retrieve highlightStyle
```
### Developer workflow
Authenticate with Suki to get a Suki authentication token, also called `suki_token`.
Create an ambient session for the patient visit. One encounter can include more than one session.
Stream visit audio over WebSocket to the Suki backend.
End the ambient session when that recording is finished. Ending a session is not the same as closing the encounter.
Retrieve generated outputs through REST endpoints.
If webhooks are enabled for your partner account, your application will receive automatic completion notifications instead of relying only on polling.
**Best practices**
* Store **partner\_id**, **partner\_token**, and issued tokens securely; rotate credentials per your security policy.
* Send API traffic over HTTPS and validate webhook signatures when your integration receives callbacks.
* Read HTTP status and error bodies from REST responses; handle auth expiry by refreshing **suki\_token** as documented.
## Next steps
Refer to the [Ambient APIs quickstart](/api-reference/quickstart) to get started.
# Ambient & Dictation APIs Changelog
Source: https://developer.suki.ai/api-reference/product-updates/changelog
API product updates and announcements for the Ambient and Dictation APIs
Suki's API versioning policy is based on the semantic versioning standard. For example, in version 1.2.3, 1 is the major version, 2 is the minor version, and 3 is the patch version.
When we release a new API version for new features or bug fixes, we increment one of these three version components depending on the type of change introduced.
**API version numbering policy**
All Suki REST APIs are currently in `v1` version.
**Release version**
The release version in this changelog page represents the incremental progress. It is not the API version. It tracks new optional fields, performance improvements, new endpoints, and other changes.
No changelog entries match this filter. Choose All or another scope.
### New endpoints
* **Note-level ambient endpoints**: You can now retrieve accumulated content, context, and structured data for a note across ambient sessions while using the interoperability feature. Use `composition_id` from [Create ambient session](/api-reference/ambient-sessions/create) as `note_id`.
* GET [/api/v1/ambient/note//content](/api-reference/ambient-content/note-content).
* GET [/api/v1/ambient/note//context](/api-reference/ambient-content/note-context).
* GET [/api/v1/ambient/note//structured-data](/api-reference/ambient-content/note-structured-data).
* **List encounter notes**: You can now list all notes linked to an EMR encounter for cross-modality ambient workflows.
* GET [/api/v1/ambient/encounter//notes](/api-reference/ambient-content/list-encounter-notes).
### Enhancements
* **Create ambient session**: You can now pass `emr_encounter_id` to enable cross-modality ambient interoperability. The response now includes `composition_id`, which you use as `note_id` with the note-level ambient endpoints. The `multilingual` parameter is deprecated. Multilingual support is enabled by default for all ambient sessions.
* POST [/api/v1/ambient/session/create](/api-reference/ambient-sessions/create).
Learn more in the [How to use ambient across modalities](/documentation/how-to/ambient-clinical-notes/use-ambient-across-modalities) documentation.
### Enhancements
* **HCC codes**: You now get [HCC codes](https://www.aapc.com/resources/what-is-hierarchical-condition-category?srsltid=AfmBOopcl-dIWrRrQFq58LGS72p58BakTdoWdEHv0P9z89c3XBXuJAOY) in the **Structured data API** and **Encounter structured data API** output for an ambient session and encounter.
* GET [/api/v1/ambient/session//structured-data](/api-reference/ambient-content/structured-data).
* GET [/api/v1/ambient/encounter//structured-data](/api-reference/ambient-content/encounter-structured-data).
* Use HCC codes in the structured data output to validate the diagnosis codes in the EMR context.
```json JSON theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
{
"structured_data": {
"diagnoses": {
"values": [
{
"codes": [
{
"code": "A00.0",
"description": "Acute myocardial infarction",
"type": "ICD10"
},
{
"code": "R05.2",
"description": "Hypertension, unspecified",
"type": "ICD10"
},
{
"code": "65",
"description": "CMS-HCC model category 65", // HCC code in the format `CMS-HCC model category `
"type": "HCC"
}
]
}
]
}
}
}
```
If an ICD-10-CM HCC diagnosis code does not map to an HCC model category, Suki looks for general HCC codes that match the diagnosis description. If no match is found, the diagnosis is returned without an HCC code.
### Enhancements
* Suki now supports **Single Auth Token authentication**: your backend can use one shared `partner_token` for all clinicians instead of a per-user token.
The shared token proves your organization is authorized, but it does not identify who is signed in. If you use this model, you **must** send `sdp_provider_id`
on every API call so Suki can route each call to the correct clinician.
Learn more in the [Provider authentication](/api-reference/provider-authentication) documentation.
```json JSON theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
{
"sdp_suki_token": "your-suki-token", // sdp_suki_token required for all API calls
// Required with Single Auth Token auth: send sdp_provider_id on Register, Login, and every API call
"sdp_provider_id": "doc1234567890"
}
```
### New endpoints
* **Info endpoints**: Added new endpoints to get the list of supported medication coding systems, dosage units, encounter relations, frequencies, medication timings, origins, and statuses.
* GET [/api/v1/info/orders/coding-systems](/api-reference/info/orders-coding-systems).
* GET [/api/v1/info/orders/dosage-units](/api-reference/info/orders-dosage-units).
* GET [/api/v1/info/orders/encounter-relations](/api-reference/info/orders-encounter-relations).
* GET [/api/v1/info/orders/frequencies](/api-reference/info/orders-frequencies).
* GET [/api/v1/info/orders/medication-timings](/api-reference/info/orders-medication-timings).
* GET [/api/v1/info/orders/origins](/api-reference/info/orders-origins).
* GET [/api/v1/info/orders/statuses](/api-reference/info/orders-statuses).
### Enhancements
* **Medication orders**: You can now send and retrieve medication orders for an ambient session and encounter using the following endpoints.
**Structured data endpoints**: These endpoints return the structured data for an ambient session and encounter.
* GET [/api/v1/ambient/session//structured-data](/api-reference/ambient-content/structured-data).
* GET [/api/v1/ambient/session//encounter-structured-data](/api-reference/ambient-content/encounter-structured-data).
**Context API**: This endpoint allows you to update the ambient session context with new EMR context and medication orders.
* POST [/api/v1/ambient/session//context](/api-reference/ambient-sessions/update-context).
**Content API**: This endpoint returns the content for an ambient session and encounter.
* GET [/api/v1/ambient/session//content](/api-reference/ambient-content/content).
* GET [/api/v1/ambient/session//encounter-content](/api-reference/ambient-content/encounter-content).
**Payload structure**:
```json JSON theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
{
"structured_data": {
"orders": {
"medication_orders": {
"values": [
{
"drug_name": "Drug Name",
"status": "Status",
"medication_code": "Medication Code",
"medication_code_type": "Medication Code Type"
}
]
}
}
}
}
```
When you send medication orders to the context API, the medication orders are validated against the EMR context. If the medication orders are not valid, the context API will return an error.
Learn more in the [Medication orders](/documentation/concepts/ambient-clinical-notes/medication-orders) documentation.
### New endpoints
* **Audio streaming and download**: We've added a new endpoint for audio streaming and download from ambient sessions. Stream or download the audio recording from an ambient session.
* GET [/api/v1/ambient/session//recording](/api-reference/ambient-content/recording).
Learn more in the [Audio streaming and download](/documentation/how-to/audio-streaming/audio-streaming-download) documentation.
### Deprecated
* **Multilingual support**: The `multilingual` parameter is deprecated in the `Create Ambient Session` endpoint. When you call create ambient session API, the multilingual support is now **set to true by default**.
### New endpoints
* **Audio Dictation API**: Added support for real-time audio transcription. Create transcription sessions, stream transcription data, and end sessions to get final transcripts. This enables use cases like Dictation, real-time captioning, and transcript generation.
* POST [/api/v1/transcription/session/create](/api-reference/audio-transcription/create-session).
* POST [/api/v1/transcription/session//end](/api-reference/audio-transcription/end-session).
* GET [/ws/transcribe](/api-reference/audio-transcription/stream-transcription).
### Enhancements
* **Code examples**: Added code examples in Python and TypeScript for all APIs, making it easier to integrate with the platform regardless of your preferred programming language.
### New endpoints
* **Info endpoints**: Introduced three new info endpoints to provide supported enum values, helping you validate context data before submission and ensuring data consistency.
* GET [/api/v1/info/encounter-types](/api-reference/info/encounter-types).
* GET [/api/v1/info/visit-types](/api-reference/info/visit-types).
* GET [/api/v1/info/provider-roles](/api-reference/info/provider-roles).
* **API reference guidelines**: Added a new [API reference guidelines](/api-reference/api-guidelines) page to help you understand API status tags, versioning, and documentation standards.
### Enhancements
* **Enhanced API documentation**: Significantly restructured and enhanced API reference documentation for better developer experience, clearer organization, and improved discoverability.
* **Problem-based charting guide**: Completely rewritten the Problem-Based Charting (PBC) guide with a clearer structure and more detailed explanations of the processing pipeline.
* **Audio streaming authentication**: Added detailed authentication guidance for both browser and non-browser clients in the Audio streaming API reference documentation.
* **Enhanced context support**: Updated the [Ambient session context APIs](/api-reference/ambient-sessions/context) to support a new `VisitContext` schema, including fields like `chief_complaint` and `visit_type`. The provider context has also been enhanced to support `provider_role`.
### Removed support
* **SNOMED codes**: SNOMED codes are no longer supported for the diagnosis context.
* **Paused state**: Paused state is no longer supported for ambient sessions.
# Provider Authentication
Source: https://developer.suki.ai/api-reference/provider-authentication
Run Register and Login to obtain Suki access token, choose Standard, Single Auth Token, or Bearer partner authentication models, and refresh tokens for Ambient API calls
**Not a Suki Partner yet?**
To use the Suki APIs, you must first register your organization as a partner. To begin, follow the [Partner onboarding](/documentation/get-started/partner-onboarding) guide to learn more or schedule a call with Suki using the **Ask AI** chat assistant.
After you register, you use a Partner Token (`partner_token`) to authenticate your API requests. The `partner_token` is a JWT that **you** provide, and it is a **required parameter** when registering a provider. Find more details on how to get a `partner_token` in the [Partner authentication](/documentation/how-to/partner-authentication) guide.
All Suki REST and WebSocket APIs require partner authentication. Call the [Register](/api-reference/authentication/register) and [Login](/api-reference/authentication/login) APIs with your Partner Token (`partner_token`) to authenticate clinicians with Suki and receive a Suki access token (`sdp_suki_token` / `suki_token`).
Suki access tokens are valid for **1 hour**.
This guide explains the **two main endpoints** for the authentication workflow:
* **Register user**: A **one-time** call to register a **new provider ** in the Suki system.
* **Authenticate user**: Call this endpoint to get a **suki\_token** (`sdp_suki_token`) for a registered provider.
## Choose your authentication model
Before integrating with Suki, determine which authentication model your organization uses. Your authentication model defines how clinician identity is sent to Suki and which fields are required during authentication and subsequent API requests.
Suki supports the following authentication models and defines your model during the [Partner onboarding](/documentation/get-started/partner-onboarding) process.
This guide is for Standard partner authentication only. For Single Auth Token authentication and Bearer partner authentication, refer to the [Single Auth Token authentication](/api-reference/single-auth-token-authentication) and [Bearer partner authentication](/api-reference/bearer-partner-authentication) guides respectively.
### 1. Standard partner authentication
Each clinician authenticates with their own `partner_token`. Suki derives provider identity from that token, so you do not need to send `provider_id` on Register or Login.
**Best for**
* Enterprise deployments where each clinician has their own account in your identity provider.
* Integrations where your IdP issues a per-user ID token or user-scoped JWT as `partner_token`.
* Flows where the token already identifies the signed-in provider without a separate `provider_id`.
### 2. Single Auth Token authentication
Your backend uses **one shared** `partner_token` for multiple clinicians. The token proves your organization is authorized, but it does not identify who is signed in. With Single Auth Token authentication, you send **provider identity on every** request: `provider_id` on Register and Login, and `sdp_provider_id` on every subsequent REST and WebSocket call.
**Best for**
* Server backends that authenticate to Suki with one organization-level token for many clinicians.
* Integrations that supply a stable `provider_id` for each clinician (from any source your system chooses).
* Identity setups that cannot include per-user claims in the `partner_token`.
If you plan to use Single Auth Token authentication, see [Single Auth Token authentication](/api-reference/single-auth-token-authentication) for more details.
### 3. Bearer partner authentication
**Bearer** is a separate partner type that Suki configures during onboarding. A Bearer partner validates the end-user identity with `provider_id` during **Login** and **Register**, instead of relying on per-user claims in the token.
**Best for**
* Partners whose shared `partner_token` cannot carry per-user identity.
* Integrations where Suki must validate `provider_id` against an agreed expression during authentication.
* Web SDK and other clients that pass `provider_id` for a Bearer partner configuration.
For Login and Register fields, validation rules, and security guidance, refer to the [Bearer partner authentication](/api-reference/bearer-partner-authentication) guide.
* Single Auth Token partners cannot be a Bearer partner.
* To refresh a Suki access token, send another **POST** request to [Login](/api-reference/authentication/login) with a valid `partner_token`.
## Register provider/user account
Use the below endpoint to **register** a new **provider/user** in the Suki platform. You only need to do this **once** for each new provider.
**Endpoint**: [Register](/api-reference/authentication/register)
**Method:** POST
### Registration scenarios
The `Register` endpoint handles three main scenarios:
* **New user registration**: If the provider **does not exist** in the Suki system, this call **creates a new user** and links them to your partner account and organization.
* **Existing user, new partner link**: If the provider **already exists** but is not linked to your partner account, this call **links them to your partner** and their existing organization.
* **Existing user, already linked**: If the provider is already registered and linked to your partner account, the API returns a `409` Conflict error.
## Authenticate provider/user session
After a provider is registered, call the [Login](/api-reference/authentication/login) endpoint to get a Suki access token (`sdp_suki_token`).
If you are a **Bearer partner**, send `provider_id` on every [Login](/api-reference/authentication/login) and [Register](/api-reference/authentication/register) request. Refer to [Bearer partner authentication](/api-reference/bearer-partner-authentication).
If you use **Single Auth Token authentication**, also send `sdp_provider_id` on every API call after Login. Refer to [Single Auth Token authentication](/api-reference/single-auth-token-authentication).
### JWKS endpoint
Suki provides a public **JWKS (JSON Web Key Set)** endpoint that you can use to verify the signature of the `sdp_suki_token` that our API returns.
**Endpoint:** [JWKS URL](/api-reference/authentication/jwks)
**Method:** GET
**Authentication:** None (Public)
### Use cases
* **Token verification**: Fetch public keys to verify JWTs issued by Suki.
* **Signature validation**: Validate the signature of the `sdp_suki_token`.
* **Key rotation**: Automatically discover new public keys when Suki rotates our signing keys.
# Ambient API Quickstart
Source: https://developer.suki.ai/api-reference/quickstart
Authenticate, create an Ambient session, stream audio, end the session, and retrieve the clinical note on staging
This quickstart walks you through one successful Ambient API session on staging: authenticate, create a session, stream visit audio, end the session, and retrieve the clinical note. Rely on a webhook when processing finishes to handle notifications.
Your application owns session controls, audio streaming, status handling, note review, and EHR handoff. For product context and when to choose Ambient APIs versus ambient SDKs, refer to [Ambient clinical documentation](/documentation/concepts/ambient-clinical-notes/ambient-documentation).
**What you will do**
1. **Authenticate** to get an `sdp_suki_token` (and **register** the user if needed).
2. **Create** an ambient session and optionally **seed context** for better notes. An ambient session is one recording for a patient visit (encounter). One encounter can include more than one session.
3. **Stream audio** over the WebSocket, send control events, and **end** the ambient session when that recording is finished.
4. **Retrieve** the clinical note and transcript, or rely on a **webhook** when processing finishes.
**Prefer one paste-ready file?** Use the [Complete staging script](#complete-staging-script) in the preferred language below, then follow the numbered steps for the same flow with full explanations.
Building in **C++**? See the C++ sample on [Login](/api-reference/authentication/login). For Ambient, Dictation, and Form filling flows, use the TypeScript or Python complete staging scripts below.
**Using an AI coding tool?**
Copy the prompt below to point your agent at the Ambient skill and [Documentation MCP](/documentation/references/mcp). For every task skill, refer to [AI coding tools](/documentation/references/ai-coding-tools).
Build ambient clinical documentation with Suki for Partners.
Fetch the Ambient build skill:
[https://developer.suki.ai/.well-known/agent-skills/suki-ambient/SKILL.md](https://developer.suki.ai/.well-known/agent-skills/suki-ambient/SKILL.md)
Connect the documentation MCP for page search:
[https://developer.suki.ai/documentation/references/mcp](https://developer.suki.ai/documentation/references/mcp)
## Access and credentials
You need partner credentials to use the Suki Ambient APIs.
Contact our [Partnership team](https://www.suki.ai/suki-partners/) to get your credentials. They will guide you through the [Onboarding process](/documentation/get-started/partner-onboarding) and provide what you need to get started.
### Prerequisites
To use the Suki Ambient APIs, you must have the following:
* An OAuth-compliant authentication system.
* JWT tokens with consistent user identifiers.
* A publicly accessible JWKS endpoint (or Okta authorization server) for token validation.
### Environments to use for development and testing
This guide uses **`https://sdp.suki-stage.com`** and **`wss://sdp.suki-stage.com`** for API and WebSocket examples (staging).
**Important**:
* The production environment is **`https://sdp.suki.ai`** and **`wss://sdp.suki.ai`**.
* The staging environment is **`https://sdp.suki-stage.com`** and **`wss://sdp.suki-stage.com`**.
* Your partnership team will confirm which environment, base URL, and credentials apply for your integration.
## Complete staging script
Replace the credential placeholders, put a 16 kHz mono LINEAR16 WAV (or raw PCM) at `audio.wav`, then run. For detailed explanations of each step, refer to the numbered steps below.
* **Python:** `pip install requests websocket-client` then `python ambient_staging.py`.
* **TypeScript (Node):** `npm install ws` then `npx tsx ambient_staging.ts` (Node 18+).
Ambient sessions must be **at least 1 minute** of audio for note generation. Shorter sessions can return status `skipped` with no note. Use a WAV or raw LINEAR16 PCM file that is long enough when you test.
```python Python expandable theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
# ambient_staging.py
# Flow: login (register once if needed) → create → context → stream → end → poll content
# pip install requests websocket-client
import base64
import json
import time
from datetime import datetime, timezone
from typing import Any, Optional
import requests
import websocket
BASE_URL = "https://sdp.suki-stage.com"
WS_URL = "wss://sdp.suki-stage.com/ws/stream"
CHUNK_BYTES = 3200 # ~100 ms of 16 kHz mono 16-bit PCM
WAV_HEADER_BYTES = 44
# Replace these with your partner credentials
PARTNER_ID = "your-partner-id"
PARTNER_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..." # Keep on your backend only
PROVIDER_ID = "provider-123" # Optional; required for Bearer / Single Auth Token partners
SDP_PROVIDER_ID = "" # Leave empty unless your partnership requires sdp_provider_id
AUDIO_PATH = "audio.wav" # 16 kHz mono LINEAR16 WAV or raw PCM
def b64(data: bytes) -> str:
return base64.b64encode(data).decode("ascii")
def rest_headers(suki_token: str) -> dict[str, str]:
headers = {
"sdp_suki_token": suki_token,
"Content-Type": "application/json",
}
if SDP_PROVIDER_ID:
headers["sdp_provider_id"] = SDP_PROVIDER_ID
return headers
def expect_status(response: requests.Response, url: str, want: int) -> None:
if response.status_code == want:
return
detail = (response.text or "")[:500]
try:
body = response.json()
if isinstance(body, dict) and body.get("message"):
detail = str(body["message"])
except ValueError:
pass
raise RuntimeError(f"HTTP {response.status_code} {url}: {detail}")
def login(partner_id: str, partner_token: str, provider_id: Optional[str] = None) -> str:
url = f"{BASE_URL}/api/v1/auth/login"
payload: dict[str, Any] = {"partner_id": partner_id, "partner_token": partner_token}
if provider_id:
payload["provider_id"] = provider_id
r = requests.post(url, json=payload, timeout=60)
expect_status(r, url, 200)
token = r.json().get("suki_token")
if not token:
raise RuntimeError("login response missing suki_token")
return token
def register_provider(
partner_id: str,
partner_token: str,
provider_name: str,
provider_org_id: str,
provider_id: Optional[str] = None,
) -> None:
url = f"{BASE_URL}/api/v1/auth/register"
payload: dict[str, Any] = {
"partner_id": partner_id,
"partner_token": partner_token,
"provider_name": provider_name,
"provider_org_id": provider_org_id,
}
if provider_id:
payload["provider_id"] = provider_id
r = requests.post(url, json=payload, timeout=60)
# New link: 201. Already linked: 409. Both are success for this flow.
if r.status_code not in (201, 409):
expect_status(r, url, 201)
def login_with_register_fallback(
partner_id: str,
partner_token: str,
provider_id: Optional[str],
provider_name: str,
provider_org_id: str,
) -> str:
try:
return login(partner_id, partner_token, provider_id)
except RuntimeError as err:
if "provider_not_registered" not in str(err):
raise
register_provider(
partner_id, partner_token, provider_name, provider_org_id, provider_id
)
return login(partner_id, partner_token, provider_id)
def create_ambient_session(suki_token: str, body: Optional[dict[str, str]] = None) -> dict[str, str]:
url = f"{BASE_URL}/api/v1/ambient/session/create"
r = requests.post(url, headers=rest_headers(suki_token), json=body or {}, timeout=60)
expect_status(r, url, 201)
data = r.json()
sid = data.get("ambient_session_id")
composition_id = data.get("composition_id")
if not sid:
raise RuntimeError("create response missing ambient_session_id")
return {
"ambient_session_id": sid,
"composition_id": composition_id or "",
}
def seed_context(suki_token: str, ambient_session_id: str) -> None:
url = f"{BASE_URL}/api/v1/ambient/session/{ambient_session_id}/context"
payload = {
"provider": {"specialty": "CARDIOLOGY", "provider_role": "ATTENDING"},
"patient": {"dob": "2000-01-01", "sex": "male"},
"visit": {
"chief_complaint": "Headache",
"encounter_type": "AMBULATORY",
"reason_for_visit": "Follow-up for migraines",
"visit_type": "ESTABLISHED_PATIENT",
},
"sections": [{"loinc": "10164-2"}, {"loinc": "48765-2"}],
"diagnoses": {
"values": [
{
"codes": [
{
"code": "I10",
"description": "Essential hypertension",
"type": "ICD10",
}
],
"diagnosis_note": "Hypertension",
}
]
},
"emr": {"target_emr": "EPIC"},
}
r = requests.post(url, headers=rest_headers(suki_token), json=payload, timeout=60)
expect_status(r, url, 200)
def strip_wav_header(raw: bytes) -> bytes:
if len(raw) >= 12 and raw[:4] == b"RIFF" and raw[8:12] == b"WAVE":
return raw[WAV_HEADER_BYTES:]
return raw
def stream_pcm_file(suki_token: str, ambient_session_id: str, path: str) -> None:
header = [
f"sdp_suki_token: {suki_token}",
f"ambient_session_id: {ambient_session_id}",
]
if SDP_PROVIDER_ID:
header.append(f"sdp_provider_id: {SDP_PROVIDER_ID}")
ws = websocket.create_connection(WS_URL, header=header, timeout=60)
try:
rfc3339 = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
ws.send(json.dumps({"type": "START_TIME", "data": b64(rfc3339.encode("utf-8"))}))
with open(path, "rb") as f:
pcm = strip_wav_header(f.read())
for i in range(0, len(pcm), CHUNK_BYTES):
ws.send(json.dumps({"type": "AUDIO", "data": b64(pcm[i : i + CHUNK_BYTES])}))
# End marker: Base64 of ASCII bytes EOF (literal RU9G)
ws.send(json.dumps({"type": "AUDIO", "data": "RU9G"}))
finally:
ws.close()
def end_ambient_session(suki_token: str, ambient_session_id: str) -> None:
url = f"{BASE_URL}/api/v1/ambient/session/{ambient_session_id}/end"
r = requests.post(url, headers=rest_headers(suki_token), timeout=60)
expect_status(r, url, 200)
def get_status(suki_token: str, ambient_session_id: str) -> str:
url = f"{BASE_URL}/api/v1/ambient/session/{ambient_session_id}/status"
r = requests.get(url, headers=rest_headers(suki_token), timeout=60)
expect_status(r, url, 200)
return r.json()["status"]
def get_content(suki_token: str, ambient_session_id: str) -> Any:
url = f"{BASE_URL}/api/v1/ambient/session/{ambient_session_id}/content"
r = requests.get(
url,
headers=rest_headers(suki_token),
params={"cumulative": "false"},
timeout=60,
)
expect_status(r, url, 200)
return r.json()
def wait_for_content(suki_token: str, ambient_session_id: str, poll_sec: float = 2.0) -> dict[str, Any]:
while True:
status = get_status(suki_token, ambient_session_id)
print("status:", status)
if status == "completed":
return {"status": status, "content": get_content(suki_token, ambient_session_id)}
if status in ("failed", "skipped", "aborted"):
return {"status": status, "content": None}
time.sleep(poll_sec)
if __name__ == "__main__":
suki_token = login_with_register_fallback(
PARTNER_ID,
PARTNER_TOKEN,
PROVIDER_ID,
provider_name="Dr. John Smith",
provider_org_id="org-123",
)
print("Authenticated")
created = create_ambient_session(suki_token, {})
ambient_session_id = created["ambient_session_id"]
print("ambient_session_id:", ambient_session_id)
print("composition_id:", created["composition_id"])
seed_context(suki_token, ambient_session_id)
print("Context seeded")
stream_pcm_file(suki_token, ambient_session_id, AUDIO_PATH)
print("Stream finished")
end_ambient_session(suki_token, ambient_session_id)
print("Session ended")
result = wait_for_content(suki_token, ambient_session_id)
print(json.dumps(result, indent=2))
```
```typescript TypeScript expandable theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
// ambient_staging.ts
// Flow: login (register once if needed) → create → context → stream → end → poll content
// Node 18+: npm install ws && npx tsx ambient_staging.ts
import fs from "node:fs";
import WebSocket from "ws";
const BASE_URL = "https://sdp.suki-stage.com";
const WS_URL = "wss://sdp.suki-stage.com/ws/stream";
const CHUNK_BYTES = 3200; // ~100 ms of 16 kHz mono 16-bit PCM
const WAV_HEADER_BYTES = 44;
// Replace these with your partner credentials
const PARTNER_ID = "your-partner-id";
const PARTNER_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."; // Keep on your backend only
const PROVIDER_ID = "provider-123"; // Optional; required for Bearer / Single Auth Token partners
const SDP_PROVIDER_ID = ""; // Leave empty unless your partnership requires sdp_provider_id
const AUDIO_PATH = "audio.wav"; // 16 kHz mono LINEAR16 WAV or raw PCM
function restHeaders(sukiToken: string): Record {
const headers: Record = {
sdp_suki_token: sukiToken,
"Content-Type": "application/json",
};
if (SDP_PROVIDER_ID) headers.sdp_provider_id = SDP_PROVIDER_ID;
return headers;
}
async function expectStatus(response: Response, url: string, want: number): Promise {
if (response.status === want) return;
const raw = await response.text();
let detail = raw.slice(0, 500);
try {
const body = JSON.parse(raw);
if (body?.message) detail = String(body.message);
} catch {
// keep raw text
}
throw new Error(`HTTP ${response.status} ${url}: ${detail}`);
}
async function login(
partnerId: string,
partnerToken: string,
providerId?: string,
): Promise {
const url = `${BASE_URL}/api/v1/auth/login`;
const body: Record = {
partner_id: partnerId,
partner_token: partnerToken,
};
if (providerId) body.provider_id = providerId;
const response = await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
});
await expectStatus(response, url, 200);
const data = (await response.json()) as { suki_token?: string };
if (!data.suki_token) throw new Error("login response missing suki_token");
return data.suki_token;
}
async function registerProvider(input: {
partnerId: string;
partnerToken: string;
providerName: string;
providerOrgId: string;
providerId?: string;
}): Promise {
const url = `${BASE_URL}/api/v1/auth/register`;
const body: Record = {
partner_id: input.partnerId,
partner_token: input.partnerToken,
provider_name: input.providerName,
provider_org_id: input.providerOrgId,
};
if (input.providerId) body.provider_id = input.providerId;
const response = await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
});
// New link: 201. Already linked: 409. Both are success for this flow.
if (response.status !== 201 && response.status !== 409) {
await expectStatus(response, url, 201);
}
}
async function loginWithRegisterFallback(
partnerId: string,
partnerToken: string,
providerId: string | undefined,
providerName: string,
providerOrgId: string,
): Promise {
try {
return await login(partnerId, partnerToken, providerId);
} catch (err) {
if (!String(err).includes("provider_not_registered")) throw err;
await registerProvider({
partnerId,
partnerToken,
providerName,
providerOrgId,
providerId,
});
return login(partnerId, partnerToken, providerId);
}
}
async function createAmbientSession(
sukiToken: string,
): Promise<{ ambientSessionId: string; compositionId: string }> {
const url = `${BASE_URL}/api/v1/ambient/session/create`;
const response = await fetch(url, {
method: "POST",
headers: restHeaders(sukiToken),
body: JSON.stringify({}),
});
await expectStatus(response, url, 201);
const data = (await response.json()) as {
ambient_session_id?: string;
composition_id?: string;
};
if (!data.ambient_session_id) {
throw new Error("create response missing ambient_session_id");
}
return {
ambientSessionId: data.ambient_session_id,
compositionId: data.composition_id ?? "",
};
}
async function seedContext(sukiToken: string, ambientSessionId: string): Promise {
const url = `${BASE_URL}/api/v1/ambient/session/${ambientSessionId}/context`;
const response = await fetch(url, {
method: "POST",
headers: restHeaders(sukiToken),
body: JSON.stringify({
provider: { specialty: "CARDIOLOGY", provider_role: "ATTENDING" },
patient: { dob: "2000-01-01", sex: "male" },
visit: {
chief_complaint: "Headache",
encounter_type: "AMBULATORY",
reason_for_visit: "Follow-up for migraines",
visit_type: "ESTABLISHED_PATIENT",
},
sections: [{ loinc: "10164-2" }, { loinc: "48765-2" }],
diagnoses: {
values: [
{
codes: [
{
code: "I10",
description: "Essential hypertension",
type: "ICD10",
},
],
diagnosis_note: "Hypertension",
},
],
},
emr: { target_emr: "EPIC" },
}),
});
await expectStatus(response, url, 200);
}
function stripWavHeader(buf: Buffer): Buffer {
if (
buf.length >= 12 &&
buf.subarray(0, 4).toString("ascii") === "RIFF" &&
buf.subarray(8, 12).toString("ascii") === "WAVE"
) {
return buf.subarray(WAV_HEADER_BYTES);
}
return buf;
}
function streamPcmFile(
sukiToken: string,
ambientSessionId: string,
path: string,
): Promise {
return new Promise((resolve, reject) => {
const headers: Record = {
sdp_suki_token: sukiToken,
ambient_session_id: ambientSessionId,
};
if (SDP_PROVIDER_ID) headers.sdp_provider_id = SDP_PROVIDER_ID;
const ws = new WebSocket(WS_URL, { headers });
ws.on("open", () => {
try {
const rfc3339 = new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
ws.send(
JSON.stringify({
type: "START_TIME",
data: Buffer.from(rfc3339, "utf8").toString("base64"),
}),
);
const pcm = stripWavHeader(fs.readFileSync(path));
for (let i = 0; i < pcm.length; i += CHUNK_BYTES) {
ws.send(
JSON.stringify({
type: "AUDIO",
data: pcm.subarray(i, i + CHUNK_BYTES).toString("base64"),
}),
);
}
// End marker: Base64 of ASCII bytes EOF
ws.send(JSON.stringify({ type: "AUDIO", data: "RU9G" }));
ws.close();
} catch (err) {
reject(err);
}
});
ws.on("message", (data) => console.log("ws message:", data.toString()));
ws.on("error", reject);
ws.on("close", () => resolve());
});
}
async function endAmbientSession(sukiToken: string, ambientSessionId: string): Promise {
const url = `${BASE_URL}/api/v1/ambient/session/${ambientSessionId}/end`;
const response = await fetch(url, {
method: "POST",
headers: restHeaders(sukiToken),
});
await expectStatus(response, url, 200);
}
async function getStatus(sukiToken: string, ambientSessionId: string): Promise {
const url = `${BASE_URL}/api/v1/ambient/session/${ambientSessionId}/status`;
const response = await fetch(url, { headers: restHeaders(sukiToken) });
await expectStatus(response, url, 200);
const data = (await response.json()) as { status: string };
return data.status;
}
async function getContent(sukiToken: string, ambientSessionId: string): Promise {
const url = `${BASE_URL}/api/v1/ambient/session/${ambientSessionId}/content?cumulative=false`;
const response = await fetch(url, { headers: restHeaders(sukiToken) });
await expectStatus(response, url, 200);
return response.json();
}
async function waitForContent(
sukiToken: string,
ambientSessionId: string,
pollMs = 2000,
): Promise<{ status: string; content: unknown }> {
for (;;) {
const status = await getStatus(sukiToken, ambientSessionId);
console.log("status:", status);
if (status === "completed") {
return { status, content: await getContent(sukiToken, ambientSessionId) };
}
if (status === "failed" || status === "skipped" || status === "aborted") {
return { status, content: null };
}
await new Promise((r) => setTimeout(r, pollMs));
}
}
async function main() {
const sukiToken = await loginWithRegisterFallback(
PARTNER_ID,
PARTNER_TOKEN,
PROVIDER_ID,
"Dr. John Smith",
"org-123",
);
console.log("Authenticated");
const created = await createAmbientSession(sukiToken);
console.log("ambient_session_id:", created.ambientSessionId);
console.log("composition_id:", created.compositionId);
await seedContext(sukiToken, created.ambientSessionId);
console.log("Context seeded");
await streamPcmFile(sukiToken, created.ambientSessionId, AUDIO_PATH);
console.log("Stream finished");
await endAmbientSession(sukiToken, created.ambientSessionId);
console.log("Session ended");
const result = await waitForContent(sukiToken, created.ambientSessionId);
console.log(JSON.stringify(result, null, 2));
}
main().catch((err) => {
console.error(err);
process.exit(1);
});
```
## Create your first Ambient session
Send a **POST** request to the [Login API](/api-reference/authentication/login) endpoint with the following parameters in the request body:
* **partner\_id**: Your unique Partner ID , which we provide to you securely offline.
* **partner\_token**: The user's OAuth 2.0 ID token (Partner Token ) from your identity provider.
* **provider\_id** (Optional for standard partners; required for some auth flows): Unique identifier for the provider .
On success the API returns a Suki Token (`suki_token`). Include it as the `sdp_suki_token` header for subsequent API calls.
If the user is not registered, call the [Register API](/api-reference/authentication/register) first, then retry `/login`. You only need to register a user once.
Each sample below is self-contained (TypeScript, Python, and cURL). Expect HTTP **200**.
```typescript TypeScript expandable theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
const BASE_URL = "https://sdp.suki-stage.com";
const response = await fetch(`${BASE_URL}/api/v1/auth/login`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
partner_id: "your-partner-id",
partner_token: "your-jwt-token",
provider_id: "provider-123", // Required for Bearer / Single Auth; omit for Standard
}),
});
const data = await response.json();
if (response.status !== 200) {
throw new Error(`Login failed: ${response.status} ${JSON.stringify(data)}`);
}
if (!data.suki_token) throw new Error("login response missing suki_token");
const sukiToken = data.suki_token as string;
console.log("suki_token:", sukiToken);
```
```python Python expandable theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
import requests
BASE_URL = "https://sdp.suki-stage.com"
response = requests.post(
f"{BASE_URL}/api/v1/auth/login",
headers={"Content-Type": "application/json"},
json={
"partner_id": "your-partner-id",
"partner_token": "your-jwt-token",
"provider_id": "provider-123", # Required for Bearer / Single Auth; omit for Standard
},
timeout=60,
)
if response.status_code != 200:
raise RuntimeError(f"Login failed: {response.status_code} {response.text}")
data = response.json()
suki_token = data.get("suki_token")
if not suki_token:
raise RuntimeError("login response missing suki_token")
print("suki_token:", suki_token)
```
```bash cURL expandable theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
curl --request POST \
--url https://sdp.suki-stage.com/api/v1/auth/login \
--header 'Content-Type: application/json' \
--data '{
"partner_id": "your-partner-id",
"partner_token": "your-jwt-token",
"provider_id": "provider-123"
}'
```
Save the `suki_token` as it is valid for **1 hour**. Request a new token before it expires.
Send a **POST** request to [Create Ambient Session API](/api-reference/ambient-sessions/create). The body can be empty for a first staging session.
Optional body fields:
* **emr\_encounter\_id** (UUID): EMR or EHR visit ID for cross-modality Ambient interoperability.
* **encounter\_id**: Required for re-ambient workflows. Alphanumeric string up to **255** characters to group sessions for the same note.
* **ambient\_session\_id**: Optional UUID v4 to identify the session; Suki generates one if omitted.
Expect HTTP **201** and a response with `ambient_session_id` and `composition_id`.
```typescript TypeScript expandable theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
const BASE_URL = "https://sdp.suki-stage.com";
const CREATE_SESSION_URL = `${BASE_URL}/api/v1/ambient/session/create`;
// Get sdp_suki_token from Login: POST /api/v1/auth/login
const sdpSukiToken = "";
// Required for single_auth partners
const sdpProviderId = "";
// Set these from your system. Omit a field by leaving the value undefined.
// All request body fields are optional. Suki generates values you omit.
// Pass emr_encounter_id for cross-modality Ambient interoperability.
const ambientSessionId: string | undefined = undefined; // Optional UUID for this Ambient session
const emrEncounterId: string | undefined = undefined; // UUID for your EMR encounter
const encounterId: string | undefined = undefined; // Required for re-ambient workflows
type CreateAmbientSessionRequest = {
ambient_session_id?: string;
emr_encounter_id?: string;
encounter_id?: string;
};
type CreateAmbientSessionResponse = {
ambient_session_id: string;
composition_id: string;
};
const payload: CreateAmbientSessionRequest = {};
if (ambientSessionId) payload.ambient_session_id = ambientSessionId;
if (emrEncounterId) payload.emr_encounter_id = emrEncounterId;
if (encounterId) payload.encounter_id = encounterId;
const headers: Record = {
"Content-Type": "application/json",
sdp_suki_token: sdpSukiToken,
sdp_provider_id: sdpProviderId,
};
const createResponse = await fetch(CREATE_SESSION_URL, {
method: "POST",
headers,
body: JSON.stringify(payload),
});
const created = (await createResponse.json()) as CreateAmbientSessionResponse;
if (createResponse.status !== 201) {
throw new Error(
`Create failed: ${createResponse.status} ${JSON.stringify(created)}`,
);
}
console.log("ambient_session_id:", created.ambient_session_id);
console.log("composition_id:", created.composition_id);
```
```python Python expandable theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
import json
import requests
BASE_URL = "https://sdp.suki-stage.com"
CREATE_SESSION_URL = f"{BASE_URL}/api/v1/ambient/session/create"
# Get sdp_suki_token from Login: POST /api/v1/auth/login
sdp_suki_token = ""
# Required for single_auth partners
sdp_provider_id = ""
# Set these from your system. Omit a field by leaving the value as None.
# All request body fields are optional. Suki generates values you omit.
# Pass emr_encounter_id for cross-modality Ambient interoperability.
ambient_session_id = None # Optional UUID for this Ambient session
emr_encounter_id = None # UUID for your EMR encounter
encounter_id = None # Required for re-ambient workflows
headers = {
"sdp_suki_token": sdp_suki_token,
"sdp_provider_id": sdp_provider_id,
"Content-Type": "application/json",
}
payload = {}
if ambient_session_id:
payload["ambient_session_id"] = ambient_session_id
if emr_encounter_id:
payload["emr_encounter_id"] = emr_encounter_id
if encounter_id:
payload["encounter_id"] = encounter_id
response = requests.post(
CREATE_SESSION_URL,
headers=headers,
json=payload,
timeout=60,
)
if response.status_code != 201:
raise RuntimeError(
f"Create failed: {response.status_code} {response.text}"
)
created = response.json()
print(json.dumps(created, indent=2))
print("ambient_session_id:", created["ambient_session_id"])
print("composition_id:", created["composition_id"])
```
```bash cURL expandable theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
curl --request POST \
--url https://sdp.suki-stage.com/api/v1/ambient/session/create \
--header 'Content-Type: application/json' \
--header 'sdp_suki_token: ' \
--header 'sdp_provider_id: ' \
--data '{
"ambient_session_id": "",
"emr_encounter_id": "",
"encounter_id": ""
}'
```
Save `ambient_session_id` and `composition_id`. Use the session ID for streaming and content calls; use `composition_id` as `note_id` for note-level APIs.
POST to [Seed Context API](/api-reference/ambient-sessions/context) after session creation to provide metadata that improves note quality.
Include fields such as `provider`, `patient`, `visit`, `sections`, `diagnoses`, and `emr`.
```typescript TypeScript expandable theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
const BASE_URL = "https://sdp.suki-stage.com";
const sdpSukiToken = "";
const ambientSessionId = "";
const sdpProviderId = ""; // Required for single_auth partners
const headers: Record = {
"Content-Type": "application/json",
sdp_suki_token: sdpSukiToken,
sdp_provider_id: sdpProviderId,
};
const contextResponse = await fetch(
`${BASE_URL}/api/v1/ambient/session/${ambientSessionId}/context`,
{
method: "POST",
headers,
body: JSON.stringify({
provider: { specialty: "CARDIOLOGY", provider_role: "ATTENDING" },
patient: { dob: "2000-01-01", sex: "male" },
visit: {
chief_complaint: "Headache",
encounter_type: "AMBULATORY",
reason_for_visit: "Follow-up for migraines",
visit_type: "ESTABLISHED_PATIENT",
},
sections: [{ loinc: "10164-2" }, { loinc: "48765-2" }],
diagnoses: {
values: [
{
codes: [
{
code: "I10",
description: "Essential hypertension",
type: "ICD10",
},
],
diagnosis_note: "Hypertension",
},
],
},
emr: { target_emr: "EPIC" },
}),
},
);
if (contextResponse.status !== 200) {
const err = await contextResponse.text();
throw new Error(`Context failed: ${contextResponse.status} ${err}`);
}
console.log("Context seeded");
```
```python Python expandable theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
import requests
BASE_URL = "https://sdp.suki-stage.com"
sdp_suki_token = ""
ambient_session_id = ""
sdp_provider_id = "" # Required for single_auth partners
headers = {
"sdp_suki_token": sdp_suki_token,
"sdp_provider_id": sdp_provider_id,
"Content-Type": "application/json",
}
payload = {
"provider": {"specialty": "CARDIOLOGY", "provider_role": "ATTENDING"},
"patient": {"dob": "2000-01-01", "sex": "male"},
"visit": {
"chief_complaint": "Headache",
"encounter_type": "AMBULATORY",
"reason_for_visit": "Follow-up for migraines",
"visit_type": "ESTABLISHED_PATIENT",
},
"sections": [{"loinc": "10164-2"}, {"loinc": "48765-2"}],
"diagnoses": {
"values": [
{
"codes": [
{
"code": "I10",
"description": "Essential hypertension",
"type": "ICD10",
}
],
"diagnosis_note": "Hypertension",
}
]
},
"emr": {"target_emr": "EPIC"},
}
response = requests.post(
f"{BASE_URL}/api/v1/ambient/session/{ambient_session_id}/context",
headers=headers,
json=payload,
timeout=60,
)
if response.status_code != 200:
raise RuntimeError(f"Context failed: {response.status_code} {response.text}")
print("Context seeded")
```
```bash cURL expandable theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
curl --request POST \
--url https://sdp.suki-stage.com/api/v1/ambient/session//context \
--header 'Content-Type: application/json' \
--header 'sdp_suki_token: ' \
--header 'sdp_provider_id: ' \
--data '{
"provider": {
"specialty": "CARDIOLOGY",
"provider_role": "ATTENDING"
},
"patient": {
"dob": "2000-01-01",
"sex": "male"
},
"visit": {
"chief_complaint": "Headache",
"encounter_type": "AMBULATORY",
"reason_for_visit": "Follow-up for migraines",
"visit_type": "ESTABLISHED_PATIENT"
},
"sections": [
{ "loinc": "10164-2" },
{ "loinc": "48765-2" }
],
"diagnoses": {
"values": [
{
"codes": [
{
"code": "I10",
"description": "Essential hypertension",
"type": "ICD10"
}
],
"diagnosis_note": "Hypertension"
}
]
},
"emr": {
"target_emr": "EPIC"
}
}'
```
Open a WebSocket to `wss://sdp.suki-stage.com/ws/stream` after session creation and context. Authenticate using `Sec-WebSocket-Protocol` (`SukiAmbientAuth,,`) for browsers or headers for non-browser clients.
Audio requirements:
* encoding: LINEAR16.
* sample\_rate: 16KHz.
* channel: Mono.
Strip WAV headers (44 bytes) before chunking. Send 100ms PCM chunks Base64-encoded in `AUDIO` messages. Required send order: `START_TIME`, one or more `AUDIO` messages, then an `AUDIO` end marker with Base64 of ASCII `EOF` (`RU9G`).
Supported `EVENT` values: `PAUSE`, `RESUME`, `CANCEL`, `ABORT` (deprecated), `KEEP_ALIVE`. Close the socket when finished, then call the end session endpoint and poll status/content.
Use the [Complete staging script](#complete-staging-script) for a full streaming client. When capture finishes, close the WebSocket, then call **end session**.
POST to [End Session API](/api-reference/ambient-sessions/end) to end the session.
```typescript TypeScript expandable theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
const BASE_URL = "https://sdp.suki-stage.com";
const sdpSukiToken = "";
const ambientSessionId = "";
const sdpProviderId = ""; // Required for single_auth partners
const headers: Record = {
sdp_suki_token: sdpSukiToken,
sdp_provider_id: sdpProviderId,
};
const endResponse = await fetch(
`${BASE_URL}/api/v1/ambient/session/${ambientSessionId}/end`,
{ method: "POST", headers },
);
if (endResponse.status !== 200) {
const err = await endResponse.text();
throw new Error(`End failed: ${endResponse.status} ${err}`);
}
console.log("Session ended");
```
```python Python expandable theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
import requests
BASE_URL = "https://sdp.suki-stage.com"
sdp_suki_token = ""
ambient_session_id = ""
sdp_provider_id = "" # Required for single_auth partners
headers = {
"sdp_suki_token": sdp_suki_token,
"sdp_provider_id": sdp_provider_id,
}
response = requests.post(
f"{BASE_URL}/api/v1/ambient/session/{ambient_session_id}/end",
headers=headers,
timeout=60,
)
if response.status_code != 200:
raise RuntimeError(f"End failed: {response.status_code} {response.text}")
print("Session ended")
```
```bash cURL expandable theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
curl --request POST \
--url https://sdp.suki-stage.com/api/v1/ambient/session//end \
--header 'sdp_suki_token: ' \
--header 'sdp_provider_id: '
```
Suki notifies your webhook with `session_summary_generated` when the note is ready. Alternatively, poll these endpoints:
* [Get Status API](/api-reference/ambient-content/status).
* [Get Content API](/api-reference/ambient-content/content).
* [Get Transcript API](/api-reference/ambient-content/transcript).
```typescript TypeScript expandable theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
const BASE_URL = "https://sdp.suki-stage.com";
const sdpSukiToken = "";
const ambientSessionId = "";
const sdpProviderId = ""; // Required for single_auth partners
const headers: Record = {
sdp_suki_token: sdpSukiToken,
sdp_provider_id: sdpProviderId,
};
const statusResponse = await fetch(
`${BASE_URL}/api/v1/ambient/session/${ambientSessionId}/status`,
{ headers },
);
const statusBody = await statusResponse.json();
if (statusResponse.status !== 200) {
throw new Error(
`Status failed: ${statusResponse.status} ${JSON.stringify(statusBody)}`,
);
}
console.log("status:", statusBody.status);
const contentResponse = await fetch(
`${BASE_URL}/api/v1/ambient/session/${ambientSessionId}/content?cumulative=false`,
{ headers },
);
const content = await contentResponse.json();
if (contentResponse.status !== 200) {
throw new Error(
`Content failed: ${contentResponse.status} ${JSON.stringify(content)}`,
);
}
console.log(JSON.stringify(content, null, 2));
```
```python Python expandable theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
import json
import requests
BASE_URL = "https://sdp.suki-stage.com"
sdp_suki_token = ""
ambient_session_id = ""
sdp_provider_id = "" # Required for single_auth partners
headers = {
"sdp_suki_token": sdp_suki_token,
"sdp_provider_id": sdp_provider_id,
}
status_response = requests.get(
f"{BASE_URL}/api/v1/ambient/session/{ambient_session_id}/status",
headers=headers,
timeout=60,
)
if status_response.status_code != 200:
raise RuntimeError(
f"Status failed: {status_response.status_code} {status_response.text}"
)
print("status:", status_response.json()["status"])
content_response = requests.get(
f"{BASE_URL}/api/v1/ambient/session/{ambient_session_id}/content",
headers=headers,
params={"cumulative": "false"},
timeout=60,
)
if content_response.status_code != 200:
raise RuntimeError(
f"Content failed: {content_response.status_code} {content_response.text}"
)
print(json.dumps(content_response.json(), indent=2))
```
```bash cURL expandable theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
curl --request GET \
--url https://sdp.suki-stage.com/api/v1/ambient/session//status \
--header 'sdp_suki_token: ' \
--header 'sdp_provider_id: '
curl --request GET \
--url 'https://sdp.suki-stage.com/api/v1/ambient/session//content?cumulative=false' \
--header 'sdp_suki_token: ' \
--header 'sdp_provider_id: '
```
### Verify your integration
Before you design the full production workflow, confirm that your staging integration can complete this path:
* Authenticate successfully and use the returned `sdp_suki_token` in follow-up requests.
* Create an ambient session and store the returned `ambient_session_id` (and `composition_id` when you need note-level APIs).
* Stream visit audio on **`/ws/stream`** with the required PCM format and message order.
* End the session and confirm that Suki starts processing.
* Retrieve note content (and optionally the transcript), or confirm your webhook receives `session_summary_generated`.
After this path works end to end on staging, continue with context quality, interoperability fields, and production rollout.
## Available cookbooks
Ambient
API
End Ambient After Streaming
Send RU9G, then end session.
5 min
Ambient
API
Authenticate Browser WebSocket Handshake
Auth browser WebSocket with protocols.
5 min
Ambient
API
Poll Session Status Before Fetching Content
Poll until status is completed.
5 min
## Available tutorials
Ambient
Build an Ambient Streaming Client
Authenticate, create a session, stream PCM audio over WebSocket, and retrieve clinical note results.
20 min
Intermediate
Webhooks
Build a Webhook Notification Receiver
Verify HMAC signatures, parse partner notifications, and handle success and failure events.
10 min
Beginner
## Next steps
After you complete your first Ambient API session:
[Authentication API](/api-reference/authentication/login) - Login, register, and JWKS configuration.
[Context API](/api-reference/ambient-sessions/context) - Seed specialty, sections, and patient info for better note accuracy.
[Audio streaming API](/api-reference/ambient-sessions/audio-stream) - WebSocket connection details and message formats.
[Webhook](/api-reference/asynchronous/webhook) - Configure webhooks to receive notifications when notes are ready.
# Security & Best Practices
Source: https://developer.suki.ai/api-reference/security-best-practices
Secure Suki integrations with HTTPS, token storage, JWT validation, webhook HMAC verification, HIPAA-aware data handling, and safe error logging
This section provides best practices for securing your Suki APIs and SDKs.
## Security best practices
All API requests must use **HTTPS** (TLS 1.2 or higher) to ensure data encryption in transit. Never send requests over unencrypted HTTP connections.
### Token management
Following are the best practices for token management:
Never expose `sdp_suki_token` in client-side code, logs, or version control. Store tokens securely on your backend server.
Implement token refresh logic to automatically obtain a new `sdp_suki_token` when the current one expires. Call the `/login` endpoint with a valid partner token to refresh.
When receiving `sdp_suki_token` from Suki, verify its signature using the public keys from the JWKS endpoint ([Authentication JWKS](/api-reference/authentication/jwks)) (`/api/auth/.well-known/jwks-pub.json`).
Your partner token must be a standards-compliant JWT signed with RS256 (RSA Signature with SHA-256) algorithm. Ensure your JWKS endpoint is publicly accessible and properly configured.
### Webhook security
Following are the best practices for webhook security:
Suki signs webhook POSTs with **HMAC-SHA-256**. A **secret key** on your **partner record** is provided by Suki. Each request sends **`generated-at`** (Unix ms) and **`X-API-Key`** (hex HMAC of `generated-at`, a colon, and the raw JSON body). Verify before you process the body. For the exact steps, refer to [Notification webhook for Partners](/documentation/webhook/signature-verification) for more details.
Your webhook callback URL must use HTTPS protocol. Never use HTTP endpoints for webhooks.
Always validate the webhook payload structure and verify the HMAC signature before processing notifications.
### Data protection
Following are the best practices for data protection:
All data transmitted to and from Suki is encrypted using TLS 1.2. Ensure your application maintains encryption standards for data at rest.
Ensure your integration complies with HIPAA requirements. Obtain patient consent before sending personal data to the platform.
Only send the minimum required data for each API call. Avoid including unnecessary patient or provider information.
### Error handling
Following are the best practices for error handling:
If you receive a `401 Unauthorized` or `403 Forbidden` response, verify your `sdp_suki_token` is valid and not expired. Re-authenticate if necessary.
For transient errors (5xx status codes), implement exponential backoff retry logic. Do not retry on 4xx client errors.
When logging API errors, never include tokens, passwords, or sensitive patient data in logs.
For more details on security and compliance, see the [Security FAQs](/documentation/references/faqs/security).
# Send Notifications API
Source: https://developer.suki.ai/api-reference/send-notifications
Receive asynchronous notifications from Suki using your notification endpoint
The Notifications API enable partners to receive asynchronous webhook notifications when ambient session processing completes or fails. You expose an HTTPS URL that accepts inbound requests. Suki calls that URL with a signed payload so your backend can pull results or update job state without relying only on polling.
Critically, your notification endpoint is called by Suki. Register and configure the webhook in your partner setup, then verify signatures before you trust the payload.
## Available endpoints
Review the webhook contract for ambient session notifications
## Related guides
Learn how Partner webhooks deliver asynchronous session updates
Verify webhook signatures before you process the payload
Configure your notification URL and partner webhook settings
## Common use cases
Receive webhook notifications when ambient processing finishes so your backend can retrieve content and update job state without polling.
React to failure and timeout notifications so clinicians and operations see accurate visit status.
Verify webhook signatures before trusting payloads that change session or job state in your system.
Use payload links to retrieve status or clinical content for the ambient session that just completed.
# Single Auth Token Authentication
Source: https://developer.suki.ai/api-reference/single-auth-token-authentication
Learn how Single Auth Token authentication works, when to send provider_id and sdp_provider_id, and how to call Login, Register, and Partner APIs
Some partners use a single shared `partner_token` for all clinicians instead of generating a unique token for each user through their identity provider. Suki refers to this authentication model as **Single Auth Token authentication**.
In this model, your backend authenticates every request using the same `partner_token`. Because the token identifies your organization rather than an individual clinician, you **must provide the clinician's identity separately**.
With Single Auth Token authentication, include the clinician identifier on every request:
* Pass `provider_id` in the request body when calling [Register](/api-reference/authentication/register) and [Login](/api-reference/authentication/login).
* Pass `sdp_provider_id` as an HTTP header on every subsequent REST API request and WebSocket connection.
**Important**
* Single Auth Token authentication **is different** from Bearer partner authentication.
* A partner can use either Bearer partner authentication or Single Auth Token authentication, but not both.
* Suki assigns your authentication model during onboarding. If you are unsure which authentication model your organization uses, contact your Suki partnership team.
## Prerequisites
Before you implement Single Auth Token authentication, confirm the following:
* **Single Auth Token configuration:** Your Suki partnership team confirms that your organization uses Single Auth Token authentication.
* **Stable clinician identifiers:** Choose a stable `provider_id` for each clinician and use the same value on every Register, Login, and later API request.
* **HTTPS required:** Send [Login](/api-reference/authentication/login) and [Register](/api-reference/authentication/register) as HTTPS POST requests with a JSON body. Do not send credentials in query parameters.
## How this authentication model differs
Single Auth Token partners authenticate the same way as other partners by sending `partner_token`, but clinician identity is not derived from that token. You provide the clinician identity separately through `provider_id` and `sdp_provider_id`.
| Topic | Standard authentication | Single Auth Token authentication |
| :----------------------------------- | :------------------------------------- | :----------------------------------------------------------------------------------------- |
| Clinician identity | Derived from `partner_token` | Passed as `provider_id` on Register and Login, and as `sdp_provider_id` on later API calls |
| `partner_token` | Per-user ID token or user-scoped token | One shared `partner_token` for multiple clinicians |
| `provider_id` on Register and Login | Optional and ignored | **Required** |
| `sdp_provider_id` on later API calls | Not required for standard partners | **Required** on every REST API request and WebSocket connection |
### Single Auth Token authentication behavior
* Register and Login identify the clinician using `provider_id` because the shared `partner_token` identifies your organization, not the clinician.
* After Login, send `sdp_provider_id` on every subsequent REST API request and WebSocket connection. The token authorizes your organization. `sdp_provider_id` tells Suki which clinician is acting.
* Use the same stable `provider_id` for a clinician on Register, Login, and later `sdp_provider_id` headers.
* A partner can use either Bearer partner authentication or Single Auth Token authentication, but not both.
## Typical flow
**Register** each clinician once with [Register](/api-reference/authentication/register). Use a stable **`provider_id`** for that person.
**Login** with [Login](/api-reference/authentication/login). Send your `partner_id`, shared `partner_token`, and the active clinician's **`provider_id`**.
**Call Suki APIs** with the **`sdp_suki_token`** returned from Login. Include the **`sdp_provider_id`** header on every REST and WebSocket request.
**Refresh** the token before it expires (**1 hour**). Call Login again with the same `partner_token` and `provider_id`.
## Send sdp\_provider\_id with all API requests
When you use Single Auth Token authentication, include the `sdp_provider_id` header on **every** Suki API request after Login.
* **Register and Login:** Send the clinician's `provider_id` in the request body so Suki can register or authenticate them.
* **All other APIs:** After Login, send `sdp_provider_id` on every REST and WebSocket request along with `sdp_suki_token`. Suki uses it to route the request to the correct clinician.
Register each provider with a stable `provider_id` before their first Login. If Login fails because the provider is not registered, call Register, then call Login again.
## Login and Register request fields
Single Auth Token partners use the same Login and Register endpoints as standard partners. You must include `provider_id` on every Login and Register call.
### Login
**Endpoint:** [Login](/api-reference/authentication/login)
**Method:** POST
| Field | Single Auth Token partners |
| :-------------- | :---------------------------------------------------------------------------------------- |
| `partner_id` | Required . Partner ID from Suki. |
| `partner_token` | Required . Shared `partner_token` your integration uses for authentication. |
| `provider_id` | Required . Stable identifier for the provider in your system. |
#### Example request
```bash cURL theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
curl -X POST https://sdp.suki-stage.com/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{
"partner_id": "your-partner-id",
"partner_token": "your-shared-partner-token",
"provider_id": "provider-123"
}'
```
On success, the API returns `suki_token`. Use it as the `sdp_suki_token` header on later API calls. Also send `sdp_provider_id` with the same clinician identifier on every request. The token is valid for **1 hour**. To refresh it, call Login again with the same `partner_token` and `provider_id`.
### Register
Register a provider once before their first Login. Refer to [Provider authentication](/api-reference/provider-authentication#registration-scenarios) for new user, existing user, and conflict responses.
**Endpoint:** [Register](/api-reference/authentication/register)
**Method:** POST
| Field | Single Auth Token partners |
| :------------------- | :--------------------------------------------------------------------------------------------------------------------------- |
| `partner_id` | Required . Partner ID from Suki. |
| `partner_token` | Required . Shared `partner_token` your integration uses for authentication. |
| `provider_id` | Required . Stable identifier for the provider in your system. Use this as the user identifier at registration. |
| `provider_name` | Required . Display name for the provider. |
| `provider_org_id` | Required . Organization the provider belongs to. |
| `provider_specialty` | Optional . Defaults to `FAMILY_MEDICINE` if omitted. |
#### Example request
```bash cURL theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
curl -X POST https://sdp.suki-stage.com/api/v1/auth/register \
-H "Content-Type: application/json" \
-d '{
"partner_id": "your-partner-id",
"partner_token": "your-shared-partner-token",
"provider_id": "provider-123",
"provider_name": "Dr. Jane Smith",
"provider_org_id": "org-123"
}'
```
For all Register fields and response codes, refer to the [Register API reference](/api-reference/authentication/register).
# User Feedback API
Source: https://developer.suki.ai/api-reference/user-feedback
Submit feedback on Ambient session entities such as transcripts or generated content
The User Feedback API enable partners to submit structured feedback tied to an ambient session and a specific entity, such as a transcript or generated content. Suki uses that feedback to track quality issues, regressions, or clinician-reported problems against concrete artifacts.
These APIs are called from your servers and authenticated with a Suki Token (`sdp_suki_token`). Critically, feedback must reference a valid ambient session entity. It is separate from Form filling feedback endpoints.
## Available endpoints
Submit feedback for an ambient session entity
## Common use cases
Capture clinician feedback against the specific ambient session artifact when generated content is wrong or incomplete.
Send thumbs-down or issue reports from your review screen so support can investigate the right transcript or note section.
# User Preferences API
Source: https://developer.suki.ai/api-reference/user-preferences
Read and update end-user preferences for the Suki platform
The User Preferences APIs enable partners to read and update preferences for the authenticated provider so your client stays aligned with Suki user-scoped settings. Typical preferences include language, layout, or other defaults exposed by the API for that user.
These APIs are called from your servers and authenticated with a Suki Token (`sdp_suki_token`). Critically, preferences are scoped to the authenticated provider. They do not replace organization-level Partner configuration.
## Available endpoints
Read or update preferences for the authenticated provider
## Related guides
Set up Partner Tokens before you call authenticated Partner APIs
See how authenticated ambient workflows fit into clinical documentation
## Common use cases
Save verbosity and section format preferences so future ambient notes match how each clinician documents.
Let clinicians change documentation preferences in your UI so later ambient sessions pick up those settings automatically.
# User Preferences
Source: https://developer.suki.ai/api-reference/user-preferences/preferences
PATCH /api/v1/user/preferences
Update user personalization preferences for clinical note generation
Use this endpoint to update and save a user's personalization preferences. These settings are saved at the user level, not per session , and will be applied to all of the user's future interactions. For details, see [Personalization](/api-reference/capabilities/personalization). Section format preferences use LOINC codes to identify note sections.
This is a `PATCH` request, you only need to send the fields you want to change.
For the best results, we recommend that you call this endpoint before the **main interaction begins** (for example, before audio streaming starts or before you call the `/end` endpoint).
This ensures the user's preferences are applied before content is generated. However, you can call this endpoint at any time to update the settings.
## Code examples
**Language tabs (agents):** Equivalent code samples are available in: Python, TypeScript. Humans see one language at a time. Use the variant that matches the user's stack; behavior is the same across tabs.
```python theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
import requests
url = "https://sdp.suki-stage.com/api/v1/user/preferences"
headers = {
"sdp_suki_token": "",
"sdp_provider_id": "",
"Content-Type": "application/json"
}
payload = {
"personalization_preference": {
"verbosity": "CONCISE", # Options: CONCISE, BALANCED, DETAILED
"section_format": [
{
"loinc": "10164-2",
"style": "NARRATIVE" # Options: NARRATIVE, BULLETED
}
]
}
}
response = requests.patch(url, json=payload, headers=headers)
if response.status_code == 200:
data = response.json()
print("Preferences updated successfully")
print(f"Updated preferences: {data}")
else:
print(f"Failed to update preferences: {response.status_code}")
print(response.json())
```
```typescript theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
const response = await fetch('https://sdp.suki-stage.com/api/v1/user/preferences', {
method: 'PATCH',
headers: {
'sdp_suki_token': '',
'sdp_provider_id': '',
'Content-Type': 'application/json'
},
body: JSON.stringify({
personalization_preference: {
verbosity: 'CONCISE', // Options: CONCISE, BALANCED, DETAILED
section_format: [
{
loinc: '10164-2',
style: 'NARRATIVE' // Options: NARRATIVE, BULLETED
}
]
}
})
});
if (response.ok) {
const data = await response.json();
console.log('Preferences updated successfully');
console.log('Updated preferences:', data);
} else {
const error = await response.json();
console.error(`Failed to update preferences: ${response.status}`, error);
}
```
# Dictation SDK Changelog
Source: https://developer.suki.ai/dictation-sdk/changelog
Dictation SDK product updates and announcements
Suki's Dictation SDK versioning policy is based on the semantic versioning standard. For example, in version 1.2.3, 1 is the major version, 2 is the minor version, and 3 is the patch version.
When we release a new Dictation SDK version for new features or bug fixes, we increment one of these three version components depending on the type of change introduced.
### New features
* **Initial beta release**: First release of the Suki Dictation SDK. Build custom speech-to-text integrations with complete control over your user interface. Currently in **beta** stage for you to try out and provide feedback.
# General
Source: https://developer.suki.ai/dictation-sdk/faqs/general
General questions about the Suki Dictation SDK
The Suki Dictation SDK is a JavaScript and React library for Dictation in the browser. It lets you add speech-to-text to your web application using Suki's hosted Dictation experience, authentication helpers, and callbacks.
Read the [Dictation SDK Overview](/dictation-sdk/introduction) for supported packages, In-field and Scratchpad modes, and how a typical session flows.
You can build Dictation in two ways:
* **In-field:** Dictation overlays a target field. This pattern fits notes, forms, and similar workflows.
* **Scratchpad:** Dictation uses a floating panel that is not tied to a single input.
Read [In-field mode](/dictation-sdk/guides/in-field-mode) and [Scratchpad mode](/dictation-sdk/guides/scratchpad-mode). The [Introduction](/dictation-sdk/introduction) page also summarizes both.
**Dictation SDK:** Your app embeds Dictation in the browser. The SDK handles platform authentication with **`SukiAuthManager`**, the iframe lifecycle, and callbacks such as **`onSubmit`**.
**Dictation APIs:** You call Suki's REST and WebSocket Dictation APIs from your own code without using this iframe SDK. That path is documented under [Audio Dictation](/documentation/concepts/dictation/dictation) and the [Dictation API](/api-reference/ambient-dictation) reference.
The [Introduction](/dictation-sdk/introduction) page includes a short note that compares the two approaches.
Every setup uses **`@suki-sdk/core`** for **`SukiAuthManager`**. You then add one Dictation package:
* **JavaScript (vanilla or non-React frameworks):** **`@suki-sdk/dictation`** plus **`@suki-sdk/core`**.
* **React:** **`@suki-sdk/dictation-react`** plus **`@suki-sdk/core`**. You still import **`DictationClient`** from **`@suki-sdk/dictation`** in examples when you construct the client in code.
Copy-paste install commands are in [Installation](/dictation-sdk/installation). Package names also appear on the [Introduction](/dictation-sdk/introduction) page.
No. The product is meant for **browser** embedding. Your page needs a real **`HTMLIFrameElement`**, **`postMessage`**, and layout so the hosted UI can mount and size correctly.
**Not supported:** Using Node.js as the runtime that hosts **`DictationClient`** or the Dictation iframe, or relying on **SSR alone** to render Dictation instead of initializing on the client after the DOM (and your container, when you use one) exists.
Read the runtime section in [Error handling](/dictation-sdk/guides/error-handling) and [Prerequisites](/dictation-sdk/prerequisites).
# Integration
Source: https://developer.suki.ai/dictation-sdk/faqs/integration
Integration questions about the Suki Dictation SDK
**JavaScript:** Use **`DictationClient`** from **`@suki-sdk/dictation`** and call **`await dictationClient.show({ ... })`** when the user should dictate. Walk through [Dictation SDK JavaScript integration](/dictation-sdk/javaScript-integration/javaScript).
**React:** Use **`DictationProvider`** and **`Dictation`** from **`@suki-sdk/dictation-react`**, with the same **`DictationClient`** instance passed into the provider. Walk through [Dictation SDK React integration](/dictation-sdk/react-integration/react).
Pick the guide that matches your stack. The [Quickstart](/dictation-sdk/quickstart) shows both patterns in one place if you want a shorter comparison.
The documentation treats **`SukiAuthManager`** and **`DictationClient`** as **long-lived** for a page or session. If you create a new **`DictationClient`** on every render or for every textarea, the iframe and session tend to tear down and start again, which feels unstable.
That guidance is spelled out in the [Quickstart](/dictation-sdk/quickstart) (recommended integration pattern) and in the best-practices sections of the [JavaScript](/dictation-sdk/javaScript-integration/javaScript) and [React](/dictation-sdk/react-integration/react) integration guides.
Call **`show()`** again with the next field's options. A new **`show()`** call **replaces** the active session. You do **not** need to call **`hide()`** manually just to move from one field to another for that pattern.
See [JavaScript integration](/dictation-sdk/javaScript-integration/javaScript) and [Configuration](/dictation-sdk/guides/configuration) for examples and option details.
Keep a **single** **`client`** and **`DictationProvider`**. Use state (for example **`activeFieldId`** or a boolean) so **only one** **`Dictation`** is mounted at a time, or change **`fieldId`** / **`initialText`** when you remount. You do **not** need to call **`hide()`** yourself for that kind of switch.
Full pattern: [React integration](/dictation-sdk/react-integration/react).
When **`Dictation`** unmounts, the SDK **automatically** calls **`hide()`**. For normal React flows you do **not** call **`hide()`** yourself.
Details: [React integration](/dictation-sdk/react-integration/react).
# Technical
Source: https://developer.suki.ai/dictation-sdk/faqs/technical
Technical questions about the Suki Dictation SDK
**Required**
* **`partnerId`**.
* **`partnerToken`**.
**Optional** (when your integration needs them)
* **`environment`** (for example **`"staging"`** or **`"production"`**).
* **`autoRegister`**.
* **`loginOnInitialize`**.
Field descriptions and **AuthConfig** details are in [Configuration](/dictation-sdk/guides/configuration) and [Authentication](/dictation-sdk/guides/authentication). Invalid **`partnerId`** or **`partnerToken`** block iframe initialization, as noted on those pages and in [Prerequisites](/dictation-sdk/prerequisites).
**`fieldId`** is a **stable, unique** string that names one Dictation session. Every callback returns it together with **`text`** so you know which field the result belongs to.
It does **not** have to match a DOM **`id`**, but using the same string as your input's **`id`** is a simple pattern.
Naming examples and guidance: [Configuration](/dictation-sdk/guides/configuration) (Field IDs in practice) and [Callbacks](/dictation-sdk/guides/callbacks).
**`onSubmit`** runs when the user **commits** the transcript. If you omit it, Dictation often **closes immediately** after the user acts, so production integrations should always implement it.
Read [Configuration](/dictation-sdk/guides/configuration) and [Callbacks](/dictation-sdk/guides/callbacks) for the full callback contract.
**`rootElement`** is the **DOM node** where the SDK mounts the Dictation iframe. The iframe **sizes to that container**, so the node should have real width and height.
**Layout:** Wrapper markup and CSS patterns are described under **Wrapper layout** on the [Configuration](/dictation-sdk/guides/configuration) page. [In-field mode](/dictation-sdk/guides/in-field-mode) and [Error handling](/dictation-sdk/guides/error-handling) cover related layout issues.
**React:** Some examples omit **`rootElement`**; the [React integration](/dictation-sdk/react-integration/react) guide explains when the minimal pattern skips it and when to pass it (including refs in real apps). [Configuration examples](/dictation-sdk/guides/examples/configuration-examples) includes sample wiring.
Work through checks in this order:
1. **Runtime:** Browser-only use, correct timing (call after the DOM and container exist and have size).
2. **Layout:** **`rootElement`** (when used), height, and CSS.
3. **Auth and CSP:** Valid partner credentials and any CSP rules that affect iframes.
4. **Configuration and callbacks:** **AuthConfig**, **ShowOptions**, and a working **`onSubmit`**.
That order is documented on [Error handling](/dictation-sdk/guides/error-handling). Use [Configuration](/dictation-sdk/guides/configuration) for every option field.
# Dictation SDK Architecture
Source: https://developer.suki.ai/dictation-sdk/guides/architecture
Learn how your application, the Dictation SDK, sign-in, and Suki-hosted Dictation fit together
Quick summary
Learn about the architecture of the Suki Dictation SDK and how your application, the Dictation SDK, sign-in, and Suki-hosted Dictation fit together.
Last updated:
August 2026
This guide explains the responsibilities for embedding Dictation in your application. You maintain control of your user interface and determine when you turn Dictation on or off.
The Suki Dictation SDK hosts the microphone experience and transcription to ensure a consistent look and behavior across every integration.
## Architecture of the Suki Dictation SDK
The Suki Dictation SDK has four main components:
* Your application.
* The Suki Auth Manager.
* The Dictation SDK.
* The Suki hosted Dictation iframe.
The following diagram illustrates the architecture of the Suki Dictation SDK and how each component fits together:
Your app never has to build the Dictation screen itself. The SDK opens that experience in the place you choose and passes results back to your code.
### What each layer does
| Layer | What it does |
| :------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Your application** | You decide when Dictation should appear, where it mounts on the page, and what to do with text when the user is done (and optional live updates if you use them). |
| **Dictation SDK** | The client you install talks to Suki, shows or hides the hosted Dictation experience, and wires your callbacks so you receive transcripts. |
| **Sign-in** | You configure this once with your partner details. It checks your setup, signs in with Suki, refreshes access when needed, and gives the hosted experience what it needs to run securely. |
| **Hosted Dictation** | The Dictation UI users speak into lives on Suki's side. It stays separate from your page's styles and layout so the experience stays consistent.
The iframe is isolated from host CSS and DOM to ensure consistent rendering across integrations. |
If you need step-by-step setup, start with [Authentication](./authentication), then the [Quickstart](../quickstart).
## Lifecycle of a Dictation session
Create and configure [SukiAuthManager](/dictation-sdk/guides/authentication#use-sukiauthmanager-for-authentication) with your partner credentials. Sign-in may run when you initialize, depending on your settings.
Create [DictationClient](/dictation-sdk/javaScript-integration/javaScript), or use the React provider and component, tied to that auth.
When the user should dictate, **open** Dictation (in-field or scratchpad). A session is active while they work.
When they finish or you no longer need Dictation, **close** it. In React, unmounting the Dictation UI usually closes the session for you.
For full implementation details for these APIs, refer to [JavaScript integration](../javaScript-integration/javaScript) for JavaScript, and [React integration](../react-integration/react) for React.
## Next steps
Refer to the [Callbacks guide](/dictation-sdk/guides/callbacks) for more details on how to use the callbacks.
# Dictation SDK Authentication
Source: https://developer.suki.ai/dictation-sdk/guides/authentication
Sign in to Suki before Dictation opens: use SukiAuthManager with the Partner ID and Partner Token Suki gives you
The Dictation SDK uses a helper called **`SukiAuthManager`** from the **`@suki-sdk/core`** package. You give it the **`partnerId`** and **`partnerToken`** Suki assigned to your app. You create this helper **once**, then hand it to **`DictationClient`** in JavaScript or to **`DictationProvider`** in React.
After sign-in works, Suki can show the Dictation experience (the screen users talk to) inside your page. The helper also renews access when it expires. If the ID or token is wrong, or sign-in never finishes, that Dictation screen will not appear.
## Prerequisites
Before you set up **`SukiAuthManager`**, you must have:
* **Partner ID** (the **`partnerId`** value): Suki gives you this when you complete [Partner onboarding](/documentation/get-started/partner-onboarding).
* **Partner Token** (the **`partnerToken`** value): The access value your program uses for this SDK. Your Suki contact explains how you get it and what it looks like.
## Use SukiAuthManager for authentication
Create the helper when your app already knows **`partnerId`** and **`partnerToken`** by running the following code:
```javascript JavaScript expandable theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
import { SukiAuthManager } from "@suki-sdk/core";
const authManager = new SukiAuthManager({
partnerId: "YOUR_PARTNER_ID", // required
partnerToken: "YOUR_PARTNER_TOKEN", // required
environment: "staging", // optional: "staging" or "production" (default "production")
loginOnInitialize: true, // optional (default false)
autoRegister: false, // optional (default false); when true, provider fields below are often required
providerId: "YOUR_PROVIDER_ID", // optional; provider identifier in your system
providerName: "YOUR_PROVIDER_NAME", // optional; full name, often required if autoRegister is true
providerOrgId: "YOUR_PROVIDER_ORG_ID", // optional; org in your system, often required if autoRegister is true
providerSpecialty: "FAMILY_MEDICINE", // optional; often required if autoRegister is true
});
```
For values you can pass as **`providerSpecialty`**, see [Specialties](/documentation/concepts/ambient-clinical-notes/specialties).
## What SukiAuthManager does
* **Checks** your settings before Dictation tries to open.
* **Signs in** to Suki's platform with your partner details.
* **Renews access** when the SDK needs a fresh session.
* **Lets** Suki's hosted Dictation UI run with the right permissions.
A bad **`partnerId`** or **`partnerToken`** stops Dictation from loading. Users will not see the Dictation UI until sign-in succeeds.
Refer to [AuthConfig](/dictation-sdk/guides/configuration#authconfig) section in [Configuration](/dictation-sdk/guides/configuration) guide for more details on the available options and how to use them.
If **`autoRegister`** is on, you may need extra **provider metadata**. Check your package README or ask your Suki contact.
## Sign in later instead of on create
You can create **`SukiAuthManager`** first and call **`login()`** when your app is ready, instead of **`loginOnInitialize: true`**. Use whatever pattern your installed SDK version supports.
## Next steps
Refer to [Quickstart](/dictation-sdk/quickstart) guide to get started with the Dictation SDK.
# Callbacks
Source: https://developer.suki.ai/dictation-sdk/guides/callbacks
Wire `onSubmit`, `onCancel`, and `onDraft` callbacks in JavaScript or React, including payload shape, `fieldId` routing, and when each handler fires
Quick summary
The Suki Dictation SDK supports three callbacks: `onSubmit`, `onCancel`, and `onDraft`. These callbacks are called when the user commits, cancels, or drafts the Dictation respectively. You can use these callbacks to handle the Dictation result, cancel the session, or receive intermediate updates while the user is dictating.
Last updated:
August 2026
**Callbacks** allow your application to run your own code when the user commits, cancels, or moves on without committing during Dictation. You supply functions; the Suki Dictation SDK calls them when those events occur. You do **not** poll on a timer or in a loop for new text.
**Registering callbacks**
When you start a session, register them in **JavaScript** as properties on the object you pass to `DictationClient.show()`, or in **React** as props on ``.
**Callback execution**
It calls **`onSubmit`** after the user commits, and **`onCancel`** if they leave without committing. If you supply **`onDraft`**, the SDK may call it when the user does **not** submit and switches to another field (or otherwise moves on without committing). **`onDraft`** is **not** invoked on every live transcript change while Dictation stays on one field. The SDK does **not** persist draft text to your systems; only your **`onDraft`** handler decides whether to store or send it to your partner backend.
**Payload**
Every callback receives the same argument: one object, `{ fieldId, text }`. The `fieldId` value is whatever you passed into `show()` or ``. A common pattern is to set it to the **same value as the target control's `id`**, so `onSubmit` can use `document.getElementById(fieldId)` and write `text` into that element.
## Supported callbacks
Dictation SDK supports the following callbacks:
Runs when the user commits the Dictation result.
**Optional**: Runs when the user discards the session without committing.
**Optional**. May run when the user leaves Dictation **without** submitting and switches context (for example, to another field). Use it if you want to capture unstaged transcript text; the SDK does not save or forward it unless you do.
**Mode** controls how Dictation is shown. Set it to **`in-field`** when the UI is tied to a container on your page, or **`scratchpad`** for a standalone Dictation UI. Refer to [In-field mode](/dictation-sdk/guides/in-field-mode) and [Scratchpad mode](/dictation-sdk/guides/scratchpad-mode) guides for behavior and layout.
For every other option you pass to `DictationClient.show()` or to `` as props, including **`rootElement`**, **`initialText`**, and these callbacks, use the [Configuration](/dictation-sdk/guides/configuration) reference.
## Callback contract payload shape
All callbacks receive a **single** argument: an object with these fields:
This is the string you invent to name this Dictation session. The SDK includes it in every callback so you know which field the `text` is for. Using the same text as your input’s `id` (for example `clinical-notes`) is a simple way to tie them together.
The transcript string for this callback (committed result, state at cancel, or interim draft, depending on which handler ran).
**Example:**
```javascript JavaScript theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
// After the user commits, your onSubmit might receive:
{
fieldId: "clinical-notes",
text: "The user said hello.",
}
```
For structured notes, use **stable**, **unique** ids per section, for example **`soap.subjective`**, **`soap.objective`**, **`soap.assessment`**, **`soap.plan`**, or **`scratchpad`** for a floating session. See [Field IDs](/dictation-sdk/guides/configuration#field-ids-in-practice).
If **`onSubmit`** is missing, Dictation often **closes immediately** after the user acts. Always implement `onSubmit`.
## Code examples
Use callbacks as properties on the object you pass to **`await dictationClient.show({ ... })`**. They sit alongside `mode`, `fieldId`, `rootElement`, `initialText`, and other options from [Configuration](/dictation-sdk/guides/configuration).
The iframe **resizes to the container** you pass as `rootElement` when you use `in-field` mode.
```javascript JavaScript theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
import { SukiAuthManager } from "@suki-sdk/core";
import { DictationClient } from "@suki-sdk/dictation";
const authManager = new SukiAuthManager({
partnerId: "YOUR_PARTNER_ID", // Required
partnerToken: "YOUR_PARTNER_TOKEN", // Required
environment: "staging", // Optional
loginOnInitialize: true,
autoRegister: false, // Optional - default is false
providerId: "YOUR_PROVIDER_ID", // Optional - required if autoRegister is true
providerName: "YOUR_PROVIDER_NAME", // Optional - required if autoRegister is true
providerOrgId: "YOUR_PROVIDER_ORG_ID", // Optional - required if autoRegister is true
providerSpecialty: "YOUR_PROVIDER_SPECIALTY", // Optional - required if autoRegister is true
});
const dictationClient = new DictationClient({ authManager });
// Example markup for in-field:
//
//
await dictationClient.show({
mode: "in-field",
fieldId: "clinical-notes", // same string as id="clinical-notes" on your textarea
rootElement: document.getElementById("clinical-notes-dictation-root"),
initialText: document.getElementById("clinical-notes")?.value ?? "",
onSubmit: ({ fieldId, text }) => {
const el = document.getElementById(fieldId);
if (el) el.value = text;
},
onCancel: ({ fieldId, text }) => {
// Optional: user cancelled without committing
},
onDraft: ({ fieldId, text }) => {
// Optional: e.g. user switched fields without submit; persist draft in your app if needed
},
});
```
Calling **`show()`** again **replaces** the active session; you do not need to call **`hide()`** between fields for that pattern. Wrap `show()` in `try` / `catch` if you want to log configuration or auth failures.
Refer to the [Error handling](/dictation-sdk/guides/error-handling) guide for more details.
For more on imperative `show()` and one client per page scope, refer to the [JavaScript integration](/dictation-sdk/javaScript-integration/javaScript) guide for more details.
Pass the same three handlers as **props** on **``**. Build **`DictationClient`** once (with `SukiAuthManager`), wrap your tree with **``**, then render `Dictation` where Dictation should be active.
When `Dictation` **unmounts**, the SDK calls **`hide()`** for you.
```jsx React theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
import { useMemo, useState } from "react";
import { SukiAuthManager } from "@suki-sdk/core";
import { DictationClient } from "@suki-sdk/dictation";
import { DictationProvider, Dictation } from "@suki-sdk/dictation-react";
function NotesApp() {
const client = useMemo(() => {
const authManager = new SukiAuthManager({
partnerId: "YOUR_PARTNER_ID", // Required
partnerToken: "YOUR_PARTNER_TOKEN", // Required
environment: "staging", // Optional
loginOnInitialize: true,
autoRegister: false, // Optional - default is false
providerId: "YOUR_PROVIDER_ID", // Optional - required if autoRegister is true
providerName: "YOUR_PROVIDER_NAME", // Optional - required if autoRegister is true
providerOrgId: "YOUR_PROVIDER_ORG_ID", // Optional - required if autoRegister is true
providerSpecialty: "YOUR_PROVIDER_SPECIALTY", // Optional - required if autoRegister is true
});
return new DictationClient({ authManager });
}, []);
const [notes, setNotes] = useState("");
// Drive from your UI: e.g. setActiveField("clinical-notes") when this field owns dictation.
const [activeField, setActiveField] = useState(null);
const handleSubmit = ({ fieldId, text }) => {
// fieldId matches id="clinical-notes" below if you use that pattern
setNotes(text);
};
const handleCancel = ({ fieldId, text }) => {
// Optional
};
const handleDraft = ({ fieldId, text }) => {
// Optional: e.g. save unstaged text when user leaves without submit
};
return (
);
}
```
If you already create `client` higher in the tree, pass it into `DictationProvider` the same way. Use state such as **`activeField`** so only **one** field owns the session.
Refer to [React integration](/dictation-sdk/react-integration/react) guide for more details.
## Available cookbooks
## Next steps
Refer to [Dictation modes](/dictation-sdk/guides/in-field-mode) and [Scratchpad mode](/dictation-sdk/guides/scratchpad-mode) guides to learn more about the different modes and how to use them.
# Configure AuthConfig & ShowOptions
Source: https://developer.suki.ai/dictation-sdk/guides/configuration
Set `AuthConfig` for `SukiAuthManager` and `ShowOptions` for each Dictation session: mode, field IDs, `rootElement`, seed text, and callbacks
Quick summary
Configuration has two parts. First, `AuthConfig`: the object you pass to `new SukiAuthManager({ ... })` from `@suki-sdk/core`. That step covers sign-in, tokens, and optional provider registration before Dictation can run.
Second, `ShowOptions`: what you pass to `DictationClient.show()` or as props on `Dictation` in React. That step sets mode and field, anchors the iframe in your layout, seeds text, and registers callbacks for each Dictation session.
Last updated:
August 2026
Dictation configuration has two steps: authenticate with **Suki for Partners**, then define how each Dictation session is shown in your app.
Build an **`AuthConfig`** object and pass it to **`new SukiAuthManager({ ... })`** from **`@suki-sdk/core`**. The manager handles your connection to the platform. Configure:
* **`partnerId` and `partnerToken`:** Credentials Suki issues for your integration.
* **Provider:** Optional fields that identify the clinician (for example **`providerId`**, **`providerName`**).
* **Environment:** Target deployment (**`"staging"`** or **`"production"`**).
* **Login behavior:** Whether the manager signs in when it is created (**`loginOnInitialize`**) or you call **`login()`** later.
**`SukiAuthManager`** signs in and refreshes tokens from these settings. Authentication must succeed before the hosted Dictation iframe can open.
Pass a **`ShowOptions`** object whenever you start a session. In JavaScript, pass it to **`DictationClient.show()`**. In React, pass the same fields as props on **`Dictation`**.
Typical **`ShowOptions`** fields include:
* **Mode:** How the Dictation UI is presented (for example in-field vs scratchpad).
* **Field:** Stable field identifiers the SDK uses for callbacks.
* **Mounting:** Where the iframe attaches in your DOM (**`rootElement`**).
* **Seed text:** Optional starting text for the session.
* **Callbacks:** Handlers for submit, cancel, and draft events.
Modes control where the iframe lives. Read [In-field mode](/dictation-sdk/guides/in-field-mode) and [Scratchpad mode](/dictation-sdk/guides/scratchpad-mode) for UX and layout. For when each callback runs, see [Callbacks](/dictation-sdk/guides/callbacks).
## AuthConfig
**`AuthConfig`** is the object you pass to **`new SukiAuthManager({ ... })`**. It is used to configure the Suki Auth Manager.
**`SukiAuthManager`** runs first in the lifecycle: it validates this configuration, signs in to the **Suki platform** with your partner credentials, and refreshes tokens so the hosted Dictation iframe can open. Until auth succeeds, Dictation does not start.
### AuthConfig properties
Partner identifier from Suki. Without a valid value, the iframe cannot initialize.
Partner auth token from Suki. Invalid credentials block the hosted Dictation UI.
**Optional**. Use **`"staging"`** or **`"production"`** so the SDK targets the right environment.
**Optional**. When **`true`**, enables provider auto-registration. Provider metadata may be required; confirm with your package README or Suki contact.
**Optional**. When **`true`**, the SDK logs in during **`SukiAuthManager`** construction. You can instead construct the manager and call **`login()`** later when your app is ready.
**Optional**. Identifier for the provider in your system.
**Optional**. Full provider display name (for example, first, middle, and last name separated by spaces). It is **required** when **`autoRegister`** is **`true`**.
**Optional**. Organization identifier for the provider in your system. It is **required** when **`autoRegister`** is **`true`**.
**Optional**. Clinical specialty (for example, **`FAMILY_MEDICINE`**). Refer to [Specialties](/documentation/concepts/ambient-clinical-notes/specialties) for values your integration may use.
If **`autoRegister`** is enabled, **provider metadata** may be required in addition to the fields above.
## ShowOptions
**`ShowOptions`** is what you pass to **`await dictationClient.show({ ... })`**. It is used to configure the Suki Dictation Client using the JavaScript package.
In React, **`Dictation`** accepts the same fields as props wherever the shipped types expose them (**`mode`**, **`fieldId`**, **`rootElement`**, **`initialText`**, **`onSubmit`**, **`onCancel`**, **`onDraft`**, and so on). Think of it as one contract: **imperative** object vs **declarative** props.
### ShowOptions properties
**`"in-field"`** overlays Dictation on a container you own (next to or over a field). **`"scratchpad"`** uses a standalone floating Dictation UI. See [In-field mode](/dictation-sdk/guides/in-field-mode) and [Scratchpad mode](/dictation-sdk/guides/scratchpad-mode).
A **stable**, **unique** string that names this Dictation instance. The SDK echoes it on every callback as `{ fieldId, text }`. See [Field IDs in practice](#field-ids-in-practice) below for naming patterns.
**Recommended**. DOM node that hosts the Dictation iframe. The iframe sizes to this container. A collapsed or zero-height parent produces a blank area. For layout patterns, refer to [Wrapper layout](#wrapper-layout).
**Optional**. Seed text shown when the session opens (for example the current value of your textarea).
Runs when the user **commits** the transcript. You receive `{ fieldId, text }`. Always implement this callback; without it, Dictation often closes immediately after the user acts. Details: [Callbacks](/dictation-sdk/guides/callbacks).
**Optional**. Runs when the user leaves without committing. Same payload shape as other callbacks.
**Optional**. May run when the user does not submit and switches context (for example, another field). Not fired on each live transcript edit. Same payload shape. See [Callbacks](/dictation-sdk/guides/callbacks).
If **`onSubmit`** is missing, Dictation often **closes immediately** after the user acts. Treat it as required in real integrations.
## Field IDs in practice
**`fieldId`** is a **stable string you choose** to identify which note field or Dictation target this session is for. The SDK includes it on every callback together with **`text`**, so you can match each transcript to the correct field in your app.
Use any stable string. A common shortcut is to use the **same value** as the target control's HTML **`id`**: then in **`onSubmit`** you can run **`document.getElementById(fieldId)`** and assign **`text`** to that element. If you prefer another naming scheme, **`fieldId`** does not have to match a DOM **`id`**; just route **`text`** in your handler using the **`fieldId`** you chose.
For structured notes, common examples are **`soap.subjective`**, **`soap.objective`**, **`soap.assessment`**, and **`soap.plan`**. For a scratchpad or floating session, a dedicated id such as **`scratchpad`** (or a namespaced value if you run more than one) keeps callbacks unambiguous.
* Each **active** Dictation instance should have its own stable id. If you call **`show()`** again, you **replace** the active session; you do not need **`hide()`** between fields for that pattern.
* The object every callback receives is documented in [Callback contract payload shape](/dictation-sdk/guides/callbacks#callback-contract-payload-shape) in the Callbacks guide.
## Callback argument shape
For the callback argument payload shape, refer to [Callback argument shape](/dictation-sdk/guides/callbacks#callback-contract-payload-shape).
## Wrapper layout
A **wrapper layout** is the layout where **`rootElement`** is a **dedicated container** (usually a **`div`**) with **stable width and height**, not the raw **`