Skip to main content

Patient Chart Service — API Contracts

Status: populated Owner: TBD Last updated: 2026-04-17 Companion: Service Template · API_PATH_CONVENTIONS · 05 API Design · FHIR-First

All responses use platform envelope { data, error?, meta? }; errors conform to ERROR_CODES.md. Authentication: Bearer JWT from identity-service. Standard headers: Authorization, X-Tenant-Id (also in JWT), Idempotency-Key on POST mutations, If-Match on PATCH using the aggregate version.

1. Product REST — Problem list

MethodPathScopeDescription
POST/v1/problemschart:problems:writeCreate a problem
GET/v1/problemschart:problems:readList problems for a patient (?patientId=&status=&category=)
GET/v1/problems/{problemId}chart:problems:readFetch one
PATCH/v1/problems/{problemId}chart:problems:writePatch attributes
POST/v1/problems/{problemId}/resolvechart:problems:writeResolve
POST/v1/problems/{problemId}/inactivatechart:problems:writeInactivate
POST/v1/problems/{problemId}/entered-in-errorchart:problems:adminEntered-in-error
POST/v1/problems/{problemId}/linkschart:problems:writeLink to encounter/note/goal/order

1.1 POST /v1/problems — request

{
"patientId": "pat_01H...",
"code": { "codings": [{ "system": "http://snomed.info/sct", "code": "44054006", "display": "Diabetes mellitus type 2" }], "text": "T2DM" },
"clinicalStatus": "active",
"verificationStatus": "confirmed",
"onsetDate": "2024-06-12",
"encounterId": "enc_01H...",
"severity": "moderate",
"notes": "Patient reports elevated fasting glucose over 6 months."
}

1.2 Response 201

{
"data": {
"id": "prb_01HZYXW...",
"version": 1,
"status": "active",
"clinicalStatus": "active",
"verificationStatus": "confirmed",
"code": { "codings": [...], "text": "T2DM" },
"onsetDate": "2024-06-12",
"createdAt": "2026-04-17T08:12:03Z",
"createdBy": { "id": "prc_01H...", "display": "Dr. Nabil" }
}
}

Errors: 422 CHART_DUPLICATE_PROBLEM, 422 CHART_CROSS_TENANT_REFERENCE, 409 CHART_INVALID_VERSION, 401/403, 404 PATIENT_NOT_FOUND.

2. Product REST — Allergies

MethodPathScope
POST/v1/allergieschart:allergies:write
GET/v1/allergieschart:allergies:read
GET/v1/allergies/{allergyId}chart:allergies:read
PATCH/v1/allergies/{allergyId}chart:allergies:write
POST/v1/allergies/{allergyId}/inactivatechart:allergies:write
POST/v1/allergies/{allergyId}/entered-in-errorchart:allergies:admin
POST/v1/allergies/assertions/nkachart:allergies:write
POST/v1/allergies/assertions/nkdachart:allergies:write
GET/v1/allergies/advisorychart:allergies:advisory (internal)

2.1 Allergy create

{
"patientId": "pat_01H...",
"substance": { "codings": [{ "system": "http://www.nlm.nih.gov/research/umls/rxnorm", "code": "11124", "display": "Penicillin" }] },
"categories": ["medication"],
"clinicalStatus": "active",
"verificationStatus": "confirmed",
"reactions": [
{ "manifestation": [{ "codings": [{ "system": "http://snomed.info/sct", "code": "247472004", "display": "Hives" }] }], "severity": "moderate" }
]
}

2.2 Advisory (internal, called by orders/medication)

GET /v1/allergies/advisory?patientId=pat_01H...&rxnormCode=11124

{ "data": { "matches": [{ "allergyId": "alg_...", "severity": "severe", "substance": { ... }, "matchType": "coded" }], "highestSeverity": "severe" } }

3. Product REST — Vitals

MethodPathScope
POST/v1/vitalschart:vitals:write
GET/v1/vitalschart:vitals:read?patientId=&code=&from=&to=&limit=
GET/v1/vitals/{vitalsSetId}chart:vitals:read
POST/v1/vitals/{vitalsSetId}/correctionschart:vitals:write
GET/v1/vitals/trendchart:vitals:read?patientId=&code=&range=7d

3.1 Vitals record

{
"patientId": "pat_01H...",
"recordedAt": "2026-04-17T08:45:00Z",
"encounterId": "enc_01H...",
"collectionLocationId": "loc_01H...",
"method": "manual",
"measurements": [
{ "code": { "codings": [{ "system": "http://loinc.org", "code": "8480-6" }] }, "value": 142, "unit": "mm[Hg]" },
{ "code": { "codings": [{ "system": "http://loinc.org", "code": "8462-4" }] }, "value": 92, "unit": "mm[Hg]" },
{ "code": { "codings": [{ "system": "http://loinc.org", "code": "8867-4" }] }, "value": 88, "unit": "/min" }
]
}

Response includes per-measurement abnormalFlag when out of configured ranges.

4. Product REST — Clinical notes

MethodPathScope
POST/v1/clinical-noteschart:notes:write
GET/v1/clinical-noteschart:notes:read?patientId=&type=&author=&status=&from=&to=
GET/v1/clinical-notes/{noteId}chart:notes:read
PATCH/v1/clinical-notes/{noteId}chart:notes:write (draft only)
POST/v1/clinical-notes/{noteId}/signchart:notes:sign
POST/v1/clinical-notes/{noteId}/cosign-requestchart:notes:sign
POST/v1/clinical-notes/{noteId}/cosignchart:notes:cosign
POST/v1/clinical-notes/{noteId}/addendachart:notes:write
POST/v1/clinical-notes/{noteId}/entered-in-errorchart:notes:admin
POST/v1/clinical-notes/{noteId}/ai-acceptedchart:notes:write
POST/v1/clinical-notes/{noteId}/readchart:notes:read (idempotent)

4.1 Note create

{
"patientId": "pat_01H...",
"encounterId": "enc_01H...",
"templateId": "soap.v1",
"sections": [
{ "key": "subjective", "text": "Patient presents with ..." },
{ "key": "objective", "text": "BP 142/92 ..." },
{ "key": "assessment", "text": "Uncontrolled T2DM." },
{ "key": "plan", "text": "Increase metformin; labs in 1 week." }
]
}

4.2 Sign

POST /v1/clinical-notes/{noteId}/sign with { "version": 3, "signatureMethod": "password" } — response either status=signed immediately or status=pending_cosign with pendingCosigners[].

5. Product REST — Chart composition

MethodPathScopeNotes
GET/v1/chart/{patientId}/bannerchart:readHeader payload
GET/v1/chart/{patientId}/summarychart:read?widgets=problems,allergies,vitals,meds,labs,imm,care-plan,orders
GET/v1/chart/{patientId}/timelinechart:read?cursor=&types=notes,orders,results,meds,vitals&from=&to=
POST/v1/chart/{patientId}/snapshot/exportchart:export`{ format: "pdf"
POST/v1/chart/{patientId}/breakglasschart:breakglass{ reason, durationMinutes }

5.1 Summary response (abbreviated)

{
"data": {
"patient": { "id": "...", "displayName": "...", "dob": "...", "sex": "..." },
"widgets": {
"problems": { "count": 4, "items": [{ "id": "prb_...", "display": "T2DM", "status": "active" }] },
"allergies": { "count": 2, "items": [...] },
"vitals": { "latestSet": { ... } },
"meds": { "count": 3, "items": [...] },
"labs": { "items": [...] },
"imm": { "items": [...] },
"care-plan": { "active": [...] }
},
"provenance": { "problems": "patient-chart", "meds": "medication-service", "labs": "laboratory-service" }
}
}

5.2 Timeline — cursor pagination

Request: GET /v1/chart/{patientId}/timeline?types=notes,vitals,meds&limit=50. Response has data[] (sorted descending by occurredAt) and meta.nextCursor (opaque base64 with {occurredAt, tiebreaker}). Follow-up: ?cursor=...&limit=50.

6. FHIR R4 read surface

Served by patient-chart-service at /fhir/R4/*, fronted by interop-service / fhir-gateway.

ResourceReadSearch params (at minimum)
ConditionGET /fhir/R4/Condition/{id}, GET /fhir/R4/Condition?...patient, clinical-status, verification-status, category, onset-date, code, _sort=-onset-date, _count
AllergyIntoleranceGET /fhir/R4/AllergyIntolerance?...patient, clinical-status, verification-status, category, code, _lastUpdated
Observation (vital-signs)GET /fhir/R4/Observation?...patient, category=vital-signs, code, date, _sort=-date, _count
CompositionGET /fhir/R4/Composition?...subject, type, status, date, _sort=-date
DocumentReferenceGET /fhir/R4/DocumentReference?...subject, type, status, period, _sort=-date

Writes via FHIR HTTP are out of scope for this release; creation/update happens only via /v1/* REST.

7. Error schema

{
"error": {
"code": "CHART_NKA_CONFLICT",
"message": "Active NKA record exists; cannot add substance allergy until NKA is inactivated.",
"details": { "nkaAllergyId": "alg_01H..." },
"correlationId": "c3f0..."
}
}

8. Rate limits

Endpoint classLimit
Mutations (POST/PATCH)60/min per user
Reads (aggregate)600/min per user
Advisory (internal)3000/min per service principal

9. Pagination

All list endpoints support cursor pagination: ?cursor= + ?limit= (max 200); response includes meta.nextCursor. Offset pagination is not supported (chart timelines are unbounded).

10. Open Questions

  • Should /v1/chart/{patientId}/timeline support server-side configured default types per role?
  • FHIR $everything operation: MVP out of scope; to be added when interop-service merges the composition bundle.