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}}/clinicalAuthentication
All endpoints require Bearer token authentication:
Authorization: Bearer <access_token>Common Headers
Content-Type: application/json
Accept: application/jsonEndpoints Summary
| Method | Endpoint | Description |
|---|---|---|
| POST | /patient_facilities/:patient_facility_id/next_visit | Create a next visit |
| PATCH | /patient_facilities/:patient_facility_id/next_visit/:id | Update a next visit |
| DELETE | /patient_facilities/:patient_facility_id/next_visit/:id | Cancel 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_visitPath Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
patient_facility_id | UUID | Yes | The 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
| Field | Type | Required | Description |
|---|---|---|---|
practitioner_id | UUID | No | Practitioner ID for the follow-up |
next_visit_date | datetime | Yes* | Absolute date/time for the visit (ISO 8601) |
next_visit_value | integer | Yes* | Relative value (e.g., 7) |
next_visit_units | string | Yes* | Units: days, weeks, months |
notes | string | No | Notes 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
| Parameter | Type | Required | Description |
|---|---|---|---|
patient_facility_id | UUID | Yes | The patient_facility ID |
appointment_id | UUID | Yes | The 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
| Field | Type | Required | Description |
|---|---|---|---|
next_visit_date | datetime | No | New absolute date/time |
next_visit_value | integer | No | Relative value |
next_visit_units | string | No | Units: days, weeks, months |
notes | string | No | Updated notes |
Business Rules
- Only appointments in
pendingorbookedstatus can be updated - If only
notesis provided, the date remains unchanged - Both
next_visit_valueandnext_visit_unitsmust 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
| Parameter | Type | Required | Description |
|---|---|---|---|
patient_facility_id | UUID | Yes | The patient_facility ID |
appointment_id | UUID | Yes | The appointment ID to cancel |
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
reason | string | No | Reason 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=trueQuery Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
follow_up | string | - | Set to true to filter follow-ups only |
facility_id | UUID | - | Filter by facility |
practitioner_id | UUID | - | Filter by practitioner |
patient_id | UUID | - | Filter by patient |
status | string | - | Filter by status: pending, booked, arrived, fulfilled, cancelled, noshow |
start_from | datetime | - | Start of date range (ISO 8601) |
start_to | datetime | - | End of date range (ISO 8601) |
upcoming | string | - | Set to true for future appointments only |
past | string | - | Set to true for past appointments only |
page | integer | 1 | Page number |
per_page | integer | 25 | Items 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-000000000001List 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=10List upcoming follow-ups for a patient
GET /clinical/appointments?follow_up=true&patient_id=019bb764-9839-781a-9383-8ad47af09cc4&upcoming=trueSuccess 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)
| Field | Type | Description |
|---|---|---|
id | UUID | Unique identifier |
status | enum | Appointment status |
appointment_type | enum | Always fixed for follow-ups |
start_time | datetime | Scheduled start time |
end_time | datetime | Scheduled end time (nullable) |
note | string | Notes for the appointment |
reason_codes | array | Reason codes (FHIR) |
patient_instruction | string | Patient instructions |
patient | object | Nested patient object |
practitioner | object | Nested practitioner object |
facility | object | Nested facility object |
created_at | datetime | Creation timestamp |
updated_at | datetime | Last update timestamp |
Status Enum
| Value | Description |
|---|---|
pending | Not yet confirmed (default for follow-ups) |
booked | Confirmed appointment |
arrived | Patient has arrived |
fulfilled | Appointment completed |
cancelled | Appointment cancelled |
noshow | Patient did not show |
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 |
display_name | string | Practitioner display name |
Facility Nested Object
| Field | Type | Description |
|---|---|---|
id | UUID | Facility ID |
name | string | Facility 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
-
Status Mapping: Follow-ups are created with
status: 'pending'(equivalent to legacyNOT_CONFIRMED) -
Appointment Type: Follow-ups use
appointment_type: 'fixed'(scheduled appointments) -
Relative Dates: The
next_visit_valueandnext_visit_unitsparameters allow scheduling relative to the current time -
Date Ranges: Use ISO 8601 format for all date/time parameters
-
Authorization: Requires appropriate permissions for appointment management