Schedules
Satia manages time and availability for appointments, bookings, and resources through the schedules and schedule_rules tables. Transactional agents consume these rules automatically via their native scheduling tools (check_availability, find_open_slots), while you (from Sati or MCP) configure and query these rules directly via SQL (database__query, database__insert, database__update, database__delete).
1. Fundamental Rule: The 24/7 Subtractive Model
⚠️ GOLDEN LAW OF THE TIME ENGINE:
In Satia, the baseline of every event table or resource is OPEN 24/7.
Any time range that is NOT explicitly blocked is considered available.
- To define a business's operating hours, create
type = 'block'rules that cover the CLOSED hours, never the open hours.- NEVER block all 24 hours and then attempt to open a window with
'availability': the'availability'type is used exclusively to open a one-off exception on a specific date (specific_date) on top of an existing recurring block.
1.1 4-Layer Evaluation Hierarchy
The availability engine evaluates every requested time slot in this exact priority order:
- Layer 0 (24/7 Open Baseline): By default, the entire calendar is open 24 hours a day, 7 days a week.
- Layer 1 (Recurring Blocks —
type = 'block'+rrule): Regular closed hours (nights, weekends, lunch breaks) are subtracted from the baseline.- Midnight-crossing blocks are supported natively: If
start_time > end_time(e.g.,start_time = '18:00:00'andend_time = '08:00:00'), the engine automatically blocks the evening segment (18:00to23:59:59) and the early-morning segment (00:00to08:00).
- Midnight-crossing blocks are supported natively: If
- Layer 2 (Date-Specific Availability Exceptions —
type = 'availability'+specific_date): If a specific date that is normally closed by a recurring rule (e.g., a holiday Saturday) needs to open for special hours, atype = 'availability'rule withspecific_date = 'YYYY-MM-DD'opens that window over the recurring block. - Layer 3 (Date-Specific Blocks / Holidays —
type = 'block'+specific_date): Holds the highest priority and closes any time range on that exact date (specific_date = 'YYYY-MM-DD'), overriding any recurring rule or prior availability window.
1.2 Quick Mental Examples
- "We are open Monday through Friday from 8:00 AM to 6:00 PM and closed Saturdays and Sundays":
- Block (
type = 'block') from18:00:00to08:00:00Monday–Friday (FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR). - Block (
type = 'block') from00:00:00to23:59:00Saturdays and Sundays (FREQ=WEEKLY;BYDAY=SA,SU).
- Block (
- "Closed for lunch from 1:00 PM to 2:00 PM on weekdays":
- Block (
type = 'block') from13:00:00to14:00:00withrrule = 'FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR'.
- Block (
- "This Saturday, December 20, we open exceptionally from 9:00 AM to 2:00 PM":
- Exception (
type = 'availability') from09:00:00to14:00:00withspecific_date = '2026-12-20'(rrule = null).
- Exception (
2. Data Model (schedules and schedule_rules)
2.1 Table schedules (The Calendar Container)
Assigns a general calendar to an entire booking/event table (e.g., citas, reservas) or an individual calendar to a specific resource record (e.g., a doctor, room, court, or service):
| Column / Field | Type | Description |
|---|---|---|
id | uuid | Unique schedule identifier (schedule_id). |
owner_type | text | 'table' (global schedule for an entire event table, e.g., citas) or 'resource' (individual schedule for a specific resource record). |
table_slug | text | (For owner_type = 'table') Business table slug (e.g., 'citas', 'reservas'). The tool automatically resolves the internal identifier. |
record_id | uuid | (For owner_type = 'resource') UUID of the specific resource record (e.g., the id of Dr. García or Court 1 in its respective table). |
owner_id | uuid | Internal identifier automatically resolved from table_slug or record_id. |
timezone | text | Business IANA timezone (e.g., 'America/Bogota', 'America/Mexico_City'). Defaults to 'America/Bogota'. |
name | text | Descriptive name (e.g., 'General Appointments Schedule', 'Dr. Martínez Calendar'). |
💡 Automatic Upsert on
schedules:
Each business table or resource has a singleschedulescontainer. If you calldatabase__insert(table_name="schedules", ...)for atable_slugorrecord_idthat already has a schedule, the system automatically updates and reuses the existingschedule(returningupserted: trueand itsschedule_id) without creating duplicate calendars.
2.2 Combining Table Schedule + Resource Schedule
When an appointment or booking involves a specific resource (for example, an appointment in citas with resource Dr. Martínez), the availability engine enforces both calendars simultaneously: the table's general schedule (owner_type = 'table', table_slug = 'citas') and the resource's individual schedule (owner_type = 'resource', record_id = '<uuid>'), in addition to verifying that existing bookings in that slot do not exceed the resource's capacity.
2.3 Table schedule_rules (Time Rules & Blocks)
| Column | Type | Description |
|---|---|---|
id | uuid | Unique rule identifier. |
schedule_id | uuid | FK referencing schedules.id. |
type | text | 'block' (closes or blocks that time window — used 99% of the time) or 'availability' (one-off exception with specific_date to open hours over a recurring block). |
start_time | time | Start time in 'HH:MM:SS' format (e.g., '18:00:00', '00:00:00'). |
end_time | time | End time in 'HH:MM:SS' format (e.g., '08:00:00', '23:59:00'). |
start_date | date | Date from which the recurring rule takes effect ('YYYY-MM-DD'). |
end_date | date | Date until which the rule applies (optional, null if indefinite). |
rrule | text | RFC 5545 recurrence rule without the "RRULE:" prefix (e.g., 'FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR'). |
specific_date | date | Exact date ('YYYY-MM-DD') for a single-day holiday or exception (when using specific_date, leave rrule as null). |
reason | text | Human-readable explanation for agents and UI (e.g., 'Closed outside business hours', 'Christmas Holiday'). |
3. Standard RRULE Patterns (No RRULE: Prefix)
- Monday to Friday:
FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR - Monday to Saturday:
FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR,SA - Weekends:
FREQ=WEEKLY;BYDAY=SA,SU - Sundays Only:
FREQ=WEEKLY;BYDAY=SU - Specific Days (Mon, Wed, Fri):
FREQ=WEEKLY;BYDAY=MO,WE,FR - Every Day:
FREQ=DAILY
4. Operational Recipes with database__query and database__insert
Note: All queries and inserts on
schedulesandschedule_rulesare automatically scoped to the active Workspace.
Step 1: Always Query Existing Schedules Before Modifying
SELECT s.id AS schedule_id, s.owner_type, s.owner_id, s.name, s.timezone,
sr.id AS rule_id, sr.type, sr.start_time, sr.end_time, sr.rrule, sr.specific_date, sr.reason
FROM schedules s
LEFT JOIN schedule_rules sr ON sr.schedule_id = s.id
ORDER BY s.name, sr.start_time
(The response automatically includes table_slug when owner_type = 'table' and record_id when owner_type = 'resource'. You can also filter directly in SQL with WHERE s.table_slug = 'citas' or WHERE s.owner_type = 'table').
Step 2: Create (or Reuse) a schedule
- Option A — General schedule for a booking/event table (
table_slug):
{
"table_name": "schedules",
"data": {
"table_slug": "citas",
"timezone": "America/Bogota",
"name": "Horario General de Citas"
},
"user_prompt": "Configure business hours Monday to Friday from 9 AM to 6 PM"
}
- Option B — Individual schedule for a specific resource (
record_id):
{
"table_name": "schedules",
"data": {
"record_id": "RESOURCE-RECORD-UUID",
"timezone": "America/Bogota",
"name": "Agenda Dra. Martínez"
},
"user_prompt": "Configure Dr. Martínez's individual schedule"
}
Step 3: Insert Rules into schedule_rules
Call database__insert on table_name="schedule_rules" (you can batch-insert multiple rules at once by passing a list in data):
{
"table_name": "schedule_rules",
"data": [
{
"schedule_id": "SCHEDULE-UUID",
"type": "block",
"start_time": "18:00:00",
"end_time": "09:00:00",
"start_date": "2026-01-01",
"rrule": "FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR",
"reason": "Closed outside business hours (Monday to Friday)"
},
{
"schedule_id": "SCHEDULE-UUID",
"type": "block",
"start_time": "00:00:00",
"end_time": "23:59:00",
"start_date": "2026-01-01",
"rrule": "FREQ=WEEKLY;BYDAY=SA,SU",
"reason": "Closed on weekends"
},
{
"schedule_id": "SCHEDULE-UUID",
"type": "block",
"start_time": "00:00:00",
"end_time": "23:59:00",
"specific_date": "2026-12-25",
"reason": "Christmas Holiday"
}
],
"user_prompt": "Configure business hours Monday to Friday 9 AM to 6 PM and close Christmas Day"
}