WorkestraDocs
PlatformCalendar & Scheduling

Schedules, Holidays & Free/Busy Rules

Multi-schedule substrate — build "Standard hours" and "Demo hours" once, point as many booking links at them as you want. Plus country holidays and book-over-named-events rules.

Three related mechanics that decide when slots are bookable beyond the inline weekly hours on a single booking link:

  • Schedules — named, reusable weekly hour patterns (e.g. "EMEA business hours", "Demo slots")
  • Holidays — country-keyed days that are auto-blocked when you opt in
  • Free/busy rules — which calendar events on connected calendars are treated as "free anyway, ignore them" (e.g. "Personal — gym")

All three are managed at /calendar/schedules and assigned per booking link.

Schedules CRUD page

Screenshot needed — /calendar/schedules with three saved schedules and the holiday country picker

Why a separate schedule entity

Inline weekly hours on a booking link work great for one rep with one rhythm. The moment you have multiple links — a 30-min intro, a 60-min discovery, an internal pairing call — and they all need to share the same hours, you're updating the same windows in three places.

A schedule is the shared row. Define it once, point N booking links at it through the schedule_id field. Edit the schedule and every link that references it picks up the change instantly.

If a link doesn't reference a schedule (schedule_id is null), it falls back to its own inline availability_hours. That's the legacy single-link behavior — still supported, never going away.

Creating a schedule

Path: /calendar/schedulesNew schedule.

FieldWhat it does
NameInternal label, e.g. "EMEA business hours"
TimezoneDefault tz the windows are interpreted in. Per-link override is still allowed.
Weekly hoursOne or more time windows per weekday (09:00–12:00, 13:00–17:00). Days you don't enable are unavailable.
Date overridesSpecific dates that override the weekly pattern. Can be blocked (whole day off) or custom windows (different hours that day only). Same shape as a booking link's inline overrides.
Observed holidaysMulti-select country chips (US, GB, FR, DE, NL, BE, ES, IT, CA, AU). When set, the holiday rows for those countries are layered on as blocked dates. See Holidays.

Saved schedules appear in a picker on the booking link form's Availability tab.

Cloning

Each schedule has a clone action — duplicate with a "(copy)" suffix. Useful for variant patterns (e.g. "EMEA hours" → clone → "EMEA hours (Q4 reduced)"). The clone is independent; later edits to the original don't propagate.

Deletion

Deleting a schedule is allowed only when no booking link references it. The dialog lists which links would break — detach them first, or pick a replacement schedule from the dropdown.

Holidays

Workestra ships with seeded holiday rows for 10 countries × 3 years (2025–2027):

CountryCode
United StatesUS
United KingdomGB
FranceFR
GermanyDE
NetherlandsNL
BelgiumBE
SpainES
ItalyIT
CanadaCA
AustraliaAU

Holidays are stored in scheduling_holidays and refreshed annually. To opt your schedule (or a specific link, if it doesn't use a schedule) into holiday observance, pick the country codes in Observed holidays.

When a slot falls on an observed holiday, it's treated as blocked. The slot picker shows the day greyed out with a small holiday badge ("New Year's Day"). The attendee can scroll past — there's no error message, just empty availability.

Adding a country we don't ship

If you need a country that's not in the seeded list, drop a request to support with the country code and a public list of dates. We'll add it in the next migration. Custom per-workspace holiday lists aren't supported today (everyone in DE sees the same German holidays) — partly because it's the right default, and partly because per-workspace holiday data is the kind of thing that drifts silently and breaks 18 months later.

Half-day holidays

Workestra doesn't model half-days as a holiday row. If your team works mornings on Christmas Eve, leave the holiday off and use a date override on your schedule with a custom morning-only window for that date.

Free/busy rules

By default, every event on your connected Google or Microsoft calendar blocks availability. That's safe but sometimes too aggressive — your "Lunch" placeholder, your "Personal — gym" block, or a no-meaning marker like "Focus" don't actually mean you're unavailable for client calls.

Free/busy rules let you say "events whose title matches X are bookable anyway."

Configured per booking link, on the Advanced tab. Each rule has:

FieldWhat it does
Match typeIncludes, Exact match, or Starts with
ValueThe string to match the calendar event's title against
Case sensitiveOptional toggle (off by default — most calendar titles are inconsistently capitalized)

Examples:

RuleBehavior
Includes "personal" (case-insensitive)"Personal — gym" and "personal Lunch" → ignored. "Person of interest" → ignored too (substring match).
Starts with "[Free]"A convention where you prefix bookable blocks with [Free].
Exact match "Tentative"One specific calendar marker.

Rules apply only to external calendar busy intervals — they don't override Workestra's own internal scheduling_calendar_events, PTO from People, FSM dispatches, or any other workload bridge. Those are always authoritative.

If multiple rules match an event, the event is bookable. There's no "any rule wins" vs "all rules win" — a single match opens it up.

Multiple calendars per host

Each host has one connected calendar provider per booking. The slot engine reads free/busy from getProviderForUser(memberId). Round-robin pools can mix providers freely (one rep on Google, another on Microsoft).

Reading from multiple calendars on the same host (e.g. "check personal AND work, write only to work") is on the roadmap but not yet shipped. Today, the connected calendar is the single source for both reads and writes.

What schedules do NOT govern

For clarity:

  • Buffers — still per-link. Buffers don't make sense to share because the meeting type (30-min intro vs 90-min workshop) drives the buffer.
  • Booking caps — daily, weekly, monthly limits stay on the booking link.
  • Min notice / max days ahead — still per-link.
  • Cancellation policy hours — per-link.

The schedule answers "when am I available in general?" The link answers "and what's the shape of this specific meeting?"