Skip to main content

Availability and Schedules

Fyso's scheduling engine calculates availability from three dedicated system entities:

  • _fyso_schedules
  • _fyso_schedule_exceptions
  • _fyso_bookings

These entities are separate from domain entities such as doctors, patients, or appointments.

Setup

Run setup_scheduling once per tenant, or call POST /api/scheduling/setup.

The setup is idempotent. It creates and publishes the three _fyso_* entities if they do not exist.

Required Entities

_fyso_schedules

Regular schedules for each professional.

fieldKeyfieldTypeRequiredDescription
professional_idtextYesUUID of the professional
rruletextYesRFC 5545 RRULE string
start_timetextNoStart time in HH:MM. Defaults to 09:00 if missing
end_timetextNoEnd time in HH:MM. Defaults to 17:00 if missing
slot_durationnumberNoSlot duration in minutes. Defaults to 30 if missing
activebooleanNoWhether the schedule is active. Defaults to true

Example:

{
"professional_id": "9d5e3e28-6eb8-49e3-8a34-1a6b7d91f002",
"rrule": "FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR",
"start_time": "09:00",
"end_time": "13:00",
"slot_duration": 30,
"active": true
}

_fyso_schedule_exceptions

Blocked days or special-hours overrides.

fieldKeyfieldTypeRequiredDescription
professional_idtextYesUUID of the professional
datedateYesException date in YYYY-MM-DD
typeselectYesblocked or special_hours
start_timetextNoRequired when type = "special_hours"
end_timetextNoRequired when type = "special_hours"

Blocked-day example:

{
"professional_id": "9d5e3e28-6eb8-49e3-8a34-1a6b7d91f002",
"date": "2026-05-01",
"type": "blocked"
}

Special-hours example:

{
"professional_id": "9d5e3e28-6eb8-49e3-8a34-1a6b7d91f002",
"date": "2026-05-02",
"type": "special_hours",
"start_time": "10:00",
"end_time": "12:00"
}

_fyso_bookings

Confirmed bookings already taken by the engine into account.

fieldKeyfieldTypeRequiredDescription
professional_idtextYesUUID of the professional
patient_idtextYesUUID of the patient/client
datedateYesBooking date in YYYY-MM-DD
timetextYesBooking time in HH:MM
durationnumberNoDuration in minutes
statusselectYesconfirmed, cancelled, or completed
notestextareaNoOptional notes

RRULE Notes

The engine reads recurring days from rrule, then combines that with start_time, end_time, and slot_duration.

  • rrule chooses which dates are active.
  • start_time and end_time define the time window for each matching date.
  • slot_duration defines the size of each generated slot.
  • If DTSTART is omitted, the engine prepends one automatically for the query range.

Typical weekday morning schedule:

FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR

Every Monday from 09:00 to 13:00:

{
"professional_id": "9d5e3e28-6eb8-49e3-8a34-1a6b7d91f002",
"rrule": "FREQ=WEEKLY;BYDAY=MO",
"start_time": "09:00",
"end_time": "13:00",
"slot_duration": 30,
"active": true
}

MCP Tool: get_available_slots

Profile: core

Calculates available slots considering schedules, exceptions, and confirmed bookings.

Parameters

ParameterTypeRequiredDescription
professional_idstringYesProfessional UUID
datestringNoSpecific date in YYYY-MM-DD
fromstringNoRange start in YYYY-MM-DD
tostringNoRange end in YYYY-MM-DD

Provide date for a single day, or from and to for a range. Maximum range: 90 days.

Example: Single Day

get_available_slots({
professional_id: "9d5e3e28-6eb8-49e3-8a34-1a6b7d91f002",
date: "2026-05-05"
})

Example: Date Range

get_available_slots({
professional_id: "9d5e3e28-6eb8-49e3-8a34-1a6b7d91f002",
from: "2026-05-05",
to: "2026-05-09"
})

Response

[
{
"date": "2026-05-05",
"time": "09:00",
"duration": 30,
"professional_id": "9d5e3e28-6eb8-49e3-8a34-1a6b7d91f002"
},
{
"date": "2026-05-05",
"time": "09:30",
"duration": 30,
"professional_id": "9d5e3e28-6eb8-49e3-8a34-1a6b7d91f002"
}
]

REST API

REST uses the same parameter names as MCP:

GET /api/scheduling/available-slots?professional_id=<uuid>&date=2026-05-05
GET /api/scheduling/available-slots?professional_id=<uuid>&from=2026-05-05&to=2026-05-09

Troubleshooting

422 Scheduling entities not found

The tenant has not been initialized for scheduling yet.

Fix:

setup_scheduling()

or:

POST /api/scheduling/setup

Empty array response

An empty array is valid when:

  • the professional has no active schedules
  • the date range has no matching RRULE occurrences
  • all generated slots are blocked by exceptions
  • all generated slots are already booked

Invalid RRULE

Invalid RRULE strings do not crash the request, but they produce no generated dates. Verify the rule format first.

Legacy Compatibility

Older tenants may still contain legacy names such as horarios, excepciones_horario, turnos, or field keys like profesional_id and fecha.

Fyso migrates those legacy system entities to _fyso_* names and English field keys. New documentation uses only the current names.

clinica Preset

The clinica preset installs domain entities such as doctors, patients, and appointments.

Those entities are not the scheduling engine's storage layer. The engine reads only _fyso_schedules, _fyso_schedule_exceptions, and _fyso_bookings.

If you install clinica and want to use get_available_slots or create_booking, run setup_scheduling and model the scheduling data in the _fyso_* entities, or add your own synchronization/business-rule layer between domain records and the scheduling system.