End a series early
/api/booking-series/{id}"End a series early from any of its visits: the rest cancel in one go and the customer hears once" (the shipped spec this follows). Only {"status":"cancelled"} is accepted: there is no other transition a series makes.
Cancels every visit in the series that is both in the future AND not already cancelled/completed; a visit that already happened, or was cancelled individually by the guest or staff before this ran, is left exactly as it was rather than double-touched. book_booking_series.status flips to cancelled in the same action, which is what refuses a second call on an already-ended series with 409. One series_cancelled notification job is queued for the whole batch, not one per visit.
Authorization
sessionCookie The dashboard's Supabase Auth session cookie, set at sign-in. Large sessions are split across
numbered chunks (…auth-token.0, .1), so treat this as a cookie family rather than one name.
Every request re-validates it against the Auth server (getUser()), never by decoding the cookie
locally: a JWT nothing has checked is not a credential. Tenancy is then read from the verified
app_metadata.company_id claim and enforced by row-level security; it is never read from request
input, on any route, ever.
role (admin / staff) is deliberately not in RLS. It gates specific actions in route code,
the operations marked admin below, so hiding a button in the UI is cosmetic only, and a route's
own check is the enforcement.
In: cookie
Path Parameters
book_booking_series id.
Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Response Body
application/json
application/json
application/json
application/json
application/json
application/json
curl -X PATCH "https://example.com/api/booking-series/string" \ -H "Content-Type: application/json" \ -d '{ "status": "cancelled" }'{ "ok": true, "cancelledCount": 0}Read one repeat series and its visits GET
The series row plus every book_appointments row it generated, in date order, each with its OWN current status, not just the ones still upcoming. What the "End series" confirm step (BookingDetailDialog) reads to say how many visits are actually about to be cancelled, versus already past or already cancelled individually; that distinction needs the full list, not a second round trip filtered ahead of time.
Read one booking GET
The single-booking twin of the row builder behind the Bookings list: same embeds, same staff-scope confinement, same redaction. Exists so Calendar can pop the exact same BookingDetailDialog in place on a click, instead of navigating to /dashboard/main/bookings?open=id and stranding the viewer on a different page once they close it. Staff-scope and redaction both follow docs/staff-privacy.md: a scoped staff member reaching a colleague's booking gets a 403, not a 404, since reads are filtered rather than hidden; and a staff caller's view of the customer is redacted per the org's staffClientVisibility setting, using this booking's own providerConfirmed as the anonymized-mode grant.