Skip to content
Article Bookings & Calendar
☀️ Light Clean and bright
📜 Sepia Warm and vintage
🌤️ Light Gray Subtle and neutral
🔵 Light Blue Calm and serene
🌙 Dark Easy on the eyes
🌑 Dark Gray Deep and modern
🔷 Dark Blue Professional and sleek
🌿 Forest Calm and natural

Bookings & Calendar

A dependency-free booking system for sessions, events, and appointments. Like the rest of SepoDesk it is file-based — bookable services are Markdown pages; bookings and day-planner todos are JSON files on disk.

Customers pick a service, choose a host and an available time on a month calendar, and pay to confirm. Admins get a full CRUD console with exports and a per-day planner. Payments reuse the shop's method config in products/_payments.md.


Services index

One page turns on the service list:

KeyRequiredDescription
titleyesHeading.
bookings_diryesFolder holding service files. Normally bookings.
ordernoSort weight.

A bookable service

One file per service inside bookings/. The body is shown above the calendar.

KeyDescription
titleService name.
durationMinutes per slot (e.g. 60). Drives how windows are sliced.
priceNumber. 0/omitted = free (no payment step; booking is auto-confirmed).
currencySymbol. Default $.
summaryShort blurb on the index card.
availabilityWeekly windows: `day\HH:MM-HH:MM` entries. Repeat a day for split hours.
hostsBookable people: `Label\id` entries. Each host has independent capacity.
lead_daysEarliest bookable day = today + lead. Default 0.
window_daysFurthest bookable day = today + window. Default 60.
payment_methodsAllowed method ids (see Payments). Empty = all configured methods.
serviceSet true to force service rendering from outside bookings/.
---
title: 1:1 Consultation
duration: 60
price: 50
currency: $
availability: [mon|09:00-12:00, mon|13:00-17:00, tue|09:00-17:00, fri|09:00-13:00]
hosts: [Jane Okoro|jane, Sam Rivera|sam]
lead_days: 1
window_days: 30
payment_methods: [bank, paypal]
---

## What to expect
A one-on-one working session…

How availability works

availability lists weekly windows by weekday (monsun). Each window is cut into back-to-back slots of duration minutes. A slot disappears once a non-cancelled booking exists for that service + host + date + start, so two hosts can hold the same time independently. Past times on the current day, and days outside the lead_dayswindow_days range, are never offered. The server re-checks the slot is free at booking time, so two people racing for the last slot can't double-book.

Payments

If price > 0, the customer picks a method from products/_payments.md (filtered by the service's payment_methods). instant methods mark the booking paid; manual/link methods leave it pending with instructions until an admin confirms. Free services skip payment and are confirmed immediately.


My Bookings

A page with my_bookings: true shows the signed-in user's history — date, time, service, host, amount, status — and a Cancel button on upcoming, non-cancelled bookings. Guests get a login prompt.

Bookings admin

A page with bookings_admin: true (admin-only) is the console:

  • Stat cards: total, upcoming, cancelled, revenue (paid).
  • A day planner — pick any date and add/check/delete todos for it.
  • All bookings table with per-row actions: Confirm, Paid, Reschedule (new date + time), Cancel, Delete.
  • CSV / PDF export of every booking, and 5-per-page pagination.
---
title: Bookings Admin
bookings_admin: true
---

Data on disk (no database)

DataLocation
Bookingscontent/bookings/data/<service>.bookings.json
Todoscontent/bookings/todos/<YYYY-MM-DD>.json
Paymentscontent/pages/products/_payments.md (shared)

<service> is the service file's basename without .md. A booking record holds the customer's uid, display name, opt-in email, host, date/time, duration, amount, method, status, and notes.

Statuses

pending (awaiting manual payment) · confirmed (free or admin-approved) · paid · cancelled. Only cancelled bookings free their slot.

API summary

EndpointAuthPurpose
GET /api/v2/bookings/slotspublicFree slots for service,date,host.
POST /api/v2/bookingssigned-inCreate a booking.
POST /api/v2/bookings/cancelsigned-inCancel own booking (admin: any).
POST /api/v2/bookings/admin/statusadminSet status.
POST /api/v2/bookings/admin/updateadminReschedule / edit.
POST /api/v2/bookings/admin/deleteadminDelete.
GET/POST /api/v2/bookings/todosadminDay-planner todos.

Reachability

bookings is in the sidebar exclusion list and the index is hidden by bookings_dir. Surface pages from _nav.md:

- [Book](/docs/bookings)
- [My Bookings](/docs/my-bookings)