Skip to main content

Admin — Bookings

Admin-side booking management. Every endpoint here calls 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).
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 its services 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 the action discriminator. The body is validated by adminBookingActionSchema (@rgss/types), a discriminated union over action:
  • approve — only valid on a pending booking. Assigns staffId to every service on the booking, then transitions pending → confirmed and stamps confirmedAt.
  • reject — only valid on a pending booking. Transitions pending → rejected, storing rejectionReason and rejectedAt.
  • assign — (re)assigns staffId to every service on the booking, regardless of status.
Minimum role: receptionist (requireRole('receptionist'))

Path parameters

Request body

Response

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 to completed, 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 as no_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

Last modified on June 29, 2026