npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@healthcloudai/hc-healthrecord-connector

v1.2.2

Published

Healthcheck Healthrecord SDK with TypeScript

Readme

Healthcheck Health Record Connector

Patient-facing SDK connector for Healthcheck EHR Services.

HCHealthRecordClient uses a configured and authenticated HCLoginClient to resolve:

  • the current patient session
  • the tenant header
  • the authorization header
  • the target EHR Services environment

The connector supports patient profile data, allergies, conditions, family history, procedures, medications, immunizations, lab results, care plans, vitals, and social history.


Installation

npm install @healthcloudai/hc-healthrecord-connector \
  @healthcloudai/hc-login-connector \
  @healthcloudai/hc-http

Import

import { HCHealthRecordClient } from "@healthcloudai/hc-healthrecord-connector";
import { HCLoginClient } from "@healthcloudai/hc-login-connector";
import { FetchClient, HCServiceError } from "@healthcloudai/hc-http";

import type {
  APIResponse,
  Patient,
  PatientData,
  Allergy,
  Problem,
  FamilyHistory,
  Procedure,
  Medication,
  Immunization,
  LabResult,
  CarePlan,
  VitalSign,
  ImportVitalsRequest,
  VitalReadingUpdate,
  SocialHistory,
  SocialHistoryAnswer,
  SocialHistoryClearBody
} from "@healthcloudai/hc-healthrecord-connector";

Setup

Create the shared HTTP client, configure and authenticate the login client, then pass both instances to HCHealthRecordClient.

const httpClient = new FetchClient();
const loginClient = new HCLoginClient(httpClient);

loginClient.configure("healthcheck", "dev");
await loginClient.login("<PATIENT_EMAIL>", "<PASSWORD>");

const healthRecords = new HCHealthRecordClient(httpClient, loginClient);

HCHealthRecordClient does not have its own configure(...) method. It reads the environment from loginClient.getEnvironment() and maps it to the EHR Services host internally.

Supported environment mapping in the current client code:

| Login environment | EHR Services host | | --- | --- | | dev | https://dev-api-ehrservices.health.cloud | | uat | https://uat-api-ehrservices.health.cloud | | prod | https://api-ehrservices.health.cloud |

If the login client returns an unsupported environment, the constructor throws ConfigError.


Optional API Key Header

If the deployment requires an API key header, set it once on the Health Record client.

healthRecords.setApiKey("x-api-key", "<API_KEY>");

The header name and value are trimmed. Empty header names or values throw ConfigError.

When set, the API key header is added to authenticated Health Record requests together with the login client's auth headers.


Authentication

All patient record methods use loginClient.getAuthHeader() internally. The login client must already have a valid ID token.

Authenticated requests include:

{
  Authorization: "Bearer <ID_TOKEN>",
  "X-Tenant-ID": "<TENANT_ID>"
}

The SDK consumer does not pass auth headers into individual methods.

getHealth() is a raw service health call and does not use the authenticated request helper.


Request Envelopes

POST and PUT methods accept resource data directly at the SDK surface. The connector wraps most values in the backend envelope internally.

SDK call:

await healthRecords.createAllergy({
  Allergen: "Peanuts",
  Reaction: "Anaphylaxis",
  Severity: "severe",
  IsActive: true
});

Request body sent by the connector:

{
  "Data": {
    "Allergen": "Peanuts",
    "Reaction": "Anaphylaxis",
    "Severity": "severe",
    "IsActive": true
  }
}

GET and DELETE methods do not send request bodies.


Response Envelopes

Most methods return:

Promise<APIResponse<T>>
export interface APIResponse<T> {
  Data: T | null;
  IsOK: boolean;
  ErrorMessage: string | null;
}

The connector validates that successful responses match this shape.

getHealth() is the exception. It returns:

Promise<HealthResponse>

Error Handling

The client uses the shared @healthcloudai/hc-http error model and its own execute(...) wrapper.

Typical runtime behavior:

| Situation | Error behavior | | --- | --- | | Unsupported EHR environment | Throws ConfigError in the constructor. | | Empty API key header name or value | Throws ConfigError from setApiKey(...). | | Missing required SDK input before a request | Throws ValidationError with code INVALID_INPUT. | | Shared HTTP layer throws APIError | Re-thrown unchanged. | | Request throws a non-APIError runtime error | Wrapped in APIError with code UNKNOWN_ERROR. | | Response is null or undefined | Throws APIError with code EMPTY_RESPONSE. | | Response is not an APIResponse envelope | Throws APIError with code INVALID_RESPONSE. | | Backend returns isOK: false | Throws HCServiceError with code BACKEND_FAILURE; original response is preserved in details. |

Example:

try {
  await healthRecords.getAllergy("<ALLERGY_ID>");
} catch (error) {
  if (error instanceof HCServiceError) {
    console.log(error.backendMessage);
    console.log(error.details);
  }
}

Common pre-request validation failures in the current client:

| Method | Validation | | --- | --- | | searchPatients(query) | query must be non-empty. | | createPatient(patient) | FirstName, LastName, and BirthDate are required. | | updateCurrentPatient(patient) | patient is required. | | createAllergy(allergy) | Allergen is required. | | createCondition(condition) | SnomedCode, ICDCode, or Description is required. | | createFamilyHistory(familyHistory) | Name is required; do not pass both BornDate and Age. | | createProcedure(procedure) | ProcedureCode or Description is required. | | createMedication(medication) | Name is required. | | createImmunization(immunization) | Vaccine or CvxCode is required. | | createLabResult(labResult) | At least one analyte is required. | | createCarePlan(carePlan) | Title is required. | | getVitalSnapshot(dateRecorded) | dateRecorded is required. | | recordVitalsSnapshot(vitals) | dateRecorded is required. | | replaceVitalsSnapshot(vitals) | dateRecorded is required. | | syncVitals(request) | Records must contain at least one vital record. | | updateVitalsReading(observationId, reading) | observationId and reading.Value are required. | | mergeSocialHistoryAnswers(answers) | At least one answer with Key is required. | | upsertSocialHistoryAnswer(answer) | Key is required. | | clearSocialHistory(request) | Keys must contain at least one non-empty key. |

ID path parameters are validated and URL-encoded. Empty IDs throw ValidationError.


Methods

Client

| Method | Returns | | --- | --- | | setApiKey(headerName, value) | void |

Health

| Method | Returns | | --- | --- | | getHealth() | Promise<HealthResponse> |

Patient

| Method | Returns | | --- | --- | | getCurrentPatient() | Promise<APIResponse<Patient>> | | getPatientByVendor(vendorPatientId) | Promise<APIResponse<Patient>> | | searchPatients(query) | Promise<APIResponse<Patient[]>> | | createPatient(patient) | Promise<APIResponse<Patient>> | | updateCurrentPatient(patient) | Promise<APIResponse<boolean>> | | deactivateEHRPatient() | Promise<APIResponse<boolean>> |

Allergies

| Method | Returns | | --- | --- | | listAllergies() | Promise<APIResponse<Allergy[]>> | | getAllergy(id) | Promise<APIResponse<Allergy>> | | createAllergy(allergy) | Promise<APIResponse<string>> | | updateAllergy(id, allergy) | Promise<APIResponse<string>> | | deleteAllergy(id) | Promise<APIResponse<boolean>> |

Conditions

| Method | Returns | | --- | --- | | listConditions(options?) | Promise<APIResponse<Problem[]>> | | getCondition(id) | Promise<APIResponse<Problem>> | | createCondition(condition) | Promise<APIResponse<string>> | | updateCondition(id, condition) | Promise<APIResponse<string>> | | deleteCondition(id) | Promise<APIResponse<boolean>> |

Family History

| Method | Returns | | --- | --- | | listFamilyHistory() | Promise<APIResponse<FamilyHistory[]>> | | getFamilyHistoryItem(id) | Promise<APIResponse<FamilyHistory>> | | createFamilyHistory(familyHistory) | Promise<APIResponse<string>> | | updateFamilyHistory(id, familyHistory) | Promise<APIResponse<string>> | | deleteFamilyHistory(id) | Promise<APIResponse<boolean>> |

Procedures

| Method | Returns | | --- | --- | | listProcedures() | Promise<APIResponse<Procedure[]>> | | getProcedure(id) | Promise<APIResponse<Procedure>> | | createProcedure(procedure) | Promise<APIResponse<string>> | | updateProcedure(id, procedure) | Promise<APIResponse<string>> | | deleteProcedure(id) | Promise<APIResponse<boolean>> |

Medications

| Method | Returns | | --- | --- | | listMedications() | Promise<APIResponse<Medication[]>> | | getMedication(id) | Promise<APIResponse<Medication>> | | createMedication(medication) | Promise<APIResponse<string>> | | updateMedication(id, medication) | Promise<APIResponse<string>> | | deleteMedication(id) | Promise<APIResponse<boolean>> |

Immunizations

| Method | Returns | | --- | --- | | listImmunizations() | Promise<APIResponse<Immunization[]>> | | getImmunization(id) | Promise<APIResponse<Immunization>> | | createImmunization(immunization) | Promise<APIResponse<string>> | | updateImmunization(id, immunization) | Promise<APIResponse<string>> | | deleteImmunization(id) | Promise<APIResponse<boolean>> |

Lab Results

| Method | Returns | | --- | --- | | listLabResults() | Promise<APIResponse<LabResult[]>> | | getLabResult(id) | Promise<APIResponse<LabResult>> | | createLabResult(labResult) | Promise<APIResponse<string>> | | updateLabResult(id, labResult) | Promise<APIResponse<string>> | | deleteLabResult(id) | Promise<APIResponse<boolean>> |

Care Plans

| Method | Returns | | --- | --- | | listCarePlans() | Promise<APIResponse<CarePlan[]>> | | getCarePlan(id) | Promise<APIResponse<CarePlan>> | | createCarePlan(carePlan) | Promise<APIResponse<string>> | | updateCarePlan(id, carePlan) | Promise<APIResponse<string>> | | deleteCarePlan(id) | Promise<APIResponse<boolean>> |

Vitals

| Method | Returns | | --- | --- | | listVitals() | Promise<APIResponse<VitalSign[]>> | | getVitalSnapshot(dateRecorded) | Promise<APIResponse<VitalSign>> | | recordVitalsSnapshot(vitals) | Promise<APIResponse<VitalObservationIds>> | | replaceVitalsSnapshot(vitals) | Promise<APIResponse<VitalObservationIds>> | | syncVitals(request) | Promise<APIResponse<boolean>> | | updateVitalsReading(observationId, reading) | Promise<APIResponse<boolean>> | | deleteVitalsSnapshot(dateRecorded) | Promise<APIResponse<number>> | | deleteVitalsReading(observationId) | Promise<APIResponse<boolean>> |

Social History

| Method | Returns | | --- | --- | | getSocialHistory() | Promise<APIResponse<SocialHistory>> | | mergeSocialHistoryAnswers(answers) | Promise<APIResponse<string>> | | upsertSocialHistoryAnswer(answer) | Promise<APIResponse<string>> | | clearSocialHistory(request) | Promise<APIResponse<string>> |


Response Examples

The examples below show representative successful backend wire responses for every public method in HCHealthRecordClient. They preserve the casing returned by the service. Public SDK request models use PascalCase.

setApiKey(headerName, value)

undefined

getHealth()

{
  "status": "ok"
}

getCurrentPatient()

{
  "data": {
    "id": "[email protected]",
    "email": "[email protected]",
    "firstName": "John",
    "lastName": "Smith",
    "phone": "555-0100",
    "birthDate": "1990-01-01T00:00:00",
    "sex": "male",
    "fhirid": "<FHIR_ID>",
    "tenantID": "healthcheck",
    "address": {
      "streetAndNumber": "1 Main St",
      "city": "Springfield",
      "state": "IL",
      "postalCode": "62701",
      "country": "US"
    }
  },
  "isOK": true
}

getPatientByVendor(vendorPatientId)

{
  "data": {
    "id": "[email protected]",
    "email": "[email protected]",
    "firstName": "John",
    "lastName": "Smith",
    "fhirid": "<FHIR_ID>",
    "tenantID": "healthcheck"
  },
  "isOK": true
}

searchPatients(query)

{
  "data": [
    {
      "id": "[email protected]",
      "email": "[email protected]",
      "firstName": "John",
      "lastName": "Smith",
      "fhirid": "<FHIR_ID>",
      "tenantID": "healthcheck"
    }
  ],
  "isOK": true
}

createPatient(patient)

{
  "data": {
    "id": "[email protected]",
    "email": "[email protected]",
    "firstName": "John",
    "lastName": "Smith",
    "birthDate": "1990-01-01T00:00:00",
    "fhirid": "<FHIR_ID>",
    "tenantID": "healthcheck"
  },
  "isOK": true
}

updateCurrentPatient(patient)

{
  "data": true,
  "isOK": true
}

deactivateEHRPatient()

{
  "data": true,
  "isOK": true
}

listAllergies()

{
  "data": [
    {
      "id": "<ALLERGY_ID>",
      "allergen": "Peanuts",
      "reaction": "Anaphylaxis",
      "severity": "severe",
      "criticality": "high",
      "onsetDate": "2020-01-15T00:00:00Z",
      "recordedDate": "2026-05-28T23:09:42.364657Z",
      "isActive": true
    }
  ],
  "isOK": true
}

getAllergy(id)

{
  "data": {
    "id": "<ALLERGY_ID>",
    "allergen": "Peanuts",
    "reaction": "Anaphylaxis",
    "severity": "severe",
    "criticality": "high",
    "onsetDate": "2020-01-15T00:00:00Z",
    "recordedDate": "2026-05-28T23:09:42.364657Z",
    "isActive": true
  },
  "isOK": true
}

createAllergy(allergy)

{
  "data": "<ALLERGY_ID>",
  "isOK": true
}

updateAllergy(id, allergy)

{
  "data": "<ALLERGY_ID>",
  "isOK": true
}

deleteAllergy(id)

{
  "data": true,
  "isOK": true
}

listConditions(options?)

{
  "data": [
    {
      "id": "<CONDITION_ID>",
      "snomedCode": "38341003",
      "icdCode": "I10",
      "description": "Essential hypertension",
      "dateIdentified": "2024-01-10T00:00:00Z",
      "recordedDate": "2026-05-19T21:13:13.290942Z",
      "isActive": true,
      "clinicalStatus": "active",
      "verificationStatus": "confirmed",
      "category": "problem-list-item"
    }
  ],
  "isOK": true
}

getCondition(id)

{
  "data": {
    "id": "<CONDITION_ID>",
    "snomedCode": "38341003",
    "icdCode": "I10",
    "description": "Essential hypertension",
    "dateIdentified": "2024-01-10T00:00:00Z",
    "recordedDate": "2026-05-19T21:13:13.290942Z",
    "isActive": true,
    "clinicalStatus": "active",
    "verificationStatus": "confirmed",
    "category": "problem-list-item"
  },
  "isOK": true
}

createCondition(condition)

{
  "data": "<CONDITION_ID>",
  "isOK": true
}

updateCondition(id, condition)

{
  "data": "<CONDITION_ID>",
  "isOK": true
}

deleteCondition(id)

{
  "data": true,
  "isOK": true
}

listFamilyHistory()

{
  "data": [
    {
      "id": "<FAMILY_HISTORY_ID>",
      "relationship": "Mother",
      "relationshipCode": "MTH",
      "name": "Sample Family Member",
      "status": "partial",
      "recordedDate": "2026-05-28T23:24:14.872165Z",
      "conditions": [
        {
          "snomedCode": "44054006",
          "description": "Type 2 diabetes mellitus",
          "onsetAge": 50
        }
      ]
    }
  ],
  "isOK": true
}

getFamilyHistoryItem(id)

{
  "data": {
    "id": "<FAMILY_HISTORY_ID>",
    "relationship": "Mother",
    "relationshipCode": "MTH",
    "name": "Sample Family Member",
    "status": "partial",
    "recordedDate": "2026-05-28T23:24:14.872165Z",
    "conditions": [
      {
        "snomedCode": "44054006",
        "description": "Type 2 diabetes mellitus",
        "onsetAge": 50
      }
    ]
  },
  "isOK": true
}

createFamilyHistory(familyHistory)

{
  "data": "<FAMILY_HISTORY_ID>",
  "isOK": true
}

updateFamilyHistory(id, familyHistory)

{
  "data": "<FAMILY_HISTORY_ID>",
  "isOK": true
}

deleteFamilyHistory(id)

{
  "data": true,
  "isOK": true
}

listProcedures()

{
  "data": [
    {
      "id": "<PROCEDURE_ID>",
      "procedureCode": "80146002",
      "codeSystem": "http://snomed.info/sct",
      "description": "Excision of appendix",
      "status": "completed",
      "date": "2024-08-12T00:00:00Z",
      "recordedDate": "2024-08-12T00:00:00Z",
      "category": "Surgical procedure"
    }
  ],
  "isOK": true
}

getProcedure(id)

{
  "data": {
    "id": "<PROCEDURE_ID>",
    "procedureCode": "80146002",
    "codeSystem": "http://snomed.info/sct",
    "description": "Excision of appendix",
    "status": "completed",
    "date": "2024-08-12T00:00:00Z",
    "recordedDate": "2024-08-12T00:00:00Z",
    "category": "Surgical procedure"
  },
  "isOK": true
}

createProcedure(procedure)

{
  "data": "<PROCEDURE_ID>",
  "isOK": true
}

updateProcedure(id, procedure)

{
  "data": "<PROCEDURE_ID>",
  "isOK": true
}

deleteProcedure(id)

{
  "data": true,
  "isOK": true
}

listMedications()

{
  "data": [
    {
      "id": "<MEDICATION_ID>",
      "name": "Lisinopril",
      "code": "29046",
      "codeSystem": "http://www.nlm.nih.gov/research/umls/rxnorm",
      "dosage": "10mg",
      "route": "oral",
      "frequency": "daily",
      "status": "active",
      "startDate": "2025-01-15T00:00:00Z",
      "recordedDate": "2026-05-19T21:13:15.833388Z",
      "isActive": true
    }
  ],
  "isOK": true
}

getMedication(id)

{
  "data": {
    "id": "<MEDICATION_ID>",
    "name": "Lisinopril",
    "code": "29046",
    "codeSystem": "http://www.nlm.nih.gov/research/umls/rxnorm",
    "dosage": "10mg",
    "route": "oral",
    "frequency": "daily",
    "status": "active",
    "startDate": "2025-01-15T00:00:00Z",
    "recordedDate": "2026-05-19T21:13:15.833388Z",
    "isActive": true
  },
  "isOK": true
}

createMedication(medication)

{
  "data": "<MEDICATION_ID>",
  "isOK": true
}

updateMedication(id, medication)

{
  "data": "<MEDICATION_ID>",
  "isOK": true
}

deleteMedication(id)

{
  "data": true,
  "isOK": true
}

listImmunizations()

{
  "data": [
    {
      "id": "<IMMUNIZATION_ID>",
      "vaccine": "Influenza vaccine",
      "cvxCode": "150",
      "status": "completed",
      "dateAdministered": "2025-10-15T10:30:00Z",
      "recordedDate": "2026-05-28T22:46:13.031818Z",
      "site": "left arm",
      "route": "intramuscular"
    }
  ],
  "isOK": true
}

getImmunization(id)

{
  "data": {
    "id": "<IMMUNIZATION_ID>",
    "vaccine": "Influenza vaccine",
    "cvxCode": "150",
    "status": "completed",
    "dateAdministered": "2025-10-15T10:30:00Z",
    "recordedDate": "2026-05-28T22:46:13.031818Z",
    "site": "left arm",
    "route": "intramuscular"
  },
  "isOK": true
}

createImmunization(immunization)

{
  "data": "<IMMUNIZATION_ID>",
  "isOK": true
}

updateImmunization(id, immunization)

{
  "data": "<IMMUNIZATION_ID>",
  "isOK": true
}

deleteImmunization(id)

{
  "data": true,
  "isOK": true
}

listLabResults()

{
  "data": [
    {
      "id": "<LAB_RESULT_ID>",
      "description": "Comprehensive Metabolic Panel",
      "loincCode": "24323-8",
      "status": "final",
      "category": "laboratory",
      "performedDate": "2026-05-28T08:00:00Z",
      "issuedDate": "2026-05-28T23:00:41.095487Z",
      "analytes": [
        {
          "id": "<ANALYTE_ID>",
          "name": "Glucose",
          "loincCode": "2345-7",
          "description": "Glucose",
          "value": "95.0",
          "numericValue": 95,
          "unit": "mg/dL",
          "status": "final",
          "performedDate": "2026-05-28T08:00:00Z"
        }
      ]
    }
  ],
  "isOK": true
}

getLabResult(id)

{
  "data": {
    "id": "<LAB_RESULT_ID>",
    "description": "Comprehensive Metabolic Panel",
    "loincCode": "24323-8",
    "status": "final",
    "category": "laboratory",
    "performedDate": "2026-05-28T08:00:00Z",
    "issuedDate": "2026-05-28T23:00:41.095487Z",
    "analytes": [
      {
        "id": "<ANALYTE_ID>",
        "name": "Glucose",
        "loincCode": "2345-7",
        "description": "Glucose",
        "value": "95.0",
        "numericValue": 95,
        "unit": "mg/dL",
        "status": "final",
        "performedDate": "2026-05-28T08:00:00Z"
      }
    ]
  },
  "isOK": true
}

createLabResult(labResult)

{
  "data": "<LAB_RESULT_ID>",
  "isOK": true
}

updateLabResult(id, labResult)

{
  "data": "<LAB_RESULT_ID>",
  "isOK": true
}

deleteLabResult(id)

{
  "data": true,
  "isOK": true
}

listCarePlans()

{
  "data": [
    {
      "id": "<CARE_PLAN_ID>",
      "title": "Hypertension management plan",
      "description": "BP control through lifestyle + medication.",
      "status": "active",
      "intent": "plan",
      "created": "2025-04-01T00:00:00Z",
      "startDate": "2025-04-01T00:00:00Z",
      "categories": ["assess-plan"],
      "goals": ["Reduce systolic BP to < 130 mmHg"],
      "goalDetails": [],
      "activities": [
        {
          "description": "Daily 30-minute brisk walk",
          "status": "in-progress",
          "startDate": "2025-04-01T00:00:00Z"
        }
      ],
      "careTeam": []
    }
  ],
  "isOK": true
}

getCarePlan(id)

{
  "data": {
    "id": "<CARE_PLAN_ID>",
    "title": "Hypertension management plan",
    "description": "BP control through lifestyle + medication.",
    "status": "active",
    "intent": "plan",
    "created": "2025-04-01T00:00:00Z",
    "startDate": "2025-04-01T00:00:00Z",
    "categories": ["assess-plan"],
    "goals": ["Reduce systolic BP to < 130 mmHg"],
    "goalDetails": [],
    "activities": [
      {
        "description": "Daily 30-minute brisk walk",
        "status": "in-progress",
        "startDate": "2025-04-01T00:00:00Z"
      }
    ],
    "careTeam": []
  },
  "isOK": true
}

createCarePlan(carePlan)

{
  "data": "<CARE_PLAN_ID>",
  "isOK": true
}

updateCarePlan(id, carePlan)

{
  "data": "<CARE_PLAN_ID>",
  "isOK": true
}

deleteCarePlan(id)

{
  "data": true,
  "isOK": true
}

listVitals()

{
  "data": [
    {
      "observationIds": {
        "HeartRate": "98dc2bde-9344-4bdb-85fc-b51e7e7d3ca0"
      },
      "dateRecorded": "2026-05-29T14:30:13.411Z",
      "heartRate": 82
    },
    {
      "observationIds": {
        "BloodPressure:systolic": "aa117cdb-69e4-4e46-abf3-5142f541fdb1",
        "HeartRate": "f9ca74fc-be37-4cf2-9181-72c093558db9"
      },
      "dateRecorded": "2026-05-29T00:20:25.029Z",
      "heartRate": 72,
      "systolicBP": 120
    },
    {
      "observationIds": {
        "HeartRate": "068a3614-6176-4842-baf1-cf868e021d25",
        "BloodPressure:diastolic": "33e1d522-4b26-4a06-9754-c3afcbb32b8d",
        "BloodPressure:systolic": "447102de-d08e-42dc-ada4-95e128b70dbe"
      },
      "dateRecorded": "2026-05-28T09:00:00Z",
      "heartRate": 75,
      "systolicBP": 120,
      "diastolicBP": 78
    },
    {
      "observationIds": {
        "RespiratoryRate": "0145ad0a-9613-47a9-a22b-2c74076d89ee",
        "Temperature": "ebe42dcd-fb43-47ed-a1ce-36093e11549d",
        "HeartRate": "2857ca09-a01b-46b2-8f72-58727f05c2ad",
        "BloodPressure:diastolic": "fd6dca6d-e3cb-4a29-b9c3-e71e47e9a78f",
        "BloodPressure:systolic": "09806f45-e0aa-4c98-8337-64c73fead2e6"
      },
      "dateRecorded": "2026-05-28T08:00:00Z",
      "temperature": 98.6,
      "temperatureUnit": "[degF]",
      "heartRate": 72,
      "respiratoryRate": 14,
      "systolicBP": 120,
      "diastolicBP": 78
    },
    {
      "observationIds": {
        "BloodPressure:diastolic": "be355332-1d51-46c2-9fa2-a9d355740f20",
        "BloodPressure:systolic": "a715fe0f-fbd5-4e04-9441-a2296b7bdab5"
      },
      "dateRecorded": "2026-05-27T19:46:17Z",
      "systolicBP": 125,
      "diastolicBP": 85
    },
    {
      "observationIds": {
        "BloodPressure:diastolic": "71f1a717-cb68-4586-b95c-5b98d9fd376b",
        "BloodPressure:systolic": "5c8d14ea-1ffc-48c6-9a3c-5a536454a5d0"
      },
      "dateRecorded": "2026-05-27T19:35:27Z",
      "systolicBP": 124,
      "diastolicBP": 84
    },
    {
      "observationIds": {
        "BloodPressure:diastolic": "b7ab4257-b309-4456-b582-598a0827cb06",
        "BloodPressure:systolic": "81ffa96e-ff59-4e60-97ae-b32a9a215cec"
      },
      "dateRecorded": "2026-05-27T19:34:20Z",
      "systolicBP": 123,
      "diastolicBP": 83
    },
    {
      "observationIds": {
        "BloodPressure:diastolic": "9f797af3-e104-4204-931e-5a90facdc260"
      },
      "dateRecorded": "2026-05-27T00:00:00Z",
      "diastolicBP": 83
    },
    {
      "observationIds": {
        "BloodPressure:diastolic": "10c3022a-fd11-42dc-bc14-e69085579924",
        "BloodPressure:systolic": "9efc3cb6-de99-4d01-b683-b2d813be40b4"
      },
      "dateRecorded": "2026-05-21T19:19:51Z",
      "systolicBP": 128,
      "diastolicBP": 98
    },
    {
      "observationIds": {
        "Temperature": "f51418f0-9aef-4497-b7b1-368a658ecb8b",
        "HeartRate": "5d2d8201-3e1d-4c9f-b9d8-ab682c1dea27",
        "BloodPressure:diastolic": "ac48bd8a-5735-479c-b55b-977f29227001",
        "BloodPressure:systolic": "729ff154-6b85-4983-8c3c-9ef4e3b3d53d"
      },
      "dateRecorded": "2026-05-21T00:00:00Z",
      "temperature": 36.6,
      "temperatureUnit": "C",
      "heartRate": 72,
      "systolicBP": 120,
      "diastolicBP": 80
    },
    {
      "observationIds": {
        "RespiratoryRate": "dfcd8cec-19dd-4297-b907-c8e146d8af69",
        "Temperature": "31827ae1-5dad-4d5b-a99a-bdcc30ab3043",
        "HeartRate": "13b82f34-995c-4353-a60f-f8cf48969283",
        "BloodPressure:diastolic": "8c1ec75b-a211-4893-84a0-89b54a920c93",
        "BloodPressure:systolic": "354a8b13-3147-4f0d-b77f-95bcebcfcde6"
      },
      "dateRecorded": "2026-05-20T00:37:36.326Z",
      "temperature": 98.6,
      "temperatureUnit": "[degF]",
      "heartRate": 72,
      "respiratoryRate": 14,
      "systolicBP": 120,
      "diastolicBP": 78
    },
    {
      "observationIds": {
        "Temperature": "bd441679-00ab-42fd-9088-66739d486d2b",
        "HeartRate": "6e59a9fc-8423-4182-81b4-e2ab78684779",
        "BloodPressure:diastolic": "c31eb955-c2fb-4578-a974-d775f097e017",
        "BloodPressure:systolic": "f08fd035-5606-4234-add4-1b10b848b31b"
      },
      "dateRecorded": "2026-05-20T00:00:00Z",
      "temperature": 36.6,
      "temperatureUnit": "C",
      "heartRate": 72,
      "systolicBP": 120,
      "diastolicBP": 80
    },
    {
      "observationIds": {
        "HeartRate": "b4f38401-4984-4c17-a86b-86d57fd6bcef",
        "BloodPressure:diastolic": "73d6066d-c8af-488e-8c79-2eea49c323d9",
        "BloodPressure:systolic": "785b3605-a5d0-4bd1-b9e8-38a7a66ca0da"
      },
      "dateRecorded": "2025-10-15T08:00:00Z",
      "heartRate": 65,
      "systolicBP": 118,
      "diastolicBP": 76
    }
  ],
  "isOK": true
}

getVitalSnapshot(dateRecorded)

{
  "data": {
    "observationIds": {
      "HeartRate": "98dc2bde-9344-4bdb-85fc-b51e7e7d3ca0"
    },
    "dateRecorded": "2026-05-29T14:30:13.411Z",
    "heartRate": 82
  },
  "isOK": true
}

recordVitalsSnapshot(vitals)

{
  "data": {
    "BloodPressure:systolic": "<OBSERVATION_ID>",
    "BloodPressure:diastolic": "<OBSERVATION_ID>",
    "HeartRate": "<OBSERVATION_ID>",
    "Temperature": "<OBSERVATION_ID>",
    "RespiratoryRate": "<OBSERVATION_ID>"
  },
  "isOK": true
}

replaceVitalsSnapshot(vitals)

{
  "data": {
    "BloodPressure:systolic": "<OBSERVATION_ID>",
    "BloodPressure:diastolic": "<OBSERVATION_ID>",
    "HeartRate": "<OBSERVATION_ID>",
    "Temperature": "<OBSERVATION_ID>",
    "RespiratoryRate": "<OBSERVATION_ID>"
  },
  "isOK": true
}

syncVitals(request)

{
  "data": true,
  "isOK": true
}

updateVitalsReading(observationId, reading)

{
  "data": true,
  "isOK": true
}

deleteVitalsSnapshot(dateRecorded)

{
  "data": 3,
  "isOK": true
}

deleteVitalsReading(observationId)

{
  "data": true,
  "isOK": true
}

getSocialHistory()

{
  "data": {
    "id": "<SOCIAL_HISTORY_ID>",
    "patientFHIRID": "<FHIR_ID>",
    "questionnaireRef": "https://healthcheck.com/fhir/Questionnaire/social-history/healthcheck",
    "status": "inprogress",
    "recordedDate": "2026-05-28T23:24:41.813707Z",
    "answers": [
      {
        "key": "SOCIALHISTORY.SMOKING.STATUS",
        "question": "Smoking status",
        "answer": "Never smoker",
        "readableAnswer": "Never smoker"
      }
    ]
  },
  "isOK": true
}

mergeSocialHistoryAnswers(answers)

{
  "data": "<SOCIAL_HISTORY_ID>",
  "isOK": true
}

upsertSocialHistoryAnswer(answer)

{
  "data": "<SOCIAL_HISTORY_ID>",
  "isOK": true
}

clearSocialHistory(request)

{
  "data": "<SOCIAL_HISTORY_ID>",
  "isOK": true
}

UPDATE Notes

These notes document update behavior present in the current HCHealthRecordClient code comments.

updateCurrentPatient(patient)

Partial field update.

  • Only provided fields are updated.
  • Omitted fields preserve existing values.
  • BirthDate is documented in code as immutable and vendor-managed.
  • Updates apply to both Athena and FHIR.
  • Recommended flow: fetch existing data, merge locally, submit the intended partial update.

updateAllergy(id, allergy)

Bulk PUT semantics internally.

  • Fetches all allergies from Athena.
  • Modifies the specific allergy identified by FHIR ID.
  • Preserves vendor IDs.
  • PUTs the entire allergy list back to the vendor.
  • Operation is not atomic; concurrent updates are unsafe and last writer wins.
  • Allergen ID is immutable after creation.
  • Changing the allergen after creation is not supported by the documented vendor constraints.
  • Vendor sync may be skipped if the allergen is not found in the reference table.
  • FHIR may be updated while vendor state remains unchanged.

updateCondition(id, condition)

FHIR-only update.

  • No per-problem Athena update endpoint is exposed according to code comments.
  • Vendor update is skipped.
  • FHIR resource is updated.
  • Athena record remains unchanged.
  • Read sync may overwrite FHIR-only changes with vendor state.
  • Do not rely on this method to update vendor-backed condition data.

Family History Update Behavior

Updating a family history record is not processed as a simple record-level update.

The backend performs a family-history synchronization workflow to ensure the patient's family-history dataset remains consistent.

For this reason, family history updates may behave differently from resources that are updated through a direct record-level write operation.

When updating a record, the route ID is always treated as the source of truth.

Procedure Update Behavior

Procedure updates operate on a single procedure record.

Unlike resources that require dataset-level synchronization, updating a procedure targets only the specified procedure entry.

When updating a procedure, the route ID is always treated as the source of truth.

Medication Update Behavior

Medication updates operate on a single medication record.

Updating a medication affects only the targeted medication entry and does not trigger dataset-level synchronization behavior.

When updating a medication, the route ID is always treated as the source of truth.

Immunization Update Behavior

Immunization updates operate on a single immunization record.

Updating an immunization affects only the targeted immunization entry and does not trigger dataset-level synchronization behavior.

When updating an immunization, the route ID is always treated as the source of truth.

CVX Codes

Immunizations can be created using either a vaccine name or a CVX code.

For maximum compatibility across vendor integrations, providing a valid CVX code is recommended whenever available.

Lab Result Update Behavior

Lab result updates replace the complete analyte collection associated with the lab result.

When updating a lab result, the supplied Analytes array becomes the complete source of truth.

Any analytes omitted from the update request will be removed from the resulting lab result.

Always send the full analyte collection that should exist after the update operation completes.

Care Plan Update Behavior

Care plan updates should be treated as full-state updates.

Collections such as:

  • Categories
  • Goals
  • GoalDetails
  • Activities
  • CareTeam

are rebuilt from the values supplied in the update request.

If a collection is omitted from the request, it may become empty after the update completes.

Recommended update workflow:

1. Retrieve the existing care plan.
2. Modify the fields that need to change.
3. Send the complete desired care plan state back to updateCarePlan().

Example:

const existing = await client.getCarePlan(id);

await client.updateCarePlan(id, {
  ...existing.Data,
  Title: "Updated Care Plan Title"
});

This approach prevents accidental loss of collection data.

Vitals Method Behavior

listVitals()

Returns all vital snapshots as APIResponse<VitalSign[]>.

Each VitalSign represents one recorded date. Its ObservationIds map contains the observation IDs for the snapshot readings.

getVitalSnapshot(dateRecorded)

Returns one vital snapshot as APIResponse<VitalSign>.

The dateRecorded value must use ISO 8601 round-trip format because the backend route applies a datetime constraint.

The backend returns 404 when the requested date does not exist.

recordVitalsSnapshot(vitals)

Creates observation resources for each supplied vital sign and returns APIResponse<VitalObservationIds>.

The returned map is keyed by reading name, such as SystolicBP, DiastolicBP, Weight, and Temperature.

The backend treats both a null result and an empty result as failure.

replaceVitalsSnapshot(vitals)

Performs a full vital snapshot replacement and returns APIResponse<VitalObservationIds>.

Observation IDs are regenerated, so previously cached IDs become invalid.

Use updateVitalsReading(observationId, reading) for single-reading updates.

The backend treats a null result as failure and an empty map as success. This differs from recordVitalsSnapshot(vitals).

syncVitals(request)

Imports mobile/app synced vitals and returns APIResponse<boolean>.

The request is sent directly to the backend without a { Data: ... } envelope. Payload fields use PascalCase names such as Records, StartDate, and EndDate.

Repeated tests should use fresh Id or timestamp values because duplicate records may be skipped by TimescaleDB.

This is separate from recordVitalsSnapshot(vitals), which creates a snapshot from normalized SDK vitals.

updateVitalsReading(observationId, reading)

Updates a single vital sign observation and returns APIResponse<boolean>.

The observationId comes from VitalObservationIds. The backend returns 404 when the observation does not exist.

Use this method for individual reading changes instead of replacing the whole snapshot.

deleteVitalsSnapshot(dateRecorded)

Deletes all vital observations for a specific date and returns APIResponse<number>.

The returned value is the number of deleted observations.

The backend returns 200 even when the result is 0 and never returns 404. This differs from deleteVitalsReading(observationId).

deleteVitalsReading(observationId)

Deletes a single vital sign observation and returns APIResponse<boolean>.

The backend returns 404 when the observation does not exist.

Social History Update Behavior

Social history uses merge/upsert semantics rather than full replacement semantics.

When updating answers:

  • Existing answers are matched by Key.
  • Matching answers are updated.
  • New answers are inserted.
  • Answers not included in the request remain unchanged.

Example:

await healthRecords.mergeSocialHistoryAnswers([
  {
    Key: "SOCIALHISTORY.SMOKING.STATUS",
    Answer: "Never"
  }
]);

This updates only the smoking status answer and preserves all other social history answers.

Clearing Answers

To remove answers, use:

clearSocialHistory(...)

Only the supplied Keys are removed.

Unrelated answers remain unchanged.


Usage Examples

Read Current Patient

const response = await healthRecords.getCurrentPatient();

if (response.Data) {
  console.log(response.Data.FirstName, response.Data.LastName);
}

Create Allergy

const response = await healthRecords.createAllergy({
  Allergen: "Peanuts",
  Reaction: "Anaphylaxis",
  Severity: "severe",
  Criticality: "high",
  IsActive: true
});

console.log(response.Data); // "<ALLERGY_ID>"

Merge Social History Answers

await healthRecords.mergeSocialHistoryAnswers([
  {
    Key: "SOCIALHISTORY.SMOKING.STATUS",
    Question: "Smoking status",
    Answer: "Never smoker",
    ReadableAnswer: "Never smoker"
  }
]);

Replace Vitals Snapshot

const response = await healthRecords.replaceVitalsSnapshot({
  DateRecorded: "2026-05-28T08:00:00Z",
  Temperature: 98.6,
  TemperatureUnit: "[degF]",
  HeartRate: 72,
  RespiratoryRate: 14,
  SystolicBP: 120,
  DiastolicBP: 78
});

console.log(response.Data); // { "BloodPressure:systolic": "<ID>", ... }

Get Vital Snapshot

const response = await healthRecords.getVitalSnapshot("2026-05-28T08:00:00Z");

console.log(response.Data);

Import Vitals

const response = await healthRecords.syncVitals({
  User: {
    TenantID: "healthcheck",
    Email: "[email protected]"
  },
  Records: [
    {
      Id: "apple-health-heart-rate-001",
      Type: "HeartRate",
      Value: 72,
      Unit: "count/min",
      StartDate: new Date("2026-05-28T08:00:00Z"),
      EndDate: new Date("2026-05-28T08:01:00Z"),
      SourceName: "Apple Health",
      SourcePlatform: "ios"
    }
  ],
  SyncTimestamp: new Date("2026-05-28T08:05:00Z"),
  DevicePlatform: "ios"
});

console.log(response.Data);

Update One Vital Reading

await healthRecords.updateVitalsReading("<OBSERVATION_ID>", {
  Value: 80,
  Unit: "/min"
});

Notes

  • All patient record methods require an authenticated patient context.
  • setApiKey(...) is optional and only needed when the deployment requires an API key header.
  • The connector returns backend APIResponse<T> objects; it does not unwrap data.
  • Backend business failures with isOK: false throw HCServiceError in the current client.
  • All field names in requests and responses use camelCase.
  • Request and response examples in this README use placeholders for patient data, tenant IDs, tokens, resource IDs, and API keys.
  • This README documents the public methods present in src/client.ts; internal protected and private helpers are not SDK surface.