Public Appointments API
Overview
This API allows patients to book appointments without authentication. It supports both telemedicine and in-person appointments with OTP verification flow.
Related GitHub Issue: #1025
Endpoints Summary
| # | Endpoint | Method | Auth | Status |
|---|---|---|---|---|
| 1 | /clinical/public_appointments/time_slots | GET | No | ✅ Done |
| 2 | /clinical/public_appointments/send_code | POST | No | ✅ Done |
| 3 | /clinical/public_appointments | POST | No | ✅ Done |
| 4 | /clinical/public_appointments/:id/verify | POST | No | ✅ Done |
| 5 | /clinical/public_appointments/:id/resend_code | POST | No | ✅ Done |
| 6 | /clinical/public_appointments/:id | PUT | No | ✅ Done |
| 7 | /clinical/public_appointments/availability | GET | No | ✅ Done |
| 8 | /clinical/facility_listings/:unique_id | GET | No | ✅ Done |
| 9 | /clinical/telemedicine_orders | POST | JWT | ✅ Done |
| 10 | /clinical/telemedicine_orders/:id | GET | JWT | ✅ Done |
| 11 | /clinical/appointment_orders | POST | JWT | ✅ Done |
| 12 | /clinical/appointment_orders/:id | GET | JWT | ✅ Done |
| 13 | /billing/webhooks | POST | No | ✅ Done |
Common Headers
Content-Type: application/json
Accept: application/json
X-Request-ID: <uuid> (optional)1. Get Time Slots
Get available time slots for a practitioner on a specific date.
Endpoint
GET /clinical/public_appointments/time_slots
Request Headers
Content-Type: application/jsonQuery Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
facility_id | UUID | ✅ Yes | Facility UUID |
practitioner_id | UUID | ✅ Yes | Practitioner UUID |
date | String | ✅ Yes | Date in YYYY-MM-DD format |
appointment_type | String | No | fixed (default), telemedicine, walk_in |
Example Request
GET /clinical/public_appointments/time_slots?facility_id=550e8400-e29b-41d4-a716-446655440000&practitioner_id=660e8400-e29b-41d4-a716-446655440001&date=2026-03-10&appointment_type=fixedSuccess Response
Status Code: 200 OK
{
"data": {
"practitioner_id": "660e8400-e29b-41d4-a716-446655440001",
"facility_id": "550e8400-e29b-41d4-a716-446655440000",
"date": "2026-03-10",
"timezone": "Asia/Kolkata",
"fees": 500,
"telemedicine_fees": 300,
"slots": [
{
"start_at": "2026-03-10T09:00:00Z",
"end_at": "2026-03-10T09:15:00Z",
"status": "available"
},
{
"start_at": "2026-03-10T09:15:00Z",
"end_at": "2026-03-10T09:30:00Z",
"status": "available"
},
{
"start_at": "2026-03-10T09:30:00Z",
"end_at": "2026-03-10T09:45:00Z",
"status": "booked"
}
]
},
"meta": {
"request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
}Response Fields
| Field | Type | Description |
|---|---|---|
practitioner_id | UUID | Practitioner ID |
facility_id | UUID | Facility ID |
date | String | Date requested |
fees | Integer | Consultation fee in paise (500 = ₹5) |
telemedicine_fees | Integer | Telemedicine fee in paise |
slots[].start_at | ISO8601 | Slot start time (UTC) |
slots[].end_at | ISO8601 | Slot end time (UTC) |
slots[].status | String | "available" or "booked" |
timezone | String | IANA timezone for display (e.g., "Asia/Kolkata") |
Error Responses
1. Validation Error - Missing Parameters
Status Code: 422 Unprocessable Entity
{
"data": null,
"error": {
"code": "validation_failed",
"message": "Validation failed",
"details": [
{ "field": "facility_id", "message": "Facility is required" },
{ "field": "practitioner_id", "message": "Practitioner is required" },
{ "field": "date", "message": "Date is required" }
]
},
"meta": {
"request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
}2. Invalid Date Format
Status Code: 422 Unprocessable Entity
{
"data": null,
"error": {
"code": "validation_failed",
"message": "Invalid date format",
"details": [
{ "field": "date", "message": "Date must be in YYYY-MM-DD format" }
]
},
"meta": {
"request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
}3. Facility Not Found
Status Code: 422 Unprocessable Entity
{
"data": null,
"error": {
"code": "validation_failed",
"message": "Facility not found",
"details": [
{ "field": "facility_id", "message": "Couldn't find Facility with id" }
]
},
"meta": {
"request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
}4. Practitioner Not Found
Status Code: 422 Unprocessable Entity
{
"data": null,
"error": {
"code": "validation_failed",
"message": "Practitioner not found",
"details": [
{ "field": "practitioner_id", "message": "Couldn't find Practitioner with id" }
]
},
"meta": {
"request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
}2. Send OTP Code
Send verification code to patient's phone number.
Endpoint
POST /clinical/public_appointments/send_code
Request Headers
Content-Type: application/jsonRequest Payload
{
"facility_id": "UUID (required)",
"practitioner_id": "UUID (required)",
"phone_number": "string (required)",
"country_code": "string (optional, default: 91)",
"patient_name": "string (optional)",
"appointment_type": "string (optional)",
"payment_option": "string (optional)",
"preferred_time": "ISO8601 (optional)"
}Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
facility_id | UUID | ✅ Yes | Facility UUID |
practitioner_id | UUID | ✅ Yes | Practitioner UUID |
phone_number | String | ✅ Yes | Phone number (e.g., "9876543210") |
country_code | String | No | Country code (e.g., "91" or "+91", defaults to "+91") |
patient_name | String | No | Patient name |
appointment_type | String | No | telemedicine, fixed, walk_in |
payment_option | String | No | BEFORE, AFTER, NO |
preferred_time | ISO8601 | No | Preferred appointment time |
Example Request
curl -X POST https://api.kulcare.com/clinical/public_appointments/send_code \
-H "Content-Type: application/json" \
-d '{
"facility_id": "550e8400-e29b-41d4-a716-446655440000",
"practitioner_id": "660e8400-e29b-41d4-a716-446655440001",
"phone_number": "9876543210",
"country_code": "91",
"patient_name": "John Doe",
"appointment_type": "TELEMEDICINE",
"payment_option": "BEFORE",
"preferred_time": "2026-03-10T10:00:00.000Z"
}'Success Response
Status Code: 201 Created
{
"data": {
"id": "770e8400-e29b-41d4-a716-446655440002",
"patient_name": "John Doe",
"phone_number": "9876543210",
"country_code": "+91",
"facility_id": "550e8400-e29b-41d4-a716-446655440000",
"practitioner_id": "660e8400-e29b-41d4-a716-446655440001",
"appointment_type": "telemedicine",
"payment_option": "BEFORE",
"preferred_time": "2026-03-10T10:00:00.000Z",
"verified": false,
"verification_attempts": 0,
"created_at": "2026-03-06T10:00:00.000Z",
"updated_at": "2026-03-06T10:00:00.000Z"
},
"meta": {
"request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
}Error Responses
1. Validation Error - Missing Phone Number
Status Code: 422 Unprocessable Entity
{
"data": null,
"error": {
"code": "validation_failed",
"message": "Phone number is required",
"details": [
{ "field": "phone_number", "message": "Phone number is required" }
]
},
"meta": {
"request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
}2. Invalid Phone Number
Status Code: 422 Unprocessable Entity
{
"data": null,
"error": {
"code": "validation_failed",
"message": "Invalid phone number",
"details": [
{ "field": "phone_number", "message": "Phone number must be at least 10 digits" }
]
},
"meta": {
"request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
}3. Facility Not Found
Status Code: 422 Unprocessable Entity
{
"data": null,
"error": {
"code": "validation_failed",
"message": "Facility not found",
"details": [
{ "field": "facility_id", "message": "Couldn't find Facility with id" }
]
},
"meta": {
"request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
}3. Create Appointment Verification
Create a new appointment verification and send SMS code.
Endpoint
POST /clinical/public_appointments
Request Headers
Content-Type: application/jsonRequest Payload
{
"facility_id": "UUID (required)",
"practitioner_id": "UUID (optional)",
"patient_name": "string (optional)",
"phone_number": "string (required)",
"country_code": "string (optional)",
"email": "string (optional)",
"gender": "string (optional)",
"address": "string (optional)",
"date_of_birth": "string (optional)",
"preferred_time": "ISO8601 (optional)",
"appointment_type": "string (optional)",
"payment_option": "string (optional)",
"reason_for_visit": "string (optional)",
"notes": "string (optional)",
"utm_source": "string (optional)",
"utm_medium": "string (optional)",
"utm_campaign": "string (optional)",
"utm_content": "string (optional)",
"utm_term": "string (optional)"
}Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
facility_id | UUID | ✅ Yes | Facility UUID |
practitioner_id | UUID | No | Practitioner UUID (or clinic_doctor_id legacy) |
clinic_doctor_id | UUID | No | Legacy - maps to practitioner_id |
patient_name | String | No | Patient name (defaults to "Patient") |
phone_number | String | ✅ Yes | Phone number |
country_code | String | No | Country code (91 → +91) |
email | String | No | Patient email |
gender | String | No | male, female, other |
address | String | No | Patient address |
date_of_birth | Date | No | DOB in YYYY-MM-DD |
preferred_time | ISO8601 | No | Preferred time |
appointment_type | String | No | telemedicine, fixed, walk_in |
payment_option | String | No | BEFORE, AFTER, NO |
reason_for_visit | String | No | Reason for visit |
notes | String | No | Additional notes |
utm_source | String | No | UTM source |
utm_medium | String | No | UTM medium |
utm_campaign | String | No | UTM campaign |
utm_content | String | No | UTM content |
utm_term | String | No | UTM term |
Example Request
{
"facility_id": "550e8400-e29b-41d4-a716-446655440000",
"practitioner_id": "660e8400-e29b-41d4-a716-446655440001",
"patient_name": "John Doe",
"phone_number": "9876543210",
"country_code": "91",
"email": "john@example.com",
"gender": "male",
"age": 30,
"date_of_birth": "1996-03-03",
"preferred_time": "2026-03-10T10:00:00.000Z",
"appointment_type": "TELEMEDICINE",
"payment_option": "BEFORE",
"reason_for_visit": "General Consultation",
"notes": "First visit"
}Success Response
Status Code: 201 Created
{
"data": {
"id": "770e8400-e29b-41d4-a716-446655440002",
"patient_name": "John Doe",
"phone_number": "9876543210",
"country_code": "+91",
"email": "john@example.com",
"gender": "male",
"facility_id": "550e8400-e29b-41d4-a716-446655440000",
"practitioner_id": "660e8400-e29b-41d4-a716-446655440001",
"appointment_type": "telemedicine",
"payment_option": "BEFORE",
"preferred_time": "2026-03-10T10:00:00.000Z",
"verified": false,
"verification_attempts": 0,
"created_at": "2026-03-06T10:00:00.000Z",
"updated_at": "2026-03-06T10:00:00.000Z"
},
"meta": {
"request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
}Error Responses
1. Validation Error - Missing Required Fields
Status Code: 422 Unprocessable Entity
{
"data": null,
"error": {
"code": "validation_failed",
"message": "Validation failed",
"details": [
{ "field": "facility_id", "message": "Facility is required" },
{ "field": "phone_number", "message": "Phone number is required" }
]
},
"meta": {
"request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
}2. Invalid Appointment Type
Status Code: 422 Unprocessable Entity
{
"data": null,
"error": {
"code": "validation_failed",
"message": "Invalid appointment type",
"details": [
{ "field": "appointment_type", "message": "Invalid appointment type. Must be one of: telemedicine, fixed, walk_in" }
]
},
"meta": {
"request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
}4. Verify OTP Code
Verify the appointment with SMS code.
Endpoint
POST /clinical/public_appointments/:id/verify
Path Parameters
| Parameter | Type | Description |
|---|---|---|
id | UUID | Appointment verification ID (from send_code response) |
Request Headers
Content-Type: application/jsonRequest Payload
{
"verification_code": "string (required)"
}| Parameter | Type | Required | Description |
|---|---|---|---|
verification_code | String | ✅ Yes | 6-digit OTP code |
Example Request
curl -X POST https://api.kulcare.com/clinical/public_appointments/770e8400-e29b-41d4-a716-446655440002/verify \
-H "Content-Type: application/json" \
-d '{
"verification_code": "123456"
}'Success Response
Status Code: 200 OK
{
"data": {
"id": "770e8400-e29b-41d4-a716-446655440002",
"patient_name": "John Doe",
"phone_number": "9876543210",
"country_code": "+91",
"facility_id": "550e8400-e29b-41d4-a716-446655440000",
"practitioner_id": "660e8400-e29b-41d4-a716-446655440001",
"verified": true,
"verified_at": "2026-03-06T10:05:00.000Z",
"verification_attempts": 1,
"created_at": "2026-03-06T10:00:00.000Z",
"updated_at": "2026-03-06T10:05:00.000Z"
},
"meta": {
"request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
}Error Responses
1. Invalid Verification Code
Status Code: 422 Unprocessable Entity
{
"data": null,
"error": {
"code": "validation_failed",
"message": "Invalid verification code",
"details": [
{ "field": "verification_code", "message": "Invalid verification code" }
]
},
"meta": {
"request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
}2. Verification Expired
Status Code: 422 Unprocessable Entity
{
"data": null,
"error": {
"code": "validation_failed",
"message": "Verification has expired",
"details": [
{ "field": "verification_code", "message": "Verification code has expired. Please request a new code." }
]
},
"meta": {
"request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
}3. Maximum Attempts Exceeded
Status Code: 422 Unprocessable Entity
{
"data": null,
"error": {
"code": "validation_failed",
"message": "Maximum verification attempts exceeded",
"details": [
{ "field": "verification_code", "message": "Too many failed attempts. Please request a new code." }
]
},
"meta": {
"request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
}4. Verification Not Found
Status Code: 422 Unprocessable Entity
{
"data": null,
"error": {
"code": "validation_failed",
"message": "Verification not found",
"details": [
{ "field": "id", "message": "Couldn't find AppointmentVerification" }
]
},
"meta": {
"request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
}5. Already Verified
Status Code: 422 Unprocessable Entity
{
"data": null,
"error": {
"code": "validation_failed",
"message": "Already verified",
"details": [
{ "field": "verification_code", "message": "This verification has already been completed" }
]
},
"meta": {
"request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
}5. Resend OTP Code
Resend verification code for an existing verification.
Endpoint
POST /clinical/public_appointments/:id/resend_code
Path Parameters
| Parameter | Type | Description |
|---|---|---|
id | UUID | Appointment verification ID |
Example Request
curl -X POST https://api.kulcare.com/clinical/public_appointments/770e8400-e29b-41d4-a716-446655440002/resend_code \
-H "Content-Type: application/json"Success Response
Status Code: 200 OK
{
"data": {
"id": "770e8400-e29b-41d4-a716-446655440002",
"patient_name": "John Doe",
"phone_number": "9876543210",
"country_code": "+91",
"facility_id": "550e8400-e29b-41d4-a716-446655440000",
"practitioner_id": "660e8400-e29b-41d4-a716-446655440001",
"verified": false,
"verification_attempts": 1,
"created_at": "2026-03-06T10:00:00.000Z",
"updated_at": "2026-03-06T10:10:00.000Z"
},
"meta": {
"request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
}Error Responses
1. Verification Not Found
Status Code: 422 Unprocessable Entity
{
"data": null,
"error": {
"code": "validation_failed",
"message": "Verification not found",
"details": [
{ "field": "id", "message": "Couldn't find AppointmentVerification" }
]
},
"meta": {
"request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
}6. Update Appointment
Update verification details and create appointment after verification.
Endpoint
PUT /clinical/public_appointments/:id
Path Parameters
| Parameter | Type | Description |
|---|---|---|
id | UUID | Appointment verification ID |
Request Payload
{
"patient_name": "string (optional)",
"email": "string (optional)",
"gender": "string (optional)",
"age": "integer (optional)",
"date_of_birth": "string (optional)",
"preferred_time": "ISO8601 (optional)",
"preferred_start_time": "ISO8601 (optional)",
"preferred_end_time": "ISO8601 (optional)",
"reason_for_visit": "string (optional)",
"notes": "string (optional)"
}| Parameter | Type | Required | Description |
|---|---|---|---|
patient_name | String | No | Patient name (or name legacy) |
name | String | No | Legacy - maps to patient_name |
email | String | No | Patient email |
gender | String | No | male, female, other |
age | Integer | No | Patient age |
date_of_birth | Date | No | DOB in YYYY-MM-DD |
preferred_time | ISO8601 | No | Preferred appointment time |
preferred_start_time | ISO8601 | No | Preferred slot start time |
preferred_end_time | ISO8601 | No | Preferred slot end time |
reason_for_visit | String | No | Reason for visit |
notes | String | No | Additional notes |
Example Request
curl -X PUT https://api.kulcare.com/clinical/public_appointments/770e8400-e29b-41d4-a716-446655440002 \
-H "Content-Type: application/json" \
-d '{
"patient_name": "John Doe",
"email": "john@example.com",
"gender": "male",
"age": 30,
"date_of_birth": "1996-03-03",
"preferred_time": "2026-03-10T10:00:00.000Z",
"reason_for_visit": "Consultation",
"notes": "First visit"
}'Success Response
Status Code: 200 OK
{
"data": {
"verification": {
"id": "770e8400-e29b-41d4-a716-446655440002",
"patient_name": "John Doe",
"phone_number": "9876543210",
"country_code": "+91",
"email": "john@example.com",
"gender": "male",
"facility_id": "550e8400-e29b-41d4-a716-446655440000",
"practitioner_id": "660e8400-e29b-41d4-a716-446655440001",
"appointment_id": "880e8400-e29b-41d4-a716-446655440003",
"verified": true,
"verified_at": "2026-03-06T10:05:00.000Z",
"created_at": "2026-03-06T10:00:00.000Z",
"updated_at": "2026-03-06T10:15:00.000Z"
},
"appointment": {
"id": "880e8400-e29b-41d4-a716-446655440003",
"patient_id": "990e8400-e29b-41d4-a716-446655440004",
"practitioner_id": "660e8400-e29b-41d4-a716-446655440001",
"facility_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "scheduled",
"appointment_type": "telemedicine",
"start_time": "2026-03-10T10:00:00.000Z",
"end_time": "2026-03-10T10:30:00.000Z",
"created_at": "2026-03-06T10:15:00.000Z",
"updated_at": "2026-03-06T10:15:00.000Z"
}
},
"meta": {
"request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
}Error Responses
1. Verification Not Verified
Status Code: 422 Unprocessable Entity
{
"data": null,
"error": {
"code": "validation_failed",
"message": "Verification required",
"details": [
{ "field": "verification", "message": "Phone number must be verified before creating appointment" }
]
},
"meta": {
"request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
}2. Slot Not Available
Status Code: 422 Unprocessable Entity
{
"data": null,
"error": {
"code": "validation_failed",
"message": "Slot not available",
"details": [
{ "field": "preferred_time", "message": "Selected time slot is not available" }
]
},
"meta": {
"request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
}7. Get Facility Listing
Get facility details by unique_id. This is a public endpoint for displaying facility information.
Endpoint
GET /clinical/facility_listings/:unique_id
Path Parameters
| Parameter | Type | Description |
|---|---|---|
unique_id | String | Facility unique identifier |
Example Request
GET /clinical/facility_listings/ABC123XYZSuccess Response
Status Code: 200 OK
{
"data": {
"facility": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "City Hospital",
"address": "123 Main Street",
"city": "Mumbai",
"state": "Maharashtra",
"pincode": "400001",
"phone": "+91-9876543210",
"logo_url": "https://cdn.kulcare.com/facilities/logo.png",
"cover_image_url": "https://cdn.kulcare.com/facilities/cover.jpg"
},
"common_doctors": [
{
"id": "660e8400-e29b-41d4-a716-446655440001",
"name": "Dr. John Smith",
"speciality": "General Physician",
"experience_years": 15,
"qualification": "MBBS, MD",
"photo_url": "https://cdn.kulcare.com/doctors/photo.jpg",
"rating": 4.8,
" consultation_fees": 500
}
],
"facility_group_locations": [
{
"id": "550e8400-e29b-41d4-a716-446655440001",
"name": "City Hospital - Branch 2",
"address": "456 Secondary Street",
"city": "Mumbai"
}
],
"facility_schedules": [
{
"day": "monday",
"open_time": "09:00",
"close_time": "18:00",
"is_available": true
}
],
"reviews": [
{
"id": "review-001",
"patient_name": "Anonymous",
"rating": 5,
"comment": "Excellent service!",
"created_at": "2026-02-15T10:00:00.000Z"
}
],
"average_rating": 4.5,
"total_reviews": 120,
"facility_services_list": [
{
"id": "service-001",
"name": "General Checkup",
"description": " routine health checkup"
}
],
"facility_procedures": [
{
"id": "procedure-001",
"name": "Blood Test",
"description": "Complete blood count",
"price": 500
}
]
},
"meta": {
"request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
}Response Fields
| Field | Type | Description |
|---|---|---|
facility | Object | Basic facility information |
facility.id | UUID | Facility ID |
facility.name | String | Facility name |
facility.address | String | Facility address |
facility.city | String | City |
facility.state | String | State |
facility.pincode | String | Pincode |
facility.phone | String | Contact phone |
facility.logo_url | String | Logo image URL |
facility.cover_image_url | String | Cover image URL |
common_doctors | Array | List of doctors at this facility |
facility_group_locations | Array | Other locations in the same group |
facility_schedules | Array | Weekly schedule |
reviews | Array | Recent reviews |
average_rating | Float | Average rating (0-5) |
total_reviews | Integer | Total number of reviews |
facility_services_list | Array | Services offered |
facility_procedures | Array | Procedures available with prices |
Error Responses
1. Facility Not Found
Status Code: 404 Not Found
{
"data": null,
"error": {
"code": "not_found",
"message": "Facility not found"
},
"meta": {
"request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
}8. Check Practitioner Availability
Check if a practitioner is available for appointments.
Endpoint
GET /clinical/public_appointments/availability
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
facility_id | UUID | ✅ Yes | Facility UUID |
practitioner_id | UUID | ✅ Yes | Practitioner UUID |
date | Date | No | Date in YYYY-MM-DD format |
appointment_type | String | No | Type: walk_in (default), fixed, telemedicine |
Success Response
Status Code: 200 OK
{
"data": {
"available": true,
"practitioner_id": "660e8400-e29b-41d4-a716-446655440001",
"facility_id": "550e8400-e29b-41d4-a716-446655440000",
"date": "2026-03-10"
},
"meta": {
"request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
}9. Create Telemedicine Order
Create a Razorpay order for telemedicine consultation. Requires JWT authentication.
Endpoint
POST /clinical/telemedicine_orders
Request Headers
Content-Type: application/json
Authorization: Bearer <jwt_token>Request Payload
{
"practitioner_id": "UUID (required)",
"patient_id": "UUID (required)",
"facility_id": "UUID (required)",
"appointment_verification_id": "UUID (optional)",
"existing_appointment_id": "UUID (optional)",
"appointment_id": "UUID (optional)",
"slot_start_time": "ISO8601 (optional)",
"slot_end_time": "ISO8601 (optional)",
"appointment_type": "string (optional)",
"payment_option": "string (optional)",
"reason_for_visit": "string (optional)",
"international_payment": "boolean (optional)",
"amount": "integer (optional)"
}| Parameter | Type | Required | Description |
|---|---|---|---|
practitioner_id | UUID | ✅ Yes | Practitioner UUID |
patient_id | UUID | ✅ Yes | Patient UUID |
facility_id | UUID | ✅ Yes | Facility UUID |
appointment_verification_id | UUID | No | From public appointment flow |
existing_appointment_id | UUID | No | Existing appointment ID |
appointment_id | UUID | No | Appointment ID |
slot_start_time | ISO8601 | No | Slot start time |
slot_end_time | ISO8601 | No | Slot end time |
appointment_type | String | No | telemedicine (default) |
payment_option | String | No | BEFORE (default), AFTER, NO |
reason_for_visit | String | No | Reason for visit |
international_payment | Boolean | No | Enable international payments |
amount | Integer | No | Amount in paise (auto-calculated if not provided) |
Example Request
curl -X POST https://api.kulcare.com/clinical/telemedicine_orders \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-d '{
"practitioner_id": "660e8400-e29b-41d4-a716-446655440001",
"patient_id": "990e8400-e29b-41d4-a716-446655440004",
"facility_id": "550e8400-e29b-41d4-a716-446655440000",
"appointment_verification_id": "770e8400-e29b-41d4-a716-446655440002",
"slot_start_time": "2026-03-10T10:00:00.000Z",
"slot_end_time": "2026-03-10T10:30:00.000Z",
"appointment_type": "telemedicine",
"payment_option": "BEFORE",
"reason_for_visit": "General Consultation"
}'Success Response
Status Code: 201 Created
{
"data": {
"id": "aa0e8400-e29b-41d4-a716-446655440005",
"facility_id": "550e8400-e29b-41d4-a716-446655440000",
"patient_id": "990e8400-e29b-41d4-a716-446655440004",
"practitioner_id": "660e8400-e29b-41d4-a716-446655440001",
"razorpay_order_id": "order_abc123",
"amount": 30000,
"currency": "INR",
"status": "pending",
"appointment_type": "telemedicine",
"payment_option": "BEFORE",
"slot_start_time": "2026-03-10T10:00:00.000Z",
"slot_end_time": "2026-03-10T10:30:00.000Z",
"reason_for_visit": "General Consultation",
"international_payment": false,
"created_at": "2026-03-06T10:00:00.000Z",
"updated_at": "2026-03-06T10:00:00.000Z"
},
"meta": {
"request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"razorpay_order_id": "order_abc123"
}
}Error Responses
1. Authentication Required
Status Code: 401 Unauthorized
{
"data": null,
"error": {
"code": "unauthorized",
"message": "You need to sign in or sign up before continuing."
},
"meta": {
"request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
}2. Validation Error - Missing Required Fields
Status Code: 422 Unprocessable Entity
{
"data": null,
"error": {
"code": "validation_failed",
"message": "Validation failed",
"details": [
{ "field": "practitioner_id", "message": "Practitioner is required" },
{ "field": "patient_id", "message": "Patient is required" },
{ "field": "facility_id", "message": "Facility is required" }
]
},
"meta": {
"request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
}10. Get Telemedicine Order
Get telemedicine order details. Requires JWT authentication.
Endpoint
GET /clinical/telemedicine_orders/:id
Request Headers
Authorization: Bearer <jwt_token>Path Parameters
| Parameter | Type | Description |
|---|---|---|
id | UUID | Telemedicine order ID |
Success Response
Status Code: 200 OK
{
"data": {
"id": "aa0e8400-e29b-41d4-a716-446655440005",
"facility_id": "550e8400-e29b-41d4-a716-446655440000",
"patient_id": "990e8400-e29b-41d4-a716-446655440004",
"practitioner_id": "660e8400-e29b-41d4-a716-446655440001",
"razorpay_order_id": "order_abc123",
"amount": 30000,
"currency": "INR",
"status": "paid",
"appointment_type": "telemedicine",
"payment_option": "BEFORE",
"slot_start_time": "2026-03-10T10:00:00.000Z",
"slot_end_time": "2026-03-10T10:30:00.000Z",
"reason_for_visit": "General Consultation",
"international_payment": false,
"appointment_id": "880e8400-e29b-41d4-a716-446655440003",
"appointment_verification_id": "770e8400-e29b-41d4-a716-446655440002",
"created_at": "2026-03-06T10:00:00.000Z",
"updated_at": "2026-03-06T10:30:00.000Z"
},
"meta": {
"request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
}Error Responses
1. Order Not Found
Status Code: 404 Not Found
{
"data": null,
"error": {
"code": "not_found",
"message": "Order not found"
},
"meta": {
"request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
}11. Create Appointment Order
Create a Razorpay order for in-person appointment. Requires JWT authentication.
Endpoint
POST /clinical/appointment_orders
Request Headers
Content-Type: application/json
Authorization: Bearer <jwt_token>Request Payload
| Parameter | Type | Required | Description |
|---|---|---|---|
practitioner_id | UUID | ✅ Yes | Practitioner UUID |
patient_id | UUID | ✅ Yes | Patient UUID |
facility_id | UUID | ✅ Yes | Facility UUID |
appointment_verification_id | UUID | No | From public appointment flow |
existing_appointment_id | UUID | No | Existing appointment ID |
appointment_id | UUID | No | Appointment ID |
slot_start_time | ISO8601 | No | Slot start time |
slot_end_time | ISO8601 | No | Slot end time |
appointment_type | String | No | fixed (default), walk_in |
payment_option | String | No | BEFORE (default), AFTER, NO |
reason_for_visit | String | No | Reason for visit |
international_payment | Boolean | No | Enable international payments |
amount | Integer | No | Amount in paise (auto-calculated if not provided) |
Example Request
curl -X POST https://api.kulcare.com/clinical/appointment_orders \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-d '{
"practitioner_id": "660e8400-e29b-41d4-a716-446655440001",
"patient_id": "990e8400-e29b-41d4-a716-446655440004",
"facility_id": "550e8400-e29b-41d4-a716-446655440000",
"appointment_verification_id": "770e8400-e29b-41d4-a716-446655440002",
"slot_start_time": "2026-03-10T10:00:00.000Z",
"slot_end_time": "2026-03-10T10:30:00.000Z",
"appointment_type": "fixed",
"payment_option": "BEFORE",
"reason_for_visit": "General Consultation"
}'Success Response
Status Code: 201 Created
{
"data": {
"id": "bb0e8400-e29b-41d4-a716-446655440006",
"facility_id": "550e8400-e29b-41d4-a716-446655440000",
"patient_id": "990e8400-e29b-41d4-a716-446655440004",
"practitioner_id": "660e8400-e29b-41d4-a716-446655440001",
"razorpay_order_id": "order_def456",
"amount": 50000,
"currency": "INR",
"status": "pending",
"appointment_type": "fixed",
"payment_option": "BEFORE",
"slot_start_time": "2026-03-10T10:00:00.000Z",
"slot_end_time": "2026-03-10T10:30:00.000Z",
"reason_for_visit": "General Consultation",
"international_payment": false,
"created_at": "2026-03-06T10:00:00.000Z",
"updated_at": "2026-03-06T10:00:00.000Z"
},
"meta": {
"request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"razorpay_order_id": "order_def456"
}
}12. Get Appointment Order
Get appointment order details. Requires JWT authentication.
Endpoint
GET /clinical/appointment_orders/:id
Request Headers
Authorization: Bearer <jwt_token>Path Parameters
| Parameter | Type | Description |
|---|---|---|
id | UUID | Appointment order ID |
Success Response
Status Code: 200 OK
{
"data": {
"id": "bb0e8400-e29b-41d4-a716-446655440006",
"facility_id": "550e8400-e29b-41d4-a716-446655440000",
"patient_id": "990e8400-e29b-41d4-a716-446655440004",
"practitioner_id": "660e8400-e29b-41d4-a716-446655440001",
"razorpay_order_id": "order_def456",
"amount": 50000,
"currency": "INR",
"status": "paid",
"appointment_type": "fixed",
"payment_option": "BEFORE",
"slot_start_time": "2026-03-10T10:00:00.000Z",
"slot_end_time": "2026-03-10T10:30:00.000Z",
"reason_for_visit": "General Consultation",
"international_payment": false,
"appointment_id": "880e8400-e29b-41d4-a716-446655440003",
"appointment_verification_id": "770e8400-e29b-41d4-a716-446655440002",
"created_at": "2026-03-06T10:00:00.000Z",
"updated_at": "2026-03-06T10:30:00.000Z"
},
"meta": {
"request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
}13. Billing Webhook
Receive Razorpay payment webhook events.
Endpoint
POST /billing/webhooks
Request Headers
Content-Type: application/json
X-Razorpay-Signature: <razorpay_signature>Request Payload (Razorpay Format)
{
"event": "payment.captured",
"payload": {
"payment": {
"entity": {
"id": "pay_abc123",
"order_id": "order_abc123",
"amount": 30000,
"currency": "INR",
"status": "captured"
}
}
}
}Supported Events
| Event | Description |
|---|---|
payment.captured | Payment successful |
payment.failed | Payment failed |
Response
Status Code: 200 OK
{
"received": true
}Enums Reference
Appointment Type
| Value | Description |
|---|---|
telemedicine | Video consultation |
fixed | Scheduled in-person appointment |
walk_in | Walk-in appointment |
Payment Option
| Value | Description |
|---|---|
BEFORE | Payment before appointment |
AFTER | Payment after appointment |
NO | No payment required |
Order Status
| Value | Description |
|---|---|
pending | Awaiting payment |
paid | Payment successful |
failed | Payment failed |
Gender
| Value | Description |
|---|---|
male | Male |
female | Female |
other | Other |
Field Mappings (Legacy → New Schema)
| Legacy Field | New Schema Field |
|---|---|
clinic_doctor_id | practitioner_id |
name | patient_name |
clinic_patient_id | patient_id |
ONCALL | telemedicine |
FIXED | fixed |
WALK_IN / WALKIN | walk_in |
91 (country_code) | +91 |
Complete Booking Flow
1. GET /clinical/public_appointments/time_slots
↓ (Get available slots with fees)
Response: { slots: [...], fees: 500, telemedicine_fees: 300 }
2. POST /clinical/public_appointments/send_code
↓ (Create verification, send OTP)
Request: { facility_id, practitioner_id, phone_number, ... }
Response: { id: "verification_id", verified: false, ... }
3. POST /clinical/public_appointments/:id/verify
↓ (Verify OTP - marks as verified)
Request: { verification_code: "123456" }
Response: { id: "verification_id", verified: true, ... }
4. PUT /clinical/public_appointments/:id
↓ (Create actual appointment)
Request: { patient_name, email, gender, ... }
Response: { verification: {...}, appointment: {...} }
5. POST /clinical/telemedicine_orders (JWT required)
↓ (Create Razorpay order for payment)
Request: { practitioner_id, patient_id, facility_id, ... }
Response: { id: "order_id", razorpay_order_id: "order_xxx", amount: 30000, status: "pending" }
6. [Frontend handles Razorpay payment]
- Use razorpay_order_id to collect payment
- Redirect to Razorpay checkout
7. POST /billing/webhooks
↓ (Razorpay notifies payment status)
Event: payment.captured or payment.failed
8. Appointment status updated to confirmedAmount Values
All amounts are in paise (1 rupee = 100 paise).
| Amount (Paise) | Amount (Rupees) |
|---|---|
| 30000 | ₹300 |
| 50000 | ₹500 |
| 100000 | ₹1,000 |
Error Codes Summary
| Code | Description | HTTP Status |
|---|---|---|
validation_failed | Request validation failed | 422 |
not_found | Resource not found | 404 |
unauthorized | Authentication required | 401 |
internal_error | Server error | 500 |
Notes for Frontend Team
-
Public Endpoints: Time slots, send_code, verify, update, availability are public (no auth required).
-
JWT Required: Telemedicine orders and appointment orders require JWT authentication.
-
OTP Flow: Always implement the OTP verification flow for public bookings.
-
Verification Expiry: OTP codes expire after 30 minutes. Show appropriate error and allow resend.
-
Max Attempts: Limit verification attempts to 3. Show error and allow resend after limit.
-
Amount in Paise: All monetary values are in paise (multiply by 100 for rupees).
-
Country Code: Input "91" becomes "+91" automatically.
-
Appointment Types: Map frontend types to backend:
ONCALL→telemedicine,FIXED→fixed.
Related Files
- Controller:
backend/domains/clinical/controllers/public_appointments_controller.rb - Controller:
backend/domains/clinical/controllers/facility_listings_controller.rb - Controller:
backend/domains/clinical/controllers/telemedicine_orders_controller.rb - Controller:
backend/domains/clinical/controllers/appointment_orders_controller.rb - Controller:
backend/domains/billing/controllers/webhooks_controller.rb - Model:
backend/domains/clinical/models/appointment_verification.rb - Model:
backend/domains/clinical/models/telemedicine_order.rb - Model:
backend/domains/clinical/models/appointment_order.rb - Blueprint:
backend/domains/clinical/blueprints/facility_listing_blueprint.rb
Support
For questions or issues, contact the Backend Team or refer to:
- API Documentation:
/api/docs - GitHub Issue: #1025