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
| Method | Path | Auth | Purpose |
|---|---|---|---|
| GET | /api/rentals/categories | Public | List active rental categories |
| GET | /api/rentals | Public | Browse listings (filter + availability overlay) |
| GET | /api/rentals/:id | Public | Listing detail |
| GET | /api/rentals/:id/availability | Public | Day-by-day monthly availability |
| POST | /api/rentals/:id/book | Customer | Create a booking |
| GET | /api/rentals/bookings/my | Customer | My bookings |
| GET | /api/rentals/bookings/:id | Owner/Vendor/Admin | Booking detail |
| PATCH | /api/rentals/bookings/:id/cancel | Owner/Vendor/Admin | Cancel a booking |
| GET | /api/vendor/rentals/listings | Vendor | My listings |
| POST | /api/vendor/rentals/listings | Vendor | Create listing |
| PATCH | /api/vendor/rentals/listings/:id | Vendor | Update listing |
| DELETE | /api/vendor/rentals/listings/:id | Vendor | Deactivate listing (soft delete) |
| POST | /api/vendor/rentals/listings/:id/block-dates | Vendor | Block dates |
| DELETE | /api/vendor/rentals/listings/:id/block-dates | Vendor | Unblock dates |
| GET | /api/vendor/rentals/bookings | Vendor | Incoming bookings |
| GET | /api/vendor/rentals/bookings/:id | Vendor | Booking detail (UUID or booking number) |
| PATCH | /api/vendor/rentals/bookings/:id/confirm | Vendor | pending → confirmed |
| PATCH | /api/vendor/rentals/bookings/:id/handover | Vendor | confirmed → active |
| PATCH | /api/vendor/rentals/bookings/:id/return | Vendor | active → returned (+ condition/damage) |
| PATCH | /api/vendor/rentals/bookings/:id/complete | Vendor | returned/active → completed |
| POST | /api/admin/rentals/listings | Admin | Create listing for any vendor |
| PATCH | /api/admin/rentals/listings/:id | Admin | Update any listing (verify, activate) |
Database Tables
| Table | Purpose |
|---|---|
rental_categories | Category catalog (name, slug, icon, color, sort order) |
rental_listings | Vendor items for rent: rate, pricing unit, deposit, units, delivery, booking rules |
rental_bookings | Bookings with price snapshot, status workflow, deposit + damage tracking |
rental_blocked_dates | Vendor-blocked dates per listing (unique on listing + date) |
vendor_agreements / agreement_signatures | Optional 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:
- Listing exists and is active
unitsRequestedwithinminimum_rental_units/maximum_rental_units(400MIN_UNITS/MAX_UNITS)- Availability — overlapping bookings in
pending|confirmed|active; rejects if booked + requested >total_units(409UNAVAILABLE) - Blocked dates — no blocked date inside the range (409
BLOCKED_DATE) - 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, defaultper_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), includingisActiveandimages - 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):
| Endpoint | From → To | Body |
|---|---|---|
PATCH /bookings/:id/confirm | pending → confirmed | — |
PATCH /bookings/:id/handover | confirmed → active | { "notes": "optional" } |
PATCH /bookings/:id/return | active → returned | { "condition": "good|damaged", "damageNoted": bool, "damageDescription": "...", "depositDeduction": 0 } |
PATCH /bookings/:id/complete | returned|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 (addsvendorIdto 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:
| Method | Path | Purpose |
|---|---|---|
| GET | /api/agreements/:id | Fetch agreement content before signing |
| POST | /api/agreements/sign | Sign (drawn/typed/checkbox); stores immutable snapshot + IP + UA; can reference rentalBookingId |
| GET | /api/agreements/my/signatures | Customer's signatures |
| GET/POST/PATCH | /api/agreements/vendor/... | Vendor CRUD; content edits bump version |
| GET | /api/agreements/vendor/:id/signatures | Signatures 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.