Admin — Bookings
Admin-side booking management. Every endpoint here callsrequireRole(...),
which first resolves the Better Auth session (UNAUTHENTICATED 401 when there
is none) and then checks the caller’s role against the hierarchy
customer < staff < receptionist < manager < owner < developer
(FORBIDDEN 403 when the level is too low).
Base URL:
admin.theroyalglow.in · Auth: Better Auth session;
minimum role receptionist on every endpoint on this page. These paths
carry no /admin prefix — the admin subdomain is the namespace, so the
full URL is e.g. admin.theroyalglow.in/api/bookings. The customer app
exposes its own, distinct /api/bookings on theroyalglow.in (see
Bookings). Money is an integer in paise;
bookingDate filters use YYYY-MM-DD; stored times render as HH:mm:ss.
Timestamps are ISO-8601 UTC.GET /api/bookings
Lists all bookings across customers (admin view), newest first (bookingDate then createdAt descending). Each booking is returned with the
customer’s name and email and its booking_service snapshot rows (ordered by
displayOrder). Optional query filters narrow the result set; this endpoint
returns the full filtered list and does not paginate (no meta).
Minimum role: receptionist (requireRole('receptionist'))
Query parameters
All optional. Filters are applied with exact equality; omitting a parameter skips that filter.Response
Errors
GET /api/bookings/[id]
Returns a single booking (any customer) with the customer’s name and email and itsservices snapshot rows.
Minimum role: receptionist (requireRole('receptionist'))
Path parameters
Response
Errors
PATCH /api/bookings/[id]
A single endpoint that performs one of three actions, selected by theaction
discriminator. The body is validated by adminBookingActionSchema
(@rgss/types), a discriminated union over action:
approve— only valid on apendingbooking. AssignsstaffIdto every service on the booking, then transitionspending → confirmedand stampsconfirmedAt.reject— only valid on apendingbooking. Transitionspending → rejected, storingrejectionReasonandrejectedAt.assign— (re)assignsstaffIdto every service on the booking, regardless of status.
receptionist (requireRole('receptionist'))
Path parameters
Request body
- Tab Title
- Tab Title
- Tab Title
Response
- Tab Title
- Tab Title
The updated booking row is returned.
Errors
POST /api/bookings/[id]/complete
Completes a booking at the counter: collects the in-person payment method, transitions the booking tocompleted, generates a GST-inclusive service
invoice, awards loyalty gems, and (optionally) applies an offer. Only
confirmed or in_progress bookings can be completed.
Minimum role: receptionist (requireRole('receptionist'))
Path parameters
Request body
paymentMethod is validated by completeBookingSchema (@rgss/types). The
offer/gems fields are read defensively from the raw body alongside the parsed
payload.
Response
Returns the completed booking plus an invoice summary, gems earned, and any discount applied (all amounts in paise).Gems are earned on the discounted total at 1 gem per ₹100 (floor) and expire
365 days from earn date. The invoice subtotal is the original (pre-discount)
base; the taxable value and GST are split from the discounted total. With no
offerId, discountPaise is 0 and the total is unchanged.Errors
POST /api/bookings/[id]/noshow
Marks a booking asno_show. Only a confirmed booking can be marked no-show
(intended for use after the appointment window). No request body is required.
No-show tier escalation is handled separately by a background job.
Minimum role: receptionist (requireRole('receptionist'))