Kulcare Docs
DeveloperBilling

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}}/billing

Authentication

All endpoints require Bearer token authentication:

Authorization: Bearer <access_token>

Common Headers

Content-Type: application/json
Accept: application/json

Endpoints

MethodEndpointDescription
GET/patient_facilities/:patient_facility_id/paymentsList payments for a patient
POST/patient_facilities/:patient_facility_id/paymentsCreate a payment
GET/patient_payments/:idGet payment details
PATCH/patient_payments/:idUpdate a payment
DELETE/patient_payments/:idSoft 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}/payments

Path Parameters

ParameterTypeRequiredDescription
patient_facility_idUUIDYesThe patient_facility ID

Query Parameters

ParameterTypeDefaultDescription
pageinteger1Page number
per_pageinteger25Items per page (max: 100)
facility_idUUID-Filter by facility
practitioner_idUUID-Filter by practitioner
appointment_idUUID-Filter by appointment
statusstring-Filter by status: pending, completed, failed, refunded, cancelled
payment_methodstring-Filter by method: cash, card, upi, bank_transfer, cheque, wallet, gateway
start_datestring-Filter from date (YYYY-MM-DD)
end_datestring-Filter to date (YYYY-MM-DD)
searchstring-Search by payment number
sort_bystringpayment_dateSort column: payment_date, amount, payment_number, status, created_at
sort_orderstringdescSort 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-28

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 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}/payments

Path Parameters

ParameterTypeRequiredDescription
patient_facility_idUUIDYesThe 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

FieldTypeRequiredDescription
amountdecimalYesPayment amount (must be > 0)
payment_methodstringYesPayment method (see enum values below)
payment_datedateNoPayment date in YYYY-MM-DD format (defaults to today)
currencystringNoCurrency code (default: INR)
facility_idUUIDNoFacility ID (defaults to patient_facility's facility)
practitioner_idUUIDNoPractitioner who received the payment
appointment_idUUIDNoAssociated appointment ID
reasonstringNoReason for payment
notesstringNoAdditional notes

Payment Method Enum Values

ValueDescription
cashCash payment
cardCredit/Debit card
upiUPI payment
bank_transferBank transfer
chequeCheque payment
walletDigital wallet
gatewayPayment 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 FieldMaps To
mode_of_paymentpayment_method
clinic_idfacility_id
doctor_idpractitioner_id

Legacy Payment Method Mapping:

Legacy ValueMaps To
CASHcash
CARDcard
ONLINEgateway
UPIupi
CHEQUEcheque
OTHERScash
PAYMENT_LINKgateway

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

ParameterTypeRequiredDescription
idUUIDYesThe payment ID

Query Parameters

ParameterTypeDefaultDescription
viewstringpatient_facilityResponse view: normal or patient_facility

Example Request

GET /billing/patient_payments/019bc196-8d50-7338-a891-dc6b3cd73a82

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",
    "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

ParameterTypeRequiredDescription
idUUIDYesThe 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

FieldPendingCompletedDescription
amountYesNoPayment amount
payment_methodYesNoPayment method
payment_dateYesNoPayment date
practitioner_idYesNoPractitioner ID
appointment_idYesNoAssociated appointment
reasonYesNoPayment reason
notesYesYesAdditional notes

Business Rules

  • Pending payments: All fields can be updated
  • Completed payments: Only notes can 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

ParameterTypeRequiredDescription
idUUIDYesThe payment ID

Query Parameters

ParameterTypeRequiredDescription
reasonstringYesReason for deletion

Example Request

DELETE /billing/patient_payments/019bc196-8d50-7338-a891-dc6b3cd73a82?reason=Payment%20recorded%20in%20error

Success 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

FieldTypeDescription
idUUIDUnique identifier
payment_numberstringAuto-generated number (format: PAY-YYYY-NNNNNN)
statusenumPayment status (see below)
payment_methodenumPayment method (see below)
currencystringCurrency code (default: INR)
amountdecimalPayment amount
payment_datedateDate of payment
patient_idUUIDPatient ID
facility_idUUIDFacility ID
patient_facility_idUUIDPatientFacility ID
appointment_idUUIDAssociated appointment (nullable)
practitioner_idUUIDPractitioner who received payment (nullable)
is_gateway_paymentbooleanWhether paid via gateway
reasonstringPayment reason (nullable)
notesstringAdditional notes (nullable)
patientobjectNested patient object
practitionerobjectNested practitioner object (nullable)
deletedbooleanWhether soft-deleted
created_atdatetimeCreation timestamp
updated_atdatetimeLast update timestamp

Status Enum

ValueDescription
pendingPayment is pending
completedPayment completed successfully
failedPayment failed
refundedPayment was refunded
cancelledPayment was cancelled

Payment Method Enum

ValueDescription
cashCash payment
cardCredit/Debit card
upiUPI payment
bank_transferBank transfer
chequeCheque payment
walletDigital wallet
gatewayPayment gateway

Patient Nested Object

FieldTypeDescription
idUUIDPatient ID
namestringPatient name
phonestringPhone number

Practitioner Nested Object

FieldTypeDescription
idUUIDPractitioner ID
namestringPractitioner name

Error Codes

CodeHTTP StatusDescription
VALIDATION_ERROR400, 422Request validation failed
RESOURCE_NOT_FOUND404Requested resource not found
UNAUTHORIZED401Authentication required
FORBIDDEN403Insufficient 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

  1. Payment Number: Auto-generated in format PAY-YYYY-NNNNNN (e.g., PAY-2026-000001)

  2. Soft Delete: Deleting a payment sets deleted_at, deleted_by_id, and deletion_reason. The status is changed to cancelled.

  3. Patient ID: Automatically derived from the patient_facility relationship.

  4. Default Status: New payments are created with status completed.

  5. Authorization: Requires manage_practice_finance permission for create/update/delete operations, and view_practice_finance or manage_practice_finance for list/show operations.

On this page