Skip to main content

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

FieldValue
Issue typeStory
SummaryRecord vaccine administrations, refusals, and corrections with contraindication checking
Epic linkIMMUN-EPIC-01
StatusIn Progress
PriorityMust
Story points8
Labelsservice:immunizations, type:backend, type:api, slice:S0
Componentsimmunization-records
FR referencesFR-IMMUN-001, FR-IMMUN-002, FR-IMMUN-013
Legacy FR refsFR-IMM-001, FR-IMM-002, FR-IMM-013
DependenciesREG-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/immunizations is called with valid vaccineCode and administeredAt, then record is created with status: completed and IMMUNIZATIONS.immunization.recorded is emitted.
  • Given a patient with an active contraindication for a vaccine, when recording is attempted without override, then 422 CONTRAINDICATION_ACTIVE is returned.
  • Given a completed record with version 1, when PUT /v1/immunizations/:id is 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/correction is called, then status transitions to entered-in-error and audit log captures actor and reason.
  • Given a duplicate clientMutationId, when submitted again within 24h, then 409 DUPLICATE_MUTATION is 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 contraindications table

Definition of Done:

  • Unit tests for contraindication guard, optimistic locking, idempotency.
  • Integration test for outbox event emission.
  • Contract test for IMMUNIZATIONS.immunization.recorded schema.
  • Coverage ≥ 80%.

IMMUN-US-002 — Historical record import

FieldValue
Issue typeStory
SummaryImport historical immunization records from paper cards and external sources
Epic linkIMMUN-EPIC-01
StatusIn Progress
PriorityMust
Story points3
Labelsservice:immunizations, type:backend, slice:S0
Componentsimmunization-records
FR referencesFR-IMMUN-001
Legacy FR refsFR-IMM-001
DependenciesIMMUN-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", when POST /v1/immunizations/historical is called, then record is created with isHistorical: true and forecast refresh is enqueued.
  • Given an import that would create a duplicate dose sequence, when submitted with same clientMutationId, then 409 DUPLICATE_MUTATION is returned.

Technical notes:

  • POST /api/v1/immunizations/historical
  • administeredAt may 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

FieldValue
Issue typeStory
SummaryCompute EPI-schedule-driven forecasts and identify defaulters
Epic linkIMMUN-EPIC-02
StatusIn Progress
PriorityMust
Story points8
Labelsservice:immunizations, type:backend, slice:S0
Componentsforecast, defaulters
FR referencesFR-IMMUN-003, FR-IMMUN-010
Legacy FR refsFR-IMM-003, FR-IMM-010
DependenciesIMMUN-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 dueDate is 28 days later and forecastStatus is due.
  • Given a patient with all doses in a series completed, when forecast is retrieved, then that series forecastStatus is complete.
  • Given a patient with latestDate for DTP-2 passed by 30 days, when GET /v1/immunizations/defaulters is queried, then patient appears with correct daysOverdue.
  • Given forecast refresh triggered by new record, when complete, then IMMUNIZATIONS.forecast.updated is emitted.

Technical notes:

  • GET /api/v1/immunizations/forecast/:patientId
  • POST /api/v1/immunizations/forecast/:patientId/refresh
  • GET /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.updated schema.

IMMUN-US-004 — National registry sync and FHIR interoperability

FieldValue
Issue typeStory
SummaryBidirectional national registry sync and FHIR R4 Immunization/Recommendation/Evaluation
Epic linkIMMUN-EPIC-03
StatusTo Do
PriorityShould
Story points8
Labelsservice:immunizations, type:backend, type:api, slice:S2
Componentsregistry-sync, FHIR
FR referencesFR-IMMUN-004, FR-IMMUN-005, FR-IMMUN-011, FR-IMMUN-012
Legacy FR refsFR-IMM-004, FR-IMM-005, FR-IMM-011, FR-IMM-012
DependenciesIMMUN-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 RegistrySyncJob is 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 failed and alert fires.
  • Given a booked immunization, when GET /fhir/R4/Immunization/:id is called, then a valid FHIR R4 Immunization resource is returned.
  • Given a patient forecast, when GET /fhir/R4/ImmunizationRecommendation?patient=:id is 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

FieldValue
Issue typeStory
SummaryPopulation coverage analytics and signed digital vaccination certificates
Epic linkIMMUN-EPIC-04
StatusTo Do
PriorityShould
Story points5
Labelsservice:immunizations, type:backend, type:api, slice:S2
Componentscoverage, certificates
FR referencesFR-IMMUN-014, FR-IMMUN-015
Legacy FR refsFR-IMM-014, FR-IMM-015
DependenciesIMMUN-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=1 is queried, then coverage is 80% for the facility.
  • Given a patient who received all scheduled doses, when GET /v1/immunizations/certificate/:patientId is 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 ImmunizationsCoverageDropped alert 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

FieldValue
Issue typeStory
SummaryRank defaulters by AI risk score for outreach prioritisation
Epic linkIMMUN-ENH-EPIC-01
StatusTo Do
PriorityCould
Story points8
Labelsservice:immunizations, type:backend, slice:S3
Componentsai, defaulters
FR referencesFR-IMMUN-ENH-001
Legacy FR refs
DependenciesIMMUN-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 riskScore DESC.
  • Given ai-gateway-service unavailable, when defaulter list is requested, then FIFO fallback by daysOverdue DESC is applied and riskScoreSource: "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.