MD.ai

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:

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-read and clipboard-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:

MD.ai Reporting integrated within MD.ai's DICOM viewer

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's status at load time.
  • report_signed
  • report_unsigned
  • report_status_changed
  • report_content_updated - Report content is changed and saved.
  • report_saved - Emitted in response to the save_report action.
  • report_finalized
  • report_discarded
  • addendum_created
  • addendum_cancelled
  • addendum_signed
  • addendum_unsigned
  • addendum_content_updated - Addendum content is changed and saved. Currently, report_content_updated is also simultaneously emitted.
  • addendum_finalized
  • recorder_on
  • recorder_off
  • microphone_devices
  • billing_code_generated
  • report_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_report

    action parameters

    • prelimGroup [required] - Prelim group name.
    • prelimTag [required] - Prelim tag name.
  • save_report

    Manually triggers a report save. The report_saved event 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_devices

    Request the list of available microphone devices. The response will be sent as a microphone_devices event, containing all available microphones and their details.

  • change_microphone

    action parameters

    • deviceId [required] - The deviceId of the microphone to activate. This value should match one of the deviceId fields returned in the microphone_devices event.

  • user_activity

    Reports 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-token header, 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 Auth query parameter.

Method: POST

API endpoint: https://chat.md.ai/api/report/init

Request Header:

x-access-token: SITE_ADMIN_TOKEN

Request 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-token header 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 userInfo for 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. UserName in particular usually contains spaces. Values returned in sessionLink are 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 sessionLink that has not expired can be loaded more than once.
  • Provisioning applies here as it does for report links: a UserEmail not 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, and UserEmail can 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:

  1. 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": true parameter in the reportingApp section 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 retrieved reportLink from the API in this window.
  2. 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:

Sync report launch iframe

Here is an example of sync enabled when opening MD.ai Reporting in a separate browser tab/window and navigating studies:

Sync report launch window

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_TOKEN

Make 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, null if 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_TOKEN

Query 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_TOKEN

Request Body:

  • clinicalInfo: Query for report by SiteID, PatientMRN and Accession. (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_TOKEN

Request Body:

  • clinicalInfo: Query for report by SiteID, PatientMRN and Accession. (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_TOKEN

Request Body:

  • clinicalInfo: Query for report by SiteID, PatientMRN and Accession. (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 to arraybuffer. (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 to false. (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_TOKEN

Request Body:

  • clinicalInfo: Query for report by SiteID, PatientMRN, and Accession. (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 to false)
{
  "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 500 error.
    • If ignoreSignDelay is set to true, 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.
  • If the report is already signed and the cooling period has ended, signing is not allowed and results in a 500 error, regardless of the ignoreSignDelay setting.

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_TOKEN

Request Body:

  • clinicalInfo: Query for report by SiteID, PatientMRN, and Accession. (required)
{
  "clinicalInfo": {
    "SiteID": "test-site",
    "PatientMRN": "test-mrn",
    "Accession": "test-accession"
  }
}

Behavior Notes:

  • The report must be signed (Final or Addendum status). Unsigning a Draft, Preliminary, or Ordered report results in a 500 error.
  • A report can only be unsigned while it is still within the site's sign cooling (sign delay) window, which matches the unsignUntil value returned by the Report Status Query endpoint. Once that window has elapsed — i.e. the report has been delivered downstream — unsigning results in a 500 error.
  • 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 to Draft.

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_TOKEN

Request Body:

  • clinicalInfo: Query for report by SiteID, PatientMRN, and Accession. (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 a 400 error. (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 500 error.
  • The addendum can only be finalized while it is still within the site's sign cooling (sign delay) window, which matches the unsignUntil value returned by the Report Status Query endpoint for reports in Addendum status. Once that window has elapsed — i.e. the addendum has already been delivered downstream — the request results in a 500 error.
  • 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 500 error.
  • If a reporting session for the report is open, it receives the addendum_finalized event, 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_TOKEN

Request Body:

  • clinicalInfo: Query for report by SiteID, PatientMRN, and Accession. (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 500 error.
  • 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 500 error.
  • After unsigning, the report reverts to Final if no other signed addendums remain, or stays in Addendum status 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_TOKEN

Request Body:

  • clinicalInfo: Query for report by SiteID, PatientMRN, and Accession. (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_TOKEN

Request Body:

  • clinicalInfo: Query for report by SiteID, PatientMRN, and Accession. (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_TOKEN

Query 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_TOKEN

Request Body:

  • clinicalInfo: Query for report by SiteID, PatientMRN, and Accession. (required)
  • dicomSR: A list of DICOM-SR files in base64 format. (required)
  • mode: replace or append. (optional, default to replace)
    • 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_TOKEN

Request 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_TOKEN

Request 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.

On this page