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:
| Key | Required | Description |
|---|---|---|
title | yes | Heading. |
bookings_dir | yes | Folder holding service files. Normally bookings. |
order | no | Sort weight. |
A bookable service
One file per service inside bookings/. The body is shown above the calendar.
| Key | Description | |
|---|---|---|
title | Service name. | |
duration | Minutes per slot (e.g. 60). Drives how windows are sliced. | |
price | Number. 0/omitted = free (no payment step; booking is auto-confirmed). | |
currency | Symbol. Default $. | |
summary | Short blurb on the index card. | |
availability | Weekly windows: `day\ | HH:MM-HH:MM` entries. Repeat a day for split hours. |
hosts | Bookable people: `Label\ | id` entries. Each host has independent capacity. |
lead_days | Earliest bookable day = today + lead. Default 0. | |
window_days | Furthest bookable day = today + window. Default 60. | |
payment_methods | Allowed method ids (see Payments). Empty = all configured methods. | |
service | Set 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 (mon…sun). 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_days…window_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)
| Data | Location |
|---|---|
| Bookings | content/bookings/data/<service>.bookings.json |
| Todos | content/bookings/todos/<YYYY-MM-DD>.json |
| Payments | content/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
| Endpoint | Auth | Purpose |
|---|---|---|
GET /api/v2/bookings/slots | public | Free slots for service,date,host. |
POST /api/v2/bookings | signed-in | Create a booking. |
POST /api/v2/bookings/cancel | signed-in | Cancel own booking (admin: any). |
POST /api/v2/bookings/admin/status | admin | Set status. |
POST /api/v2/bookings/admin/update | admin | Reschedule / edit. |
POST /api/v2/bookings/admin/delete | admin | Delete. |
GET/POST /api/v2/bookings/todos | admin | Day-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)