Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/IvanchoDev89/maleku-system/llms.txt

Use this file to discover all available pages before exploring further.

The Bookings API handles reservation creation for both accommodation properties and guided tours. All bookings begin in pending status and transition to confirmed once a Stripe payment is completed. Dynamic pricing applies weekend rates, extra-guest surcharges, and weekly discounts automatically. A unique confirmation code in the format CRT-XXXXXXXX is generated for every booking.
Creating a booking requires a verified email address. Requests from unverified accounts are rejected with 403 Forbidden. Verify your email first via POST /api/v1/auth/verify-email.

Create Property Booking

POST /api/v1/bookings/property

Creates a new property/room booking. The room_id field is required — bookings are priced at the room level, not the property level. A PostgreSQL advisory lock is acquired on the (room_id, check_in, check_out) combination before inserting the booking to prevent race conditions. Auth: Bearer token — verified email required Rate limit: 10 requests/minute per IP
property_id
string
required
UUID of the property to book.
room_id
string
required
UUID of the specific room within that property. Must belong to property_id.
check_in
string
required
ISO 8601 datetime for arrival. Must be in the future.
check_out
string
required
ISO 8601 datetime for departure. Must produce at least 1 night.
guests
integer
default:"1"
Total number of guests. Must be ≥ 1.
guest_name
string
required
Lead guest full name (2–255 characters).
guest_email
string
required
Lead guest email address.
guest_phone
string
Lead guest phone number (optional).
guest_notes
string
Special requests or notes for the vendor (optional).
Example request
curl -X POST https://api.malekusystem.com/api/v1/bookings/property \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
  -H "Content-Type: application/json" \
  -d '{
    "property_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "room_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
    "check_in": "2025-06-15T15:00:00Z",
    "check_out": "2025-06-20T11:00:00Z",
    "guests": 2,
    "guest_name": "Ana Rodríguez",
    "guest_email": "ana@example.com",
    "guest_phone": "+50688887777",
    "guest_notes": "Early check-in requested if possible."
  }'
Response 201
{
  "id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
  "user_id": "d4e5f6a7-b8c9-0123-def0-234567890123",
  "vendor_id": "e5f6a7b8-c9d0-1234-ef01-345678901234",
  "property_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "room_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
  "tour_id": null,
  "booking_type": "property",
  "status": "pending",
  "guest_name": "Ana Rodríguez",
  "guest_email": "ana@example.com",
  "guest_phone": "+50688887777",
  "guest_notes": "Early check-in requested if possible.",
  "total_amount": 925.00,
  "currency": "USD",
  "confirmation_code": "CRT-3F8A1C2D",
  "created_at": "2025-05-01T09:14:22Z"
}
The booking system uses PostgreSQL advisory locks to prevent double-booking. When two requests arrive for the same room and overlapping dates simultaneously, the second request blocks until the first transaction commits, then re-checks availability before proceeding. This eliminates the TOCTOU (time-of-check/time-of-use) race condition entirely.

Create Tour Booking

POST /api/v1/bookings/tour

Creates a new tour booking. An advisory lock is acquired on (tour_id, booking_date) before inserting. The participants count is validated against the tour’s max_group_size. Auth: Bearer token — verified email required Rate limit: 10 requests/minute per IP
tour_id
string
required
UUID of the tour to book.
booking_date
string
required
ISO 8601 datetime for the tour date/time. Must be in the future.
participants
integer
default:"1"
Number of participants. Must not exceed the tour’s max_group_size.
guest_name
string
required
Lead guest full name.
guest_email
string
required
Lead guest email address.
guest_phone
string
Lead guest phone (optional).
guest_notes
string
Special requests (optional).
Response 201 — same BookingResponse shape as property booking, with booking_type: "tour", tour_id populated, and property_id / room_id as null.

List Bookings

GET /api/v1/bookings/

Returns paginated bookings scoped to the authenticated user’s role:
  • CLIENT — sees only their own bookings
  • VENDOR — sees all bookings for their properties and tours
  • ADMIN / SUPER_ADMIN — sees all bookings platform-wide
status
string
Filter by booking status: pending, confirmed, cancelled, completed, refunded.
booking_type
string
Filter by type: property or tour.
page
integer
default:"1"
Page number.
page_size
integer
default:"20"
Items per page. Maximum 100.
Auth: Bearer token

Get Booking

GET /api/v1/bookings/

Returns a single booking with all related objects (user, vendor, property, room, tour) eagerly loaded. Access is scoped by role — clients can only access their own bookings, vendors only those belonging to their profile. Auth: Bearer token Response 404{ "detail": "Booking not found" } Response 403{ "detail": "Not authorized" }

Cancel Booking

PUT /api/v1/bookings//status

Updates booking status. The status field drives the allowed transitions:
  • VENDOR — can set status to confirmed or cancelled
  • CLIENT — can only set status to cancelled (their own booking)
  • ADMIN / SUPER_ADMIN — can set any status
status
string
required
New status value. Accepted: confirmed, cancelled, completed.
Auth: Bearer token
When a booking is cancelled after payment has been captured, trigger a refund via POST /api/v1/stripe/bookings/{id}/refund. See the Payments API for details.

Price Preview

POST /api/v1/bookings/preview

Returns a full price breakdown for a room/date combination without creating a booking. Use this to display the pricing summary to users before they confirm. Auth: Bearer token Rate limit: 30 requests/minute per IP
room_id
string
required
UUID of the room to price.
check_in
string
required
ISO 8601 arrival datetime.
check_out
string
required
ISO 8601 departure datetime.
guests
integer
default:"1"
Total number of guests.
Response 200PricePreviewResponse
{
  "nights": 5,
  "weekday_nights": 3,
  "weekend_nights": 2,
  "weekday_price": 120.00,
  "weekend_price": 145.00,
  "weekday_total": 360.00,
  "weekend_total": 290.00,
  "base_subtotal": 650.00,
  "guests": 2,
  "max_occupancy": 2,
  "extra_guests": 0,
  "extra_guest_price": 15.00,
  "extra_guests_total": 0.00,
  "weekly_discount_percent": 0,
  "weekly_discount_amount": 0.00,
  "subtotal": 650.00,
  "commission_amount": 65.00,
  "total": 650.00,
  "currency": "USD"
}

Booking Status Flow

Bookings follow a defined lifecycle:
pending  ──▶  confirmed  ──▶  completed
   │               │
   └───▶  cancelled └──▶  refunded
StatusDescription
pendingBooking created, awaiting Stripe payment
confirmedPayment captured successfully via Stripe webhook
completedStay/tour has concluded
cancelledCancelled by client, vendor, or admin
refundedPayment refunded through Stripe

Pricing Logic

Property bookings apply a multi-factor pricing calculation:
  1. Weekday rateRoom.price_per_night for Sunday–Thursday nights
  2. Weekend rateRoom.weekend_price for Friday–Saturday nights
  3. Extra guest surchargeRoom.extra_guest_price × (guests − max_occupancy) when guests exceed the room’s base occupancy
  4. Weekly discountProperty.weekly_discount% reduction on subtotal for stays ≥ 7 nights
Tour bookings use: Tour.price × participants.

Build docs developers (and LLMs) love