API

Bookings - Authenticated

Protected booking endpoints for registered customers and owners.

Base path: /api/v1/bookings

Used by: customer-booki-web-app · Role: customer
organizationId: From the customer's JWT token (embedded at registration — sent automatically via Authorization: Bearer header)

These endpoints require authentication (Authorization: Bearer <accessToken> header) and are used by registered customers to manage their bookings.


POST /api/v1/bookings — Create Booking (Authenticated)

Auth: Protected (Authorization: Bearer <accessToken>)
Used by: customer-booki-web-app · Role: customer
Description: Create a booking as an authenticated customer. Logs booking in customer's history.

Request

{
  "packageId": "69de52bc4f3e4272e30c7a15",
  "bookingDate": "2026-04-20",
  "bookingTime": "14:30",
  "preferredPaymentMethod": "maya" // (optional)
}

Fields:

  • packageId (string, required): Service package ID (24-hex)
  • bookingDate (string, required): YYYY-MM-DD (future)
  • bookingTime (string, required): HH:MM (future)
  • preferredPaymentMethod (string, optional): cash or maya

Note: organizationId is not sent in the request body. It is read from the customer's JWT token server-side.

Response (201 Created)

{
  "message": "Your booking reservation has been submitted. You can view and manage it anytime from your dashboard.",
  "bookingId": "69defc2e27e1a548fd0cce1c"
}

Error Responses

401 Unauthorized — No valid token:

{
  "statusCode": 401,
  "message": "Access token is required to proceed."
}

400 Bad Request — Past date/time:

{
  "statusCode": 400,
  "message": "Please select a valid date for your booking. Past dates are not available."
}

cURL Example

curl -X POST http://localhost:4001/api/v1/bookings \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer eyJhbGc..." \
  -d '{
    "packageId": "69de52bc4f3e4272e30c7a15",
    "bookingDate": "2026-04-20",
    "bookingTime": "14:30",
    "preferredPaymentMethod": "maya"
  }'

GET /api/v1/bookings — List Customer Bookings

Auth: Protected (Authorization: Bearer <accessToken>)
Used by: customer-booki-web-app · Role: customer
organizationId: From JWT token (filters results to customer's organization automatically)
Description: Retrieve all bookings for the authenticated customer.

Request

Query Parameters (optional):

  • page (number, default: 1): Page number
  • limit (number, default: 10, max: 100): Results per page
  • status (string): Filter by booking status. Accepted values: pending-verification, verified, pending, confirmed, declined, pending-reschedule, confirmed-reschedule, declined-reschedule, rescheduled, pending-cancel, confirmed-cancel, declined-cancel, cancelled, cancelled-admin, paid, ongoing, completed, no-show, unpaid-expired, expired
  • search (string): Search term (max 100 chars)
  • sort (string, default: _id): Sort field
  • order (string, default: desc): asc or desc
GET /api/v1/bookings?page=1&limit=10&status=confirmed&order=desc

Response (200 OK)

{
  "items": [
    {
      "_id": "507f1f77bcf86cd799439041",
      "organizationId": "507f191e810c19729de860ea",
      "packageId": "507f1f77bcf86cd799439031",
      "bookingDate": "2026-04-10",
      "bookingTime": "14:30",
      "status": "confirmed",
      "preferredPaymentMethod": "maya",
      "createdAt": "2026-04-01T09:15:00Z"
    },
    {
      "_id": "507f1f77bcf86cd799439042",
      "organizationId": "507f191e810c19729de860ea",
      "packageId": "507f1f77bcf86cd799439032",
      "bookingDate": "2026-04-15",
      "bookingTime": "10:00",
      "status": "pending",
      "createdAt": "2026-04-01T10:20:00Z"
    }
  ],
  "pages": 3,
  "pageRange": "1-2 of 25"
}

Error Responses

401 Unauthorized — No token:

{
  "statusCode": 401,
  "message": "Access token is required to proceed."
}

cURL Example

curl -X GET "http://localhost:4001/api/v1/bookings?page=1&limit=10&status=confirmed" \
  -H "Authorization: Bearer eyJhbGc..."

PUT /api/v1/bookings/:id/reschedule — Reschedule Booking

Auth: Protected (Authorization: Bearer <accessToken>)
Used by: customer-booki-web-app · Role: customer
Description: Reschedule an existing booking to a new date/time.

Request

{
  "bookingDate": "2026-04-12",
  "bookingTime": "16:00",
  "rescheduleReason": "Customer requested new time due to schedule conflict"
}

Fields:

  • bookingDate (string, required): New date (YYYY-MM-DD, future)
  • bookingTime (string, required): New time (HH:MM, future)
  • rescheduleReason (string, optional): Reason for reschedule (10-500 chars)

Note: bookableId and organizationId are not sent in the request body. Both are read from the customer's JWT token server-side.

Response (200 OK)

Response message depends on the booking's current status:

{
  "message": "Your booking has been successfully rescheduled. We hope to see you again soon!"
}
{
  "message": "We've received your reschedule request. You'll receive a notification once it's reviewed and a decision is made."
}

Error Responses

400 Bad Request — New time is in past:

{
  "statusCode": 400,
  "message": "Please select a valid time slot for your rescheduled booking. Past date and time are not available."
}

cURL Example

curl -X PUT "http://localhost:4001/api/v1/bookings/507f1f77bcf86cd799439041/reschedule" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer eyJhbGc..." \
  -d '{
    "bookingDate": "2026-04-12",
    "bookingTime": "16:00",
    "rescheduleReason": "Customer requested new time"
  }'

PUT /api/v1/bookings/:id/cancel — Cancel Booking

Auth: Protected (Authorization: Bearer <accessToken>)
Used by: customer-booki-web-app · Role: customer
Description: Cancel an existing booking.

Request

{
  "cancelReason": "Had to travel for urgent business meeting"
}

Fields:

  • cancelReason (string, optional): Reason for cancellation (10-500 chars)

Note: bookableId and organizationId are not sent in the request body. Both are read from the customer's JWT token server-side.

Response (200 OK)

Response message depends on the booking's current status:

{
  "message": "Your booking has been successfully cancelled. We hope to see you again soon!"
}
{
  "message": "We've received your cancellation request. You'll receive a notification once it's reviewed and a decision is made."
}

Error Responses

400 Bad Request — Booking cannot be cancelled:

{
  "statusCode": 400,
  "message": "This booking can no longer be cancelled."
}

cURL Example

curl -X PUT "http://localhost:4001/api/v1/bookings/507f1f77bcf86cd799439041/cancel" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer eyJhbGc..." \
  -d '{
    "bookableId": "507f1f77bcf86cd799439051",
    "cancelReason": "Had to travel"
  }'

Booking Status Lifecycle

PENDING → CONFIRMED (owner accepts)
       → DECLINED (owner declines)
       → RESCHEDULED (customer reschedules)
       → CANCELLED (customer cancels)
       → NO_SHOW (customer doesn't show up)
  • New bookings start as PENDING until owner confirms.
  • CONFIRMED bookings can be rescheduled or cancelled by customer.
  • DECLINED bookings cannot be modified; customer must rebook.
  • CANCELLED bookings are soft-deleted (mark deletedAt).

Notes

  • All authenticated endpoints require a valid Authorization: Bearer <accessToken> header.
  • Dates must be in the future; past dates are rejected.
  • Pagination defaults to page 1, limit 10 (max 100).
  • See booki-api/src/validations/booking.validation.ts for validation rules.