requireRole(...),
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).
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
UNAUTHENTICATED — 401
UNAUTHENTICATED — 401
FORBIDDEN — 403
FORBIDDEN — 403
receptionist.INTERNAL_ERROR — 500
INTERNAL_ERROR — 500
retryable: true.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
UNAUTHENTICATED — 401
UNAUTHENTICATED — 401
FORBIDDEN — 403
FORBIDDEN — 403
receptionist.NOT_FOUND — 404
NOT_FOUND — 404
INTERNAL_ERROR — 500
INTERNAL_ERROR — 500
retryable: true.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
- approve
- reject
- assign
Response
- approve / reject
- assign
Errors
UNAUTHENTICATED — 401
UNAUTHENTICATED — 401
FORBIDDEN — 403
FORBIDDEN — 403
receptionist.VALIDATION_ERROR — 400
VALIDATION_ERROR — 400
adminBookingActionSchema (unknown action, missing
staffId/rejectionReason, etc.). details carries field errors.NOT_FOUND — 404
NOT_FOUND — 404
BOOKING_INVALID_STATUS_TRANSITION — 409
BOOKING_INVALID_STATUS_TRANSITION — 409
approve/reject attempted on a booking that is not pending.INTERNAL_ERROR — 500
INTERNAL_ERROR — 500
retryable: true.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).offerId, discountPaise is 0 and the total is unchanged.Errors
UNAUTHENTICATED — 401
UNAUTHENTICATED — 401
FORBIDDEN — 403
FORBIDDEN — 403
receptionist.VALIDATION_ERROR — 400
VALIDATION_ERROR — 400
completeBookingSchema (invalid/missing paymentMethod). details
carries field errors.NOT_FOUND — 404
NOT_FOUND — 404
offerId not found.BOOKING_INVALID_STATUS_TRANSITION — 409
BOOKING_INVALID_STATUS_TRANSITION — 409
confirmed or in_progress.OFFER_EXPIRED — 409
OFFER_EXPIRED — 409
OFFER_NOT_APPLICABLE — 409
OFFER_NOT_APPLICABLE — 409
INTERNAL_ERROR — 500
INTERNAL_ERROR — 500
retryable: true.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'))
Path parameters
Response
Errors
UNAUTHENTICATED — 401
UNAUTHENTICATED — 401
FORBIDDEN — 403
FORBIDDEN — 403
receptionist.NOT_FOUND — 404
NOT_FOUND — 404
BOOKING_INVALID_STATUS_TRANSITION — 409
BOOKING_INVALID_STATUS_TRANSITION — 409
confirmed status.INTERNAL_ERROR — 500
INTERNAL_ERROR — 500
retryable: true.