Skip to main content

Immunizations Service — API Contracts

Status: populated Owner: TBD Last updated: 2026-04-18 Companion: Service Template

1. Base URL

/api/v1 (internal Kong routing); FHIR base: /fhir/R4

All requests require:

  • Authorization: Bearer <JWT> (Keycloak)
  • X-Tenant-ID: <tenantId>
  • Module entitlement: clinical.immunizations

2. Immunizations

POST /v1/immunizations

Record a vaccine administration.

Roles: CLINICIAN, NURSE, VACCINATION_OFFICER

Request body:

{
"patientId": "pat_01J...",
"vaccineCode": { "system": "http://hl7.org/fhir/sid/cvx", "code": "120", "display": "DTaP-Hib-IPV" },
"status": "completed",
"doseNumber": 1,
"seriesDoses": 3,
"lotNumber": "LOT-2024-0041",
"expiryDate": "2025-06-01",
"administeredAt": "2026-04-15T08:30:00Z",
"site": { "system": "http://snomed.info/sct", "code": "368209003", "display": "Right arm" },
"route": { "system": "http://snomed.info/sct", "code": "78421000", "display": "Intramuscular" },
"doseQuantity": { "value": 0.5, "unit": "mL" },
"performerId": "usr_01J...",
"facilityId": "fac_01J...",
"encounterId": "enc_01J...",
"notes": "Patient tolerated well",
"clientMutationId": "uuid-for-idempotency"
}

Response 201:

{
"immunizationId": "imm_01J...",
"patientId": "pat_01J...",
"status": "completed",
"vaccineCode": { "system": "http://hl7.org/fhir/sid/cvx", "code": "120", "display": "DTaP-Hib-IPV" },
"doseNumber": 1,
"administeredAt": "2026-04-15T08:30:00Z",
"version": 1,
"createdAt": "2026-04-15T08:32:10Z"
}

Error codes:

CodeHTTPCondition
PATIENT_NOT_FOUND404patientId not found
PATIENT_DECEASED422Patient vital status is deceased
INVALID_VACCINE_CODE422vaccineCode not in EPI vocabulary
CONTRAINDICATION_ACTIVE422Active contraindication exists
DUPLICATE_MUTATION409clientMutationId already processed

POST /v1/immunizations/refusal

Record a patient refusal.

Roles: CLINICIAN, NURSE, VACCINATION_OFFICER

Request body:

{
"patientId": "pat_01J...",
"vaccineCode": { "system": "http://hl7.org/fhir/sid/cvx", "code": "120", "display": "DTaP-Hib-IPV" },
"status": "not-done",
"statusReason": { "system": "http://terminology.hl7.org/CodeSystem/v3-ActReason", "code": "PATOBJ", "display": "Patient objection" },
"recordedAt": "2026-04-15T08:30:00Z",
"performerId": "usr_01J...",
"facilityId": "fac_01J..."
}

Response 201: Same shape as POST /v1/immunizations, status not-done.


GET /v1/immunizations/:id

Get a single immunization record.

Roles: CLINICIAN, NURSE, VACCINATION_OFFICER, PATIENT (own records only)

Response 200:

{
"immunizationId": "imm_01J...",
"patientId": "pat_01J...",
"status": "completed",
"vaccineCode": { "system": "http://hl7.org/fhir/sid/cvx", "code": "120", "display": "DTaP-Hib-IPV" },
"doseNumber": 1,
"seriesDoses": 3,
"lotNumber": "LOT-2024-0041",
"administeredAt": "2026-04-15T08:30:00Z",
"site": { "system": "http://snomed.info/sct", "code": "368209003", "display": "Right arm" },
"route": { "system": "http://snomed.info/sct", "code": "78421000", "display": "Intramuscular" },
"doseQuantity": { "value": 0.5, "unit": "mL" },
"performerId": "usr_01J...",
"facilityId": "fac_01J...",
"encounterId": "enc_01J...",
"version": 1,
"createdAt": "2026-04-15T08:32:10Z",
"updatedAt": "2026-04-15T08:32:10Z"
}

GET /v1/immunizations

List immunization records. Supports filtering.

Roles: CLINICIAN, NURSE, VACCINATION_OFFICER, PATIENT (own)

Query params: patientId (required for non-admin), status, vaccineCode, from, to, facilityId, page, pageSize

Response 200:

{
"items": [ /* ImmunizationRecord[] */ ],
"total": 12,
"page": 1,
"pageSize": 20
}

PUT /v1/immunizations/:id

Amend an existing record (lot, site, route, notes only).

Roles: CLINICIAN, VACCINATION_OFFICER

Request body:

{
"lotNumber": "LOT-2024-0042",
"notes": "Corrected lot number",
"version": 1
}

Response 200: Updated ImmunizationRecord.


PUT /v1/immunizations/:id/correction

Mark a record as entered-in-error.

Roles: CLINICIAN, ADMIN

Request body:

{
"reason": "Recorded against wrong patient",
"version": 1
}

Response 200: { "immunizationId": "...", "status": "entered-in-error" }


3. Forecast

GET /v1/immunizations/forecast/:patientId

Get the current immunization forecast (ImmunizationRecommendation).

Roles: CLINICIAN, NURSE, VACCINATION_OFFICER, PATIENT (own)

Response 200:

{
"forecastId": "fcst_01J...",
"patientId": "pat_01J...",
"calculatedAt": "2026-04-15T09:00:00Z",
"recommendations": [
{
"vaccineCode": { "system": "http://hl7.org/fhir/sid/cvx", "code": "140", "display": "Influenza" },
"doseNumber": 2,
"seriesDoses": 2,
"forecastStatus": "due",
"dueDate": "2026-05-15",
"earliestDate": "2026-04-30",
"latestDate": "2026-06-30",
"series": "Influenza annual"
}
]
}

POST /v1/immunizations/forecast/:patientId/refresh

Trigger an immediate forecast recalculation.

Roles: CLINICIAN, ADMIN

Response 202: { "message": "Forecast refresh enqueued" }


4. Contraindications

POST /v1/immunizations/contraindications

Add a contraindication for a patient-antigen pair.

Roles: CLINICIAN

Request body:

{
"patientId": "pat_01J...",
"vaccineCode": { "system": "http://hl7.org/fhir/sid/cvx", "code": "03", "display": "MMR" },
"reason": { "system": "http://snomed.info/sct", "code": "416098002", "display": "Allergy to egg protein" },
"recordedAt": "2026-04-15T08:30:00Z",
"performerId": "usr_01J..."
}

Response 201: { "contraindicationId": "cind_01J...", ... }


DELETE /v1/immunizations/contraindications/:id

Remove (inactivate) a contraindication.

Roles: CLINICIAN

Response 204


5. Defaulters

GET /v1/immunizations/defaulters

List patients with overdue doses.

Roles: VACCINATION_OFFICER, ADMIN

Query params: facilityId, vaccineCode, daysOverdue (min), page, pageSize

Response 200:

{
"items": [
{
"patientId": "pat_01J...",
"patientName": "...",
"vaccineCode": { "code": "120", "display": "DTaP-Hib-IPV" },
"doseNumber": 2,
"dueDate": "2026-02-01",
"daysOverdue": 75
}
],
"total": 34,
"page": 1,
"pageSize": 20
}

6. Coverage Reporting

GET /v1/immunizations/coverage

Get population coverage statistics.

Roles: ADMIN, ANALYST

Query params: facilityId, vaccineCode, from, to, ageGroup

Response 200:

{
"vaccineCode": { "code": "120", "display": "DTaP-Hib-IPV" },
"doseNumber": 1,
"eligiblePopulation": 850,
"vaccinatedCount": 702,
"coveragePercent": 82.6,
"facilityId": "fac_01J...",
"period": { "from": "2026-01-01", "to": "2026-03-31" }
}

7. Digital Certificates

GET /v1/immunizations/certificate/:patientId

Generate a signed vaccination certificate for the patient.

Roles: CLINICIAN, PATIENT (own)

Response 200:

{
"patientId": "pat_01J...",
"issuedAt": "2026-04-18T10:00:00Z",
"issuer": "Ghasi eHealth Platform",
"certificate": "<signed JWT>",
"qrData": "<base64-encoded QR payload>"
}

8. FHIR R4 Surface

FHIR resourceOperationPath
ImmunizationreadGET /fhir/R4/Immunization/:id
ImmunizationsearchGET /fhir/R4/Immunization?patient=:id&status=&date=
ImmunizationRecommendationreadGET /fhir/R4/ImmunizationRecommendation/:id
ImmunizationRecommendationsearchGET /fhir/R4/ImmunizationRecommendation?patient=:id
ImmunizationEvaluationreadGET /fhir/R4/ImmunizationEvaluation/:id
ImmunizationEvaluationsearchGET /fhir/R4/ImmunizationEvaluation?patient=:id

All FHIR responses use Content-Type: application/fhir+json.


9. Historical Import

POST /v1/immunizations/historical

Import a historical immunization record.

Roles: CLINICIAN, DATA_MIGRATION

Request body: Same shape as POST /v1/immunizations with additional source field ({ "type": "paper_card" | "external_emr" | "national_registry", "reference": "..." }).

Response 201: ImmunizationRecord with status: "completed" and isHistorical: true.