Patient Payments API
API endpoints for managing patient payments in the Kulcare billing system.
Overview
The Patient Payments API allows you to create, list, view, update, and soft-delete payment records for patients at a facility. All payments are scoped to a patient_facility (the relationship between a patient and a facility).
Base URL
{{base_url}}/billingAuthentication
All endpoints require Bearer token authentication:
Authorization: Bearer <access_token>Common Headers
Content-Type: application/json
Accept: application/jsonEndpoints
| Method | Endpoint | Description |
|---|---|---|
| GET | /patient_facilities/:patient_facility_id/payments | List payments for a patient |
| POST | /patient_facilities/:patient_facility_id/payments | Create a payment |
| GET | /patient_payments/:id | Get payment details |
| PATCH | /patient_payments/:id | Update a payment |
| DELETE | /patient_payments/:id | Soft delete a payment |
1. List Patient Payments
Retrieves a paginated list of payments for a specific patient_facility.
Request
GET /billing/patient_facilities/{patient_facility_id}/paymentsPath Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
patient_facility_id | UUID | Yes | The patient_facility ID |
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
page | integer | 1 | Page number |
per_page | integer | 25 | Items per page (max: 100) |
facility_id | UUID | - | Filter by facility |
practitioner_id | UUID | - | Filter by practitioner |
appointment_id | UUID | - | Filter by appointment |
status | string | - | Filter by status: pending, completed, failed, refunded, cancelled |
payment_method | string | - | Filter by method: cash, card, upi, bank_transfer, cheque, wallet, gateway |
start_date | string | - | Filter from date (YYYY-MM-DD) |
end_date | string | - | Filter to date (YYYY-MM-DD) |
search | string | - | Search by payment number |
sort_by | string | payment_date | Sort column: payment_date, amount, payment_number, status, created_at |
sort_order | string | desc | Sort order: asc or desc |
Example Request
GET /billing/patient_facilities/019bb764-9839-781a-9383-8ad47af09cc5/payments?page=1&per_page=10&status=completed&start_date=2026-01-01&end_date=2026-02-28Success Response
Status: 200 OK
{
"data": [
{
"id": "019bc196-8d50-7338-a891-dc6b3cd73a82",
"payment_number": "PAY-2026-000001",
"status": "completed",
"payment_method": "cash",
"currency": "INR",
"patient_id": "019bb764-9839-781a-9383-8ad47af09cc4",
"facility_id": "019bb761-4a1c-72e9-9a04-75c5bf5f0152",
"patient_facility_id": "019bb764-9839-781a-9383-8ad47af09cc5",
"appointment_id": "019bbb9d-5b88-7ec4-a391-0e52fa5c9807",
"practitioner_id": "019bb761-4a1c-72e9-9a04-75c5bf5f0153",
"amount": 1500.0,
"payment_date": "2026-02-19",
"is_gateway_payment": false,
"reason": "Consultation payment",
"notes": "Payment for general consultation",
"patient": {
"id": "019bb764-9839-781a-9383-8ad47af09cc4",
"name": "John Doe",
"phone": "+919876543210"
},
"practitioner": {
"id": "019bb761-4a1c-72e9-9a04-75c5bf5f0153",
"name": "Dr. Jane Smith"
},
"deleted": false,
"created_at": "2026-02-19T10:00:00Z",
"updated_at": "2026-02-19T10:00:00Z"
}
],
"meta": {
"request_id": "019bc197-1234-5678-9abc-def012345678",
"pagination": {
"current_page": 1,
"per_page": 10,
"total_entries": 25,
"total_pages": 3
}
}
}2. Create Patient Payment
Creates a new payment record for a patient_facility.
Request
POST /billing/patient_facilities/{patient_facility_id}/paymentsPath Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
patient_facility_id | UUID | Yes | The patient_facility ID |
Request Body
{
"payment": {
"amount": 1500.0,
"payment_method": "cash",
"payment_date": "2026-02-19",
"currency": "INR",
"facility_id": "019bb761-4a1c-72e9-9a04-75c5bf5f0152",
"practitioner_id": "019bb761-4a1c-72e9-9a04-75c5bf5f0153",
"appointment_id": null,
"reason": "Consultation payment",
"notes": "Payment for general consultation"
}
}Body Parameters
| Field | Type | Required | Description |
|---|---|---|---|
amount | decimal | Yes | Payment amount (must be > 0) |
payment_method | string | Yes | Payment method (see enum values below) |
payment_date | date | No | Payment date in YYYY-MM-DD format (defaults to today) |
currency | string | No | Currency code (default: INR) |
facility_id | UUID | No | Facility ID (defaults to patient_facility's facility) |
practitioner_id | UUID | No | Practitioner who received the payment |
appointment_id | UUID | No | Associated appointment ID |
reason | string | No | Reason for payment |
notes | string | No | Additional notes |
Payment Method Enum Values
| Value | Description |
|---|---|
cash | Cash payment |
card | Credit/Debit card |
upi | UPI payment |
bank_transfer | Bank transfer |
cheque | Cheque payment |
wallet | Digital wallet |
gateway | Payment gateway (Razorpay, etc.) |
Legacy Parameter Support (Backward Compatibility)
The API also accepts flat parameters for backward compatibility with legacy clients:
{
"amount": 1500.0,
"mode_of_payment": "CASH",
"clinic_id": "019bb761-4a1c-72e9-9a04-75c5bf5f0152",
"doctor_id": "019bb761-4a1c-72e9-9a04-75c5bf5f0153",
"payment_date": "2026-02-19",
"reason": "Consultation payment"
}Legacy Field Mapping:
| Legacy Field | Maps To |
|---|---|
mode_of_payment | payment_method |
clinic_id | facility_id |
doctor_id | practitioner_id |
Legacy Payment Method Mapping:
| Legacy Value | Maps To |
|---|---|
CASH | cash |
CARD | card |
ONLINE | gateway |
UPI | upi |
CHEQUE | cheque |
OTHERS | cash |
PAYMENT_LINK | gateway |
Success Response
Status: 201 Created
{
"data": {
"id": "019bc196-8d50-7338-a891-dc6b3cd73a82",
"payment_number": "PAY-2026-000001",
"status": "completed",
"payment_method": "cash",
"currency": "INR",
"patient_id": "019bb764-9839-781a-9383-8ad47af09cc4",
"facility_id": "019bb761-4a1c-72e9-9a04-75c5bf5f0152",
"patient_facility_id": "019bb764-9839-781a-9383-8ad47af09cc5",
"appointment_id": null,
"practitioner_id": null,
"amount": 1500.0,
"payment_date": "2026-02-19",
"is_gateway_payment": false,
"reason": "Consultation payment",
"patient": {
"id": "019bb764-9839-781a-9383-8ad47af09cc4",
"name": "John Doe",
"phone": "+919876543210"
},
"practitioner": null,
"deleted": false,
"created_at": "2026-02-19T10:00:00Z",
"updated_at": "2026-02-19T10:00:00Z"
},
"meta": {
"request_id": "019bc197-1234-5678-9abc-def012345678"
}
}Error Responses
400 Bad Request - Validation Error
{
"error": {
"code": "VALIDATION_ERROR",
"message": "amount is required",
"details": {}
},
"meta": {
"request_id": "019bc197-1234-5678-9abc-def012345678"
}
}404 Not Found - Patient Facility Not Found
{
"error": {
"code": "RESOURCE_NOT_FOUND",
"message": "PatientFacility not found",
"details": {
"resource": "PatientFacility",
"id": "019bb764-9839-781a-9383-8ad47af09cc5"
}
},
"meta": {
"request_id": "019bc197-1234-5678-9abc-def012345678"
}
}3. Get Payment Details
Retrieves details of a specific payment.
Request
GET /billing/patient_payments/{id}Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | UUID | Yes | The payment ID |
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
view | string | patient_facility | Response view: normal or patient_facility |
Example Request
GET /billing/patient_payments/019bc196-8d50-7338-a891-dc6b3cd73a82Success Response
Status: 200 OK
{
"data": {
"id": "019bc196-8d50-7338-a891-dc6b3cd73a82",
"payment_number": "PAY-2026-000001",
"status": "completed",
"payment_method": "cash",
"currency": "INR",
"patient_id": "019bb764-9839-781a-9383-8ad47af09cc4",
"facility_id": "019bb761-4a1c-72e9-9a04-75c5bf5f0152",
"patient_facility_id": "019bb764-9839-781a-9383-8ad47af09cc5",
"appointment_id": "019bbb9d-5b88-7ec4-a391-0e52fa5c9807",
"practitioner_id": "019bb761-4a1c-72e9-9a04-75c5bf5f0153",
"amount": 1500.0,
"payment_date": "2026-02-19",
"is_gateway_payment": false,
"reason": "Consultation payment",
"notes": "Payment received at reception",
"patient": {
"id": "019bb764-9839-781a-9383-8ad47af09cc4",
"name": "John Doe",
"phone": "+919876543210"
},
"practitioner": {
"id": "019bb761-4a1c-72e9-9a04-75c5bf5f0153",
"name": "Dr. Jane Smith"
},
"deleted": false,
"created_at": "2026-02-19T10:00:00Z",
"updated_at": "2026-02-19T10:00:00Z"
},
"meta": {
"request_id": "019bc197-1234-5678-9abc-def012345678"
}
}Error Response
404 Not Found
{
"error": {
"code": "RESOURCE_NOT_FOUND",
"message": "Payment not found",
"details": {
"resource": "Payment",
"id": "00000000-0000-0000-0000-000000000000"
}
},
"meta": {
"request_id": "019bc197-1234-5678-9abc-def012345678"
}
}4. Update Payment
Updates an existing payment record.
Request
PATCH /billing/patient_payments/{id}Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | UUID | Yes | The payment ID |
Request Body
For Pending Payments (all fields updatable)
{
"payment": {
"amount": 2000.0,
"payment_method": "upi",
"payment_date": "2026-02-19",
"practitioner_id": "019bb761-4a1c-72e9-9a04-75c5bf5f0153",
"appointment_id": "019bbb9d-5b88-7ec4-a391-0e52fa5c9807",
"reason": "Follow-up consultation",
"notes": "Updated payment notes"
}
}For Completed Payments (only notes updatable)
{
"payment": {
"notes": "Payment received at reception desk"
}
}Updatable Fields
| Field | Pending | Completed | Description |
|---|---|---|---|
amount | Yes | No | Payment amount |
payment_method | Yes | No | Payment method |
payment_date | Yes | No | Payment date |
practitioner_id | Yes | No | Practitioner ID |
appointment_id | Yes | No | Associated appointment |
reason | Yes | No | Payment reason |
notes | Yes | Yes | Additional notes |
Business Rules
- Pending payments: All fields can be updated
- Completed payments: Only
notescan be updated - Failed/Refunded/Cancelled payments: Cannot be updated
- Deleted payments: Cannot be updated
Success Response
Status: 200 OK
{
"data": {
"id": "019bc196-8d50-7338-a891-dc6b3cd73a82",
"payment_number": "PAY-2026-000001",
"status": "completed",
"payment_method": "cash",
"currency": "INR",
"patient_id": "019bb764-9839-781a-9383-8ad47af09cc4",
"facility_id": "019bb761-4a1c-72e9-9a04-75c5bf5f0152",
"patient_facility_id": "019bb764-9839-781a-9383-8ad47af09cc5",
"appointment_id": "019bbb9d-5b88-7ec4-a391-0e52fa5c9807",
"practitioner_id": "019bb761-4a1c-72e9-9a04-75c5bf5f0153",
"amount": 1500.0,
"payment_date": "2026-02-19",
"is_gateway_payment": false,
"reason": "Consultation payment",
"notes": "Payment received at reception desk",
"patient": {
"id": "019bb764-9839-781a-9383-8ad47af09cc4",
"name": "John Doe",
"phone": "+919876543210"
},
"practitioner": {
"id": "019bb761-4a1c-72e9-9a04-75c5bf5f0153",
"name": "Dr. Jane Smith"
},
"deleted": false,
"created_at": "2026-02-19T10:00:00Z",
"updated_at": "2026-02-19T10:30:00Z"
},
"meta": {
"request_id": "019bc197-1234-5678-9abc-def012345678"
}
}Error Responses
422 Unprocessable Entity - Cannot Update
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Cannot update a deleted payment",
"details": {}
},
"meta": {
"request_id": "019bc197-1234-5678-9abc-def012345678"
}
}5. Delete Payment
Soft deletes a payment record. The payment is not permanently removed but marked as deleted.
Request
DELETE /billing/patient_payments/{id}?reason={deletion_reason}Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | UUID | Yes | The payment ID |
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
reason | string | Yes | Reason for deletion |
Example Request
DELETE /billing/patient_payments/019bc196-8d50-7338-a891-dc6b3cd73a82?reason=Payment%20recorded%20in%20errorSuccess Response
Status: 204 No Content
No response body.
Error Responses
422 Unprocessable Entity - Missing Reason
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Reason is required for deletion",
"details": {
"field": "reason"
}
},
"meta": {
"request_id": "019bc197-1234-5678-9abc-def012345678"
}
}404 Not Found
{
"error": {
"code": "RESOURCE_NOT_FOUND",
"message": "Payment not found",
"details": {
"resource": "Payment",
"id": "019bc196-8d50-7338-a891-dc6b3cd73a82"
}
},
"meta": {
"request_id": "019bc197-1234-5678-9abc-def012345678"
}
}Data Models
Payment Object
| Field | Type | Description |
|---|---|---|
id | UUID | Unique identifier |
payment_number | string | Auto-generated number (format: PAY-YYYY-NNNNNN) |
status | enum | Payment status (see below) |
payment_method | enum | Payment method (see below) |
currency | string | Currency code (default: INR) |
amount | decimal | Payment amount |
payment_date | date | Date of payment |
patient_id | UUID | Patient ID |
facility_id | UUID | Facility ID |
patient_facility_id | UUID | PatientFacility ID |
appointment_id | UUID | Associated appointment (nullable) |
practitioner_id | UUID | Practitioner who received payment (nullable) |
is_gateway_payment | boolean | Whether paid via gateway |
reason | string | Payment reason (nullable) |
notes | string | Additional notes (nullable) |
patient | object | Nested patient object |
practitioner | object | Nested practitioner object (nullable) |
deleted | boolean | Whether soft-deleted |
created_at | datetime | Creation timestamp |
updated_at | datetime | Last update timestamp |
Status Enum
| Value | Description |
|---|---|
pending | Payment is pending |
completed | Payment completed successfully |
failed | Payment failed |
refunded | Payment was refunded |
cancelled | Payment was cancelled |
Payment Method Enum
| Value | Description |
|---|---|
cash | Cash payment |
card | Credit/Debit card |
upi | UPI payment |
bank_transfer | Bank transfer |
cheque | Cheque payment |
wallet | Digital wallet |
gateway | Payment gateway |
Patient Nested Object
| Field | Type | Description |
|---|---|---|
id | UUID | Patient ID |
name | string | Patient name |
phone | string | Phone number |
Practitioner Nested Object
| Field | Type | Description |
|---|---|---|
id | UUID | Practitioner ID |
name | string | Practitioner name |
Error Codes
| Code | HTTP Status | Description |
|---|---|---|
VALIDATION_ERROR | 400, 422 | Request validation failed |
RESOURCE_NOT_FOUND | 404 | Requested resource not found |
UNAUTHORIZED | 401 | Authentication required |
FORBIDDEN | 403 | Insufficient permissions |
TypeScript Interfaces
// Payment status enum
type PaymentStatus = 'pending' | 'completed' | 'failed' | 'refunded' | 'cancelled';
// Payment method enum
type PaymentMethod = 'cash' | 'card' | 'upi' | 'bank_transfer' | 'cheque' | 'wallet' | 'gateway';
// Patient nested object
interface PaymentPatient {
id: string;
name: string;
phone: string;
}
// Practitioner nested object
interface PaymentPractitioner {
id: string;
name: string;
}
// Payment object
interface Payment {
id: string;
payment_number: string;
status: PaymentStatus;
payment_method: PaymentMethod;
currency: string;
amount: number;
payment_date: string;
patient_id: string;
facility_id: string;
patient_facility_id: string;
appointment_id: string | null;
practitioner_id: string | null;
is_gateway_payment: boolean;
reason: string | null;
notes: string | null;
patient: PaymentPatient;
practitioner: PaymentPractitioner | null;
deleted: boolean;
created_at: string;
updated_at: string;
}
// Create payment request
interface CreatePaymentRequest {
payment: {
amount: number;
payment_method: PaymentMethod;
payment_date?: string;
currency?: string;
facility_id?: string;
practitioner_id?: string;
appointment_id?: string;
reason?: string;
notes?: string;
};
}
// Update payment request (pending)
interface UpdatePaymentRequestPending {
payment: {
amount?: number;
payment_method?: PaymentMethod;
payment_date?: string;
practitioner_id?: string;
appointment_id?: string;
reason?: string;
notes?: string;
};
}
// Update payment request (completed - only notes)
interface UpdatePaymentRequestCompleted {
payment: {
notes?: string;
};
}
// List payments query params
interface ListPaymentsParams {
page?: number;
per_page?: number;
facility_id?: string;
practitioner_id?: string;
appointment_id?: string;
status?: PaymentStatus;
payment_method?: PaymentMethod;
start_date?: string;
end_date?: string;
search?: string;
sort_by?: 'payment_date' | 'amount' | 'payment_number' | 'status' | 'created_at';
sort_order?: 'asc' | 'desc';
}
// API response wrapper
interface ApiResponse<T> {
data: T;
meta: {
request_id: string;
pagination?: {
current_page: number;
per_page: number;
total_entries: number;
total_pages: number;
};
};
}
// Error response
interface ApiError {
error: {
code: string;
message: string;
details: Record<string, unknown>;
};
meta: {
request_id: string;
};
}Usage Examples
React Query Hooks
// hooks/usePatientPayments.ts
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { apiClient } from '@/lib/api-client';
export function usePatientPayments(
patientFacilityId: string,
params?: ListPaymentsParams
) {
return useQuery({
queryKey: ['patient-payments', patientFacilityId, params],
queryFn: () =>
apiClient.get(`/billing/patient_facilities/${patientFacilityId}/payments`, { params }),
});
}
export function usePayment(id: string) {
return useQuery({
queryKey: ['payment', id],
queryFn: () => apiClient.get(`/billing/patient_payments/${id}`),
enabled: !!id,
});
}
export function useCreatePayment() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({
patientFacilityId,
data,
}: {
patientFacilityId: string;
data: CreatePaymentRequest;
}) =>
apiClient.post(`/billing/patient_facilities/${patientFacilityId}/payments`, data),
onSuccess: (_, { patientFacilityId }) => {
queryClient.invalidateQueries({ queryKey: ['patient-payments', patientFacilityId] });
},
});
}
export function useUpdatePayment() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({
id,
data,
}: {
id: string;
data: UpdatePaymentRequestPending | UpdatePaymentRequestCompleted;
}) =>
apiClient.patch(`/billing/patient_payments/${id}`, data),
onSuccess: (_, { id }) => {
queryClient.invalidateQueries({ queryKey: ['payment', id] });
queryClient.invalidateQueries({ queryKey: ['patient-payments'] });
},
});
}
export function useDeletePayment() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({
id,
reason,
}: {
id: string;
reason: string;
}) =>
apiClient.delete(`/billing/patient_payments/${id}?reason=${encodeURIComponent(reason)}`),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['patient-payments'] });
},
});
}Component Usage
// components/PaymentList.tsx
import { usePatientPayments, useDeletePayment } from '@/hooks/usePatientPayments';
export function PaymentList({ patientFacilityId }: { patientFacilityId: string }) {
const { data, isLoading } = usePatientPayments(patientFacilityId, {
status: 'completed',
sort_by: 'payment_date',
sort_order: 'desc',
});
const deletePayment = useDeletePayment();
const handleDelete = (paymentId: string) => {
const reason = prompt('Enter deletion reason:');
if (reason) {
deletePayment.mutate({ id: paymentId, reason });
}
};
if (isLoading) return <div>Loading...</div>;
return (
<div>
{data?.data.map((payment) => (
<div key={payment.id}>
<span>{payment.payment_number}</span>
<span>{payment.amount}</span>
<span>{payment.payment_method}</span>
<button onClick={() => handleDelete(payment.id)}>Delete</button>
</div>
))}
</div>
);
}Notes
-
Payment Number: Auto-generated in format
PAY-YYYY-NNNNNN(e.g.,PAY-2026-000001) -
Soft Delete: Deleting a payment sets
deleted_at,deleted_by_id, anddeletion_reason. The status is changed tocancelled. -
Patient ID: Automatically derived from the
patient_facilityrelationship. -
Default Status: New payments are created with status
completed. -
Authorization: Requires
manage_practice_financepermission for create/update/delete operations, andview_practice_financeormanage_practice_financefor list/show operations.