Royal Glow internal docs · now fully interactive — Steps, API tables, file trees & live status
Royal Glow Docs
API Reference

Admin — Bookings

Admin endpoints to list, view, approve, reject, assign, complete, and no-show bookings.

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'))

GET /api/bookings?status=pending&serviceType=salon&date=2026-06-15

Query parameters

All optional. Filters are applied with exact equality; omitting a parameter skips that filter.

Prop

Type

Response

{
  "success": true,
  "data": {
    "bookings": [
      {
        "id": "bk_8f2a1c9e4d",
        "bookingNumber": "BK-RS-2606-H-38291",
        "branchId": "br_rayasandra01",
        "customerId": "usr_abc123",
        "status": "pending",
        "serviceType": "salon",
        "bookingDate": "2026-06-15",
        "startTime": "11:00:00",
        "endTime": "12:00:00",
        "totalAmountPaise": 130000,
        "totalDurationMinutes": 60,
        "notes": "Prefer a window seat if available.",
        "isWalkin": false,
        "isMembershipSession": false,
        "offerId": null,
        "spaMembershipId": null,
        "confirmedAt": null,
        "completedAt": null,
        "cancellationReason": null,
        "cancelledAt": null,
        "rejectionReason": null,
        "rejectedAt": null,
        "rescheduleCount": 0,
        "createdAt": "2026-06-01T06:15:00.000Z",
        "updatedAt": "2026-06-01T06:15:00.000Z",
        "customerName": "Aarav Sharma",
        "customerEmail": "[email protected]",
        "services": [
          {
            "id": "bs_1",
            "bookingId": "bk_8f2a1c9e4d",
            "serviceId": "svc_haircut001",
            "staffId": null,
            "serviceNameSnapshot": "Signature Haircut",
            "priceAtBookingPaise": 80000,
            "durationMinutes": 45,
            "displayOrder": 0
          }
        ]
      }
    ]
  }
}

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'))

GET /api/bookings/bk_8f2a1c9e4d

Path parameters

Prop

Type

Response

{
  "success": true,
  "data": {
    "booking": {
      "id": "bk_8f2a1c9e4d",
      "bookingNumber": "BK-RS-2606-H-38291",
      "branchId": "br_rayasandra01",
      "customerId": "usr_abc123",
      "status": "confirmed",
      "serviceType": "salon",
      "bookingDate": "2026-06-15",
      "startTime": "11:00:00",
      "endTime": "12:00:00",
      "totalAmountPaise": 130000,
      "totalDurationMinutes": 60,
      "notes": null,
      "isWalkin": false,
      "isMembershipSession": false,
      "offerId": null,
      "spaMembershipId": null,
      "confirmedAt": "2026-06-01T07:00:00.000Z",
      "completedAt": null,
      "cancellationReason": null,
      "cancelledAt": null,
      "rejectionReason": null,
      "rejectedAt": null,
      "rescheduleCount": 0,
      "createdAt": "2026-06-01T06:15:00.000Z",
      "updatedAt": "2026-06-01T07:00:00.000Z",
      "customerName": "Aarav Sharma",
      "customerEmail": "[email protected]",
      "services": [
        {
          "id": "bs_1",
          "bookingId": "bk_8f2a1c9e4d",
          "serviceId": "svc_haircut001",
          "staffId": "stf_priya01",
          "serviceNameSnapshot": "Signature Haircut",
          "priceAtBookingPaise": 80000,
          "durationMinutes": 45,
          "displayOrder": 0
        }
      ]
    }
  }
}

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'))

PATCH /api/bookings/bk_8f2a1c9e4d
Content-Type: application/json

Path parameters

Prop

Type

Request body

Prop

Type

{
  "action": "approve",
  "staffId": "stf_priya01"
}
{
  "action": "reject",
  "rejectionReason": "No therapist available for the requested slot."
}
{
  "action": "assign",
  "staffId": "stf_priya01"
}

Response

The updated booking row is returned.

{
  "success": true,
  "data": {
    "booking": {
      "id": "bk_8f2a1c9e4d",
      "bookingNumber": "BK-RS-2606-H-38291",
      "branchId": "br_rayasandra01",
      "customerId": "usr_abc123",
      "status": "confirmed",
      "serviceType": "salon",
      "bookingDate": "2026-06-15",
      "startTime": "11:00:00",
      "endTime": "12:00:00",
      "totalAmountPaise": 130000,
      "totalDurationMinutes": 60,
      "notes": null,
      "isWalkin": false,
      "isMembershipSession": false,
      "offerId": null,
      "spaMembershipId": null,
      "confirmedAt": "2026-06-01T07:00:00.000Z",
      "completedAt": null,
      "cancellationReason": null,
      "cancelledAt": null,
      "rejectionReason": null,
      "rejectedAt": null,
      "rescheduleCount": 0,
      "createdAt": "2026-06-01T06:15:00.000Z",
      "updatedAt": "2026-06-01T07:00:00.000Z"
    }
  }
}

The booking id and the updated booking_service rows are returned.

{
  "success": true,
  "data": {
    "bookingId": "bk_8f2a1c9e4d",
    "services": [
      {
        "id": "bs_1",
        "bookingId": "bk_8f2a1c9e4d",
        "serviceId": "svc_haircut001",
        "staffId": "stf_priya01",
        "serviceNameSnapshot": "Signature Haircut",
        "priceAtBookingPaise": 80000,
        "durationMinutes": 45,
        "displayOrder": 0
      }
    ]
  }
}

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'))

POST /api/bookings/bk_8f2a1c9e4d/complete
Content-Type: application/json

Path parameters

Prop

Type

Request body

paymentMethod is validated by completeBookingSchema (@rgss/types). The offer/gems fields are read defensively from the raw body alongside the parsed payload.

Prop

Type

{
  "paymentMethod": "upi",
  "offerId": "off_monsoon20"
}

Response

Returns the completed booking plus an invoice summary, gems earned, and any discount applied (all amounts in paise).

{
  "success": true,
  "data": {
    "booking": {
      "id": "bk_8f2a1c9e4d",
      "bookingNumber": "BK-RS-2606-H-38291",
      "branchId": "br_rayasandra01",
      "customerId": "usr_abc123",
      "status": "completed",
      "serviceType": "salon",
      "bookingDate": "2026-06-15",
      "startTime": "11:00:00",
      "endTime": "12:00:00",
      "totalAmountPaise": 130000,
      "totalDurationMinutes": 60,
      "notes": null,
      "isWalkin": false,
      "isMembershipSession": false,
      "offerId": null,
      "spaMembershipId": null,
      "confirmedAt": "2026-06-01T07:00:00.000Z",
      "completedAt": "2026-06-15T06:30:00.000Z",
      "cancellationReason": null,
      "cancelledAt": null,
      "rejectionReason": null,
      "rejectedAt": null,
      "rescheduleCount": 0,
      "createdAt": "2026-06-01T06:15:00.000Z",
      "updatedAt": "2026-06-15T06:30:00.000Z"
    },
    "invoice": {
      "invoiceNumber": "INV-1-2627-92921",
      "totalPaise": 104000,
      "gstPaise": 15864
    },
    "gemsEarned": 10,
    "discountPaise": 26000
  }
}

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'))

POST /api/bookings/bk_8f2a1c9e4d/noshow

Path parameters

Prop

Type

Response

{
  "success": true,
  "data": {
    "booking": {
      "id": "bk_8f2a1c9e4d",
      "bookingNumber": "BK-RS-2606-H-38291",
      "branchId": "br_rayasandra01",
      "customerId": "usr_abc123",
      "status": "no_show",
      "serviceType": "salon",
      "bookingDate": "2026-06-15",
      "startTime": "11:00:00",
      "endTime": "12:00:00",
      "totalAmountPaise": 130000,
      "totalDurationMinutes": 60,
      "notes": null,
      "isWalkin": false,
      "isMembershipSession": false,
      "offerId": null,
      "spaMembershipId": null,
      "confirmedAt": "2026-06-01T07:00:00.000Z",
      "completedAt": null,
      "cancellationReason": null,
      "cancelledAt": null,
      "rejectionReason": null,
      "rejectedAt": null,
      "rescheduleCount": 0,
      "createdAt": "2026-06-01T06:15:00.000Z",
      "updatedAt": "2026-06-15T07:00:00.000Z"
    }
  }
}

Errors

OpenReport an issue

Was this page helpful?

On this page