Skip to main content

Ghasi e-Prescribing Gateway Service — User Stories

Service: ghasi-eprescribing-gateway-service Story prefix: EPRX-US Last updated: 2026-04-18

Stories

EPRX-US-001 — Deploy gateway with gepgw_* schema and Kong routes

FieldValue
Issue typeStory
SummaryGateway infrastructure deployed: Postgres schema, Kong routes, Keycloak clients
Epic linkEPRX-EPIC-01
StatusDone
PriorityMust
Story points8
Labelsservice:eprescribing-gateway, type:infra, slice:S0
Componentsghasi-eprescribing-gateway-service
FR referencesFR-EPRX-001
Legacy FR refsFR-RX-001
DependenciesIDENT-US-001, TENANT-US-001

User story: As a platform engineer, when preparing for the first-party pilot, I want the gateway deployed with its Postgres schema, Kong routes, and Keycloak client registrations so that EHR and Pharmacy can begin sending requests.

Acceptance criteria (Gherkin):

  • Given the service is deployed, when I call GET /health/ready, then { status: ok, db: ok, nats: ok } is returned.
  • Given Kong is configured, when I POST to /v1/ghasi-e-prescribing-gateway/fhir/MedicationRequest, then the request routes to the service.
  • Given gepgw_* migrations run, when I inspect the database, then all tables exist with RLS enabled.

Technical notes:

  • Tables: gepgw_medication_requests, gepgw_medication_dispenses, gepgw_subscriptions, gepgw_idempotency_records, gepgw_outbox.
  • Module entitlement key: ehr.ghasi_eprescribing_gateway.

Definition of Done: Standard DoD applies. CI gate: tenant-isolation spec green.


EPRX-US-002 — Configure fhir-gateway interop proxy path

FieldValue
Issue typeStory
Summaryfhir-gateway proxies /fhir/R4/interop/ghasi-eprescribing/* to gateway
Epic linkEPRX-EPIC-01
StatusDone
PriorityMust
Story points3
Labelsservice:eprescribing-gateway, type:infra, slice:S0
Componentsfhir-gateway, ghasi-eprescribing-gateway-service
FR referencesFR-EPRX-001
Legacy FR refs
DependenciesEPRX-US-001, INTEROP-US-001

User story: As orders-service (EHR backend), when I submit a MedicationRequest to the gateway, I want to use /fhir/R4/interop/ghasi-eprescribing/MedicationRequest so that centralized routing policy and audit apply consistently.

Acceptance criteria (Gherkin):

  • Given fhir-gateway is configured with the interop routing table, when orders-service POSTs to /fhir/R4/interop/ghasi-eprescribing/MedicationRequest, then the request is forwarded to e-prescribing-gateway and the 201 response is returned.
  • Given pharmacy-service POSTs to /fhir/R4/interop/ghasi-eprescribing/MedicationDispense, then same forwarding applies.

Definition of Done: Standard DoD applies. Integration test verifies proxy routing.


EPRX-US-003 — EHR creates MedicationRequest (idempotent, ETag, correlation ID)

FieldValue
Issue typeStory
SummaryEHR backend creates MR; gateway returns 201 with ETag and prescription business ID
Epic linkEPRX-EPIC-02
StatusIn Progress
PriorityMust
Story points8
Labelsservice:eprescribing-gateway, type:backend, type:api, slice:S1
Componentsghasi-eprescribing-gateway-service, orders-service
FR referencesFR-EPRX-002, FR-EPRX-004, FR-EPRX-006
Legacy FR refsFR-RX-001, FR-RX-004, FR-RX-006
DependenciesEPRX-US-001

User story: As orders-service (EHR backend), when a clinician activates a medication order, I want to POST a FHIR R4 MedicationRequest to the gateway so that the prescription is durably persisted with a prescription business ID and ETag for downstream pharmacy notification.

Acceptance criteria (Gherkin):

  • Given a valid ehr-backend JWT and Idempotency-Key, when I POST a conformant FHIR MR, then 201 is returned with ETag, Location, and X-Prescription-Business-Id headers.
  • Given the same Idempotency-Key and payload are retried, when I POST again, then 201 is returned with the original resource (no duplicate).
  • Given a pharmacy-backend JWT, when I POST a MR, then 403 FORBIDDEN_WRITE_PERSONA is returned.
  • Given a valid create, then eprescribing.medication_request.created.v1 is emitted to NATS within 5 s.
  • Given an audit log query, then the eprescribing.mr.created audit record contains tenantId, actorId, prescriptionBusinessId.

Technical notes:

  • Persistence: gepgw_medication_requests.
  • NATS subject: eprescribing.medication_request.created.v1.
  • Golden FHIR fixture: test/fixtures/fhir/medication-request-valid.json.

Definition of Done: Standard DoD applies. AC-RX-001 satisfied.


EPRX-US-004 — FHIR profile validation on MR (IG package)

FieldValue
Issue typeStory
SummaryGateway validates MR against tenant-pinned IG; returns OperationOutcome on failure
Epic linkEPRX-EPIC-02
StatusIn Progress
PriorityMust
Story points5
Labelsservice:eprescribing-gateway, type:backend, slice:S1, type:fhir
Componentsghasi-eprescribing-gateway-service
FR referencesFR-EPRX-003
Legacy FR refsFR-RX-003
DependenciesEPRX-US-003

User story: As the gateway, when a MedicationRequest arrives, I want to validate it against the tenant's pinned IG profile so that only conformant prescriptions are persisted.

Acceptance criteria (Gherkin):

  • Given a MR missing a required field (e.g., status), when I POST it, then 422 with FHIR OperationOutcome containing actionable diagnostics is returned.
  • Given a valid MR per the pinned IG, when I POST it, then 201 is returned.
  • Given the validator is unavailable, when I POST, then 503 VALIDATOR_UNAVAILABLE is returned (strict mode) or the request proceeds with a warning (permissive mode per tenant config).

Technical notes:

  • Phase 1: Zod schema validation. Phase 2: HAPI FHIR validator.
  • NFR-RX-004: 100% of golden FHIR fixtures pass validator in CI.

Definition of Done: Standard DoD applies. Golden fixtures CI gate green (NFR-RX-004).


EPRX-US-005 — ETag concurrency on MR update

FieldValue
Issue typeStory
SummaryMR update requires If-Match ETag; stale ETag returns 412
Epic linkEPRX-EPIC-02
StatusIn Progress
PriorityMust
Story points5
Labelsservice:eprescribing-gateway, type:backend, slice:S1
Componentsghasi-eprescribing-gateway-service
FR referencesFR-EPRX-005
Legacy FR refsFR-RX-005
DependenciesEPRX-US-003

User story: As orders-service, when I need to update a prescription (e.g., dosage change), I want ETag-based concurrency enforcement so that I cannot overwrite a prescription that has already been updated by another process.

Acceptance criteria (Gherkin):

  • Given a freshly fetched ETag, when I PUT the MR with If-Match: <etag>, then 200 and a new ETag are returned.
  • Given a stale ETag (another actor updated in between), when I PUT, then 412 PRECONDITION_FAILED with current resource in body is returned.
  • Given a PUT without If-Match, then 428 PRECONDITION_REQUIRED is returned.

Technical notes:

  • ETag = W/"sha256(canonical_fhir_resource)".
  • Client recovery path documented in INTEGRATOR_GUIDE.md.

Definition of Done: Standard DoD applies. AC-RX-004 satisfied.


EPRX-US-006 — EHR cancels a prescription

FieldValue
Issue typeStory
SummaryEHR backend cancels MR; gateway updates status and notifies pharmacy
Epic linkEPRX-EPIC-02
StatusTo Do
PriorityMust
Story points3
Labelsservice:eprescribing-gateway, type:backend, slice:S1
Componentsghasi-eprescribing-gateway-service
FR referencesFR-EPRX-002
Legacy FR refsFR-RX-001
DependenciesEPRX-US-003, EPRX-US-009

User story: As orders-service, when a clinician cancels an active prescription, I want to update the MR status to cancelled so that the pharmacy is notified and stops processing the prescription.

Acceptance criteria (Gherkin):

  • Given an active MR, when I PATCH with status: cancelled and valid If-Match, then 200 is returned and the pharmacy Subscription fires with the updated status.
  • Given pharmacy tries to dispense a cancelled MR, then gateway returns 422 PRESCRIPTION_CANCELLED.

Technical notes:

  • Emits eprescribing.medication_request.cancelled.v1.

Definition of Done: Standard DoD applies.


EPRX-US-007 — Pharmacy creates MedicationDispense

FieldValue
Issue typeStory
SummaryPharmacy backend records fulfillment; gateway validates and correlates to prescription
Epic linkEPRX-EPIC-03
StatusIn Progress
PriorityMust
Story points8
Labelsservice:eprescribing-gateway, type:backend, type:api, slice:S1
Componentsghasi-eprescribing-gateway-service, pharmacy-service
FR referencesFR-EPRX-007
Legacy FR refsFR-RX-002
DependenciesEPRX-US-003, EPRX-US-009

User story: As pharmacy-service, when a pharmacist dispenses a medication, I want to POST a FHIR MedicationDispense to the gateway so that the dispense is recorded, correlated to the prescription, and the EHR is notified.

Acceptance criteria (Gherkin):

  • Given a valid pharmacy-backend JWT and a known MedicationRequest ID, when I POST a conformant FHIR MD, then 201 is returned with ETag and X-Prescription-Business-Id.
  • Given an ehr-backend JWT, when I POST a MD, then 403 FORBIDDEN_WRITE_PERSONA.
  • Given a MD referencing an unknown MR, when I POST, then 422 PRESCRIPTION_NOT_FOUND.
  • Given a valid create, then eprescribing.medication_dispense.created.v1 is emitted and the EHR Subscription fires within SLO.

Technical notes:

  • AC-RX-001 (full end-to-end) and AC-RX-003 (profile validation on MD) satisfied in this story.

Definition of Done: Standard DoD applies.


EPRX-US-008 — Partial fill support

FieldValue
Issue typeStory
SummaryPharmacy records partial dispense; gateway allows while tracking remaining quantity
Epic linkEPRX-EPIC-03
StatusTo Do
PriorityShould
Story points3
Labelsservice:eprescribing-gateway, type:backend, slice:S1
Componentsghasi-eprescribing-gateway-service
FR referencesFR-EPRX-008
Legacy FR refs
DependenciesEPRX-US-007

User story: As pharmacy-service, when stock allows only a partial fill, I want to record a partial MedicationDispense so that the remaining quantity is tracked and the prescriber is notified.

Acceptance criteria (Gherkin):

  • Given an MR for 30 tablets, when I POST a MD with quantity.value: 10, then 201 is returned with status: in-progress on the MD.
  • Given the partial fill notification fires to EHR, then it includes the dispensed quantity and remaining quantity.

Definition of Done: Standard DoD applies.


EPRX-US-009 — Register and receive Subscription notifications

FieldValue
Issue typeStory
SummaryEHR and Pharmacy register Subscriptions; receive signed HTTPS notifications
Epic linkEPRX-EPIC-04
StatusIn Progress
PriorityMust
Story points8
Labelsservice:eprescribing-gateway, type:backend, slice:S1
Componentsghasi-eprescribing-gateway-service
FR referencesFR-EPRX-009
Legacy FR refsFR-RX-009
DependenciesEPRX-US-003, EPRX-US-007

User story: As EHR/Pharmacy backends, when a prescription event occurs, I want to receive a signed HTTPS notification so that I can react in near-real-time without polling.

Acceptance criteria (Gherkin):

  • Given a registered Subscription with a valid HTTPS endpoint, when a MR is created, then an HTTPS POST with X-Ghasi-Signature header is delivered within 10 s (NFR-RX-002).
  • Given the Subscription fires, when the consumer verifies HMAC-SHA256(payload, signingKey), then the signature matches.
  • Given a duplicate delivery (same X-Ghasi-Delivery-Id), when the consumer deduplicates, then no duplicate side effects.

Technical notes:

  • AC-RX-005 satisfied here.
  • At-least-once delivery; consumer idempotency on X-Ghasi-Delivery-Id.

Definition of Done: Standard DoD applies.


EPRX-US-010 — DLQ for failed Subscription deliveries

FieldValue
Issue typeStory
SummaryFailed notifications enqueue in DLQ; observable and replayable
Epic linkEPRX-EPIC-04
StatusIn Progress
PriorityMust
Story points5
Labelsservice:eprescribing-gateway, type:backend, slice:S1
Componentsghasi-eprescribing-gateway-service
FR referencesFR-EPRX-010
Legacy FR refsFR-RX-010
DependenciesEPRX-US-009

User story: As an SRE, when a Subscription endpoint is unavailable, I want failed notifications to enter a DLQ so that I can observe, investigate, and replay them once the endpoint recovers.

Acceptance criteria (Gherkin):

  • Given a Subscription endpoint returns 500 on delivery, when 5 retry attempts exhaust, then the event enters DLQ and eprescribing.subscription.delivery_failed.v1 is emitted.
  • Given a DLQ entry, when I call the admin replay endpoint, then the notification is re-delivered and marked success in DLQ.

Technical notes:

  • DLQ observable in eprescribing-gateway/outbox-subscriptions dashboard.

Definition of Done: Standard DoD applies. AC-RX-005 DLQ path satisfied.


EPRX-US-011 — Three-party end-to-end correlation test

FieldValue
Issue typeStory
SummaryCI gate: EHR creates MR → pharmacy receives notification → pharmacy creates MD → EHR notified
Epic linkEPRX-EPIC-04
StatusTo Do
PriorityMust
Story points5
Labelsservice:eprescribing-gateway, type:test, slice:S1
Componentsghasi-eprescribing-gateway-service
FR referencesFR-EPRX-009, FR-EPRX-010
Legacy FR refsFR-RX-009
DependenciesEPRX-US-007, EPRX-US-009

User story: As an engineer, I want a contract test that proves the full three-party prescribe → dispense → notify correlation works end-to-end so that regressions are caught before production.

Acceptance criteria (Gherkin):

  • Given EHR creates MR and Pharmacy creates MD, when E2E test runs, then both Subscription notifications fire and prescriptionBusinessId matches across all events and audit records.

Technical notes:

  • File: test/e2e/three-party-correlation.e2e.spec.ts. AC-RX-006 satisfied.

Definition of Done: Standard DoD applies. Must be CI gate.


EPRX-US-012 — HIPAA-equivalent audit trail

FieldValue
Issue typeStory
SummaryAll MR/MD mutations emit audit records with correlation ID
Epic linkEPRX-EPIC-05
StatusIn Progress
PriorityMust
Story points5
Labelsservice:eprescribing-gateway, type:compliance, slice:S0
Componentsghasi-eprescribing-gateway-service, audit-service
FR referencesFR-EPRX-011, FR-EPRX-012
Legacy FR refsFR-RX-011, FR-RX-012
DependenciesEPRX-US-003, EPRX-US-007

User story: As a compliance officer, when any prescription or dispense record is created, updated, or deleted, I want a complete audit record with actor, tenant, resource type/ID, action, and prescription business ID so that we satisfy HIPAA Security Rule equivalent audit requirements.

Acceptance criteria (Gherkin):

  • Given a MR is created, when I query the audit service, then a record exists with action: create, resourceType: MedicationRequest, tenantId, actorId, prescriptionBusinessId, timestamp.
  • Given a MR is cancelled, then audit record with action: cancel exists.
  • Given an unauthorized access attempt (403 response), then eprescribing.auth.forbidden audit record is emitted.

Technical notes:

  • Security reviewer sign-off required; audit coverage automated test in CI.

Definition of Done: Standard DoD applies. Security reviewer sign-off.


EPRX-US-013 — Tenant isolation adversarial test

FieldValue
Issue typeStory
SummaryZero cross-tenant MR/MD leakage proven by CI adversarial test
Epic linkEPRX-EPIC-06
StatusIn Progress
PriorityMust
Story points3
Labelsservice:eprescribing-gateway, type:security, type:test, slice:S0
Componentsghasi-eprescribing-gateway-service
FR referencesFR-EPRX-013
Legacy FR refsFR-RX-008
DependenciesEPRX-US-001

User story: As a security engineer, I want to verify that a Tenant B JWT cannot access any Tenant A prescriptions so that cross-tenant PHI leakage is provably prevented.

Acceptance criteria (Gherkin):

  • Given Tenant A has MR records, when Tenant B JWT searches, then zero results returned.
  • Given Tenant B tries to GET a specific mr_id from Tenant A, then 404 is returned (not 403).

Technical notes:

  • tenant-isolation.integration.spec.ts — mandatory CI gate, never removable.

Definition of Done: Standard DoD applies. CI gate permanent.


EPRX-US-014 — Module entitlement gate

FieldValue
Issue typeStory
SummaryUnlicensed tenants receive 403 MODULE_NOT_LICENSED on all gateway operations
Epic linkEPRX-EPIC-06
StatusIn Progress
PriorityMust
Story points2
Labelsservice:eprescribing-gateway, type:security, slice:S0
Componentsghasi-eprescribing-gateway-service
FR referencesFR-EPRX-013
Legacy FR refs
DependenciesIDENT-US-001

User story: As a platform operator, when a tenant has not licensed the e-prescribing gateway module, I want all operations blocked so that billing boundaries are enforced.

Acceptance criteria (Gherkin):

  • Given tenant lacks ehr.ghasi_eprescribing_gateway entitlement, when any endpoint is called, then 403 MODULE_NOT_LICENSED.
  • Given a licensed tenant, when the same endpoint is called with valid auth, then the operation proceeds normally.

Definition of Done: Standard DoD applies.


EPRX-US-015 — Task renewals and clarifications (P1)

FieldValue
Issue typeStory
SummaryPharmacy creates renewal Task; EHR approves/rejects via Task workflow
Epic linkEPRX-EPIC-07
StatusTo Do
PriorityShould
Story points8
Labelsservice:eprescribing-gateway, type:backend, slice:S2
Componentsghasi-eprescribing-gateway-service
FR referencesFR-EPRX-014
Legacy FR refsFR-RX-020
DependenciesEPRX-US-006, EPRX-US-007

User story: As pharmacy-service, when a patient needs a prescription renewal, I want to create a Task requesting renewal so that the prescriber can approve or reject it in the EHR without out-of-band communication.

Acceptance criteria (Gherkin):

  • Given pharmacy creates a Task with intent: order and requester: Organization/pharm_01, when EHR POSTs a Task status update to accepted, then the Task status changes and both parties are notified.
  • Given EHR rejects the renewal, then Task status is rejected and pharmacy is notified.

Definition of Done: Standard DoD applies. FR-RX-020 satisfied.


EPRX-US-016 — Pharmacy directory search (P1)

FieldValue
Issue typeStory
SummaryEHR searches pharmacy Organizations and Endpoints for routing
Epic linkEPRX-EPIC-07
StatusTo Do
PriorityShould
Story points5
Labelsservice:eprescribing-gateway, type:api, slice:S2
Componentsghasi-eprescribing-gateway-service, provider-directory-service
FR referencesFR-EPRX-015
Legacy FR refsFR-RX-021
DependenciesEPRX-US-001, PROVDIR-US-001

User story: As an EHR clinician, when prescribing, I want to search the pharmacy directory to select the patient's preferred pharmacy so that the prescription is routed to the correct destination.

Acceptance criteria (Gherkin):

  • Given pharmacy organizations are registered, when I GET /fhir/Organization?name=Kabul+Pharmacy, then matching FHIR Organization resources are returned.
  • Given an Organization, when I GET linked Endpoints, then FHIR Endpoint resources with connectionType and address are returned.

Definition of Done: Standard DoD applies. FR-RX-021 satisfied.


EPRX-US-017 — Bulk export for MoH reporting (P2 gated)

FieldValue
Issue typeStory
SummaryAsync bulk FHIR export of prescriptions for MoH/HMIS reporting
Epic linkEPRX-EPIC-07
StatusTo Do
PriorityCould
Story points13
Labelsservice:eprescribing-gateway, type:backend, slice:S3
Componentsghasi-eprescribing-gateway-service
FR referencesFR-EPRX-016
Legacy FR refsFR-RX-022
DependenciesEPRX-EPIC-07

User story: As a Ministry of Health analyst, when generating national medication use reports, I want to trigger a bulk FHIR export of prescription data so that HMIS/national indicators can be populated.

Acceptance criteria (Gherkin):

  • Given MoH-reporting role and bulk_export:admin scope, when I POST /$export, then an async job is created; polling returns NDJSON download links when complete.
  • Given no authorization, when I POST /$export, then 403 is returned.

Technical notes:

  • Gated in P2_GATED.md. Requires bulk hosting ADR and licensing decision.

Definition of Done: Standard DoD applies. Gated on ADR acceptance.


EPRX-US-018 — Third-party EMR onboarding (Phase 3)

FieldValue
Issue typeStory
SummaryNon-Ghasi EMR registers B2B credentials and passes contract tests
Epic linkEPRX-EPIC-08
StatusTo Do
PriorityShould
Story points8
Labelsservice:eprescribing-gateway, type:infra, slice:S3
Componentsghasi-eprescribing-gateway-service
FR referencesFR-EPRX-017
Legacy FR refs
DependenciesEPRX-EPIC-07

User story: As a national health program manager, when onboarding a third-party EMR to the national e-prescribing gateway, I want a documented registration process with B2B client credentials and contract test harness so that the integration is safe and verifiable.

Acceptance criteria (Gherkin):

  • Given a third-party EMR registers OAuth2 client credentials with ehr-backend persona scope, when it POSTs a valid MR, then 201 is returned with prescription business ID.
  • Given the contract test harness is run against the third-party client simulation, when all three-party golden fixtures pass, then onboarding is approved.

Definition of Done: Standard DoD applies. Partner harness documented.


EPRX-US-019 — External pharmacy onboarding (Phase 3)

FieldValue
Issue typeStory
SummaryNon-Ghasi pharmacy registers and receives Subscription notifications
Epic linkEPRX-EPIC-08
StatusTo Do
PriorityShould
Story points5
Labelsservice:eprescribing-gateway, type:infra, slice:S3
Componentsghasi-eprescribing-gateway-service
FR referencesFR-EPRX-017
Legacy FR refs
DependenciesEPRX-US-018, EPRX-US-009

User story: As a national health program manager, when onboarding an external pharmacy to receive national prescriptions, I want the pharmacy to register its endpoint and receive signed notifications so that prescription routing works across organizational boundaries.

Acceptance criteria (Gherkin):

  • Given an external pharmacy with pharmacy-backend persona, when it registers a Subscription and an EHR creates a prescription for that pharmacy, then the Subscription fires to the external endpoint within SLO.

Definition of Done: Standard DoD applies.