Skip to main content
Was this helpful?

Booking API

Reservations and Public Booking

Create reservations, expose public booking, and track payment-required flows

This page documents the current enterprise reservation surface: internal reservations scoped to the active organisation, organisation-based public booking, legacy token booking, and Stripe webhook handling for payment confirmation.

JWT or user API keyPublic booking is unauthenticatedActive organisationStripe webhooks

Source

  • Reservations controller: backend-nestjs/src/reservations/reservations.controller.ts
  • Public booking controller: backend-nestjs/src/resources/public-booking.controller.ts
  • Stripe webhook controller: backend-nestjs/src/payments/stripe-webhook.controller.ts
  • DTOs: backend-nestjs/src/dto/reservation.dto.ts, backend-nestjs/src/dto/public-booking.dto.ts, backend-nestjs/src/reservations/dto/list-reservations.query.dto.ts

Authentication and Permissions

  • Internal reservation CRUD requires JwtAuthGuard plus ReservationAccessGuard.
  • Public booking routes are unauthenticated.
  • Reservation, resource, and public-booking availability flows all reuse the same organisation-scoped availability logic.
  • Payment-required public reservations are confirmed from Stripe webhook events, not from the initial browser request.

Endpoint Reference

Internal Reservations

MethodPathPurposeRequest or queryAuthSource
POST/api/reservationsCreate a reservation in the active organisation.Body: reservation fieldsJWT or user API keyreservations/reservations.controller.ts
GET/api/reservationsList reservations in the active organisation.Query: filter fieldsJWT or user API keyreservations/reservations.controller.ts
GET/api/reservations/:idGet one reservation.Path: idJWT or user API keyreservations/reservations.controller.ts
PATCH/api/reservations/:idUpdate one reservation.Path: id, body: partial reservation fieldsJWT or user API keyreservations/reservations.controller.ts
DELETE/api/reservations/:idDelete one reservation.Path: idJWT or user API keyreservations/reservations.controller.ts

Public Booking

MethodPathPurposeRequest or queryAuthSource
GET/api/public/booking/organisations/:slugResolve the branded public booking catalog for one organisation.Path: slugPublicresources/public-booking.controller.ts
GET/api/public/booking/organisations/:slug/availabilityRead public availability for one service on one day.Path: slug, query: date,resourceTypeId,resourceId?Publicresources/public-booking.controller.ts
POST/api/public/booking/organisations/:slug/reserveCreate a public reservation from the organisation booking page.Path: slug, body: booking fieldsPublicresources/public-booking.controller.ts
GET/api/public/booking/organisations/:slug/checkout-statusPoll the Stripe-backed payment state for a public reservation.Path: slug, query: sessionIdPublicresources/public-booking.controller.ts
GET/api/public/booking/:tokenResolve public booking metadata for a legacy resource token.Path: tokenPublicresources/public-booking.controller.ts
GET/api/public/booking/:token/availabilityRead available slots for a day for one legacy published resource.Path: token, query: datePublicresources/public-booking.controller.ts
POST/api/public/booking/:token/reserveCreate a public reservation from a legacy resource token.Path: token, body: booking fieldsPublicresources/public-booking.controller.ts
GET/api/public/booking/:token/checkout-statusPoll the Stripe-backed payment state for a legacy public reservation.Path: token, query: sessionIdPublicresources/public-booking.controller.ts

Stripe Webhook

MethodPathPurposeRequest or queryAuthSource
POST/api/payments/stripe/webhookHandle payment success, failure, and checkout-expiration events.Header: Stripe-Signature, raw bodyStripe signaturepayments/stripe-webhook.controller.ts

Request Shapes

Internal reservations

CreateReservationDto and UpdateReservationDto

  • startTime: required on create, ISO date-time
  • endTime: required on create, ISO date-time, must be after startTime
  • quantity: optional int, minimum 1
  • customerInfo: optional object
  • notes: optional sanitized string, max 2048 chars
  • resourceTypeId: optional positive int, required for the enterprise type flow
  • resourceId: optional positive int, used as a compatibility bridge or single-resource write
  • resourceIds: optional unique positive integer array for multi-resource same-type reservations
  • status: update-only enum pending|confirmed|completed|cancelled|waitlist

Query:

  • resourceId: optional int >= 1
  • resourceTypeId: optional int >= 1
  • status: optional reservation status
  • startFrom: optional ISO date-time
  • endTo: optional ISO date-time

Public booking

CreateOrganisationPublicBookingDto

  • resourceTypeId: required positive int
  • resourceId: optional positive int for specific-resource booking
  • startTime: required ISO date-time
  • endTime: required ISO date-time
  • quantity: required int, minimum 1
  • customerName: required string
  • customerEmail: required email
  • customerPhone: required string
  • notes: optional string

PublicOrganisationAvailabilityQueryDto

  • date: required ISO date string
  • resourceTypeId: required positive int
  • resourceId: optional positive int

CreatePublicBookingDto for the legacy token route

  • startTime: required ISO date-time
  • endTime: required ISO date-time
  • quantity: required int, minimum 1
  • customerName: required string
  • customerEmail: required email
  • customerPhone: required string
  • notes: optional string

Example Calls

Create a reservation

curl -X POST "$PRIMECAL_API/api/reservations" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"startTime": "2026-04-01T08:00:00.000Z",
"endTime": "2026-04-01T09:00:00.000Z",
"resourceTypeId": 8,
"resourceIds": [21, 22],
"quantity": 2
}'

Read organisation public availability

curl "$PRIMECAL_API/api/public/booking/organisations/acme-spa/availability?date=2026-05-20&resourceTypeId=8"

Create a public booking

curl -X POST "$PRIMECAL_API/api/public/booking/organisations/acme-spa/reserve" \
-H "Content-Type: application/json" \
-d '{
"resourceTypeId": 8,
"startTime": "2026-04-01T08:00:00.000Z",
"endTime": "2026-04-01T09:00:00.000Z",
"quantity": 1,
"customerName": "May B. Late",
"customerEmail": "may@example.com",
"customerPhone": "+36301112222"
}'

Example response for a payment-required booking:

{
"reservationId": 901,
"status": "pending_payment",
"paymentStatus": "pending",
"requiresPayment": true,
"quotedAmount": 2500,
"quotedCurrency": "eur",
"checkoutUrl": "https://checkout.stripe.com/c/pay/cs_test_123",
"stripeCheckoutSessionId": "cs_test_123",
"paymentData": {
"provider": "stripe",
"mode": "checkout",
"checkoutUrl": "https://checkout.stripe.com/c/pay/cs_test_123",
"stripeCheckoutSessionId": "cs_test_123",
"amount": 2500,
"currency": "eur",
"status": "pending",
"statusUrl": "/api/public/booking/organisations/acme-spa/checkout-status?sessionId=cs_test_123"
}
}

Read public checkout status

curl "$PRIMECAL_API/api/public/booking/organisations/acme-spa/checkout-status?sessionId=cs_test_123"

Example response:

{
"reservationId": 901,
"status": "confirmed",
"paymentStatus": "succeeded",
"requiresPayment": true,
"canRetryPayment": false
}

Response and Behavior Notes

  • Internal reservations are scoped to the active organisation.
  • Reservation quote fields are stored canonically on the reservation: quotedUnitAmount, quotedTotalAmount, quotedCurrency, and paymentRequiredSnapshot.
  • Multi-resource reservations can assign multiple concrete resources of the same resource type.
  • When payment is required, public booking creates a pending_payment reservation, returns paymentData, and waits for Stripe webhook confirmation before moving it to confirmed.
  • paymentData currently uses Stripe Checkout with:
    • provider: "stripe"
    • mode: "checkout"
    • checkoutUrl
    • stripeCheckoutSessionId
    • quote snapshot fields such as amount and currency
  • Public catalog payloads expose whether the organisation can currently accept Stripe-backed payments so the client can disable payment-required booking types before reservation creation.
  • The public booking client can keep a reservation in a retryable pending_payment state and poll checkout-status after the Stripe return.
  • PrimeCal does not store raw card data.

Best Practices

  • Set the active organisation before listing reservations in multi-org clients.
  • Validate date ordering client-side before submitting reservation writes.
  • Treat public booking tokens as secrets. Regenerate them when links leak or staff changes occur.
  • Add rate limiting or anti-bot protection in front of public booking forms.
  • Use Stripe webhooks as the source of truth for payment-required booking confirmation.