Integration Guide
MD.ai reporting can be effortlessly integrated into any PACS, RIS, or web application using iframes. By leveraging our secure token-based authentication, you can enable direct access to reports within an iframe embedded in your application. This eliminates the need for users to log in separately on MD.ai, streamlining workflows and enhancing user experience.
You can also choose to launch our reporting app in a new tab or a new window. We also support syncing new reports in the same reporting window, whether it is an iframe or a separate window, so that launching a new exam or item in the worlist will also open the new report in the same existing reporting window.
Each site (SiteID) provisioned by MD.ai is assigned site administrators who have full control over the reports being generated within the system. As a site admin, you have the ability to assign reports to specific users by passing the necessary user details in your POST request. This allows the users, who are already logged in to your system, to directly access the reporting interface and begin dictating reports immediately without the need to go through another login process on MD.ai.
We will work with your team to designate site administrators and they will then be able to configure and manage launching these reports within your iframe.
Embed MD.ai Reporting as Iframe or WebView2
To embed MD.ai reporting within your application using an iframe or WebView2, follow these steps:
1. Generate report link
First, you need to generate a report link by sending a POST request using the MD.ai Reporting API. In addition to all the relevant clinicalInfo mentioned, your request MUST include the userInfo parameter in the JSON body:
userInfo:
UserEmail: Email of the user you want to assign the report to
UserName: Name of the user you want to assign the report to
Auth: The MD.ai access token from a site admin of your site on MD.ai. This is important and only the token from one of the site admins will work.Site admin access token requirement
Please note the inclusion of the Auth, UserName, and UserEmail parameters within the
userInfo. The Auth parameter must contain the access token from a site admin. The report will
not launch in your iframe if these parameters are missing, since logging in to the MD.ai interface
from an iframe is prohibited. Therefore, only site admins can create authenticated report links
necessary for iframe integration. But in case the x-access-token in the headers already contains
the token from a site admin, you can skip adding the Auth field.
Example POST request
POST /api/report/launch/clinical HTTP/1.1
Host: chat.md.ai
Content-Type: application/json
x-access-token: YOUR_ACCESS_TOKEN
{
"clinicalInfo": {
"SiteID": "test",
"PatientMRN": "test-mrn",
"Accession": "test-accession",
"PatientName": "John Doe",
"PatientBirthDate": "2024-01-01",
"PatientAge": "50",
"PatientSex": "M",
"Race": "White",
"Ethnicity": "Hispanic or Latino",
"Modality": "CT",
"BodyPartExamined": "Abdomen",
"StudyDescription": "CT abdomen w/wo contrast",
"StudyDate": "2024-01-01",
"StudyTime": "122009",
"StudyInstanceUID": "1.2.1.2",
"ReasonForExam": "History of colon cancer and new onset abdominal pain.",
"KeyFindings": "This is a test report",
"ReferringPhysician": "Dr. John Doe, M.D.",
"ReportingPhysician": "Dr. Jane Doe, M.D.",
"Notes": "No allergies or medications",
"ProcedureCode": "CTABDWWO",
"Metadata": {
"MSH": "MSH|^~\\&|HIS|GeneralHospital|ADT|GH|20251029113000||ADT^A01|123456|P|2.5"
}
},
"reportingApp": {
"AppLanguage": "en",
"SpeechLanguage": "en",
"ReturnFormat": "HL7",
"SyncLaunch": true
},
"userInfo": {
"Auth": "SITE_ADMIN_MDAI_ACCESS_TOKEN",
"UserName": "testphysician",
"UserEmail": "testphysician@md.ai"
},
"dicomSR": [
{
"fileData": "MIIEpQ..." // DICOM-SR file data (base64 format)
},
{
"fileData": "RQNASw..." // DICOM-SR file data (base64 format)
}
],
"response": {
"type": "link"
}
}Example response
The response will include the generated report link.
{
"status": "Draft",
"owner": {
"name": "testphysician",
"email": "testphysician@md.ai"
},
"reportLink": "https://chat.md.ai/reporting/launch-report/clinical?token=8ojevgk4rr"
}The owner of this report will now be testphysician and you have successfully created an authenticated and encrypted report link for them to start working on the report. You can now embed the reportLink to launch in a new window/iframe.
2. Embed iframe or WebView2
Once you have the generated report link, you can embed it within an iframe (or WebView2) in your application. For an iframe, simply set the src attribute to the report link.
<iframe
src="https://chat.md.ai/reporting/launch-report/clinical?token=8ojevgk4rr"
allow="microphone;screen-wake-lock;clipboard-read;clipboard-write;hid"
title="MD.ai Reporting"
></iframe>You can customize the width, height, and other attributes of the iframe to suit your application requirements.
For cross-origin iframes, the allow attribute in the iframe specifies essential permissions required for full functionality:
microphone: For voice recording/transcription.screen-wake-lock: Prevents the screen from going to sleep during active sessions.clipboard-readandclipboard-write: Allows copying and pasting text in the iframe.hid: Allows connecting to any preferred dictation device. Once allowed connect to the device by going to User settings -> Dictation Device -> Connect.
Many of our customers have added a button within their worklists that launches both their image viewer and the MD.ai reporting interface side by side, streamlining the workflow and improving user efficiency.
Here is an example of the reporting system integrated within MD.ai's DICOM viewer and annotation platform as an iframe:
Receive report events
The iframe (or WebView2 component) will emit events to the parent window via postMessage calls. The parent application can listen to these events and act accordingly. For example, a common use-case is to listen for the report_signed event to advance to the next exam. The initial report_ready event signals when the report is loaded and ready for editing or actions from the parent application.
Currently, the application emits the following events:
report_ready- The report is loaded and ready. Carries the report'sstatusat load time.report_signedreport_unsignedreport_status_changedreport_content_updated- Report content is changed and saved.report_saved- Emitted in response to thesave_reportaction.report_finalizedreport_discardedaddendum_createdaddendum_cancelledaddendum_signedaddendum_unsignedaddendum_content_updated- Addendum content is changed and saved. Currently,report_content_updatedis also simultaneously emitted.addendum_finalizedrecorder_onrecorder_offmicrophone_devicesbilling_code_generatedreport_transfer_cancelled- The user dismissed the report ownership transfer prompt (see below).report_transfer_proceeded- The user accepted the report ownership transfer prompt (see below).report_transferred- The report the user is currently editing was taken over by another user.
Report ownership transfer events
When a report link is launched for a draft that is currently owned by a different user, the
reporting app shows a Transfer Ownership of Report? prompt instead of opening the report
directly. report_transfer_cancelled and report_transfer_proceeded are emitted when the
launching user clicks Cancel or Proceed on this prompt, respectively. Separately, the user
who was previously editing that draft (in another session) receives report_transferred,
notifying them that the report has been taken over — its newOwnerName field contains the name of
the new owner.
Event data
type
Event type.
reportId
ID of the report.
clinicalInfo
Object containing the report's clinical info metadata, including: SiteID, PatientMRN,
Accession.
signedAt
Date the report was signed. Only provided by the report_signed and addendum_signed events.
status
Report status, one of: ORDERED, DRAFT, PRELIMINARY, FINAL, ADDENDUM. Provided by the
report_ready event as a snapshot of the status at load time, and by the report_status_changed
event on each subsequent change. May be null on report_ready if the report has no status set
yet; report_status_changed always carries a value.
content
Report or addendum content. Report content is provided by the report_content_updated and report_signed events. Addendum content is provided by the addendum_content_updated and addendum_signed events.
html_content
Report or addendum content in html format. Report content is provided by the report_signed events. Addendum content is provided by the addendum_signed event.
devices
List of available microphone devices. Only provided by the microphone_devices event. Each item in the list has the following structure, and at most one device will have isActive set to true, indicating the microphone currently in use:
{
"deviceId": "string",
"label": "string",
"isActive": "boolean"
}billingCode
Billing code data. Only provided by the billing_code_generated event. Each billingCode object has the following structure:
{
"cpt": {
"Code": "string",
"Description": "string"
},
"icd10": [
{
"Code": "string",
"Description": "string"
}
]
}- cpt — object with CPT code and description, or null if no CPT code is available.
- icd10 — array of objects, each with ICD-10 code and description. Empty if no ICD-10 codes.
newOwnerName
Name of the user the report was transferred to. Only provided by the report_transferred event.
Example
window.addEventListener('message', (event) => {
// Validate message origin
if (event.origin !== 'https://subdomain.md.ai') return;
// Validate message source
if (event.source !== iframeElement.contentWindow) return;
// Handle different event types
if (!event.data) return;
switch (event.data.type) {
case 'report_ready':
handleReportReady(event.data);
break;
case 'report_signed':
handleReportSigned(event.data);
break;
case 'report_unsigned':
handleReportUnsigned(event.data);
break;
case 'report_status_changed':
handleReportStatusChanged(event.data);
break;
}
});For WebView2, the above code example should be adapted using CoreWebView2.WebMessageReceived. Note that the message received will be a serialized JSON string rather than an object.
Send report actions
The parent application can also send actions to the iframe. Currently, the application supports the following actions:
-
sign_report -
unsign_report -
prelim_reportaction parameters
prelimGroup[required] - Prelim group name.prelimTag[required] - Prelim tag name.
-
save_reportManually triggers a report save. The
report_savedevent will be emitted in response to this action. -
create_addendum -
cancel_addendum -
sign_addendum -
unsign_addendum -
toggle_recorder -
select_next_bracket -
select_prev_bracket -
apply_report_ai -
get_microphone_devicesRequest the list of available microphone devices. The response will be sent as a
microphone_devicesevent, containing all available microphones and their details. -
change_microphoneaction parameters
deviceId[required] - ThedeviceIdof the microphone to activate. This value should match one of thedeviceIdfields returned in themicrophone_devicesevent.
-
user_activityReports that the user is active. For HIPAA compliance, MD.ai Reporting signs a user out after a period of inactivity, but it can only observe interaction inside the iframe — a user working in your application reads as idle. Send this action while your own user is active (for example, on mouse and keyboard input, throttled to once every few seconds) and that interaction will count against the timeout.
Example
// Get iframe element
const iframeElement = document.getElementById('mdai-reporting');
if (!iframeElement) return;
// Optional
const parameters = {};
// Send message to iframe. For security, always specify the target origin.
iframeElement.contentWindow.postMessage(
{ action: 'sign_report', ...parameters },
'https://subdomain.md.ai',
);For WebView2, the above code example should be adapted using CoreWebView2.PostWebMessageAsString or CoreWebView2.PostWebMessageAsJson (both are supported). The domain does not need to be specified.
Pre-warm the Reporting Session
If you mount the MD.ai iframe once and keep it alive across studies, you can establish the reporting session while the radiologist is still choosing a study, so that opening one does not wait for the application to load and authenticate. This is optional; report links work without it.
Point the iframe at the session link on mount, then update its src to the real report link once a study is opened.
There are two ways to get that session link, and both establish the same session:
- From your backend (recommended) — your server requests the link with the site admin token in the
x-access-tokenheader, exactly as it does for a report link, and receives a URL carrying a short-lived token. The site admin token never reaches the browser. - Directly in the browser — your front end builds the URL itself, passing the site admin token as the
Authquery parameter.
Option 1: Request the session link from your backend (recommended)
Method: POST
API endpoint: https://chat.md.ai/api/report/init
Request Header:
x-access-token: SITE_ADMIN_TOKENRequest Body:
- clinicalInfo: Site and locale of the session. There is no study yet, so no patient or study fields are accepted here.
- SiteID: The site identifier. (required)
- SiteLocation: The location identifier. (optional)
- AppLanguage and SpeechLanguage: Locale for the app and for dictation. (optional)
- userInfo: The user the session is for, in the same shape you send when generating a report link.
- UserName: Name of the user. (required)
- UserEmail: Email of the user. (required)
- Auth: Site admin access token. (optional — only needed if the
x-access-tokenheader is not a site admin token.)
Example POST request
POST /api/report/init HTTP/1.1
Host: chat.md.ai
Content-Type: application/json
x-access-token: SITE_ADMIN_MDAI_ACCESS_TOKEN
{
"clinicalInfo": {
"SiteID": "test",
"AppLanguage": "en",
"SpeechLanguage": "en"
},
"userInfo": {
"UserName": "testphysician",
"UserEmail": "testphysician@md.ai"
}
}Example response
{
"sessionLink": "https://chat.md.ai/reporting/init?token=9f1c8a2e...&AppLanguage=en&SpeechLanguage=en",
"expiresAt": "2026-01-15T18:32:41.000Z"
}Set the iframe src to the returned sessionLink:
<iframe
id="mdai-reporting"
src="https://chat.md.ai/reporting/init?token=9f1c8a2e..."
allow="microphone;screen-wake-lock;clipboard-read;clipboard-write;hid"
title="MD.ai Reporting"
></iframe>The session link is short-lived
The token in sessionLink expires 5 minutes after it is issued, as given by expiresAt. Request
a link when you are about to mount the iframe rather than caching one for reuse. The expiry
applies only to loading the link — once the iframe has loaded it, the session it establishes lasts
as long as the iframe does, and carries across the navigation to the report link.
Option 2: Build the session URL in the browser
URL: https://chat.md.ai/reporting/init
Query parameters:
- SiteID: The site identifier. (required)
- SiteLocation: The location identifier. (optional)
- Auth: The MD.ai access token from a site admin of your site, exactly as in
userInfofor a report link. (required) - UserName: Name of the user the session is for. (required)
- UserEmail: Email of the user the session is for. (required)
- AppLanguage and SpeechLanguage: Locale for the app and for dictation. (optional)
Example
<iframe
id="mdai-reporting"
src="https://chat.md.ai/reporting/init?SiteID=test&Auth=TOKEN&UserName=testphysician&UserEmail=testphysician@md.ai"
allow="microphone;screen-wake-lock;clipboard-read;clipboard-write;hid"
title="MD.ai Reporting"
></iframe>This exposes the site admin token
The Auth parameter here is a live site admin access token, and it is visible to anyone with
access to the browser — in the URL bar, in browser history, and in referrer headers and proxy logs
along the way. That token can create authenticated report links for any user at your site, so
prefer Option 1 wherever your integration has a backend to make the request from.
Open the study
When the radiologist opens a study, generate the report link as usual and update the same iframe:
const iframeElement = document.getElementById('mdai-reporting');
iframeElement.src = reportLink;Until a study is opened, the iframe displays Ready and waiting for a study. If the session could not be established, it displays the reason instead, so a misconfigured integration is visible immediately rather than surfacing later as a slow launch.
The session route authenticates the user against the site and warms the application, including the assets used for dictation. It does not create a report, and it is not a substitute for the report link. No study is opened until you set src to a link generated by the Reporting API.
Do not pre-warm by launching a report
A study-less pre-warm must use this route, not a report link with placeholder identifiers. Because
reports are keyed on SiteID, PatientMRN, and Accession, a launch sent with placeholder
values (commonly the logged-in user's email or username) creates a real report for a patient who
does not exist, and every later pre-warm with the same values lands on that same report. It
accumulates a template and dictated content over time, and the radiologist then sees it flash
before their actual study loads. Launches whose patient identifiers are an email address, or match
the launching user's own identity, are rejected for this reason.
Keep the iframe mounted
Update the src of the existing iframe rather than removing and re-creating the element. The
session established by the pre-warm is held in the browser and carries across the navigation to
the report link, which is what makes the study open faster.
Pre-warm for the user the report will be assigned to
Use the same UserEmail here that you send in userInfo when generating the report link. If they
differ, the study still opens correctly, but the pre-warmed session belongs to the wrong user and
is replaced during the launch, so the pre-warm gains nothing.
Notes
- URL-encode the parameter values you place in the URL yourself.
UserNamein particular usually contains spaces. Values returned insessionLinkare already encoded. - Study parameters carrying no value are ignored, so one URL template can serve both the pre-warm and the launch. A study parameter with a value is rejected, since that is a report link sent to the wrong route.
- Repeating the pre-warm is safe. It resolves the same user and reuses the same session every time, so an iframe that reloads during a shift has no side effects. A
sessionLinkthat has not expired can be loaded more than once. - Provisioning applies here as it does for report links: a
UserEmailnot yet known to MD.ai is registered on the site the first time it is seen. - If the user is already logged in to MD.ai,
Auth,UserName, andUserEmailcan be omitted and the session is established for that user. This does not apply to iframe integrations, where logging in to MD.ai is prohibited. - The pre-warm emits no report events and accepts no report actions. Both begin once a study is launched, with
report_ready.
Launch Reports in Persistent Window/Iframe
To synchronize and launch new reports automatically within the same iframe/tab/window when navigating to a new study in your application or worklist, please follow the steps outlined below:
- The first time a user launches a report in a new reporting iframe/tab/window, that needs to remain synchronized, you must add the
"SyncLaunch": trueparameter in thereportingAppsection of the API call as mentioned here. This parameter is essential for establishing the initial synchronization and will keep track of all new report launches in the same site for that user. Use the retrievedreportLinkfrom the API in this window. - When the user switches to a different study in your application, make another call to the same endpoint with the new study's clinical information. This action will then automatically update the previously opened reporting iframe, tab, or window with this study's report, maintaining synchronization without requiring additional user intervention. The user does not have to click any buttons or manually open the report link in this case.
Embedded (iframe/WebView2) synchronization
When MD.ai reporting is embedded in an iframe or WebView2, launch synchronization is disabled by
default to avoid unexpected navigation inside your host application. If you need SyncLaunch to
update the embedded report, MD.ai will enable it for your site — please reach out to your MD.ai
contact. Synchronization in a separate browser tab or window works without any additional setup.
Here is an example of sync enabled with iframe integration when navigating studies within our DICOM viewer:
Here is an example of sync enabled when opening MD.ai Reporting in a separate browser tab/window and navigating studies:
Query Report Status
The Report Status Query endpoint allows you to check the status of reports - Ordered, Draft, Preliminary, Final, or Addendum. This endpoint supports various query conditions to retrieve specific reports based on the clinical information provided.
Method: POST
API endpoint: https://chat.md.ai/api/report/status
Request Header:
x-access-token: SITE_ADMIN_TOKENMake sure to add the access token from a site admin.
Request Body:
- clinicalInfo: A list of dictionary objects that store the clinical information/tags required for querying the report status. Each object in the list is required to include the following fields:
- SiteID: The site identifier (required).
- SiteLocation: The location identifier (optional).
- PatientMRN: The medical record number of the patient (required).
- Accession: The accession number of the report (required).
You must provide all three fields (SiteID, PatientMRN, and Accession) in each object.
Example request:
{
"clinicalInfo": [
{
"SiteID": "test",
"PatientMRN": "test-mrn1",
"Accession": "test-accession1"
},
{
"SiteID": "test",
"PatientMRN": "test-mrn2",
"Accession": "test-accession2"
}
]
}Response:
A list of reports based on the query will be returned. The response includes the following parameters:
- reportId: Unique hash identifier for the report.
- status: Current status of the report -
Ordered,Draft,Preliminary,Final,Addendum. - userEmail: Email ID of the user associated with the report.
- unsignUntil: Time (in milliseconds since Unix epoch) until a report can be unsigned,
nullif the unsigning period has expired or the report is in DRAFT status.
[
{
"reportId": "test-report",
"status": "Draft",
"userEmail": "user1@example.com",
"unsignUntil": null,
"clinicalInfo": [
{
"SiteID": "test",
"PatientMRN": "test-mrn1",
"Accession": "test-accession1"
}
]
},
{
"reportId": "test-report2",
"status": "Final",
"userEmail": "user2@example.com",
"unsignUntil": 1747937665956,
"clinicalInfo": [
{
"SiteID": "test",
"PatientMRN": "test-mrn2",
"Accession": "test-accession2"
}
]
}
]Query Report Content
The Report Content Query endpoint allows you to retrieve the full report content for a specific clinical case. This endpoint requires key clinical identifiers to locate the corresponding report.
Method: GET
API Endpoint: https://chat.md.ai/api/report/content
Request Header:
x-access-token: SITE_ADMIN_TOKENQuery Parameters:
- SiteID: The site identifier. (required)
- SiteLocation: The location identifier. (optional)
- PatientMRN: Medical Record Number of the patient. (required)
- Accession: Accession number of the report. (required)
Response:
Returns the report content associated with the provided clinical information.
{
"content": "xxxx"
}Access Reports
Signed or draft reports can be exported in multiple formats, including HTML, JSON and RTF via API endpoints, or PDF, DOCX, and RTF from within our reporting application.
Export as HTML
We allow access to reports in HTML format via a direct API call, which can then be rendered directly in the client application.
The primary report content is encapsulated within <div class="report-content">, while addenda are structured within multiple <div class="report-addendum-content"> elements.
Method: POST
API endpoint: https://chat.md.ai/api/report/export/html
Request Header:
x-access-token: SITE_ADMIN_TOKENRequest Body:
- clinicalInfo: Query for report by
SiteID,PatientMRNandAccession. (required) - timeZone: Specifies the time zone for all formatted dates. (optional)
{
"clinicalInfo": {
"SiteID": "test",
"PatientMRN": "test-mrn",
"Accession": "test-accession"
},
"timeZone": "America/New_York" // optional
}Response:
{
"html": "..."
}Export as JSON
We allow access to reports in JSON format via a direct API call with all the metadata, which can then be processed directly in the client application.
The report text can be extracted from the reportInfo.content field and addenda from the addendumInfo.content field of the response body, as shown in the example below.
Method: POST
API endpoint: https://chat.md.ai/api/report/export/json
Request Header:
x-access-token: SITE_ADMIN_TOKENRequest Body:
- clinicalInfo: Query for report by
SiteID,PatientMRNandAccession. (required)
{
"clinicalInfo": {
"SiteID": "test",
"PatientMRN": "test-mrn",
"Accession": "test-accession"
}
}Response:
{
"siteInfo": {
"headerImgType": null,
"headerImg": null,
"footerImg": null
},
"clinicalInfo": [
{
"SiteID": "test",
"Modality": "CT",
"Accession": "test-accession",
"StudyDate": "2025-03-01",
"StudyTime": "000000",
"PatientAge": "45Y",
"PatientMRN": "test-mrn",
"PatientSex": "M",
"PatientName": "John Doe",
"ReasonForExam": "",
"BodyPartExamined": "Abdomen",
"PatientBirthDate": "",
"StudyDescription": "CT Abdomen w Contrast",
"StudyInstanceUID": "",
"ReferringPhysician": "",
"ReportingPhysician": ""
}
],
"addendumInfo": [
{
"content": "Some test addendum content",
"signedAt": "2025-03-02T17:02:39.013Z",
"signedBy": {
"name": "Jane Doe",
"email": "jane.doe@test.com",
"signature": null
}
}
],
"prelimInfo": [
{
"prelimTag": {
"name": "Abdomen",
"group": "Residents"
},
"prelimAt": "2025-02-20T14:00:35.013Z",
"prelimBy": {
"name": "Emily Chen",
"email": "emily.chen@test.com",
"signature": null
},
"reviewedAt": "2025-02-21T15:01:36.013Z",
"reviewedBy": {
"name": "Jane Doe",
"email": "jane.doe@test.com",
"signature": null
}
}
],
"reportInfo": {
"content": "This is a test report.\n\nNormal Exam.",
"signedAt": "2025-03-01T16:01:38.013Z",
"signedBy": {
"name": "Jane Doe",
"email": "jane.doe@test.com",
"signature": null
}
}
}Export as RTF
We allow access to reports in RTF (Rich Text Format) via a direct API call. The response is a binary payload that can be saved as a .rtf file and opened in standard word processors (e.g., Microsoft Word).
Method: POST
API endpoint: https://chat.md.ai/api/report/export/rtf
Request Header:
x-access-token: SITE_ADMIN_TOKENRequest Body:
- clinicalInfo: Query for report by
SiteID,PatientMRNandAccession. (required) - timeZone: Specifies the time zone for all formatted dates. (optional)
- responseType: Specifies the RTF data format in the response.
Supported values:
arraybuffer,base64(case-insensitive). Defaults toarraybuffer. (optional) - contentOnly: When set to
true, only report and addendum content are returned. Site header/footer images and user signatures are excluded from the RTF output. Defaults tofalse. (optional)
{
"clinicalInfo": {
"SiteID": "test",
"PatientMRN": "test-mrn",
"Accession": "test-accession"
},
"timeZone": "America/New_York" // optional
}Response:
The response body is an ArrayBuffer representing the RTF file.
Example usage:
import fs from 'fs';
import axios from 'axios';
async function downloadReportAsRtf() {
const res = await axios.post(
'https://chat.md.ai/api/report/export/rtf',
{
clinicalInfo: {
SiteID: 'test',
PatientMRN: 'test-mrn',
Accession: 'test-accession',
},
timeZone: 'America/New_York',
},
{
responseType: 'arraybuffer',
headers: {
'x-access-token': 'SITE_ADMIN_TOKEN',
},
},
);
fs.writeFileSync('report.rtf', res.data);
}Programmatic Report Signing
The Programmatic Report Signing API allows site administrators to sign reports programmatically. This is especially useful in scenarios where automated workflows are required.
Method: POST
API Endpoint: https://chat.md.ai/api/report/sign
Request Header:
x-access-token: SITE_ADMIN_TOKENRequest Body:
- clinicalInfo: Query for report by
SiteID,PatientMRN, andAccession. (required) - timeZone: Specifies the IANA time zone for the site. This can be configured either in the request body or as a default time zone in the site configuration. (optional)
- ignoreSignDelay: If set to
true, any sign cooling time period defined at the site level will be ignored, and the report will be signed immediately. This applies both when signing an unsigned report and when finalizing a report that is already signed but still within the cooling period. To finalize an addendum the same way, use the Programmatic Addendum Signing API. (optional, default tofalse)
{
"clinicalInfo": {
"SiteID": "test-site",
"PatientMRN": "test-mrn",
"Accession": "test-accession"
},
"timeZone": "America/New_York", // optional
"ignoreSignDelay": false // optional
}Behavior Notes:
- If the report is unsigned, the API signs the report normally.
- If the report is already signed and still within the cooling period:
- By default, the API enforces the site's configured sign cooling period. During this period, attempting to sign the report again will result in a
500error. - If
ignoreSignDelayis set totrue, the API ends the cooling period and finalizes the report immediately, delivering it downstream. The report is not re-signed — its original sign time and content are preserved.
- By default, the API enforces the site's configured sign cooling period. During this period, attempting to sign the report again will result in a
- If the report is already signed and the cooling period has ended, signing is not allowed and results in a
500error, regardless of theignoreSignDelaysetting.
Response:
No response body is returned. A successful request will result in a 200 status code.
Programmatic Report Unsign
The Programmatic Report Unsign API allows site administrators to unsign reports programmatically, reverting a signed report back to Draft (or Preliminary, if the report has an active preliminary read). This is the API equivalent of the unsign_report iframe action, and is useful for keeping report status in sync when an external system manages its own sign delay.
Method: POST
API Endpoint: https://chat.md.ai/api/report/unsign
Request Header:
x-access-token: SITE_ADMIN_TOKENRequest Body:
- clinicalInfo: Query for report by
SiteID,PatientMRN, andAccession. (required)
{
"clinicalInfo": {
"SiteID": "test-site",
"PatientMRN": "test-mrn",
"Accession": "test-accession"
}
}Behavior Notes:
- The report must be signed (
FinalorAddendumstatus). Unsigning aDraft,Preliminary, orOrderedreport results in a500error. - A report can only be unsigned while it is still within the site's sign cooling (sign delay) window, which matches the
unsignUntilvalue returned by the Report Status Query endpoint. Once that window has elapsed — i.e. the report has been delivered downstream — unsigning results in a500error. - Unsigning a report clears content generated at sign time (addenda, billing codes, and report shares created after signing). The most recent prelim read, if any, is restored and the report returns to
Preliminary; otherwise it returns toDraft.
Response:
No response body is returned. A successful request will result in a 200 status code.
Programmatic Addendum Signing
The Programmatic Addendum Signing API allows site administrators to finalize a signed addendum immediately, cutting short the site's sign cooling (sign delay) window so the addendum is delivered downstream right away. This is the addendum equivalent of calling the Programmatic Report Signing API with ignoreSignDelay set to true, and is the API equivalent of the in-app Sign Now action on an addendum.
The addendum itself is authored and signed by the radiologist in the reporting app (or via the sign_addendum iframe action). This endpoint does not create or sign an addendum — it only releases one that has already been signed.
Method: POST
API Endpoint: https://chat.md.ai/api/report/addendum/sign
Request Header:
x-access-token: SITE_ADMIN_TOKENRequest Body:
- clinicalInfo: Query for report by
SiteID,PatientMRN, andAccession. (required) - ignoreSignDelay: Must be set to
true. Releasing an already-signed addendum is the only operation this endpoint performs, so the flag is required rather than optional; this keeps the request shape identical to the Programmatic Report Signing call. Omitting it results in a400error. (required)
{
"clinicalInfo": {
"SiteID": "test-site",
"PatientMRN": "test-mrn",
"Accession": "test-accession"
},
"ignoreSignDelay": true
}Behavior Notes:
- The most recently signed addendum on the report is finalized. If the report has no signed addendum, the request results in a
500error. - The addendum can only be finalized while it is still within the site's sign cooling (sign delay) window, which matches the
unsignUntilvalue returned by the Report Status Query endpoint for reports inAddendumstatus. Once that window has elapsed — i.e. the addendum has already been delivered downstream — the request results in a500error. - The addendum is not re-signed. Its sign time, content, and author are preserved; only the remaining delay is skipped.
- The addendum is delivered exactly once. Finalizing early cancels the pending delayed delivery, so no duplicate is sent when the original window would have elapsed.
- If the site does not have sign cooling enabled, there is no delay to skip and the request results in a
500error. - If a reporting session for the report is open, it receives the
addendum_finalizedevent, which can be used to confirm the addendum was released.
Response:
No response body is returned. A successful request will result in a 200 status code.
Programmatic Addendum Unsign
The Programmatic Addendum Unsign API allows site administrators to unsign the most recently signed addendum on a report. This is the API equivalent of the unsign_addendum iframe action.
Method: POST
API Endpoint: https://chat.md.ai/api/report/addendum/unsign
Request Header:
x-access-token: SITE_ADMIN_TOKENRequest Body:
- clinicalInfo: Query for report by
SiteID,PatientMRN, andAccession. (required)
{
"clinicalInfo": {
"SiteID": "test-site",
"PatientMRN": "test-mrn",
"Accession": "test-accession"
}
}Behavior Notes:
- The most recently signed addendum on the report is unsigned. If the report has no signed addendum, the request results in a
500error. - An addendum can only be unsigned while it is still within the site's sign cooling (sign delay) window. Once that window has elapsed — i.e. the addendum has been delivered downstream — unsigning results in a
500error. - After unsigning, the report reverts to
Finalif no other signed addendums remain, or stays inAddendumstatus if earlier signed addendums are still present.
Response:
No response body is returned. A successful request will result in a 200 status code.
Programmatic Report Prelim
The Programmatic Report Prelim API allows site administrators to prelim reports programmatically. This is especially useful in scenarios where automated workflows are required.
Method: POST
API Endpoint: https://chat.md.ai/api/report/prelim
Request Header:
x-access-token: SITE_ADMIN_TOKENRequest Body:
- clinicalInfo: Query for report by
SiteID,PatientMRN, andAccession. (required) - prelimGroup: Prelim group name. (required)
- prelimTag: Prelim tag name. (required)
{
"clinicalInfo": {
"SiteID": "test-site",
"PatientMRN": "test-mrn",
"Accession": "test-accession"
},
"prelimGroup": "Resident",
"prelimTag": "Abdomen"
}Response:
No response body is returned. A successful request will result in a 200 status code.
Programmatic Report Delete
The Programmatic Report Delete API allows site administrators to delete reports programmatically, cannot delete signed reports. This is especially useful in scenarios where automated workflows are required.
Method: POST
API Endpoint: https://chat.md.ai/api/report/delete
Request Header:
x-access-token: SITE_ADMIN_TOKENRequest Body:
- clinicalInfo: Query for report by
SiteID,PatientMRN, andAccession. (required)
{
"clinicalInfo": {
"SiteID": "test-site",
"PatientMRN": "test-mrn",
"Accession": "test-accession"
}
}Response:
No response body is returned. A successful request will result in a 200 status code.
Query Preliminary Reports
The Preliminary Reports Query endpoint allows site administrators to check available preliminary reports in site.
Method: GET
API Endpoint: https://chat.md.ai/api/report/site/prelims
Request Header:
x-access-token: SITE_ADMIN_TOKENQuery Parameters:
- SiteID: Site ID. (required)
- prelimGroup: Prelim group name. (required)
- prelimTag: Prelim tag name. (required)
Response:
{
"reportId": "W0mqkzAvYR",
"clinicalInfo": [
{
"SiteID": "test",
"Accession": "test-accession",
"PatientMRN": "test-mrn"
}
],
"userEmail": "jane.doe@test.com"
}Update Report DICOM-SR Files
The Update Report DICOM-SR Files API allows site administrators to replace or append DICOM-SR files to an existing report in site.
Method: POST
API Endpoint: https://chat.md.ai/api/report/update-dicomsr
Request Header:
x-access-token: SITE_ADMIN_TOKENRequest Body:
- clinicalInfo: Query for report by
SiteID,PatientMRN, andAccession. (required) - dicomSR: A list of DICOM-SR files in base64 format. (required)
- mode:
replaceorappend. (optional, default toreplace)- replace mode: Replace existing DICOM-SR files with new ones.
- append mode: Append new DICOM-SR files to existing ones.
{
"clinicalInfo": {
"SiteID": "test-site",
"PatientMRN": "test-mrn",
"Accession": "test-accession"
},
"dicomSR": [
{
"fileData": "MIIEpQ..." // DICOM-SR file data (base64 format)
},
{
"fileData": "RQNASw..." // DICOM-SR file data (base64 format)
}
],
"mode": "replace"
}Response:
No response body is returned. A successful request will result in a 200 status code.
Reset Order
The Reset Order endpoint allows site administrators to revert a report from Draft status back to its original Ordered status. This action removes any user assigned to the report and clears all existing report content.
Only reports that are currently in Draft status and were initially created in Ordered status can be reset.
Method: POST
API Endpoint: https://chat.md.ai/api/report/reset-order
Request Header:
x-access-token: SITE_ADMIN_TOKENRequest Body:
- SiteID: The site identifier. (required)
- SiteLocation: The location identifier. (optional)
- PatientMRN: Medical Record Number of the patient. (required)
- Accession: Accession number of the report. (required)
Example request:
{
"SiteID": "test",
"PatientMRN": "test-mrn",
"Accession": "test-accession"
}Response:
No response body is returned. A successful request will result in a 200 status code.
Unassign Report
The Unassign Report endpoint allows site administrators to release a report's ownership back to an unassigned Ordered state while preserving all existing report content. This lets a report be "released" so another user can pick it up, without deleting the draft.
Unlike Programmatic Report Delete and Reset Order — both of which discard the report content — this endpoint keeps the content, dictation, and applied template intact. The released report is automatically converted back to a Draft assigned to the next user who launches the same study, with its content preserved.
Only reports currently in Draft status can be unassigned. Signed or finalized reports are rejected.
Method: POST
API Endpoint: https://chat.md.ai/api/report/unassign
Request Header:
x-access-token: SITE_ADMIN_TOKENRequest Body:
- SiteID: The site identifier. (required)
- SiteLocation: The location identifier. (optional)
- PatientMRN: Medical Record Number of the patient. (required)
- Accession: Accession number of the report. (required)
Example request:
{
"SiteID": "test",
"PatientMRN": "test-mrn",
"Accession": "test-accession"
}Response:
No response body is returned. A successful request will result in a 200 status code.