| Time | Patient | Doctor | Status | Reason | Actions |
|---|
| ID | Name | Phone | Date of Birth | Gender | Appointments |
|---|
Get up and running with the Hospital Booking API in 3 steps.
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"
curl -s "$BASE_URL/health" | python3 -m json.tool
// Expected response:
{ "status": "ok", "auth_enabled": true }
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" },
...
]
}
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
Walk through a typical booking scenario: find a doctor, check slots, register a patient, and book an appointment.
curl -s "$BASE_URL/api/v1/specialties" \ -H "Authorization: Bearer $API_KEY" | python3 -m json.tool
Pick a specialty — e.g. spec_001 (Cardiology)
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)
# 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
# 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
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.
curl -s "$BASE_URL/api/v1/appointments?patient_id=pat_0004&status=confirmed" \ -H "Authorization: Bearer $API_KEY" | python3 -m json.tool
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
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
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 }
}
Base URL: https://hospital-booking-demo-production.up.railway.app/api/v1
/api/v1/specialties — List all specialtiescurl -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" }
]
}/api/v1/doctors?specialty_id=&name=&available_on= — Search doctors# 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"
}
]
}/api/v1/doctors/{doctor_id}/slots?from=&to=&status= — Get available slots# 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)
/api/v1/patients — Register new patientcurl -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" }
}
}/api/v1/patients/lookup?phone= — Look up patient by phone# 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" }
}/api/v1/appointments — Book appointmentcurl -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" } }/api/v1/appointments?patient_id=&status=&from=&to= — List appointments# 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"
}
]
}/api/v1/appointments/{id} — Get appointment detailcurl -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"
}
}/api/v1/appointments/{id} — Reschedule (change slot)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" } }/api/v1/appointments/{id} — Cancel appointmentcurl -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"
}
}/api/v1/reset — Reset demo data (no auth)curl -s -X POST "$BASE_URL/api/v1/reset"
// Response 200
{
"message": "Demo data reset successfully",
"summary": { "slots": 608, "patients": 3, "appointments": 2 }
}/api/v1/hospitals — Hospital infocurl -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"
}
}
]
}/health — Health check (no auth)curl -s "$BASE_URL/health"
// Response 200
{ "status": "ok", "auth_enabled": true }{
"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