Patients
Create, read, update, transfer, and delete patient records.
Patient records are identified by their file number (MRN), unique within a clinic.
| Method | Path | Permission |
|---|---|---|
GET | /api/patients | patient:read |
GET | /api/patients/{fileNumber} | patient:read |
POST | /api/patients | patient:write |
PUT | /api/patients/{fileNumber} | patient:write |
POST | /api/patients/{fileNumber}/transfer | patient:write |
POST | /api/patients/{fileNumber}/labs | lab:write |
DELETE | /api/patients/{fileNumber}/labs | lab:write |
DELETE | /api/patients/{fileNumber} | patient:delete |
Role-based scoping
Two roles see a filtered view, enforced server-side: doctors (without an additional elevated role) see only patients whose primary provider they are; reception sees demographics only — clinical fields (medications, problems, labs, encounters, vitals) are omitted from their responses and ignored in their writes.
Endpoints
List patients
curl -b cookies.txt http://localhost:4000/api/patientsconst patients = await fetch('http://localhost:4000/api/patients', {
credentials: 'include',
}).then((r) => r.json());Returns an array of full patient objects (see below).
Get a patient
curl -b cookies.txt http://localhost:4000/api/patients/1042{
"fileNumber": "1042",
"name": "Hodan Warsame",
"age": 58,
"sex": "F",
"pcp": "Dr. Amina Yusuf",
"primaryProviderId": "usr_abc123",
"status": "active",
"initials": "HW",
"phone": "+252 61 555 0142",
"bloodType": "O+",
"alerts": ["Penicillin allergy"],
"vitals": { "bp": "128/82", "hr": "74", "temp": "36.8", "spo2": "97", "takenAt": "2026-06-08 09:30" },
"vitalsTrend": { "label": "BP (systolic)", "unit": "mmHg", "points": [134, 131, 128] },
"allergies": [{ "substance": "Penicillin", "reaction": "Rash", "severity": "moderate" }],
"medications": [{ "name": "Metformin", "dose": "500 mg", "frequency": "2× daily" }],
"problems": [{ "label": "Type 2 diabetes", "since": "2019" }],
"labs": [{ "name": "HbA1c", "value": "6.9%", "flag": "high", "takenAt": "2026-05-30" }],
"labTrend": { "label": "HbA1c", "unit": "%", "points": [7.4, 7.1, 6.9] },
"encounters": [
{ "date": "2026-05-30", "type": "Follow-up", "provider": "Dr. Amina Yusuf", "summary": "Diabetes review; HbA1c improving." }
]
}404 if the file number doesn't exist in your active clinic.
Create a patient
POST /api/patients with the full record. Returns 201 and the created patient.
| Field | Type | Notes |
|---|---|---|
fileNumber | string | Digits only; unique per clinic. Auto-assigned when empty |
name | string | Required |
age | number | 0–150; defaults to 0 when omitted |
sex | "M" | "F" | Defaults to "M" when omitted |
status | "active" | "inpatient" | "discharged" | Defaults to "active" |
initials | string | 1–4 chars; derived from the name when omitted |
source | enum | manual (default) | ai — ai rows show an "Added by AI" badge |
pcp | string | Display name of the primary care provider |
primaryProviderId | string | null | User id of the responsible clinician |
phone | string | Contact number (demographic; visible to reception) |
bloodType | A+ | A- | B+ | B- | AB+ | AB- | O+ | O- | "" | Clinical PHI — redacted for reception |
alerts | string[] | Prominent warnings |
allergies | array | { substance, reaction, severity: mild|moderate|severe } |
medications | array | { name, dose, frequency } |
problems | array | { label, since } |
labs | array | { name, value, flag: normal|high|low|critical, takenAt } |
encounters | array | { date, type, provider, summary } |
vitals | object | { bp, hr, temp, spo2, takenAt } |
vitalsTrend, labTrend | object | { label, unit, points: number[] } |
Only name is strictly required; the rest accept safe defaults so sparse records
(e.g. from an AI import) still save. The import
path stamps source: "ai", and a missing fileNumber is auto-assigned.
Update a patient
PUT /api/patients/{fileNumber} takes the same full payload as create (the app
sends the whole record on edit) and returns the updated patient.
Transfer a patient
Reassign the patient to another clinician in the same clinic:
curl -b cookies.txt -X POST http://localhost:4000/api/patients/1042/transfer \
-H "Content-Type: application/json" \
-d '{ "providerId": "usr_def456" }'400 if the provider isn't a member of the clinic; returns the updated patient.
Append lab results
POST /api/patients/{fileNumber}/labs appends one or more analysis results to the
record without replacing anything else — unlike the full PUT update. It is gated
by the dedicated lab:write permission (the Lab role and full clinicians), so lab
staff can submit results without patient-edit rights.
| Field | Type | Notes |
|---|---|---|
labs | array | Required. 1–50 entries |
labs[].name | string | Required. Test name, e.g. "Hemoglobin" |
labs[].value | string | Required. e.g. "14.2 g/dL" |
labs[].flag | enum | Required. normal | high | low | critical |
labs[].takenAt | string | Required. Display date, e.g. "Jun 11, 2026" |
curl -b cookies.txt -X POST http://localhost:4000/api/patients/1042/labs \
-H "Content-Type: application/json" \
-d '{ "labs": [{ "name": "Hemoglobin", "value": "14.2 g/dL", "flag": "normal", "takenAt": "Jun 11, 2026" }] }'Returns 201 with the full updated patient; 404 if the file number doesn't exist.
New results are appended after the existing ones.
Delete a lab result
DELETE /api/patients/{fileNumber}/labs removes a single result from a patient's
record. The lab has no id on the client, so it's identified by its fields.
| Field | Type | Notes |
|---|---|---|
name | string | Required. Test name |
value | string | Required. The recorded value |
takenAt | string | Required. The recorded date |
curl -b cookies.txt -X DELETE http://localhost:4000/api/patients/1042/labs \
-H "Content-Type: application/json" \
-d '{ "name": "Hemoglobin", "value": "14.2 g/dL", "takenAt": "Jun 11, 2026" }'Returns 200 with the updated patient; 404 if the file number doesn't exist.
Gated by lab:write (like appending), so lab staff can correct their own entries.
Delete a patient
curl -b cookies.txt -X DELETE http://localhost:4000/api/patients/1042Returns 204. Requires patient:delete (owner or admin).
Every mutation is written to the activity log and notifies the clinic's members in real time.