Today's Appointments
0
Total Patients
0
Upcoming (7 days)
0
Cancellation Rate
0%

Appointments

Time Patient Doctor Status Reason Actions

Doctor Schedule

This Week

Patients

ID Name Phone Date of Birth Gender Email Appointments

Quick Start Guide

Get up and running with the Hospital Booking API in 3 steps.

Step 1: Set your variables

Set these once in your terminal to use in all examples below:

BASE_URL="https://hospital-booking-demo-production.up.railway.app"
API_KEY="hb_j5mQRynLKt_jg8qc7ONUg5BFiWIpLJ83"

Step 2: Verify the API is running

curl -s "$BASE_URL/health" | python3 -m json.tool
// Expected response:
{ "status": "ok", "auth_enabled": true }

Step 3: Make your first authenticated call

curl -s "$BASE_URL/api/v1/specialties" \
  -H "Authorization: Bearer $API_KEY" | python3 -m json.tool
// Expected response:
{
  "specialties": [
    { "id": "spec_001", "name": "Cardiology", "description": "Heart and vascular system" },
    { "id": "spec_002", "name": "Dermatology", "description": "Skin, hair, and nail conditions" },
    ...
  ]
}

Authentication

All /api/v1/* endpoints require an API key. Use either header format:

// Option A — Bearer token
curl -H "Authorization: Bearer $API_KEY" ...

// Option B — X-API-Key header
curl -H "X-API-Key: $API_KEY" ...

Public endpoints (no auth needed): GET /health, POST /api/v1/reset

Complete Booking Flow (End-to-End Example)

Walk through a typical booking scenario: find a doctor, check slots, register a patient, and book an appointment.

1. Browse specialties

curl -s "$BASE_URL/api/v1/specialties" \
  -H "Authorization: Bearer $API_KEY" | python3 -m json.tool

Pick a specialty — e.g. spec_001 (Cardiology)

2. Find doctors in that specialty

curl -s "$BASE_URL/api/v1/doctors?specialty_id=spec_001" \
  -H "Authorization: Bearer $API_KEY" | python3 -m json.tool

Note the doctor_id — e.g. doc_001 (Dr. Sari Wijaya)

3. Check available slots for the doctor

# Get slots for a specific date range
curl -s "$BASE_URL/api/v1/doctors/doc_001/slots?from=2026-05-22&to=2026-05-22&status=available" \
  -H "Authorization: Bearer $API_KEY" | python3 -m json.tool

Pick an available slot_id from the response — e.g. slot_0042

4. Register a new patient (or look up existing)

# Option A: Register a new patient
curl -s -X POST "$BASE_URL/api/v1/patients" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "full_name": "Andi Prasetyo",
    "phone": "+628111222333",
    "date_of_birth": "1990-06-15",
    "gender": "male",
    "email": "andi@example.com"
  }' | python3 -m json.tool

# Option B: Look up an existing patient by phone
curl -s "$BASE_URL/api/v1/patients/lookup?phone=%2B628123456789" \
  -H "Authorization: Bearer $API_KEY" | python3 -m json.tool

Note the patient_id from the response — e.g. pat_0004

5. Book the appointment

curl -s -X POST "$BASE_URL/api/v1/appointments" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "patient_id": "pat_0004",
    "doctor_id": "doc_001",
    "slot_id": "slot_0042",
    "reason_for_visit": "Chest pain and shortness of breath for 2 weeks"
  }' | python3 -m json.tool

The appointment is now confirmed. The slot is automatically marked as booked.

6. Verify — list the patient's appointments

curl -s "$BASE_URL/api/v1/appointments?patient_id=pat_0004&status=confirmed" \
  -H "Authorization: Bearer $API_KEY" | python3 -m json.tool

Reschedule & Cancel

Reschedule an appointment

Change the slot of an existing appointment. The old slot is released back to available.

# First, find a new available slot for the same doctor
curl -s "$BASE_URL/api/v1/doctors/doc_001/slots?from=2026-05-26&to=2026-05-26&status=available" \
  -H "Authorization: Bearer $API_KEY" | python3 -m json.tool

# Then reschedule
curl -s -X PATCH "$BASE_URL/api/v1/appointments/appt_00003" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "slot_id": "slot_0055"
  }' | python3 -m json.tool

Cancel an appointment

Cancellation releases the slot and marks the appointment as cancelled.

curl -s -X DELETE "$BASE_URL/api/v1/appointments/appt_00003" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "reason": "Patient feels better and no longer needs visit"
  }' | python3 -m json.tool

Reset Demo Data

Re-seeds all data (slots, patients, appointments) back to the initial state. No auth required.

curl -s -X POST "$BASE_URL/api/v1/reset" | python3 -m json.tool
// Response:
{
  "message": "Demo data reset successfully",
  "summary": { "slots": 608, "patients": 3, "appointments": 2 }
}

API Reference

Base URL: https://hospital-booking-demo-production.up.railway.app/api/v1

Specialties

GET/api/v1/specialties — List all specialties
curl + Response
curl -s "$BASE_URL/api/v1/specialties" \
  -H "Authorization: Bearer $API_KEY"
// Response 200
{
  "specialties": [
    { "id": "spec_001", "name": "Cardiology", "description": "Heart and vascular system" },
    { "id": "spec_002", "name": "Dermatology", "description": "Skin, hair, and nail conditions" },
    { "id": "spec_003", "name": "General Practice", "description": "Primary care and general health" },
    { "id": "spec_004", "name": "Pediatrics", "description": "Children's health and development" },
    { "id": "spec_005", "name": "Orthopedics", "description": "Bone, joint, and muscle conditions" },
    { "id": "spec_006", "name": "Ophthalmology", "description": "Eye care and vision" }
  ]
}

Doctors

GET/api/v1/doctors?specialty_id=&name=&available_on= — Search doctors
curl + Response
# Search by specialty
curl -s "$BASE_URL/api/v1/doctors?specialty_id=spec_001" \
  -H "Authorization: Bearer $API_KEY"

# Search by name (partial match, case-insensitive)
curl -s "$BASE_URL/api/v1/doctors?name=sari" \
  -H "Authorization: Bearer $API_KEY"

# Search by availability on a date
curl -s "$BASE_URL/api/v1/doctors?available_on=2026-05-22" \
  -H "Authorization: Bearer $API_KEY"

# Combine filters
curl -s "$BASE_URL/api/v1/doctors?specialty_id=spec_001&available_on=2026-05-22" \
  -H "Authorization: Bearer $API_KEY"
// Response 200
{
  "doctors": [
    {
      "id": "doc_001",
      "name": "Dr. Sari Wijaya, Sp.JP",
      "specialty": { "id": "spec_001", "name": "Cardiology" },
      "hospital": { "id": "hosp_001", "name": "RS Mekari Sehat - Jakarta" },
      "consultation_fee": 350000,
      "bio": "Cardiologist with 15 years of experience...",
      "languages": ["id", "en"],
      "photo_url": "https://ui-avatars.com/api/?name=Dr.+Sari+Wijaya",
      "next_available_slot": "2026-05-22T10:00:00+07:00"
    }
  ]
}
GET/api/v1/doctors/{doctor_id}/slots?from=&to=&status= — Get available slots
curl + Response
# Get available slots for a specific date
curl -s "$BASE_URL/api/v1/doctors/doc_001/slots?from=2026-05-22&to=2026-05-22&status=available" \
  -H "Authorization: Bearer $API_KEY"

# Get all slots (including booked) for a date range
curl -s "$BASE_URL/api/v1/doctors/doc_001/slots?from=2026-05-22&to=2026-05-24&status=all" \
  -H "Authorization: Bearer $API_KEY"
// Response 200
{
  "doctor_id": "doc_001",
  "slots": [
    { "id": "slot_0042", "doctor_id": "doc_001", "start_time": "2026-05-22T10:00:00+07:00", "end_time": "2026-05-22T10:30:00+07:00", "status": "available" },
    { "id": "slot_0043", "doctor_id": "doc_001", "start_time": "2026-05-22T10:30:00+07:00", "end_time": "2026-05-22T11:00:00+07:00", "status": "available" }
  ]
}

Query params: from (YYYY-MM-DD), to (YYYY-MM-DD), status = available | booked | blocked | all (default: available)

Patients

POST/api/v1/patients — Register new patient
curl + Response
curl -s -X POST "$BASE_URL/api/v1/patients" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "full_name": "Budi Santoso",
    "phone": "+628123456789",
    "date_of_birth": "1985-03-12",
    "gender": "male",
    "email": "budi@example.com"
  }'

Required: full_name, phone  |  Optional: date_of_birth, gender (male/female), email

// Response 201
{
  "patient": {
    "id": "pat_0004",
    "full_name": "Budi Santoso",
    "phone": "+628123456789",
    "date_of_birth": "1985-03-12",
    "gender": "male",
    "email": "budi@example.com",
    "created_at": "2026-05-19T14:33:00+07:00"
  }
}
// Error 409 — phone already registered
{
  "error": {
    "code": "PATIENT_ALREADY_EXISTS",
    "message": "A patient with this phone number is already registered",
    "details": { "patient_id": "pat_0001" }
  }
}
GET/api/v1/patients/lookup?phone= — Look up patient by phone
curl + Response
# Note: + must be URL-encoded as %2B
curl -s "$BASE_URL/api/v1/patients/lookup?phone=%2B628123456789" \
  -H "Authorization: Bearer $API_KEY"
// Response 200
{
  "patient": {
    "id": "pat_0001",
    "full_name": "Budi Santoso",
    "phone": "+628123456789",
    "date_of_birth": "1985-03-12",
    "gender": "male",
    "email": "budi.santoso@email.com",
    "created_at": "2026-05-19T14:00:00+07:00"
  }
}
// Error 404 — not found
{
  "error": { "code": "PATIENT_NOT_FOUND", "message": "No patient found with this phone number" }
}

Appointments

POST/api/v1/appointments — Book appointment
curl + Response
curl -s -X POST "$BASE_URL/api/v1/appointments" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "patient_id": "pat_0001",
    "doctor_id": "doc_001",
    "slot_id": "slot_0042",
    "reason_for_visit": "Chest pain and shortness of breath for 2 weeks"
  }'

Required: patient_id, doctor_id, slot_id  |  Optional: reason_for_visit

// Response 201
{
  "appointment": {
    "id": "appt_00003",
    "patient": { "id": "pat_0001", "full_name": "Budi Santoso", "phone": "+628123456789" },
    "doctor": { "id": "doc_001", "name": "Dr. Sari Wijaya, Sp.JP", "specialty": "Cardiology" },
    "slot": { "id": "slot_0042", "start_time": "2026-05-22T10:00:00+07:00", "end_time": "2026-05-22T10:30:00+07:00" },
    "status": "confirmed",
    "reason_for_visit": "Chest pain and shortness of breath for 2 weeks",
    "created_at": "2026-05-19T14:33:00+07:00",
    "updated_at": "2026-05-19T14:33:00+07:00"
  }
}
// Error 409 — slot taken
{ "error": { "code": "SLOT_UNAVAILABLE", "message": "Selected slot is no longer available" } }

// Error 409 — patient has overlapping appointment
{
  "error": {
    "code": "PATIENT_HAS_CONFLICT",
    "message": "Patient already has an appointment at this time",
    "details": { "conflicting_appointment": { "id": "appt_00001", "..." : "..." } }
  }
}

// Error 400 — slot in the past
{ "error": { "code": "SLOT_IN_PAST", "message": "Cannot book a slot in the past" } }
GET/api/v1/appointments?patient_id=&status=&from=&to= — List appointments
curl + Response
# Filter by patient and status
curl -s "$BASE_URL/api/v1/appointments?patient_id=pat_0001&status=confirmed" \
  -H "Authorization: Bearer $API_KEY"

# Filter by date range
curl -s "$BASE_URL/api/v1/appointments?from=2026-05-22&to=2026-05-28" \
  -H "Authorization: Bearer $API_KEY"

# Get all appointments
curl -s "$BASE_URL/api/v1/appointments" \
  -H "Authorization: Bearer $API_KEY"

Filters: patient_id, status (confirmed | rescheduled | cancelled | completed | no_show), from (YYYY-MM-DD), to (YYYY-MM-DD)

// Response 200
{
  "appointments": [
    {
      "id": "appt_00001",
      "patient": { "id": "pat_0001", "full_name": "Budi Santoso", "phone": "+628123456789" },
      "doctor": { "id": "doc_001", "name": "Dr. Sari Wijaya, Sp.JP", "specialty": "Cardiology" },
      "slot": { "id": "slot_0001", "start_time": "2026-05-22T08:00:00+07:00", "end_time": "2026-05-22T08:30:00+07:00" },
      "status": "confirmed",
      "reason_for_visit": "Chest pain and shortness of breath for 2 weeks",
      "created_at": "2026-05-19T14:00:00+07:00",
      "updated_at": "2026-05-19T14:00:00+07:00"
    }
  ]
}
GET/api/v1/appointments/{id} — Get appointment detail
curl + Response
curl -s "$BASE_URL/api/v1/appointments/appt_00001" \
  -H "Authorization: Bearer $API_KEY"
// Response 200
{
  "appointment": {
    "id": "appt_00001",
    "patient": { "id": "pat_0001", "full_name": "Budi Santoso", "phone": "+628123456789" },
    "doctor": { "id": "doc_001", "name": "Dr. Sari Wijaya, Sp.JP", "specialty": "Cardiology" },
    "slot": { "id": "slot_0001", "start_time": "2026-05-22T08:00:00+07:00", "end_time": "2026-05-22T08:30:00+07:00" },
    "status": "confirmed",
    "reason_for_visit": "Chest pain and shortness of breath for 2 weeks",
    "cancellation_reason": null,
    "created_at": "2026-05-19T14:00:00+07:00",
    "updated_at": "2026-05-19T14:00:00+07:00"
  }
}
PATCH/api/v1/appointments/{id} — Reschedule (change slot)
curl + Response
curl -s -X PATCH "$BASE_URL/api/v1/appointments/appt_00001" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "slot_id": "slot_0055"
  }'

Required: slot_id (must belong to the same doctor, must be available)

// Response 200
{
  "appointment": {
    "id": "appt_00001",
    "patient": { "id": "pat_0001", "full_name": "Budi Santoso", "phone": "+628123456789" },
    "doctor": { "id": "doc_001", "name": "Dr. Sari Wijaya, Sp.JP", "specialty": "Cardiology" },
    "slot": { "id": "slot_0055", "start_time": "2026-05-26T09:00:00+07:00", "end_time": "2026-05-26T09:30:00+07:00" },
    "status": "rescheduled",
    "reason_for_visit": "Chest pain and shortness of breath for 2 weeks",
    "created_at": "2026-05-19T14:00:00+07:00",
    "updated_at": "2026-05-19T15:20:00+07:00"
  }
}
// Error 409 — new slot not available
{ "error": { "code": "SLOT_UNAVAILABLE", "message": "Selected slot is no longer available" } }

// Error 400 — cannot reschedule cancelled appointment
{ "error": { "code": "CANNOT_RESCHEDULE_CANCELLED_APPOINTMENT", "message": "Cannot reschedule a cancelled appointment" } }

// Error 400 — appointment is in the past
{ "error": { "code": "CANNOT_RESCHEDULE_PAST_APPOINTMENT", "message": "Cannot reschedule a past appointment" } }
DELETE/api/v1/appointments/{id} — Cancel appointment
curl + Response
curl -s -X DELETE "$BASE_URL/api/v1/appointments/appt_00001" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "reason": "Patient feels better and no longer needs visit"
  }'

Optional: reason (cancellation reason)

// Response 200
{
  "appointment": {
    "id": "appt_00001",
    "patient": { "id": "pat_0001", "full_name": "Budi Santoso", "phone": "+628123456789" },
    "doctor": { "id": "doc_001", "name": "Dr. Sari Wijaya, Sp.JP", "specialty": "Cardiology" },
    "slot": { "id": "slot_0001", "start_time": "2026-05-22T08:00:00+07:00", "end_time": "2026-05-22T08:30:00+07:00" },
    "status": "cancelled",
    "reason_for_visit": "Chest pain and shortness of breath for 2 weeks",
    "cancellation_reason": "Patient feels better and no longer needs visit",
    "created_at": "2026-05-19T14:00:00+07:00",
    "updated_at": "2026-05-19T16:00:00+07:00"
  }
}

Utility

POST/api/v1/reset — Reset demo data (no auth)
curl + Response
curl -s -X POST "$BASE_URL/api/v1/reset"
// Response 200
{
  "message": "Demo data reset successfully",
  "summary": { "slots": 608, "patients": 3, "appointments": 2 }
}
GET/api/v1/hospitals — Hospital info
curl + Response
curl -s "$BASE_URL/api/v1/hospitals" \
  -H "Authorization: Bearer $API_KEY"
// Response 200
{
  "hospitals": [
    {
      "id": "hosp_001",
      "name": "RS Mekari Sehat - Jakarta",
      "address": "Jl. Jend. Sudirman Kav. 52-53, Senayan, Jakarta Selatan 12190",
      "phone": "+6221-5551234",
      "operating_hours": {
        "monday": "08:00-17:00", "tuesday": "08:00-17:00", "wednesday": "08:00-17:00",
        "thursday": "08:00-17:00", "friday": "08:00-17:00", "saturday": "08:00-13:00",
        "sunday": "Closed"
      }
    }
  ]
}
GET/health — Health check (no auth)
curl + Response
curl -s "$BASE_URL/health"
// Response 200
{ "status": "ok", "auth_enabled": true }

Error Format

{
  "error": {
    "code": "SLOT_UNAVAILABLE",
    "message": "Selected slot is no longer available",
    "details": {}
  }
}

Error codes: UNAUTHORIZED, VALIDATION_ERROR, PATIENT_NOT_FOUND, DOCTOR_NOT_FOUND, SLOT_NOT_FOUND, PATIENT_ALREADY_EXISTS, SLOT_UNAVAILABLE, SLOT_IN_PAST, PATIENT_HAS_CONFLICT, APPOINTMENT_NOT_FOUND, CANNOT_RESCHEDULE_CANCELLED_APPOINTMENT, CANNOT_RESCHEDULE_PAST_APPOINTMENT, ALREADY_CANCELLED