Skip to content
Hermes Health API
API referenceOpenAPI spec

Guide

Get clinical history for a patient

Retrieve diagnoses, procedures, fills and labs, then query them.

Patient Histories retrieves a patient’s diagnoses, procedures, prescription fills and lab results from their claims.

It is independent of Site Sonar. Site Sonar tells you where a patient was seen; Patient Histories tells you what happened there. Turn on either, both or neither.

Turn on retrieval

Set patientHistories on the patient body you send to POST /v0/companies/{companyId}/projects/{projectId}/patients:

Create the patient with retrieval on
curl
curl https://api.hermeshealth.ai/v0/companies/COMPANY_ID/projects/PROJECT_ID/patients \
  -X POST \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "firstName": "Jane",
  "lastNames": [
    "Doe"
  ],
  "dateOfBirth": "1990-04-12",
  "sex": "female",
  "zipCodes": [
    "94110",
    "02139"
  ],
  "patientHistories": true
}'
import requests

response = requests.post(
    "https://api.hermeshealth.ai/v0/companies/COMPANY_ID/projects/PROJECT_ID/patients",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
    json={
        "firstName": "Jane",
        "lastNames": [
            "Doe",
        ],
        "dateOfBirth": "1990-04-12",
        "sex": "female",
        "zipCodes": [
            "94110",
            "02139",
        ],
        "patientHistories": True,
    },
)
response.raise_for_status()
print(response.json())
const response = await fetch("https://api.hermeshealth.ai/v0/companies/COMPANY_ID/projects/PROJECT_ID/patients", {
    method: "POST",
    headers: {
        Authorization: "Bearer YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    body: JSON.stringify({
      "firstName": "Jane",
      "lastNames": [
        "Doe"
      ],
      "dateOfBirth": "1990-04-12",
      "sex": "female",
      "zipCodes": [
        "94110",
        "02139"
      ],
      "patientHistories": true
    }),
});
if (!response.ok) throw new Error(`Request failed: ${response.status}`);
const data = await response.json();

Omitting the flag means off.

Turn it on for a patient you already created

Send the flag to PATCH /v0/companies/{companyId}/projects/{projectId}/patients/{patientId}/search-level:

Turn on retrieval
curl
curl https://api.hermeshealth.ai/v0/companies/COMPANY_ID/projects/PROJECT_ID/patients/PATIENT_ID/search-level \
  -X PATCH \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "patientHistories": true
}'
import requests

response = requests.patch(
    "https://api.hermeshealth.ai/v0/companies/COMPANY_ID/projects/PROJECT_ID/patients/PATIENT_ID/search-level",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
    json={
        "patientHistories": True,
    },
)
response.raise_for_status()
print(response.json())
const response = await fetch("https://api.hermeshealth.ai/v0/companies/COMPANY_ID/projects/PROJECT_ID/patients/PATIENT_ID/search-level", {
    method: "PATCH",
    headers: {
        Authorization: "Bearer YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    body: JSON.stringify({
      "patientHistories": true
    }),
});
if (!response.ok) throw new Error(`Request failed: ${response.status}`);
const data = await response.json();

Patch semantics: an omitted flag keeps its current value, where an omitted flag on create means off. This will not turn siteSonar off.

Get the identity right

Both products match claims against who the patient was at the time of care, so the same two fields matter. lastNames needs maiden and prior names, and zipCodes needs the ZIPs where the patient lived when treated, not now. A thin result has the same causes, covered in Find where a patient was treated.

If the project’s purpose requires an authorization, retrieval does not run until one is uploaded and approved.

Know when it finished

The patientHistories flag changes from true to false when the run finishes. Poll the patient and read patient.patientHistories from the response (the flag sits inside its patient object), or subscribe a webhook to patient / columnChanged on that column. See Receive webhook events.

A run that fails does not clear the flag; it is retried quietly, so a patient can sit at patientHistories: true for longer than one run normally takes.

Read the data

Five tables carry what the retrieval found. All take the same query syntax — see Query large tables.

RouteHolds
POST /v0/diagnoses/browseDiagnoses recorded against a visit
POST /v0/procedures/browseProcedures performed
POST /v0/prescription-fills/browsePrescriptions filled
POST /v0/lab-accessions/browseLab orders
POST /v0/lab-results/browseResults against those orders

Scope to one patient

Every one of them takes the same filter:

Patient and project filter
json
{
  "where": {
    "patient": { "id": [{ "equals": "<patient_id>" }] },
    "project": { "id": [{ "equals": "<project_id>" }] }
  }
}

Filter through the joined entity, not an ID column on the root: patient.id, not diagnosis.patientId. An unknown key is accepted and ignored, so the wrong one returns 200 with rows you did not ask for.

Export it

Send Accept: text/csv and omit take to stream every matching row as CSV. That is the intended path for a bulk extract; aggregate and total are dropped from CSV output.

Show it to your own users

Embed the read-only UI rather than rendering browse rows yourself — see Embed clinical data. These schemas change as claims coverage expands, and the iframe absorbs that. The browse routes are the right tool for server-to-server querying and exports.

Next steps