Skip to main content

Rentals API

Base paths: /api/rentals (public + customer), /api/vendor/rentals (vendor), /api/admin/rentals (admin) Files: ayts-api/src/routes/rentals.ts, ayts-api/src/routes/vendor-rentals.ts, ayts-api/src/routes/admin.ts

Endpoint Summary

MethodPathAuthPurpose
GET/api/rentals/categoriesPublicList active rental categories
GET/api/rentalsPublicBrowse listings (filter + availability overlay)
GET/api/rentals/:idPublicListing detail
GET/api/rentals/:id/availabilityPublicDay-by-day monthly availability
POST/api/rentals/:id/bookCustomerCreate a booking
GET/api/rentals/bookings/myCustomerMy bookings
GET/api/rentals/bookings/:idOwner/Vendor/AdminBooking detail
PATCH/api/rentals/bookings/:id/cancelOwner/Vendor/AdminCancel a booking
GET/api/vendor/rentals/listingsVendorMy listings
POST/api/vendor/rentals/listingsVendorCreate listing
PATCH/api/vendor/rentals/listings/:idVendorUpdate listing
DELETE/api/vendor/rentals/listings/:idVendorDeactivate listing (soft delete)
POST/api/vendor/rentals/listings/:id/block-datesVendorBlock dates
DELETE/api/vendor/rentals/listings/:id/block-datesVendorUnblock dates
GET/api/vendor/rentals/bookingsVendorIncoming bookings
GET/api/vendor/rentals/bookings/:idVendorBooking detail (UUID or booking number)
PATCH/api/vendor/rentals/bookings/:id/confirmVendorpending → confirmed
PATCH/api/vendor/rentals/bookings/:id/handoverVendorconfirmed → active
PATCH/api/vendor/rentals/bookings/:id/returnVendoractive → returned (+ condition/damage)
PATCH/api/vendor/rentals/bookings/:id/completeVendorreturned/active → completed
POST/api/admin/rentals/listingsAdminCreate listing for any vendor
PATCH/api/admin/rentals/listings/:idAdminUpdate any listing (verify, activate)

Database Tables

TablePurpose
rental_categoriesCategory catalog (name, slug, icon, color, sort order)
rental_listingsVendor items for rent: rate, pricing unit, deposit, units, delivery, booking rules
rental_bookingsBookings with price snapshot, status workflow, deposit + damage tracking
rental_blocked_datesVendor-blocked dates per listing (unique on listing + date)
vendor_agreements / agreement_signaturesOptional signed rental agreements (see Agreements)

Public Endpoints

GET /api/rentals/categories

Returns active categories ordered by sort_order.

GET /api/rentals

Browse listings with joined category + vendor info.

Query params: category (slug), categoryId, vendorId, search (title ilike), startDate + endDate (adds available_units per listing), limit (max 50), offset

{ "success": true, "data": [ ...listings ], "total": 42, "limit": 20, "offset": 0 }

GET /api/rentals/:id

Listing detail with category, vendor contact, and linked agreement (vendor_agreements) if any. Only active listings are returned.

GET /api/rentals/:id/availability

Query params: year, month (defaults to current)

Returns a per-date map for the month. A date is unavailable when booked units ≥ total_units or the vendor blocked it.

{
"success": true,
"data": {
"listing": { "id": "...", "total_units": 3 },
"availability": {
"2026-07-04": { "availableUnits": 2, "blocked": false },
"2026-07-05": { "availableUnits": 0, "blocked": true, "reason": "Maintenance" }
}
}
}

Customer Endpoints (auth required)

POST /api/rentals/:id/book

{
"startDate": "2026-07-10",
"endDate": "2026-07-12",
"unitsRequested": 1,
"deliveryRequested": false,
"deliveryAddress": "optional — required for delivery",
"customerName": "Juan Dela Cruz",
"customerPhone": "+63917...",
"customerEmail": "optional",
"notes": "optional",
"agreementSignatureId": "optional uuid"
}

Server-side validation, in order:

  1. Listing exists and is active
  2. unitsRequested within minimum_rental_units / maximum_rental_units (400 MIN_UNITS / MAX_UNITS)
  3. Availability — overlapping bookings in pending|confirmed|active; rejects if booked + requested > total_units (409 UNAVAILABLE)
  4. Blocked dates — no blocked date inside the range (409 BLOCKED_DATE)
  5. Duration ≥ 1 day (400 INVALID_DATES)

Pricing (snapshotted on the booking):

rental_amount = rate × durationDays × unitsRequested
total_amount = rental_amount + delivery_fee (if delivery) + security_deposit

Returns 201 with the booking, including booking_number in the format RNT-YYYYMMDD-XXXX. The vendor is notified in-app + push + email (best-effort).

GET /api/rentals/bookings/my

Paginated (status, limit max 50, offset). Matches user_id or customer_email so guest-era bookings surface after registration.

GET /api/rentals/bookings/:id

Full booking with listing + vendor. 403 unless caller is the customer, the listing's vendor, or an admin.

PATCH /api/rentals/bookings/:id/cancel

Body: { "reason": "optional" }. Allowed for customer, vendor, or admin. 409 ALREADY_FINAL if status is completed, cancelled, or returned. Sets cancelled_at and cancellation_reason.


Vendor Endpoints

All routes under /api/vendor/rentals require a vendor account (403 NOT_VENDOR otherwise) and are scoped to the vendor's own rows.

Listings

  • GET /listings — own listings with category info
  • POST /listings — create; Zod-validated: categoryId, title, description?, pricingUnit (per_hour|per_day|per_week, default per_day), rate, securityDeposit (default 0), totalUnits (default 1), minimumRentalUnits/maximumRentalUnits, pickupAddress?, pickupInstructions?, offersDelivery + deliveryFee?, advanceBookingDays (default 1), turnaroundUnits (default 1), agreementId?
  • PATCH /listings/:id — partial update (camelCase keys mapped to snake_case), including isActive and images
  • DELETE /listings/:id — soft delete (is_active = false)

Blocked Dates

  • POST /listings/:id/block-dates{ "dates": ["2026-07-15", ...], "reason": "optional" } (upsert, idempotent)
  • DELETE /listings/:id/block-dates{ "dates": [...] }

Booking Workflow

Each transition enforces the current status as a precondition (409 CONFLICT on mismatch):

EndpointFrom → ToBody
PATCH /bookings/:id/confirmpending → confirmed
PATCH /bookings/:id/handoverconfirmed → active{ "notes": "optional" }
PATCH /bookings/:id/returnactive → returned{ "condition": "good|damaged", "damageNoted": bool, "damageDescription": "...", "depositDeduction": 0 }
PATCH /bookings/:id/completereturned|active → completed

GET /bookings supports status, limit, offset; GET /bookings/:id accepts a UUID or a booking number.


Admin Endpoints

  • POST /api/admin/rentals/listings — create a listing on behalf of any vendor (adds vendorId to the vendor payload)
  • PATCH /api/admin/rentals/listings/:id — update any listing; used by the admin panel's Active and Verified toggles (isActive, isVerified)

Admin booking oversight reads rental_bookings directly via Supabase in the admin panel (no dedicated API route yet).


Agreements

Listings can link a vendor agreement (rental_listings.agreement_id). Related endpoints in ayts-api/src/routes/agreements.ts:

MethodPathPurpose
GET/api/agreements/:idFetch agreement content before signing
POST/api/agreements/signSign (drawn/typed/checkbox); stores immutable snapshot + IP + UA; can reference rentalBookingId
GET/api/agreements/my/signaturesCustomer's signatures
GET/POST/PATCH/api/agreements/vendor/...Vendor CRUD; content edits bump version
GET/api/agreements/vendor/:id/signaturesSignatures collected for an agreement

Status Workflow Reference

pending ──confirm──▶ confirmed ──handover──▶ active ──return──▶ returned ──complete──▶ completed

└────────────── cancel (any non-final state) ──▶ cancelled └─ complete also allowed from active

Note vs. plan: the original plan (plan/rental-system/rental-system.md) specified extra statuses (delivered, picked_up, overdue), deposit release states, late-fee computation, and PayMongo payment integration. The shipped implementation uses the simplified flow above with cash handling and deposit deduction captured at return time. See Feature Status for gaps.