Patients

mb.patients. CRUD over patient records, list, listPatientFlows, and the getConnections hydrate helper.

A patient is the org-scoped record keyed by your patient_id. The SDK gives you CRUD, two list endpoints, and a convenience helper that hydrates connections into full EHR catalog entries.

MethodHTTP
mb.patients.create(input)POST /patients
mb.patients.retrieve(id)GET /patients/{id}
mb.patients.update(id, input)PUT /patients/{id}
mb.patients.delete(id)DELETE /patients/{id}
mb.patients.list(params?)GET /patients
mb.patients.listPatientFlows(patientId, params?)GET /patients/{id}/patient-flows
mb.patients.getConnections(patientId, { hydrate: true })Composite

Create

patient_id is yours to choose. Use a stable internal identifier (chart id, member id, internal user id). Reserved prefixes (pat_, pf_, conn_, fhirsrc_, wh_, evt_) are rejected.

server/routes/create-patient.ts
import { mb } from "../medblocks";

const patient = await mb.patients.create({
  patient_id: "user_42",
  email: "jane@example.com",
  name: "Jane Doe",
  metadata: { signup_source: "intake" },
});

A 409 from a duplicate patient_id arrives as MedblocksConflictError with code: "external_id_already_exists".

You can skip create entirely and let mb.patientFlow.init upsert the patient on the fly.

Retrieve

retrieve returns a PatientDetail. The patient plus a connections[] array including failed-auth attempts. Use this when you need the current connection picture.

server/routes/get-patient.ts
const detail = await mb.patients.retrieve("user_42");
console.log(detail.connections);

A 404 arrives as MedblocksNotFoundError.

Update

update is a full replace. Omitted optional fields are cleared on the server (emailnull, namenull, metadata{}). The body’s patient_id must match the URL id.

server/routes/update-patient.ts
await mb.patients.update("user_42", {
  patient_id: "user_42",
  email: "new@example.com",
  name: "Jane Q. Doe",
});

Delete

Cascades to remove every connection and grant the patient had. Returns a tombstone for confirmation.

server/routes/delete-patient.ts
const tombstone = await mb.patients.delete("user_42");
// { id: "user_42", resource_type: "patient", deleted: true }

List

Cursor-paginated. The return is a ListPromise<Patient>. await it for the first page, or call .autoPagingIterator() for full traversal.

server/routes/list-patients.ts
const firstPage = await mb.patients.list({ limit: 50 });
for (const patient of firstPage.data) {
  console.log(patient.id);
}

for await (const patient of mb.patients.list({ limit: 100 }).autoPagingIterator()) {
  console.log(patient.id);
}

See Pagination for next_cursor, has_more, and the iterator contract.

List Patient Flows For One Patient

History for one patient. Every flow they have run, in reverse chronological order. Useful for showing prior attempts in a dashboard or a retry prompt.

server/routes/patient-flow-history.ts
const page = await mb.patients.listPatientFlows("user_42", { limit: 50 });
for (const flow of page.data) {
  console.log(flow.id, flow.status, flow.connections.length);
}

getConnections. Hydrated Connections

The connections[] returned by retrieve carries the EHR id (connection_id) but not the EHR’s display name, logo, or portal URL. getConnections does that join for you: one patient retrieve plus one catalog lookup per unique connection_id, dedup’d within the call.

server/routes/connected-facilities.ts
const facilities = await mb.patients.getConnections("user_42", { hydrate: true });

for (const f of facilities) {
  console.log(f.name, f.logo_url, f.fhir_base_url);
}

Failed-auth connections are skipped (they have id: null and never yield FHIR data). The hydrate: true opt-in is currently mandatory; a future release will switch to a single server-side ?expand=connections.source query without changing the SDK signature.

Errors

SubclassWhen
MedblocksInvalidRequestErrorpatient_id uses a reserved prefix, exceeds 200 chars, or email is malformed.
MedblocksNotFoundErrorThe patient does not exist (retrieve, update, del, listPatientFlows).
MedblocksConflictErrorexternal_id_already_exists on create.

See Errors.