Availability and Calendar API
Two routes read from the same Calendar connector cache but expose deliberately different levels of detail — see Security Posture and the Calendar connector page for why the split exists.
GET /api/availability
Org-internal team free/busy for the Availability surface. Returns busy block times plus
per-person sync state — no event titles or details are selected or returned by this route at
all, not just hidden in the UI. The query itself only selects busy/free state, scoped to
employees (is_employee = true, email matching the internal domain).
GET /api/calendar
Org-internal calendar intelligence from the event-detail cache — titles, attendees, project
matching, unmatched-meeting review. This is the route that exposes what /api/availability
deliberately withholds, and it is scoped more narrowly as a result (see the Calendar connector
page for exactly which roles can see it).
POST /api/calendar
Creates an approval proposal for a calendar write — it never writes to Google directly.
{ "action": "propose_event", "..." : "..." }
{ "action": "override_project", "event_id": "calendar-event-uuid", "project_id": "project-uuid" }
{ "action": "clear_override", "event_id": "calendar-event-uuid" }
patchForManualProjectOverride / patchForClearedProjectOverride write to
calendar_event_project_overrides, a table separate from the sync cache, so the next hourly sync
doesn't silently erase a human correction. The proposal-shaped actions go through
buildCalendarApprovalRow and land in the same approvals queue as every other proposed action —
execution happens later, through calendar-act,
after a human approves.
Where the code lives
sable-agents-demo/web/api/availability.js,_availability.jssable-agents-demo/web/api/calendar.js,_calendar.jssable-agents-demo/web/api/_approvals.js—shapeApproval, shared with the general approvals route