Remove a time-away entry (admin, or manage_staff)
/api/providers/{id}/blocked-periodsThe provider's ordinary hours (and any approved date-specific override) apply again over that range immediately. Scoped to both id and this provider: a bare id match would let a caller delete a whole-venue closure or another provider's entry by reusing a uuid from the same table.
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
Provider id.
Query Parameters
The blocked-period row id.
uuidResponse Body
application/json
application/json
application/json
application/json
application/json
curl -X DELETE "https://example.com/api/providers/string/blocked-periods?id=497f6eca-6276-4993-bfeb-53cbbbba6f08"{ "ok": true}Mark a staff member away (admin, or manage_staff) POST
Writes a book_blocked_periods row (migration 0001) with this provider's id, covering `firstDay` 00:00 to `lastDay` 23:59:59 as one instant range: the same "wall clock wearing a +00" convention every other date-scoped write in this app uses. No engine change was needed to make this take effect. book_blocked_periods with a `provider_id` was already read on every public path that decides whether a provider is bookable (GET /api/book/{slug}/availability, POST /api/book/{slug}/appointments, the guest reschedule inside /api/book/{slug}/manage/{token}) before this route existed to write one. A `provider_id IS NULL` row is a DIFFERENT, older feature (a whole-venue closure, set from Organization > Special hours); this route never writes one. **Conflicts** against existing, non-cancelled appointments anywhere in the range are checked live, the same `findConflictingAppointments` pattern `POST /api/providers/{id}/availability-overrides` and `POST /api/organization/special-hours` already prove: refused with 409 unless `conflictAcknowledged: true`. Every caller who can reach this route already holds `manage_staff`, so unlike the override route there is no staff-vs-admin branch; acknowledgment alone is the gate, same as special-hours' own reasoning. Self-service (a staff member marking their own leave) is deliberately out of scope: this is the same admin/manage_staff boundary as the rest of provider CRUD, not the tiered self-request flow `book_availability_overrides` uses.
List availability-override requests GET
Staff sees only their own linked provider's rows (server-forced, ignoring any client filter; there is none to send); admin sees every row across every status, so this one endpoint powers both a "needs attention" view (filter client-side on `status: pending`, or `approved` with `conflictAcknowledged` and `conflictCount > 0`) and a full history view. `conflictCount` is recomputed live on every call for an admin caller (always 0 for staff); migration 0052 deliberately does not persist which appointments conflicted, so a later cancellation or reassignment through the ordinary calendar screen makes a row stop needing attention with no write required.