Project

General

Profile

Actions

New feature / Change #8924

open

Epics #8706: Bootstrap project and recover all feature we have so far (milestone #1)

Implement Fhir for external connectivity with health ecosystems

Added by Olivier Bitsch 22 days ago. Updated 21 days ago.

Status:
In Study
Priority:
Normal
Assignee:
Category:
-
Start date:
11/24/2025
Due date:
% Done:

0%

Estimated time:
80:00 h

Description

Summary

FHIR API integration for Appointment and Encounter resources.

Description

As an external EMR system, I need FHIR R4 compliant API endpoints to create appointments and retrieve encounters, enabling interoperability with healthcare systems like OpenEMR.

Requirements

FHIR Endpoints:

POST /api/fhir/Appointment → Create appointment (returns FHIR Appointment)
GET /api/fhir/Appointment → List appointments (returns FHIR Bundle)
GET /api/fhir/Appointment/:id → Get appointment (returns FHIR Appointment)
PUT /api/fhir/Appointment/:id → Update appointment
DELETE /api/fhir/Appointment/:id → Cancel appointment

GET /api/fhir/Encounter → List encounters (returns FHIR Bundle)
GET /api/fhir/Encounter/:id → Get encounter (returns FHIR Encounter)

FHIR → Internal Model Mapping:

FHIR Resource Internal Model
Appointment Appointment + Consultation
Appointment.participant (Patient) Participant (user, email, phone)
Appointment.participant (Practitioner) Participant (doctor)
Encounter Consultation

Appointment Mapping:

FHIR Field Internal Field
Appointment.start Appointment.scheduled_at
Appointment.end Appointment.end_expected_at
Appointment.status Appointment.status
Appointment.serviceType Type (Online/InPerson)
Appointment.participant[Patient] Participant (beneficiary)
Appointment.participant[Practitioner] Participant (doctor)
Appointment.reason Consultation.description
Appointment.identifier External reference (store in metadata)

Encounter Mapping:

FHIR Field Internal Field
Encounter.status Consultation status
Encounter.period.start Consultation.created_at
Encounter.period.end Consultation.closed_at
Encounter.appointment Reference to Appointment
Encounter.subject Consultation.beneficiary
Encounter.participant Consultation.owned_by (doctor)

Status Mappings:

FHIR Appointment Internal Status
proposed -
booked Scheduled
cancelled Cancelled
fulfilled (completed)
FHIR Encounter Internal Status
planned pending
in-progress active
finished closed

Request Body Example (Create Appointment):

{
  "resourceType": "Appointment",
  "status": "booked",
  "start": "2025-01-15T10:00:00Z",
  "end": "2025-01-15T10:30:00Z",
  "serviceType": [{"coding": [{"code": "online"}]}],
  "identifier": [{"system": "openemr", "value": "APT-12345"}],
  "participant": [
    {
      "actor": {
        "reference": "#patient1",
        "type": "Patient"
      },
      "status": "accepted"
    },
    {
      "actor": {
        "reference": "#practitioner1",
        "type": "Practitioner"
      },
      "status": "accepted"
    }
  ],
  "contained": [
    {
      "resourceType": "Patient",
      "id": "patient1",
      "name": [{"family": "Doe", "given": ["John"]}],
      "telecom": [
        {"system": "email", "value": "john@example.com"},
        {"system": "phone", "value": "+1234567890"}
      ]
    },
    {
      "resourceType": "Practitioner",
      "id": "practitioner1",
      "name": [{"family": "Smith", "given": ["Dr"]}],
      "telecom": [{"system": "email", "value": "dr.smith@clinic.com"}]
    }
  ]
}

Response (FHIR Appointment):

{
  "resourceType": "Appointment",
  "id": "123",
  "status": "booked",
  "start": "2025-01-15T10:00:00Z",
  "end": "2025-01-15T10:30:00Z",
  ...
}

Validation Rules:

  • Patient participant required with name and contact (email or phone)
  • Practitioner participant required with email (must exist in system)
  • Start date required and must be in future
  • Cannot update after consultation started

Search Parameters:

  • identifier - Search by external system ID
  • appointment.identifier - Search encounters by appointment ID

Acceptance Criteria

  • POST /fhir/Appointment creates Consultation + Appointment + Participants
  • POST /fhir/Appointment returns valid FHIR Appointment response
  • GET /fhir/Appointment returns FHIR Appointment or Bundle
  • GET /fhir/Encounter returns FHIR Encounter or Bundle
  • Search by external identifier works
  • Invalid FHIR body returns 400 with validation errors
  • Practitioner not in system returns 400 error
  • Status mappings work bidirectionally
  • Cannot modify appointment with active consultation

Notes

  • Store original FHIR body for audit/reference

Actions #1

Updated by Olivier Bitsch 22 days ago

  • Description updated (diff)
Actions #2

Updated by Olivier Bitsch 21 days ago

  • Parent task set to #8706
Actions #3

Updated by Olivier Bitsch 21 days ago

  • Estimated time set to 80:00 h
Actions #4

Updated by Olivier Bitsch 21 days ago

  • Assignee set to Gor Grigoryan
Actions #5

Updated by Gor Grigoryan 21 days ago


## Summary
FHIR R4 API integration for Appointment and Encounter resources.

## Description
As an external EMR system, I need FHIR R4 compliant API endpoints to create appointments and retrieve encounters, enabling interoperability with healthcare systems like OpenEMR.

## Requirements

FHIR Endpoints:
POST /api/fhir/Appointment → Create appointment (returns FHIR Appointment)
GET /api/fhir/Appointment → List appointments (returns FHIR Bundle)
GET /api/fhir/Appointment/:id → Get appointment (returns FHIR Appointment)
PUT /api/fhir/Appointment/:id → Update appointment
DELETE /api/fhir/Appointment/:id → Cancel appointment

GET /api/fhir/Encounter → List encounters (returns FHIR Bundle)
GET /api/fhir/Encounter/:id → Get encounter (returns FHIR Encounter)

FHIR → Internal Model Mapping:

| FHIR Resource | Internal Model |
|---------------|----------------|
| Appointment | Appointment + Consultation |
| Appointment.participant (Patient) | Participant (user, email, phone) |
| Appointment.participant (Practitioner) | Participant (doctor) |
| Encounter | Consultation |

Appointment Mapping:
| FHIR Field | Internal Field |
|------------|----------------|
| Appointment.start | Appointment.scheduled_at |
| Appointment.end | Appointment.end_expected_at |
| Appointment.status | Appointment.status |
| Appointment.serviceType | Type (Online/InPerson) |
| Appointment.participant[Patient] | Participant (beneficiary) |
| Appointment.participant[Practitioner] | Participant (doctor) |
| Appointment.reason | Consultation.description |
| Appointment.identifier | External reference (store in metadata) |

Encounter Mapping:
| FHIR Field | Internal Field |
|------------|----------------|
| Encounter.status | Consultation status |
| Encounter.period.start | Consultation.created_at |
| Encounter.period.end | Consultation.closed_at |
| Encounter.appointment | Reference to Appointment |
| Encounter.subject | Consultation.beneficiary |
| Encounter.participant | Consultation.owned_by (doctor) |

Status Mappings:

| FHIR Appointment | Internal Status |
|------------------|-----------------|
| proposed | - |
| booked | Scheduled |
| cancelled | Cancelled |
| fulfilled | (completed) |

| FHIR Encounter | Internal Status |
|----------------|-----------------|
| planned | pending |
| in-progress | active |
| finished | closed |

Request Body Example (Create Appointment):

  {
    "resourceType": "Appointment",
    "status": "booked",
    "start": "2025-01-15T10:00:00Z",
    "end": "2025-01-15T10:30:00Z",
    "serviceType": [{"coding": [{"code": "online"}]}],
    "identifier": [{"system": "openemr", "value": "APT-12345"}],
    "participant": [
      {
        "actor": {
          "reference": "#patient1",
          "type": "Patient"
        },
        "status": "accepted"
      },
      {
        "actor": {
          "reference": "#practitioner1",
          "type": "Practitioner"
        },
        "status": "accepted"
      }
    ],
    "contained": [
      {
        "resourceType": "Patient",
        "id": "patient1",
        "name": [{"family": "Doe", "given": ["John"]}],
        "telecom": [
          {"system": "email", "value": "john@example.com"},
          {"system": "phone", "value": "+1234567890"}
        ]
      },
      {
        "resourceType": "Practitioner",
        "id": "practitioner1",
        "name": [{"family": "Smith", "given": ["Dr"]}],
        "telecom": [{"system": "email", "value": "dr.smith@clinic.com"}]
      }
    ]
  }

  Response (FHIR Appointment):
  {
    "resourceType": "Appointment",
    "id": "123",
    "status": "booked",
    "start": "2025-01-15T10:00:00Z",
    "end": "2025-01-15T10:30:00Z",
    ...
  }

  Validation Rules:
  - Patient participant required with name and contact (email or phone)
  - Practitioner participant required with email (must exist in system)
  - Start date required and must be in future
  - Cannot update after consultation started

  Search Parameters:
  - identifier - Search by external system ID
  - appointment.identifier - Search encounters by appointment ID

  Acceptance Criteria

  - POST /fhir/Appointment creates Consultation + Appointment + Participants
  - POST /fhir/Appointment returns valid FHIR Appointment response
  - GET /fhir/Appointment returns FHIR Appointment or Bundle
  - GET /fhir/Encounter returns FHIR Encounter or Bundle
  - Search by external identifier works
  - Invalid FHIR body returns 400 with validation errors
  - Practitioner not in system returns 400 error
  - Status mappings work bidirectionally
  - Cannot modify appointment with active consultation

  Notes

  - Store original FHIR body for audit/reference
Actions #6

Updated by Gor Grigoryan 21 days ago

  • Description updated (diff)
Actions #7

Updated by Olivier Bitsch 21 days ago

  • Description updated (diff)
Actions #8

Updated by Gor Grigoryan 21 days ago

  • Description updated (diff)
Actions #9

Updated by Olivier Bitsch 21 days ago

  • Description updated (diff)
Actions #10

Updated by Olivier Bitsch 21 days ago

  • Description updated (diff)
Actions

Also available in: Atom PDF