Immunizations Service — User Stories
Service: immunizations-service Story prefix: IMMUN-US Last updated: 2026-04-18
Stories
IMMUN-US-001 — Immunization recording with contraindication guard
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Record vaccine administrations, refusals, and corrections with contraindication checking |
| Epic link | IMMUN-EPIC-01 |
| Status | In Progress |
| Priority | Must |
| Story points | 8 |
| Labels | service:immunizations, type:backend, type:api, slice:S0 |
| Components | immunization-records |
| FR references | FR-IMMUN-001, FR-IMMUN-002, FR-IMMUN-013 |
| Legacy FR refs | FR-IMM-001, FR-IMM-002, FR-IMM-013 |
| Dependencies | REG-US-001 |
User story: As a vaccination officer, when administering vaccines, I want to record doses with lot numbers and contraindication checking so that the immunization history is accurate and patient safety is protected.
Acceptance criteria (Gherkin):
- Given a registered patient with no contraindications, when
POST /v1/immunizationsis called with valid vaccineCode and administeredAt, then record is created withstatus: completedandIMMUNIZATIONS.immunization.recordedis emitted. - Given a patient with an active contraindication for a vaccine, when recording is attempted without override, then 422
CONTRAINDICATION_ACTIVEis returned. - Given a completed record with version 1, when
PUT /v1/immunizations/:idis called with correct version, then lot/site/route fields are updated and version increments to 2. - Given an erroneous record, when
PUT /v1/immunizations/:id/correctionis called, then status transitions toentered-in-errorand audit log captures actor and reason. - Given a duplicate
clientMutationId, when submitted again within 24h, then 409DUPLICATE_MUTATIONis returned (idempotent).
Technical notes:
POST /api/v1/immunizations,POST /api/v1/immunizations/refusal,PUT /api/v1/immunizations/:id,PUT /api/v1/immunizations/:id/correction- Contraindication check performed in-process from
contraindicationstable
Definition of Done:
- Unit tests for contraindication guard, optimistic locking, idempotency.
- Integration test for outbox event emission.
- Contract test for
IMMUNIZATIONS.immunization.recordedschema. - Coverage ≥ 80%.
IMMUN-US-002 — Historical record import
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Import historical immunization records from paper cards and external sources |
| Epic link | IMMUN-EPIC-01 |
| Status | In Progress |
| Priority | Must |
| Story points | 3 |
| Labels | service:immunizations, type:backend, slice:S0 |
| Components | immunization-records |
| FR references | FR-IMMUN-001 |
| Legacy FR refs | FR-IMM-001 |
| Dependencies | IMMUN-US-001 |
User story: As a data entry clerk, when registering a child with prior vaccination history, I want to import historical records so that the forecast correctly reflects doses already received.
Acceptance criteria (Gherkin):
- Given a valid historical record payload with
source.type: "paper_card", whenPOST /v1/immunizations/historicalis called, then record is created withisHistorical: trueand forecast refresh is enqueued. - Given an import that would create a duplicate dose sequence, when submitted with same
clientMutationId, then 409DUPLICATE_MUTATIONis returned.
Technical notes:
POST /api/v1/immunizations/historicaladministeredAtmay be in the past (no future-date restriction for historical records)
Definition of Done:
- Unit tests for historical import use case.
- Integration test verifying forecast refresh enqueued after import.
IMMUN-US-003 — EPI schedule forecast and defaulter tracking
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Compute EPI-schedule-driven forecasts and identify defaulters |
| Epic link | IMMUN-EPIC-02 |
| Status | In Progress |
| Priority | Must |
| Story points | 8 |
| Labels | service:immunizations, type:backend, slice:S0 |
| Components | forecast, defaulters |
| FR references | FR-IMMUN-003, FR-IMMUN-010 |
| Legacy FR refs | FR-IMM-003, FR-IMM-010 |
| Dependencies | IMMUN-US-001 |
User story: As a vaccination officer, when planning clinic sessions, I want accurate next-due dates for each patient and a defaulter list so that no child misses their vaccination window.
Acceptance criteria (Gherkin):
- Given a child who received DTP-1 on day 0, when forecast is retrieved, then DTP-2
dueDateis 28 days later andforecastStatusisdue. - Given a patient with all doses in a series completed, when forecast is retrieved, then that series
forecastStatusiscomplete. - Given a patient with
latestDatefor DTP-2 passed by 30 days, whenGET /v1/immunizations/defaultersis queried, then patient appears with correctdaysOverdue. - Given forecast refresh triggered by new record, when complete, then
IMMUNIZATIONS.forecast.updatedis emitted.
Technical notes:
GET /api/v1/immunizations/forecast/:patientIdPOST /api/v1/immunizations/forecast/:patientId/refreshGET /api/v1/immunizations/defaulters- Forecast engine: deterministic rule-based; EPI schedule loaded from config
Definition of Done:
- Unit tests covering all EPI schedule antigen series (BCG, OPV, DTP, HepB, Measles, PCV, Rota).
- Integration test:
forecast-refresh.integration.spec.ts. - Tenant isolation test for defaulter list.
- Contract test for
IMMUNIZATIONS.forecast.updatedschema.
IMMUN-US-004 — National registry sync and FHIR interoperability
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Bidirectional national registry sync and FHIR R4 Immunization/Recommendation/Evaluation |
| Epic link | IMMUN-EPIC-03 |
| Status | To Do |
| Priority | Should |
| Story points | 8 |
| Labels | service:immunizations, type:backend, type:api, slice:S2 |
| Components | registry-sync, FHIR |
| FR references | FR-IMMUN-004, FR-IMMUN-005, FR-IMMUN-011, FR-IMMUN-012 |
| Legacy FR refs | FR-IMM-004, FR-IMM-005, FR-IMM-011, FR-IMM-012 |
| Dependencies | IMMUN-US-001, INTEROP-US-001 |
User story: As an interoperability engineer, when connecting to the national immunization registry, I want records to sync bidirectionally and FHIR resources to be queryable so that the national health system has complete coverage data.
Acceptance criteria (Gherkin):
- Given a newly recorded immunization, when the nightly registry sync runs, then a
RegistrySyncJobis created and HL7 VXU dispatch is triggered via interop-service. - Given a sync job that fails, when retried up to 5 times, then job status transitions to
failedand alert fires. - Given a booked immunization, when
GET /fhir/R4/Immunization/:idis called, then a valid FHIR R4 Immunization resource is returned. - Given a patient forecast, when
GET /fhir/R4/ImmunizationRecommendation?patient=:idis called, then ImmunizationRecommendation bundle is returned with correct recommendations.
Technical notes:
- Registry sync via outbox → interop-service
- FHIR base path
/fhir/R4
Definition of Done:
- Registry sync integration test with mock interop-service.
- FHIR Immunization read test.
- Contract test for FHIR Immunization schema.
IMMUN-US-005 — Coverage reporting and digital certificates
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Population coverage analytics and signed digital vaccination certificates |
| Epic link | IMMUN-EPIC-04 |
| Status | To Do |
| Priority | Should |
| Story points | 5 |
| Labels | service:immunizations, type:backend, type:api, slice:S2 |
| Components | coverage, certificates |
| FR references | FR-IMMUN-014, FR-IMMUN-015 |
| Legacy FR refs | FR-IMM-014, FR-IMM-015 |
| Dependencies | IMMUN-US-001, IMMUN-US-003 |
User story: As a district health officer, when reviewing immunization programme performance, I want real-time coverage statistics and the ability to generate patient certificates so that programme gaps are visible and patients have portable proof of vaccination.
Acceptance criteria (Gherkin):
- Given 10 eligible infants in a facility where 8 received BCG, when
GET /v1/immunizations/coverage?vaccineCode=BCG&doseNumber=1is queried, then coverage is 80% for the facility. - Given a patient who received all scheduled doses, when
GET /v1/immunizations/certificate/:patientIdis called, then a signed JWT certificate is returned with correct dose list and QR data. - Given coverage drops below 70% for a facility-antigen combination, when alert evaluates, then
ImmunizationsCoverageDroppedalert fires.
Technical notes:
- Coverage from materialized view
coverage_by_antigen - Certificate signed RS256 with key from platform JWKS
Definition of Done:
- Coverage report unit test with known dataset.
- Certificate signature verification test.
- Alert threshold integration test.
IMMUN-ENH-US-001 — AI-assisted defaulter prioritisation
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Rank defaulters by AI risk score for outreach prioritisation |
| Epic link | IMMUN-ENH-EPIC-01 |
| Status | To Do |
| Priority | Could |
| Story points | 8 |
| Labels | service:immunizations, type:backend, slice:S3 |
| Components | ai, defaulters |
| FR references | FR-IMMUN-ENH-001 |
| Legacy FR refs | — |
| Dependencies | IMMUN-US-003, AI-GATEWAY-US-001 |
User story: As a vaccination officer, when planning community outreach, I want defaulters ranked by risk so that the highest-priority children are contacted first.
Acceptance criteria (Gherkin):
- Given a defaulter list, when AI risk scores are requested, then list is returned sorted by
riskScoreDESC. - Given ai-gateway-service unavailable, when defaulter list is requested, then FIFO fallback by
daysOverdueDESC is applied andriskScoreSource: "fallback"is included in the response. - Given an AI risk score result, when displayed to the vaccination officer, then it is labelled as "AI advisory — requires human review".
Definition of Done:
- Unit test for fallback path.
- AI advisory label verified in API response schema.