m MiniEHR / learn
Foundry ↗About MiniEHROpen workspace ↗
LESSON 5 OF 95 min read

5. Walk through the API

Complete this only after the supervised local app is running. These curl examples are a learning walkthrough, not a recorded new test pass. Use invented data. curl -i shows response headers/status and body. GET reads; POST creates or performs an action; PUT replaces the allowed editable fields.

1. Register a patient

sh
curl -i http://127.0.0.1:5080/api/patients \
  -H 'Content-Type: application/json' \
  -d '{"medicalRecordNumber":"SYN-TUTORIAL-001","givenName":"Synthetic","familyName":"Example","birthDate":"2000-01-01"}'

Expect 201, a Location header, and a body containing a real generated id and version: 1. Copy the ID into this variable—replace the placeholder:

sh
MINIEHR_PATIENT_ID='paste-the-patient-id-here'

Repeating registration with the same normalized MRN returns 409. Use a different synthetic MRN for a second run. The app does not provide deletion/reset endpoints.

2. Read and correct demographics

sh
curl -i "http://127.0.0.1:5080/api/patients/$MINIEHR_PATIENT_ID"
curl -i 'http://127.0.0.1:5080/api/patients?page=1&pageSize=10'
curl -i -X PUT "http://127.0.0.1:5080/api/patients/$MINIEHR_PATIENT_ID" \
  -H 'Content-Type: application/json' \
  -d '{"givenName":"Synthetic","familyName":"Corrected","birthDate":"2000-01-01","version":1}'

The correction produces patient version 2. Repeating the old version 1 correction returns 409. MRN is immutable through this update API.

3. Start an encounter

sh
curl -i "http://127.0.0.1:5080/api/patients/$MINIEHR_PATIENT_ID/encounters" \
  -H 'Content-Type: application/json' \
  -d '{"startedAt":"2020-01-01T10:00:00Z"}'

Expect 201, status Open, encounter version 1. The fixed past timestamp avoids future-date failures and is deliberately synthetic. Copy the returned encounter ID; it is different from the patient ID:

sh
MINIEHR_ENCOUNTER_ID='paste-the-encounter-id-here'

4. Record vitals

sh
curl -i "http://127.0.0.1:5080/api/encounters/$MINIEHR_ENCOUNTER_ID/vitals" \
  -H 'Content-Type: application/json' \
  -d '{"measuredAt":"2020-01-01T10:05:00Z","measurements":{"systolicMmHg":120,"diastolicMmHg":80,"heartRateBpm":72,"temperatureCelsius":36.5},"encounterVersion":1}'

Expect 201 with item containing the vital record and encounterVersion: 2. Missing measurement fields become null. Copy item.id if you want to correct that record; do not confuse it with the encounter ID.

5. Write and correct a note

sh
curl -i "http://127.0.0.1:5080/api/encounters/$MINIEHR_ENCOUNTER_ID/notes" \
  -H 'Content-Type: application/json' \
  -d '{"text":"Synthetic tutorial encounter. No real patient information.","encounterVersion":2}'

Expect 201, note version 1, encounterVersion 3. Copy the note ID:

sh
MINIEHR_NOTE_ID='paste-the-note-id-here'
curl -i -X PUT "http://127.0.0.1:5080/api/encounters/$MINIEHR_ENCOUNTER_ID/notes/$MINIEHR_NOTE_ID" \
  -H 'Content-Type: application/json' \
  -d '{"text":"Corrected synthetic tutorial note.","version":1,"encounterVersion":3}'

Expect 200, note version 2, encounterVersion 4. These numbers assume exactly the preceding writes and no others. Always use actual response versions if another client/browser changes the record.

6. Close permanently

sh
curl -i "http://127.0.0.1:5080/api/encounters/$MINIEHR_ENCOUNTER_ID/close" \
  -H 'Content-Type: application/json' \
  -d '{"version":4}'

Expect 200, status Closed, a closedAt timestamp, encounter version 5. Repeating the same close returns the original closure details. Try creating another note with encounterVersion 5: expect 409 and no new note. GET notes/vitals still works. Do not attempt this closure until your synthetic edits are complete; there is no reopen operation.

Endpoint reference

Replace {id} with the relevant GUID; braces are notation, not literal URL text. Patient routes use PatientsController; clinical routes use ClinicalController.

MethodPathBody / response
GET/healthReadiness status; 200 or 503
POST/api/patientsMRN, names, birthDate → patient, 201
GET/api/patientsPage of patients
GET/api/patients/{id}Patient details
PUT/api/patients/{id}Names, birthDate, version → patient
POST/api/patients/{patientId}/encountersstartedAt → encounter, 201
GET/api/patients/{patientId}/encountersPage of encounters
GET/api/encounters/{id}Encounter details
PUT/api/encounters/{id}startedAt, version → encounter
POST/api/encounters/{id}/closeversion → closed encounter
POST/api/encounters/{encounterId}/vitalsmeasuredAt, measurements, encounterVersion → content-write response, 201
GET/api/encounters/{encounterId}/vitalsPage of vitals
GET/api/encounters/{encounterId}/vitals/{id}Vital record
PUT/api/encounters/{encounterId}/vitals/{id}measuredAt, measurements, version, encounterVersion → content-write response
POST/api/encounters/{encounterId}/notestext, encounterVersion → content-write response, 201
GET/api/encounters/{encounterId}/notesPage of notes
GET/api/encounters/{encounterId}/notes/{id}Note
PUT/api/encounters/{encounterId}/notes/{id}text, version, encounterVersion → content-write response

Patient creation's full property names are medicalRecordNumber, givenName, familyName, birthDate. Timestamp fields include an offset. Versions must be positive. Clinical corrections require the correct parent; mismatches return 404.

All collection endpoints accept page/pageSize, default 1/20, with pageSize 1–100. Response shape: { "page": 1, "pageSize": 20, "items": [...] }; there is no total count. Patients sort by normalized MRN; clinical lists by ID, not chronological time. Offset pages can shift when new records arrive.

Vitals PUT replaces the complete set of measurements. Omitting a previous value removes it, rather than preserving it as a PATCH might. Blood pressure must remain paired and at least one value must remain.

Error handling

A Problem Details body commonly has status, title, and detail; validation failures may contain an errors object. Treat status as the contract instead of relying on an exact message string. On 409, read the latest resource and decide how to reapply your intended change; do not blindly repeat stale input. A network error after submitting a write does not prove it failed—check saved state first.

Checkpoint

Explain why patient version 2 did not make the new encounter start at version 2, and why note version 2 corresponded to encounter version 4. Each entity has its own counter, and encounter content writes also advance the parent counter.

Next: Frontend and hosting.

MiniEHR learning guide · Based on this project’s source · Read on GitHub