Kulcare Docs
DeveloperAppointments

Next Visit & Follow-ups API

API endpoints for managing follow-up appointments (next visits) in the Kulcare clinical system.


Overview

The Next Visit API allows you to create, update, and cancel follow-up appointments for patients. Follow-ups are scheduled appointments that remind patients to return for continued care.

Base URL

{{base_url}}/clinical

Authentication

All endpoints require Bearer token authentication:

Authorization: Bearer <access_token>

Common Headers

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

Endpoints Summary

MethodEndpointDescription
POST/patient_facilities/:patient_facility_id/next_visitCreate a next visit
PATCH/patient_facilities/:patient_facility_id/next_visit/:idUpdate a next visit
DELETE/patient_facilities/:patient_facility_id/next_visit/:idCancel a next visit
GET/appointments (with follow_up=true)List follow-ups

Part 1: Next Visit Management

1. Create Next Visit

Creates a follow-up appointment for a patient.

Request

POST /clinical/patient_facilities/{patient_facility_id}/next_visit

Path Parameters

ParameterTypeRequiredDescription
patient_facility_idUUIDYesThe patient_facility ID

Request Body

Option A: Absolute Date

{
  "practitioner_id": "019bb761-4a1c-72e9-9a04-75c5bf5f0153",
  "next_visit_date": "2026-03-01T10:00:00Z",
  "notes": "Follow-up for blood pressure check"
}

Option B: Relative Date

{
  "practitioner_id": "019bb761-4a1c-72e9-9a04-75c5bf5f0153",
  "next_visit_value": 7,
  "next_visit_units": "days",
  "notes": "Follow-up in a week"
}

Body Parameters

FieldTypeRequiredDescription
practitioner_idUUIDNoPractitioner ID for the follow-up
next_visit_datedatetimeYes*Absolute date/time for the visit (ISO 8601)
next_visit_valueintegerYes*Relative value (e.g., 7)
next_visit_unitsstringYes*Units: days, weeks, months
notesstringNoNotes for the follow-up

*Either next_visit_date OR (next_visit_value + next_visit_units) is required.

Relative Date Examples

// 7 days from now
{ "next_visit_value": 7, "next_visit_units": "days" }

// 2 weeks from now
{ "next_visit_value": 2, "next_visit_units": "weeks" }

// 1 month from now
{ "next_visit_value": 1, "next_visit_units": "months" }

// 3 months from now
{ "next_visit_value": 3, "next_visit_units": "months" }

Success Response

Status: 201 Created

{
  "data": {
    "id": "019bc196-8d50-7338-a891-dc6b3cd73a82",
    "status": "pending",
    "appointment_type": "fixed",
    "start_time": "2026-03-01T10:00:00Z",
    "end_time": null,
    "note": "Follow-up for blood pressure check",
    "reason_codes": null,
    "patient_instruction": null,
    "patient": {
      "id": "019bb764-9839-781a-9383-8ad47af09cc4",
      "name": "John Doe",
      "phone": "+919876543210"
    },
    "practitioner": {
      "id": "019bb761-4a1c-72e9-9a04-75c5bf5f0153",
      "display_name": "Dr. Jane Smith"
    },
    "facility": {
      "id": "019bb761-4a1c-72e9-9a04-75c5bf5f0152",
      "name": "City Clinic"
    },
    "created_at": "2026-02-19T10:00:00Z",
    "updated_at": "2026-02-19T10:00:00Z"
  },
  "meta": {
    "request_id": "uuid-here"
  }
}

Error Responses

422 Unprocessable Entity - Missing Date

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Either next_visit_date or (next_visit_value + next_visit_units) is required",
    "details": [
      {
        "field": "base",
        "message": "Either next_visit_date or (next_visit_value + next_visit_units) is required"
      }
    ]
  },
  "meta": {
    "request_id": "uuid-here"
  }
}

422 Unprocessable Entity - Invalid Units

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "next_visit_units must be one of: days, weeks, months",
    "details": [
      {
        "field": "base",
        "message": "next_visit_units must be one of: days, weeks, months"
      }
    ]
  },
  "meta": {
    "request_id": "uuid-here"
  }
}

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": "uuid-here"
  }
}

2. Update Next Visit

Updates a follow-up appointment's date and/or notes.

Request

PATCH /clinical/patient_facilities/{patient_facility_id}/next_visit/{appointment_id}

Path Parameters

ParameterTypeRequiredDescription
patient_facility_idUUIDYesThe patient_facility ID
appointment_idUUIDYesThe appointment ID to update

Request Body

{
  "next_visit_date": "2026-03-05T14:00:00Z",
  "notes": "Rescheduled to afternoon"
}

Or with relative date:

{
  "next_visit_value": 14,
  "next_visit_units": "days",
  "notes": "Extended follow-up period"
}

Body Parameters

FieldTypeRequiredDescription
next_visit_datedatetimeNoNew absolute date/time
next_visit_valueintegerNoRelative value
next_visit_unitsstringNoUnits: days, weeks, months
notesstringNoUpdated notes

Business Rules

  • Only appointments in pending or booked status can be updated
  • If only notes is provided, the date remains unchanged
  • Both next_visit_value and next_visit_units must be provided together for relative date

Success Response

Status: 200 OK

{
  "data": {
    "id": "019bc196-8d50-7338-a891-dc6b3cd73a82",
    "status": "pending",
    "appointment_type": "fixed",
    "start_time": "2026-03-05T14:00:00Z",
    "end_time": null,
    "note": "Rescheduled to afternoon",
    "patient": {
      "id": "019bb764-9839-781a-9383-8ad47af09cc4",
      "name": "John Doe"
    },
    "practitioner": {
      "id": "019bb761-4a1c-72e9-9a04-75c5bf5f0153",
      "display_name": "Dr. Jane Smith"
    },
    "facility": {
      "id": "019bb761-4a1c-72e9-9a04-75c5bf5f0152",
      "name": "City Clinic"
    },
    "created_at": "2026-02-19T10:00:00Z",
    "updated_at": "2026-02-19T12:00:00Z"
  },
  "meta": {
    "request_id": "uuid-here"
  }
}

Error Response

422 Unprocessable Entity - Cannot Update

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Appointment cannot be updated",
    "details": [
      {
        "field": "base",
        "message": "Appointment cannot be updated"
      }
    ]
  },
  "meta": {
    "request_id": "uuid-here"
  }
}

3. Cancel Next Visit

Cancels a follow-up appointment.

Request

DELETE /clinical/patient_facilities/{patient_facility_id}/next_visit/{appointment_id}?reason={reason}

Path Parameters

ParameterTypeRequiredDescription
patient_facility_idUUIDYesThe patient_facility ID
appointment_idUUIDYesThe appointment ID to cancel

Query Parameters

ParameterTypeRequiredDescription
reasonstringNoReason for cancellation

Success Response

Status: `204 No Content**

No response body.

Error Response

422 Unprocessable Entity - Cannot Cancel

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Cannot cancel appointment with status 'cancelled'",
    "details": [
      {
        "field": "base",
        "message": "Cannot cancel appointment with status 'cancelled'"
      }
    ]
  },
  "meta": {
    "request_id": "uuid-here"
  }
}

Part 2: List Follow-ups

4. List Follow-ups

Retrieves a paginated list of follow-up appointments.

Request

GET /clinical/appointments?follow_up=true

Query Parameters

ParameterTypeDefaultDescription
follow_upstring-Set to true to filter follow-ups only
facility_idUUID-Filter by facility
practitioner_idUUID-Filter by practitioner
patient_idUUID-Filter by patient
statusstring-Filter by status: pending, booked, arrived, fulfilled, cancelled, noshow
start_fromdatetime-Start of date range (ISO 8601)
start_todatetime-End of date range (ISO 8601)
upcomingstring-Set to true for future appointments only
paststring-Set to true for past appointments only
pageinteger1Page number
per_pageinteger25Items per page (max: 100)

Example Requests

List all follow-ups for a facility

GET /clinical/appointments?follow_up=true&facility_id=01900002-0000-7000-8000-000000000001

List follow-ups for a practitioner within date range

GET /clinical/appointments?follow_up=true&facility_id=01900002-0000-7000-8000-000000000001&practitioner_id=01900004-0000-7000-8000-000000000004&start_from=2026-02-18T00:00:00Z&start_to=2027-02-20T23:59:59Z&page=1&per_page=10

List upcoming follow-ups for a patient

GET /clinical/appointments?follow_up=true&patient_id=019bb764-9839-781a-9383-8ad47af09cc4&upcoming=true

Success Response

Status: 200 OK

{
  "data": [
    {
      "id": "019bc196-8d50-7338-a891-dc6b3cd73a82",
      "status": "pending",
      "appointment_type": "fixed",
      "start_time": "2026-03-01T10:00:00Z",
      "end_time": null,
      "note": "Follow-up for blood pressure check",
      "patient": {
        "id": "019bb764-9839-781a-9383-8ad47af09cc4",
        "name": "John Doe",
        "phone": "+919876543210"
      },
      "practitioner": {
        "id": "019bb761-4a1c-72e9-9a04-75c5bf5f0153",
        "display_name": "Dr. Jane Smith"
      },
      "facility": {
        "id": "019bb761-4a1c-72e9-9a04-75c5bf5f0152",
        "name": "City Clinic"
      },
      "created_at": "2026-02-19T10:00:00Z",
      "updated_at": "2026-02-19T10:00:00Z"
    }
  ],
  "meta": {
    "request_id": "uuid-here",
    "pagination": {
      "current_page": 1,
      "per_page": 10,
      "total_entries": 25,
      "total_pages": 3
    }
  }
}

Data Models

Appointment Object (Follow-up)

FieldTypeDescription
idUUIDUnique identifier
statusenumAppointment status
appointment_typeenumAlways fixed for follow-ups
start_timedatetimeScheduled start time
end_timedatetimeScheduled end time (nullable)
notestringNotes for the appointment
reason_codesarrayReason codes (FHIR)
patient_instructionstringPatient instructions
patientobjectNested patient object
practitionerobjectNested practitioner object
facilityobjectNested facility object
created_atdatetimeCreation timestamp
updated_atdatetimeLast update timestamp

Status Enum

ValueDescription
pendingNot yet confirmed (default for follow-ups)
bookedConfirmed appointment
arrivedPatient has arrived
fulfilledAppointment completed
cancelledAppointment cancelled
noshowPatient did not show

Patient Nested Object

FieldTypeDescription
idUUIDPatient ID
namestringPatient name
phonestringPhone number

Practitioner Nested Object

FieldTypeDescription
idUUIDPractitioner ID
display_namestringPractitioner display name

Facility Nested Object

FieldTypeDescription
idUUIDFacility ID
namestringFacility name

TypeScript Interfaces

// Appointment status enum
type AppointmentStatus = 'pending' | 'booked' | 'arrived' | 'fulfilled' | 'cancelled' | 'noshow';

// Appointment type enum
type AppointmentType = 'walk_in' | 'fixed' | 'telemedicine';

// Next visit units enum
type NextVisitUnits = 'days' | 'weeks' | 'months';

// Patient nested object
interface AppointmentPatient {
  id: string;
  name: string;
  phone: string;
}

// Practitioner nested object
interface AppointmentPractitioner {
  id: string;
  display_name: string;
}

// Facility nested object
interface AppointmentFacility {
  id: string;
  name: string;
}

// Appointment object
interface Appointment {
  id: string;
  status: AppointmentStatus;
  appointment_type: AppointmentType;
  start_time: string;
  end_time: string | null;
  note: string | null;
  reason_codes: unknown;
  patient_instruction: string | null;
  patient: AppointmentPatient;
  practitioner: AppointmentPractitioner | null;
  facility: AppointmentFacility;
  created_at: string;
  updated_at: string;
}

// Create next visit request (absolute date)
interface CreateNextVisitRequestAbsolute {
  practitioner_id?: string;
  next_visit_date: string;
  notes?: string;
}

// Create next visit request (relative date)
interface CreateNextVisitRequestRelative {
  practitioner_id?: string;
  next_visit_value: number;
  next_visit_units: NextVisitUnits;
  notes?: string;
}

type CreateNextVisitRequest = CreateNextVisitRequestAbsolute | CreateNextVisitRequestRelative;

// Update next visit request
interface UpdateNextVisitRequest {
  next_visit_date?: string;
  next_visit_value?: number;
  next_visit_units?: NextVisitUnits;
  notes?: string;
}

// List follow-ups query params
interface ListFollowUpsParams {
  follow_up: 'true';
  facility_id?: string;
  practitioner_id?: string;
  patient_id?: string;
  status?: AppointmentStatus;
  start_from?: string;
  start_to?: string;
  upcoming?: 'true';
  past?: 'true';
  page?: number;
  per_page?: number;
}

// 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: Array<{ field: string; message: string }> | Record<string, unknown>;
  };
  meta: {
    request_id: string;
  };
}

React Query Hooks Example

// hooks/useNextVisit.ts
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { apiClient } from '@/lib/api-client';

// Create next visit
export function useCreateNextVisit() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: ({
      patientFacilityId,
      data,
    }: {
      patientFacilityId: string;
      data: CreateNextVisitRequest;
    }) =>
      apiClient.post(
        `/clinical/patient_facilities/${patientFacilityId}/next_visit`,
        data
      ),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['appointments', 'follow-ups'] });
    },
  });
}

// Update next visit
export function useUpdateNextVisit() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: ({
      patientFacilityId,
      appointmentId,
      data,
    }: {
      patientFacilityId: string;
      appointmentId: string;
      data: UpdateNextVisitRequest;
    }) =>
      apiClient.patch(
        `/clinical/patient_facilities/${patientFacilityId}/next_visit/${appointmentId}`,
        data
      ),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['appointments', 'follow-ups'] });
    },
  });
}

// Cancel next visit
export function useCancelNextVisit() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: ({
      patientFacilityId,
      appointmentId,
      reason,
    }: {
      patientFacilityId: string;
      appointmentId: string;
      reason?: string;
    }) =>
      apiClient.delete(
        `/clinical/patient_facilities/${patientFacilityId}/next_visit/${appointmentId}?reason=${encodeURIComponent(reason || '')}`
      ),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['appointments', 'follow-ups'] });
    },
  });
}

// List follow-ups
export function useFollowUps(params: Omit<ListFollowUpsParams, 'follow_up'>) {
  return useQuery({
    queryKey: ['appointments', 'follow-ups', params],
    queryFn: () =>
      apiClient.get('/clinical/appointments', {
        params: { follow_up: 'true', ...params },
      }),
  });
}

Notes

  1. Status Mapping: Follow-ups are created with status: 'pending' (equivalent to legacy NOT_CONFIRMED)

  2. Appointment Type: Follow-ups use appointment_type: 'fixed' (scheduled appointments)

  3. Relative Dates: The next_visit_value and next_visit_units parameters allow scheduling relative to the current time

  4. Date Ranges: Use ISO 8601 format for all date/time parameters

  5. Authorization: Requires appropriate permissions for appointment management

On this page