HH/docs/external-api.md
2026-07-12 11:18:20 +03:00

16 KiB

PX360 External API Documentation

Base URL: /api/v1/external/

Authentication

All requests require an X-API-Key header.

X-API-Key: <your-api-key>

API keys are managed through the Django admin panel. Each key can be scoped to:

  • A specific hospital (restricts data access to that hospital)
  • Specific entities (complaints, inquiries, observations, appreciations, suggestions, doctor_ratings)
  • Empty allowed_entities = access to all entities

Creating an API Key

Via Django admin or programmatically:

from apps.integrations.models import ExternalAPIKey

api_key, raw_key = ExternalAPIKey.create_key(
    name="Partner Integration",
    hospital=hospital_object,  # Optional: scope to a hospital
    allowed_entities=["complaints", "inquiries"],  # Optional: empty = all
    rate_limit=60,  # Requests per minute
    description="Integration with partner system",
)
# raw_key is shown ONCE - store it securely

Error Responses

HTTP Status Meaning
401 Missing or invalid API key
403 API key does not have access to the requested entity, or hospital scope mismatch
404 Requested resource not found
400 Validation error (missing/invalid fields)

Lookup Endpoints

These endpoints provide dropdown/reference data for building forms.

List Hospitals

GET /api/v1/external/hospitals/

Response:

{
  "count": 3,
  "results": [
    { "id": "uuid", "name": "Al Nuzha", "code": "HH-N" },
    { "id": "uuid", "name": "Al Olya", "code": "HH-A" },
    { "id": "uuid", "name": "Al Suwaidi", "code": "HH-S" }
  ]
}

List Location Types

GET /api/v1/external/location-types/

Response:

{
  "count": 4,
  "results": [
    { "value": "OP", "label": "Outpatient", "label_ar": "خارجي" },
    { "value": "IP", "label": "Inpatient", "label_ar": "تنويم" },
    { "value": "ER", "label": "Emergency", "label_ar": "طوارئ" },
    { "value": "GENERAL", "label": "General", "label_ar": "عام" }
  ]
}

List Areas

GET /api/v1/external/areas/?hospital=<hospital_name>
Parameter Required Description
hospital Yes Hospital name (case-insensitive)
location_type No Filter by location type (OP, IP, ER, GENERAL)

Response:

{
  "count": 27,
  "results": [
    { "id": "uuid", "name": "Emergency", "name_ar": "", "code": "emergency" }
  ]
}

List Departments

GET /api/v1/external/departments/?hospital=<hospital_name>
Parameter Required Description
hospital Yes Hospital name (case-insensitive)

Response:

{
  "count": 37,
  "results": [
    { "id": "uuid", "name": "Critical Care Department", "name_en": "Critical Care Department", "name_ar": "", "code": "hh_n_critical_care_department" }
  ]
}

List Sections

GET /api/v1/external/sections/?hospital=<hospital_name>&department=<department_name>
Parameter Required Description
hospital Yes Hospital name (case-insensitive)
department Yes Department name (case-insensitive)

Response:

{
  "count": 3,
  "results": [
    { "id": "uuid", "name": "ICU", "name_ar": "", "code": "hh_n_critical_care_department__icu" }
  ]
}

Complaints

Create a Complaint

POST /api/v1/external/complaints/

Request Body:

Field Required Type Description
hospital Yes string Hospital name (case-insensitive)
title Yes string Complaint title (max 500 chars)
description Yes string Detailed complaint description
contact_name Yes string Reporter's name (max 200 chars)
contact_phone Yes string Reporter's phone (max 20 chars)
contact_email No string Reporter's email
relation_to_patient No string One of: patient, relative, friend, other
patient_name No string Patient name
national_id No string National ID number
incident_date No date Date of incident (YYYY-MM-DD)
expected_result No string Expected resolution
location_type No string One of: OP, IP, ER, GENERAL
area No string Area name (resolved by hospital)
department No string Department name (resolved by hospital)
section No string Section name (requires department)

Example:

{
  "hospital": "Al Nuzha",
  "title": "Long wait time in ER",
  "description": "I waited 3 hours in the emergency room without being seen.",
  "contact_name": "John Doe",
  "contact_phone": "+966501234567",
  "contact_email": "john@example.com",
  "location_type": "ER",
  "area": "Emergency",
  "department": "Critical Care Department",
  "section": "ICU"
}

Response (201):

{
  "success": true,
  "reference_number": "CMP-20260609-167152",
  "status": "open"
}

List Complaints

GET /api/v1/external/complaints/list/
Parameter Required Description
page No Page number (default: 1)
page_size No Items per page (default: 20, max: 100)
created_from No Filter by created date from (YYYY-MM-DD or YYYY-MM-DDTHH:MM:SS)
created_to No Filter by created date to (date-only values auto-extend to end of day)
updated_from No Filter by updated date from
updated_to No Filter by updated date to
status No Filter by status value

Retrieve a Complaint

GET /api/v1/external/complaints/<reference_number>/

Response:

{
  "id": "uuid",
  "reference_number": "CMP-20260609-167152",
  "title": "Long wait time in ER",
  "description": "I waited 3 hours...",
  "status": "open",
  "severity": "medium",
  "priority": "medium",
  "contact_name": "John Doe",
  "contact_phone": "+966501234567",
  "contact_email": "john@example.com",
  "relation_to_patient": "",
  "patient_name": "",
  "incident_date": null,
  "expected_result": "",
  "resolution": "",
  "satisfaction": "",
  "hospital_name": "Al Nuzha",
  "created_at": "2026-06-09T13:38:03.808618+03:00",
  "updated_at": "2026-06-09T13:38:03.808683+03:00"
}

Resolution & Satisfaction Fields

Field Description
resolution Free-text description of the resolution taken (empty if unresolved)
satisfaction Patient satisfaction: satisfied, neutral, dissatisfied, no_response, or empty

Set Patient Satisfaction

Sets the patient satisfaction level for a complaint. A resolution must be recorded first — returns 400 if the complaint has no resolution.

PATCH /api/v1/external/complaints/<reference_number>/satisfaction/

Request Body:

Field Required Type Description
satisfaction Yes string One of: satisfied, neutral, dissatisfied, no_response

Example:

{
  "satisfaction": "satisfied"
}

Response (200):

{
  "success": true,
  "reference_number": "CMP-20260609-167152",
  "satisfaction": "satisfied"
}

Inquiries

Create an Inquiry

POST /api/v1/external/inquiries/

Request Body:

Field Required Type Description
hospital Yes string Hospital name
subject Yes string Inquiry subject (max 500 chars)
message Yes string Inquiry message
contact_name Yes string Contact name
contact_phone Yes string Contact phone
contact_email No string Contact email
category No string One of: appointment, billing, medical_records, general, other
location_type No string One of: OP, IP, ER, GENERAL
area No string Area name
department No string Department name
section No string Section name (requires department)

Response (201):

{
  "success": true,
  "reference_number": "INQ-20260609-124293",
  "status": "open"
}

List / Retrieve Inquiries

GET /api/v1/external/inquiries/list/
GET /api/v1/external/inquiries/<reference_number>/

Same pagination and date/status filters as complaints.

Retrieve Response:

{
  "id": "uuid",
  "reference_number": "INQ-20260609-124293",
  "subject": "Appointment rescheduling",
  "message": "I need to reschedule my cardiology appointment.",
  "category": "appointment",
  "status": "open",
  "contact_name": "Jane Smith",
  "contact_phone": "+966509876543",
  "contact_email": "jane@example.com",
  "hospital_name": "Al Nuzha",
  "created_at": "2026-06-09T13:36:22.730962+03:00",
  "updated_at": "2026-06-09T13:36:22.731020+03:00"
}

Observations

Create an Observation

POST /api/v1/external/observations/

Request Body:

Field Required Type Description
hospital Yes string Hospital name
description Yes string Observation description
title No string Title (max 300 chars)
severity No string One of: low, medium, high, critical (default: medium)
category No UUID Observation category UUID
location_text No string Free-text location description
incident_datetime No datetime When the incident occurred
contact_name No string Reporter name (anonymous supported)
contact_phone No string Reporter phone
contact_email No string Reporter email
reporter_staff_id No string Staff ID of reporter
patient_file_number No string Patient file number

Response (201):

{
  "success": true,
  "reference_number": "OBS-O8TCIC",
  "status": "new"
}

List / Retrieve Observations

GET /api/v1/external/observations/list/
GET /api/v1/external/observations/<tracking_code>/

Retrieve Response:

{
  "id": "uuid",
  "reference_number": "OBS-O8TCIC",
  "title": "",
  "description": "Wet floor near the main entrance, no warning sign posted.",
  "severity": "medium",
  "status": "new",
  "location_text": "Main entrance lobby",
  "incident_datetime": "2026-06-09T13:36:23.542046+03:00",
  "contact_name": "Anonymous Reporter",
  "contact_phone": "",
  "contact_email": "",
  "reporter_staff_id": "",
  "patient_file_number": "",
  "hospital_name": "Al Nuzha",
  "category_name": null,
  "created_at": "2026-06-09T13:36:23.540024+03:00",
  "updated_at": "2026-06-09T13:36:23.540079+03:00"
}

Appreciations

Create an Appreciation

POST /api/v1/external/appreciations/

Request Body:

Field Required Type Description
hospital Yes string Hospital name
message Yes string Appreciation message
contact_name Yes string Submitter name
contact_phone Yes string Submitter phone

Response (201):

{
  "success": true,
  "reference_number": "APR-202607-0001",
  "status": "draft"
}

List / Retrieve Appreciations

GET /api/v1/external/appreciations/list/
GET /api/v1/external/appreciations/<reference_number>/

Retrieve Response:

{
  "id": "uuid",
  "reference_number": "APR-202607-0001",
  "message_en": "Dr. Ahmed was incredibly kind and thorough.",
  "status": "draft",
  "hospital_name": "Al Nuzha",
  "is_anonymous": false,
  "created_at": "2026-06-09T13:36:23.788028+03:00",
  "updated_at": "2026-06-09T13:36:23.788077+03:00"
}

Suggestions

Create a Suggestion

POST /api/v1/external/suggestions/

Request Body:

Field Required Type Description
hospital Yes string Hospital name
message Yes string Suggestion message
contact_name Yes string Submitter name
contact_phone Yes string Submitter phone
title No string Title (auto-generated from message if omitted)
category No string One of: clinical_care, staff_service, facility, communication, appointment, billing, food_service, cleanliness, technology, general, other
rating No integer Rating 1-5

Response (201):

{
  "success": true,
  "reference_number": "SGT-202607-0001",
  "status": "submitted"
}

List / Retrieve Suggestions

GET /api/v1/external/suggestions/list/
GET /api/v1/external/suggestions/<reference_number>/

Retrieve Response:

{
  "id": "uuid",
  "reference_number": "SGT-202607-0001",
  "title": "Digital queue system",
  "message": "Please implement a digital queue management system.",
  "category": "technology",
  "rating": null,
  "status": "submitted",
  "feedback_type": "suggestion",
  "sentiment": "neutral",
  "contact_name": "Suggestor",
  "contact_phone": "+966504445566",
  "hospital_name": "Al Nuzha",
  "created_at": "2026-06-09T13:36:24.051998+03:00",
  "updated_at": "2026-06-09T13:36:24.052047+03:00"
}

Doctor Ratings

Submit a Doctor Rating

Submit a patient rating for a doctor. The doctor_id must be an existing employee ID in the Staff table for the given hospital.

POST /api/v1/external/doctor-ratings/

Request Body:

Field Required Type Description
hospital_id Yes integer Hospital ID
doctor_id Yes string Doctor's employee ID (must exist in the hospital's Staff records)
rating Yes integer Rating from 1 to 5
doctor_name No string Doctor name (stored for reference)
feedback No string Patient feedback text
rating_date No date Date of rating (YYYY-MM-DD, defaults to current date)
patient_uhid No string Patient unique health ID
patient_name No string Patient name
patient_type No string One of: IP, OP, ER, DC
department_name No string Department name
admit_date No date Admission date (YYYY-MM-DD)
discharge_date No date Discharge date (YYYY-MM-DD)

Example:

{
  "hospital_id": 1,
  "doctor_id": "10738",
  "doctor_name": "Dr. Omaymah Yaqoub",
  "rating": 4,
  "feedback": "Great doctor, very caring and attentive.",
  "rating_date": "2026-06-10",
  "patient_uhid": "UHID12345",
  "patient_name": "Ahmed Ali",
  "patient_type": "OP",
  "department_name": "Internal Medicine"
}

Response (201):

{
  "success": true,
  "rating_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "staff_id": "f1e2d3c4-b5a6-7890-abcd-ef1234567890",
  "doctor_name": "OMAYMAH YAQOUB ELAMEIAN",
  "rating": 4
}

Error Responses:

HTTP Status Condition
400 doctor_id not found in the hospital's Staff records
400 rating outside 1-5 range
400 hospital_id not found
403 Hospital does not match API key scope

Error Example (doctor not found):

{
  "doctor_id": [
    "Doctor with employee ID '99999' not found at this hospital."
  ]
}

Common Patterns

Location Hierarchy

Locations follow a cascading hierarchy. Use lookup endpoints to populate dependent dropdowns:

1. GET /hospitals/           → pick hospital
2. GET /location-types/      → pick location type
3. GET /areas/?hospital=X    → pick area (filtered by hospital)
4. GET /departments/?hospital=X  → pick department
5. GET /sections/?hospital=X&department=Y  → pick section

Date Filtering

All list endpoints support date range filtering:

GET /complaints/list/?created_from=2026-01-01&created_to=2026-06-30
GET /complaints/list/?updated_from=2026-06-01T08:00:00&updated_to=2026-06-09T17:00:00
  • Date-only values (YYYY-MM-DD): created_from starts at 00:00, created_to extends to 23:59:59
  • Full datetime values (YYYY-MM-DDTHH:MM:SS) are also accepted

Pagination

All list endpoints return paginated results:

{
  "count": 150,
  "page": 1,
  "page_size": 20,
  "results": [...]
}
Parameter Default Max
page 1 -
page_size 20 100

Reference Number Formats

Entity Prefix Format
Complaint CMP- CMP-YYYYMMDD-XXXXXX
Inquiry INQ- INQ-YYYYMMDD-XXXXXX
Observation OBS- OBS-XXXXXX (auto-generated by model)
Appreciation APR- APR-YYYYMM-NNNN
Suggestion SGT- SGT-YYYYMM-NNNN