update documentation

This commit is contained in:
root
2026-05-26 19:28:44 -04:00
parent 9ab73e8ce6
commit 1a1c418087
46 changed files with 2292 additions and 8089 deletions
+329 -431
View File
@@ -1,498 +1,396 @@
# API Routes Inventory — RentalDriveGo (Complete)
# API Architecture and Route Design
Base URL: `https://api.RentalDriveGo.com/api/v1`
This document explains how the API is structured today, how requests move through the stack, and what each route group is responsible for.
## Middleware Legend
- 🔒 `requireCompanyAuth` — valid Clerk JWT (Employee)
- 🏢 `requireTenant` — attaches `req.company` + `req.companyId` + `req.employee`
- 💳 `requireSubscription` — blocks SUSPENDED/PENDING companies
- 🎫 `requireRenterAuth` — valid Renter JWT
- 🔑 `requireApiKey` — company public API key in `x-api-key` header
- 👑 `requireAdmin` — RentalDriveGo super-admin role
Source of truth for the runtime wiring:
> **Tenant isolation rule:** Every 🏢 route must use `where: { companyId: req.companyId }` in every query.
- `apps/api/src/app.ts`
- `apps/api/src/modules/*`
- `apps/api/src/middleware/*`
- `apps/api/src/swagger/openapi.ts`
---
## Runtime Overview
## Auth — Company Employees
The API is an Express application mounted under `/api/v1` with a few non-versioned utility endpoints:
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| POST | `/auth/company/signup` | — | Create Clerk user + Company + AmanPay/PayPal customer |
| POST | `/auth/company/verify-email` | — | Verify email token |
- `GET /health` returns process health.
- `GET /docs` serves Swagger UI.
- `GET /api/v1/openapi.json` returns the generated OpenAPI document.
- `/storage/*` serves uploaded assets such as logos, hero images, vehicle photos, and reservation/customer documents.
*(Clerk handles login/logout/session refresh on the frontend)*
The app boot sequence in `apps/api/src/app.ts` is intentionally ordered:
---
1. CORS is applied first.
2. storage guards are applied before static serving so private customer license files are never anonymously retrievable.
3. Swagger UI is mounted before Helmet so the UI assets are not blocked by CSP.
4. webhook routes that require raw payload handling are mounted before `express.json()`.
5. Helmet, request logging, JSON parsing, and the module routers are mounted.
6. the centralized error middleware converts validation, Prisma, and application errors into consistent JSON responses.
## Auth — Renters
## Request Lifecycle
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| POST | `/auth/renter/signup` | — | Create renter account |
| POST | `/auth/renter/login` | — | Email + password → JWT |
| POST | `/auth/renter/logout` | 🎫 | Invalidate token |
| POST | `/auth/renter/verify-email` | — | Email verification token |
| POST | `/auth/renter/forgot-password` | — | Send reset email |
| POST | `/auth/renter/reset-password` | — | Reset with token |
| GET | `/auth/renter/me` | 🎫 | Get renter profile |
| PATCH | `/auth/renter/me` | 🎫 | Update profile (name, phone, locale, currency) |
| POST | `/auth/renter/me/fcm-token` | 🎫 | Register FCM push token |
Most internal company routes follow the same pattern:
---
1. Express router receives the request.
2. Zod schemas validate `req.params`, `req.query`, and `req.body` through `parseParams`, `parseQuery`, and `parseBody`.
3. auth middleware resolves the caller identity.
4. tenant middleware resolves the company record.
5. subscription middleware blocks suspended or pending companies where required.
6. role middleware enforces employee or admin permissions.
7. the route handler calls a service function.
8. the service applies business rules and calls a repo or Prisma directly.
9. a presenter may normalize the payload for frontend consumption.
10. `ok()` or `created()` wraps successful responses in `{ "data": ... }`.
## Subscriptions (AmanPay/PayPal subscription — RentalDriveGo collects)
Common exceptions:
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/subscriptions/plans` | — | List plans + prices (public) |
| GET | `/subscriptions/me` | 🔒🏢 | Current subscription |
| POST | `/subscriptions/checkout` | 🔒🏢 | Create AmanPay/PayPal Checkout session |
| POST | `/subscriptions/portal` | 🔒🏢 | Open AmanPay/PayPal Customer Portal |
| GET | `/subscriptions/invoices` | 🔒🏢💳 | Invoice history |
| POST | `/subscriptions/webhook` | — (sig) | AmanPay/PayPal subscription webhook |
- `GET /health` and `GET /api/v1/docs` return raw JSON, not the `{ data }` envelope.
- webhook endpoints return minimal receipt payloads.
- some public endpoints return graceful fallback responses if the database is unavailable.
---
## Security and Access Model
## Companies
There are four main access patterns.
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/companies/me` | 🔒🏢 | Company profile |
| PATCH | `/companies/me` | 🔒🏢💳 | Update profile |
| GET | `/companies/me/brand` | 🔒🏢 | Brand settings |
| PATCH | `/companies/me/brand` | 🔒🏢💳 | Update branding |
| POST | `/companies/me/brand/logo` | 🔒🏢💳 | Upload logo |
| POST | `/companies/me/brand/hero` | 🔒🏢💳 | Upload hero image |
| POST | `/companies/me/brand/subdomain/check` | 🔒🏢💳 | Check subdomain availability |
| POST | `/companies/me/brand/custom-domain` | 🔒🏢💳 | Add custom domain (Pro) |
| GET | `/companies/me/brand/custom-domain/status` | 🔒🏢💳 | Domain verification status |
| DELETE | `/companies/me/brand/custom-domain` | 🔒🏢💳 | Remove custom domain |
| GET | `/companies/me/api-key` | 🔒🏢💳 | Get public API key |
| POST | `/companies/me/api-key/regenerate` | 🔒🏢💳 | Regenerate API key |
| GET | `/companies/me/stripe-connect` | 🔒🏢💳 | Connect status |
| POST | `/companies/me/stripe-connect` | 🔒🏢💳 | Start Connect OAuth |
| POST | `/companies/me/stripe-connect/webhook` | — (sig) | Connect events |
### Employee JWT
---
`requireCompanyAuth` validates a Bearer token with `type === "employee"`, loads the employee, and attaches:
## Team
- `req.employee`
- `req.company`
- `req.companyId`
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/team` | 🔒🏢💳 | List employees |
| POST | `/team/invite` | 🔒🏢💳 | Invite by email |
| PATCH | `/team/:id/role` | 🔒🏢💳 | Change role |
| DELETE | `/team/:id` | 🔒🏢💳 | Remove employee |
This is the default for dashboard/company management routes.
---
### Tenant Resolution
## Vehicles (Company fleet — strictly isolated)
`requireTenant` reloads the company from `req.companyId` and guarantees the tenant context exists. Company-scoped modules then query by `companyId`.
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/vehicles` | 🔒🏢💳 | List `?status=&category=&published=` |
| POST | `/vehicles` | 🔒🏢💳 | Add vehicle (checks plan limit) |
| GET | `/vehicles/:id` | 🔒🏢💳 | Detail (validates companyId) |
| PATCH | `/vehicles/:id` | 🔒🏢💳 | Update |
| DELETE | `/vehicles/:id` | 🔒🏢💳 | Archive |
| POST | `/vehicles/:id/photos` | 🔒🏢💳 | Upload photos (Cloudinary) |
| DELETE | `/vehicles/:id/photos/:idx` | 🔒🏢💳 | Remove photo |
| PATCH | `/vehicles/:id/publish` | 🔒🏢💳 | Toggle published |
| GET | `/vehicles/:id/availability` | 🔒🏢💳 | `?startDate=&endDate=` |
| GET | `/vehicles/:id/reservations` | 🔒🏢💳 | Reservation history |
| GET | `/vehicles/:id/maintenance` | 🔒🏢💳 | Maintenance log |
| POST | `/vehicles/:id/maintenance` | 🔒🏢💳 | Add maintenance entry |
### Subscription Guard
---
`requireSubscription` blocks company routes when the company status is `PENDING` or `SUSPENDED`. Public site and marketplace routes are intentionally not behind this guard.
## Offers (Company promotional offers)
### Role-Based Access Control
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/offers` | 🔒🏢💳 | List all offers `?active=&public=` |
| POST | `/offers` | 🔒🏢💳 | Create offer |
| GET | `/offers/:id` | 🔒🏢💳 | Offer detail |
| PATCH | `/offers/:id` | 🔒🏢💳 | Update offer |
| DELETE | `/offers/:id` | 🔒🏢💳 | Delete offer |
| POST | `/offers/:id/activate` | 🔒🏢💳 | Activate offer |
| POST | `/offers/:id/deactivate` | 🔒🏢💳 | Deactivate offer |
| POST | `/offers/:id/feature` | 🔒🏢💳 | Toggle featured (plan-gated) |
| GET | `/offers/:id/stats` | 🔒🏢💳 | Redemption stats, revenue impact |
Employee roles are hierarchical:
---
- `OWNER`
- `MANAGER`
- `AGENT`
## Reservations (Company view — includes all sources)
Admin roles are hierarchical:
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/reservations` | 🔒🏢💳 | List `?status=&vehicleId=&source=&startDate=&endDate=` |
| POST | `/reservations` | 🔒🏢💳 | Create (dashboard) |
| GET | `/reservations/:id` | 🔒🏢💳 | Detail |
| PATCH | `/reservations/:id` | 🔒🏢💳 | Update |
| POST | `/reservations/:id/confirm` | 🔒🏢💳 | Confirm + charge |
| POST | `/reservations/:id/checkin` | 🔒🏢💳 | Check in `{ mileage }` |
| POST | `/reservations/:id/checkout` | 🔒🏢💳 | Check out + complete `{ mileage }` |
| POST | `/reservations/:id/cancel` | 🔒🏢💳 | Cancel `{ reason }` |
| POST | `/reservations/:id/no-show` | 🔒🏢💳 | Mark no-show |
| POST | `/reservations/:id/reply-review` | 🔒🏢💳 | Reply to renter review |
- `SUPER_ADMIN`
- `ADMIN`
- `SUPPORT`
- `FINANCE`
- `VIEWER`
---
### Renter JWT
## Customers (Company CRM)
`requireRenterAuth` validates a Bearer token with `type === "renter"` and attaches `req.renterId`.
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/customers` | 🔒🏢💳 | List `?q=&flagged=` |
| POST | `/customers` | 🔒🏢💳 | Create |
| GET | `/customers/:id` | 🔒🏢💳 | Detail + reservation history |
| PATCH | `/customers/:id` | 🔒🏢💳 | Update |
| POST | `/customers/:id/flag` | 🔒🏢💳 | Flag `{ reason }` |
| DELETE | `/customers/:id/flag` | 🔒🏢💳 | Remove flag |
`optionalRenterAuth` is used on marketplace routes so public reads still work without a token.
---
### API Key
## Payments (AmanPay/PayPal rental — rental revenue)
`requireApiKey` accepts a company public API key in `x-api-key`. This is the low-friction integration path for public company-site style calls.
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/payments` | 🔒🏢💳 | List payments |
| GET | `/payments/:id` | 🔒🏢💳 | Payment detail |
| POST | `/payments/:id/refund` | 🔒🏢💳 | Issue refund |
| POST | `/payments/webhook` | — (sig) | AmanPay/PayPal rental webhook |
## Implementation Pattern
---
The API codebase is organized per module. The most common pattern is:
## Analytics
- `*.routes.ts`: route definitions and middleware composition
- `*.schemas.ts`: Zod input validation
- `*.service.ts`: business rules and orchestration
- `*.repo.ts`: Prisma access helpers
- `*.presenter.ts`: response shaping
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/analytics/summary` | 🔒🏢💳 | KPIs `?period=7d|30d|90d` |
| GET | `/analytics/revenue` | 🔒🏢💳 | Revenue time series |
| GET | `/analytics/vehicles` | 🔒🏢💳 | Per-vehicle performance |
| GET | `/analytics/utilization` | 🔒🏢💳 | Fleet utilization |
| GET | `/analytics/offers` | 🔒🏢💳 | Offer performance + redemptions |
| GET | `/analytics/sources` | 🔒🏢💳 | Booking source breakdown |
The `vehicles` module is representative:
---
- `vehicle.routes.ts` mounts auth, tenant, and subscription guards once for the router.
- route handlers validate input and call service functions.
- `vehicle.service.ts` contains business rules such as publish-state normalization and availability calculation.
- `vehicle.repo.ts` owns the Prisma queries and enforces tenant filters in reads.
- `vehicle.presenter.ts` shapes the response returned to clients.
## Notifications — Company
Some modules split specialized workflows into additional services instead of one large file. The reservations module is the clearest example:
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/notifications/company` | 🔒🏢💳 | In-app notification log `?unread=true` |
| POST | `/notifications/company/:id/read` | 🔒🏢💳 | Mark as read |
| POST | `/notifications/company/read-all` | 🔒🏢💳 | Mark all read |
| GET | `/notifications/company/preferences` | 🔒🏢💳 | Get notification preferences |
| PATCH | `/notifications/company/preferences` | 🔒🏢💳 | Update preferences per type+channel |
- `reservation.service.ts` handles CRUD and list behavior
- `reservation.lifecycle.service.ts` handles confirm/checkin/checkout/close/extend/cancel transitions
- `reservation.inspection.service.ts` handles pickup/dropoff inspections
- `reservation.additional-driver.service.ts` handles approval workflows
- `reservation.photo.service.ts` handles pickup/dropoff photo uploads
- `reservation.document.service.ts` generates contract and billing payloads
---
## Route Groups
## Notifications — Renter
The table below describes the current route groups mounted in `app.ts`.
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/notifications/renter` | 🎫 | Renter in-app notifications |
| POST | `/notifications/renter/:id/read` | 🎫 | Mark as read |
| POST | `/notifications/renter/read-all` | 🎫 | Mark all read |
| GET | `/notifications/renter/preferences` | 🎫 | Get preferences |
| PATCH | `/notifications/renter/preferences` | 🎫 | Update preferences |
| Base path | Access | Purpose | Notes |
| --- | --- | --- | --- |
| `/health` | Public | Process health | Non-versioned utility endpoint |
| `/docs` | Public | Swagger UI | Backed by `openapi.ts` |
| `/api/v1/openapi.json` | Public | OpenAPI JSON | Generated from Zod schemas and manual path entries |
| `/api/v1/auth/company` | Public | Company signup | `complete-signup` and `verify-email` now return disabled errors |
| `/api/v1/auth/employee` | Public plus employee JWT | Employee login, password reset, profile, language | Dashboard/staff authentication path |
| `/api/v1/auth/renter` | Renter JWT for profile routes | Renter profile and push token | `signup` and `login` are currently disabled in this codebase |
| `/api/v1/admin` | Admin JWT | Platform operations | Includes company admin, billing, subscriptions, pricing, audit, and admin-user management |
| `/api/v1/marketplace` | Public, optional renter JWT | Marketplace discovery and reservation intake | Designed for discovery and lead capture across companies |
| `/api/v1/site` | Public | White-label company site APIs | Drives each company-branded booking site |
| `/api/v1/subscriptions` | Mixed | Plan listing, webhooks, subscription lifecycle | Public, webhook, and authenticated sub-routers share the same prefix |
| `/api/v1/vehicles` | Employee JWT + tenant + subscription | Fleet management | Includes photos, status, calendar blocks, maintenance, and availability |
| `/api/v1/reservations` | Employee JWT + tenant + subscription | Reservation operations | Central booking lifecycle for internal staff |
| `/api/v1/team` | Employee JWT + tenant + subscription | Employee/team management | Owner-controlled invite, role, activation, removal |
| `/api/v1/customers` | Employee JWT + tenant + subscription | Company CRM and license handling | Includes protected license-image retrieval and approval flows |
| `/api/v1/offers` | Employee JWT + tenant + subscription | Promotional offers | Company marketing and promo codes |
| `/api/v1/analytics` | Employee JWT + tenant + subscription | Dashboard metrics and reports | Includes summary, dashboard, source, and report routes |
| `/api/v1/notifications` | Employee or renter JWT depending on route | Notification inbox and preferences | Separate company and renter surfaces |
| `/api/v1/companies` | Employee JWT + tenant + subscription | Company profile and settings | Brand, domains, contract settings, insurance policies, pricing rules, accounting, API key |
| `/api/v1/payments` | Mixed | Rental payment webhooks and company payment actions | Webhooks are public; operational routes are company-authenticated |
| `/api/v1/reviews` | Employee JWT + tenant + subscription | Review moderation | Company-side review visibility and replies |
| `/api/v1/complaints` | Employee JWT + tenant + subscription | Complaint handling | Internal complaint tracking |
| `/api/v1/webhooks` | Public | External webhook receivers | Currently includes `/clerk` placeholder |
---
## Functional Breakdown by Module
## Renter — Marketplace Actions
### Company and employee onboarding
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/renter/reservations` | 🎫 | All reservations (cross-company) |
| GET | `/renter/reservations/:id` | 🎫 | Reservation detail |
| POST | `/renter/reservations/:id/cancel` | 🎫 | Cancel `{ reason }` |
| GET | `/renter/saved-companies` | 🎫 | Saved companies |
| POST | `/renter/saved-companies/:slug` | 🎫 | Save company |
| DELETE | `/renter/saved-companies/:slug` | 🎫 | Unsave company |
| POST | `/renter/reviews` | 🎫 | Submit review (completed reservation only) |
| GET | `/renter/reviews` | 🎫 | Renter's review history |
- `POST /api/v1/auth/company/signup` creates the company tenant, owner employee, default subscription state, and related baseline records in one transaction.
- The signup flow generates a unique company slug, hashes the owner password, starts a 90-day trial window, and sends account-created notifications.
- Employee auth supports login, forgot-password, reset-password, profile read, and language updates.
---
### Company profile and business configuration
## Global Marketplace API (public, optional renter auth)
The `companies` module owns tenant-level settings:
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/marketplace/offers` | opt 🎫 | Featured + recent offers across all companies |
| GET | `/marketplace/companies` | opt 🎫 | Company list `?city=&rating=&hasOffer=` |
| GET | `/marketplace/search` | opt 🎫 | Search vehicles `?city=&startDate=&endDate=&category=&maxPrice=` |
| GET | `/marketplace/[slug]` | opt 🎫 | Company marketplace profile |
| GET | `/marketplace/[slug]/vehicles` | opt 🎫 | Published vehicles (no internal fields) |
| GET | `/marketplace/[slug]/offers` | opt 🎫 | Active public offers |
| POST | `/marketplace/[slug]/book` | opt 🎫 | Book (renter auth preferred, guest allowed) |
| GET | `/marketplace/[slug]/reviews` | opt 🎫 | Company reviews |
| POST | `/marketplace/offers/:code/validate` | opt 🎫 | Validate promo code |
- company identity
- brand settings and asset upload
- subdomain/custom-domain configuration
- contract settings used by rental documents
- insurance policies
- pricing rules
- accounting settings
- public API key generation/regeneration
---
This module is where most long-lived company configuration lives.
## Company Public Site API (slug-resolved, no user auth)
### Fleet management
> Used by SSR pages on `{slug}.RentalDriveGo.com`. Never subscription-gated.
The `vehicles` module handles:
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/site/:slug/brand` | 🔑 | Branding for SSR |
| GET | `/site/:slug/vehicles` | 🔑 | Published vehicles |
| GET | `/site/:slug/vehicles/:id` | 🔑 | Vehicle detail + availability |
| GET | `/site/:slug/offers` | 🔑 | Active offers |
| POST | `/site/:slug/availability` | 🔑 | Date range availability check |
| POST | `/site/:slug/book` | 🔑 | Create booking + payment |
| POST | `/site/:slug/book/validate-code` | 🔑 | Validate promo code |
| GET | `/site/:slug/booking/:id` | 🔑 | Booking status (confirmation page) |
| POST | `/site/:slug/contact` | 🔑 | Contact form → email to company |
- CRUD for vehicles
- publish/unpublish
- explicit status updates
- photo upload and deletion
- availability checks
- monthly vehicle calendar events
- manual calendar blocks
- maintenance logs
---
Uploads are persisted through the storage service, then surfaced under `/storage/*`.
## Admin (RentalDriveGo internal)
### Reservations and rental workflow
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/admin/companies` | 👑 | All companies |
| GET | `/admin/companies/:id` | 👑 | Company detail + subscription |
| PATCH | `/admin/companies/:id/status` | 👑 | Force status |
| GET | `/admin/offers/featured` | 👑 | All featured offers |
| GET | `/admin/metrics` | 👑 | Platform MRR, churn, signups |
| GET | `/admin/notifications/stats` | 👑 | Delivery rates per channel |
The reservation aggregate is the center of the operational API.
---
Important transitions:
## Standard Response Formats
1. create draft reservation
2. confirm reservation after license checks
3. check in and move the reservation to `ACTIVE`
4. check out and mark the reservation `COMPLETED`
5. close the reservation operationally after post-rental tasks are done
The lifecycle service also handles:
- extensions with conflict checks
- cancellations
- vehicle status synchronization
- review token generation
- review request email dispatch on close
The reservations module also owns:
- contract and billing payload generation
- pickup/dropoff inspections
- additional-driver approval
- pickup/dropoff photo upload
### Customer CRM and license compliance
Customers are company-scoped CRM records, even when a renter identity also exists. The `customers` module supports:
- customer CRUD
- flag/unflag flows
- license image upload and protected retrieval
- license validation and manager approval
This is intentionally separate from renter identity because one renter may interact with multiple companies while each company still needs its own operational customer record.
### Marketplace and public site
The public surface is split in two modules on purpose.
`marketplace` is the cross-company discovery layer:
- featured/public offers
- marketplace cities
- listed companies
- vehicle search
- marketplace reservation intake
- review submission by token
- company public pages under `/:slug`
`site` is the white-label company booking API:
- company brand payload
- published vehicle catalog
- booking options
- date-range availability
- promo validation
- booking creation
- payment initialization
- booking lookup
- company contact form
Both modules deliberately include graceful degradation paths when the database is unavailable so public pages can fail softer than internal operations.
### Payments
Rental payments are company-side operational payments, distinct from SaaS subscription billing.
The `payments` module handles:
- AmanPay and PayPal rental webhooks
- listing payments by company or reservation
- initializing online charges
- capturing PayPal orders
- recording manual payments
- refunding successful online payments
Payment success updates both the payment record and the reservation paid amount.
### Subscriptions and SaaS billing
The `subscriptions` module owns the platform billing lifecycle:
- public plan/provider/feature listing
- company subscription read endpoints
- trial start
- checkout
- plan changes
- cancellation/resume/reactivation
- provider webhooks
- admin overrides
It also exposes background-job style functions for:
- trial expiration
- payment-pending timeout
- past-due timeout
- suspension timeout
- period-end cancellation
### Notifications
Notifications are multi-channel and multi-audience:
- company inbox
- renter inbox
- unread counts
- preferences per type and channel
- notification history
Templates and preference records live in the database; delivery orchestration lives in `services/notificationService.ts`.
### Admin surface
The admin router is broad because it covers platform operations across multiple domains:
- admin authentication and 2FA
- company listing, inspection, status changes, deletion, impersonation
- renter blocking/unblocking
- platform metrics
- notification inspection
- audit log access
- admin-user and permission management
- billing account and billing invoice operations
- pricing config, plan features, and promotions
- subscription overrides and extensions
- marketplace homepage configuration
This is the only route group allowed to work across tenants.
## Important Workflow Examples
### 1. Company signup
`POST /api/v1/auth/company/signup`
- validates the payload with Zod
- checks for duplicate company and owner email addresses
- generates a unique slug
- hashes the owner password
- creates the tenant, owner employee, and initial subscription state in one transaction
- sends localized account-created notifications
### 2. Public booking from a company site
`POST /api/v1/site/:slug/book`
- resolves the company from the slug
- verifies vehicle availability for the requested date range
- upserts the company-scoped customer record
- computes total days and base rental price
- applies promo code discount if present
- applies pricing rules
- stores a draft reservation
- attaches reservation insurance snapshots and additional-driver snapshots
- triggers license validation flags
Payment is then started with `POST /api/v1/site/:slug/booking/:id/pay`.
### 3. Reservation lifecycle after booking
- reservation starts in `DRAFT`
- staff confirms it after license compliance checks
- vehicle status moves to `RESERVED`
- checkin moves reservation to `ACTIVE` and vehicle to `RENTED`
- checkout moves reservation to `COMPLETED`, records mileage, and generates a review token
- close marks the operational workflow complete and can send the review request email
## Error Handling and Response Shape
The API standardizes error handling in `http/errors/errorMiddleware.ts`.
Special handling exists for:
- Zod validation errors -> `400 validation_error`
- Prisma `P2025` -> `404 not_found`
- Prisma `P2002` -> `409 conflict`
- custom `AppError` subclasses -> explicit status and machine-readable code
Successful route handlers usually return:
```json
// Success
{ "data": { ... } }
// Paginated
{ "data": [...], "total": 47, "page": 1, "pageSize": 20, "totalPages": 3 }
// Error
{ "error": "vehicle_not_found", "message": "Vehicle not found or access denied", "statusCode": 404 }
// Subscription error
{ "error": "subscription_suspended", "message": "...", "statusCode": 402, "billingUrl": "https://..." }
{
"data": {}
}
```
## Common Query Params
`page`, `pageSize` (max 100), `sortBy`, `sortOrder` (asc|desc), `q` (search)
Common deviations:
---
- `/health`
- `/api/v1/docs`
- webhook receipt payloads
- file downloads such as admin invoice PDFs
## Payment Routes — AmanPay + PayPal
## Documentation Artifacts
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| POST | `/payments/amanpay-webhook` | — (HMAC sig) | AmanPay event webhook (subscriptions + rentals) |
| POST | `/payments/paypal/create-order` | 🔒🏢 or 🎫 | Create PayPal order (subscription or rental) |
| POST | `/payments/paypal/capture` | 🔒🏢 or 🎫 | Capture PayPal order after approval |
| POST | `/payments/paypal/refund` | 🔒🏢💳 | Refund PayPal capture |
| GET | `/payments` | 🔒🏢💳 | Payment history (AmanPay + PayPal combined) |
| GET | `/payments/:id` | 🔒🏢💳 | Payment detail |
| POST | `/payments/:id/refund` | 🔒🏢💳 | Refund (AmanPay or PayPal auto-detected) |
There are two documentation layers in the codebase:
---
1. runtime API docs from Swagger/OpenAPI at `/docs` and `/api/v1/openapi.json`
2. this markdown document, which explains design decisions, route grouping, and request flow
## Documents — Contracts & Invoices (On-Demand PDF Generation)
> **Design rule:** PDFs are NEVER stored. Every request hits the DB, assembles data, renders PDF via `@react-pdf/renderer`, and streams the result. Reference numbers (`contractNumber`, `invoiceNumber`) ARE stored in the DB and assigned once via atomic transaction.
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/documents/reservations/:id/contract` | 🔒🏢💳 | Generate + stream contract PDF. `?download=true` forces file download vs inline view. |
| GET | `/documents/reservations/:id/invoice` | 🔒🏢💳 | Generate + stream invoice PDF. |
| POST | `/documents/reservations/:id/send` | 🔒🏢💳 | Generate + email PDF(s) as attachment. Body: `{ type: "contract"|"invoice"|"both", email?: string }` |
| GET | `/documents/settings` | 🔒🏢💳 | Get company's contract settings (terms, prefixes, tax config) |
| PATCH | `/documents/settings` | 🔒🏢💳 | Update contract settings. Sequence counters are protected from API reset. |
| GET | `/documents/settings/preview-contract` | 🔒🏢💳 | Preview contract PDF with sample data (for settings page preview) |
| GET | `/documents/settings/preview-invoice` | 🔒🏢💳 | Preview invoice PDF with sample data |
| GET | `/renter/reservations/:id/contract` | 🎫 | Renter downloads their own contract (renterId must match) |
| GET | `/renter/reservations/:id/invoice` | 🎫 | Renter downloads their own invoice |
---
## Damage Inspections
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| POST | `/reservations/:id/inspections` | 🔒🏢💳 | Create check-in or check-out inspection (with damage points) |
| GET | `/reservations/:id/inspections` | 🔒🏢💳 | Get both inspections (checkin + checkout) |
| GET | `/reservations/:id/inspections/checkin` | 🔒🏢💳 | Check-in inspection detail |
| GET | `/reservations/:id/inspections/checkout` | 🔒🏢💳 | Check-out inspection detail |
---
## Billing (per-reservation line items)
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/reservations/:id/billing` | 🔒🏢💳 | Get full itemized billing breakdown (JSON — for invoice preview) |
| PATCH | `/reservations/:id/billing` | 🔒🏢💳 | Add damage charges and extras to reservation |
---
## Accounting Reports
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/accounting/settings` | 🔒🏢💳 | Get company accounting settings |
| PATCH | `/accounting/settings` | 🔒🏢💳 | Update accounting settings (period, accountant, taxes, fuel policy) |
| GET | `/accounting/report` | 🔒🏢💳 | Get accounting report as JSON `?period=MONTHLY&from=&to=` |
| GET | `/accounting/report/pdf` | 🔒🏢💳 | Stream accounting report PDF |
| GET | `/accounting/report/csv` | 🔒🏢💳 | Stream CSV file |
| POST | `/accounting/report/send` | 🔒🏢💳 | Email report to accountant `{ period, from, to, format }` |
| GET | `/accounting/report/periods` | 🔒🏢💳 | List available periods with labels and date ranges |
---
## Admin Panel Routes (admin.RentalDriveGo.com → api.RentalDriveGo.com/admin)
> All require admin JWT. Role hierarchy: VIEWER < FINANCE < SUPPORT < ADMIN < SUPER_ADMIN
### Admin Auth
| Method | Path | Role | Description |
|--------|------|------|-------------|
| POST | `/admin/auth/login` | — | Login `{ email, password, totpCode? }` |
| POST | `/admin/auth/logout` | VIEWER | Invalidate token |
| GET | `/admin/auth/me` | VIEWER | Current admin profile |
| PATCH | `/admin/auth/me` | VIEWER | Update name/password |
| POST | `/admin/auth/2fa/setup` | VIEWER | Generate TOTP secret + QR code |
| POST | `/admin/auth/2fa/verify` | VIEWER | Verify and enable 2FA |
### Admin User Management (SUPER_ADMIN only)
| Method | Path | Role | Description |
|--------|------|------|-------------|
| GET | `/admin/users` | SUPER_ADMIN | List admin users |
| POST | `/admin/users` | SUPER_ADMIN | Create admin user |
| PATCH | `/admin/users/:id` | SUPER_ADMIN | Edit name, role |
| POST | `/admin/users/:id/deactivate` | SUPER_ADMIN | Deactivate admin |
| POST | `/admin/users/:id/reset-password` | SUPER_ADMIN | Force password reset |
| PATCH | `/admin/users/:id/permissions` | SUPER_ADMIN | Override permissions |
### Company Management (SUPPORT+)
| Method | Path | Role | Description |
|--------|------|------|-------------|
| GET | `/admin/companies` | SUPPORT | List all companies |
| POST | `/admin/companies` | SUPPORT | Create company |
| GET | `/admin/companies/:id` | SUPPORT | Company detail |
| PATCH | `/admin/companies/:id` | SUPPORT | Edit company |
| POST | `/admin/companies/:id/suspend` | SUPPORT | Suspend `{ reason }` |
| POST | `/admin/companies/:id/reactivate` | SUPPORT | Reactivate |
| DELETE | `/admin/companies/:id` | ADMIN | Delete company |
| PATCH | `/admin/companies/:id/subscription` | SUPPORT | Override subscription |
| POST | `/admin/companies/:id/invoices/:invoiceId/mark-paid` | ADMIN | Manual mark paid |
### Company Staff Management (SUPPORT+)
| Method | Path | Role | Description |
|--------|------|------|-------------|
| GET | `/admin/companies/:id/employees` | SUPPORT | List employees |
| POST | `/admin/companies/:id/employees` | SUPPORT | Add employee |
| GET | `/admin/companies/:id/employees/:empId` | SUPPORT | Employee detail |
| PATCH | `/admin/companies/:id/employees/:empId` | SUPPORT | Edit employee |
| PATCH | `/admin/companies/:id/employees/:empId/role` | SUPPORT | Change role |
| POST | `/admin/companies/:id/employees/:empId/deactivate` | SUPPORT | Deactivate |
| POST | `/admin/companies/:id/employees/:empId/reactivate` | SUPPORT | Reactivate |
| POST | `/admin/companies/:id/employees/:empId/reset-password` | SUPPORT | Send reset |
| POST | `/admin/companies/:id/employees/:empId/impersonate` | ADMIN | Generate impersonation token |
### Renters, Metrics, Audit
| Method | Path | Role | Description |
|--------|------|------|-------------|
| GET | `/admin/renters` | SUPPORT | List renters |
| PATCH | `/admin/renters/:id` | SUPPORT | Update renter |
| POST | `/admin/renters/:id/block` | SUPPORT | Block `{ reason }` |
| POST | `/admin/renters/:id/unblock` | SUPPORT | Unblock |
| GET | `/admin/metrics` | FINANCE | Platform KPIs |
| GET | `/admin/metrics/subscriptions` | FINANCE | Plan breakdown |
| GET | `/admin/audit-logs` | ADMIN | Paginated audit log |
| GET | `/admin/audit-logs/export` | ADMIN | CSV export |
---
## Advanced Features — New Routes
### Damage Reports
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/reservations/:id/damage-reports` | 🔒🏢💳 | Get check-in + check-out damage reports |
| POST | `/reservations/:id/damage-reports` | 🔒🏢💳 | Save damage report. Body: `{ type: "CHECKIN"|"CHECKOUT", damages: DamageZone[], fuelLevel, mileage, photos[], inspectedBy, customerSignedAt, customerName }` |
| PATCH | `/reservations/:id/damage-reports/:type` | 🔒🏢💳 | Update a damage report (before finalizing) |
### Insurance Policies (Company configuration)
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/insurance-policies` | 🔒🏢💳 | List company's insurance policies |
| POST | `/insurance-policies` | 🔒🏢💳 | Create policy |
| PATCH | `/insurance-policies/:id` | 🔒🏢💳 | Update policy |
| DELETE | `/insurance-policies/:id` | 🔒🏢💳 | Deactivate policy |
| GET | `/reservations/:id/insurances` | 🔒🏢💳 | Insurance selections for a reservation |
| POST | `/reservations/:id/insurances` | 🔒🏢💳 | Apply insurance(s) to reservation. Body: `{ policyIds: string[] }` |
### Additional Drivers
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/reservations/:id/additional-drivers` | 🔒🏢💳 | List additional drivers |
| POST | `/reservations/:id/additional-drivers` | 🔒🏢💳 | Add additional driver. Body: `{ firstName, lastName, driverLicense, licenseExpiry, dateOfBirth, ... }` |
| PATCH | `/reservations/:id/additional-drivers/:did` | 🔒🏢💳 | Update driver info |
| DELETE | `/reservations/:id/additional-drivers/:did` | 🔒🏢💳 | Remove additional driver |
| POST | `/reservations/:id/additional-drivers/:did/approve-license` | 🔒🏢💳 (OWNER/MANAGER) | Approve flagged license. Body: `{ decision: "APPROVE"|"DENY", note }` |
### Driver License Validation
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| POST | `/customers/:id/validate-license` | 🔒🏢💳 | Validate license expiry + flag if <3 months. Returns `{ status, daysUntilExpiry, requiresApproval }` |
| POST | `/customers/:id/approve-license` | 🔒🏢💳 (OWNER/MANAGER) | Approve or deny flagged license. Body: `{ decision: "APPROVE"|"DENY", note }` |
### Pricing Rules
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/pricing-rules` | 🔒🏢💳 | List company's pricing rules |
| POST | `/pricing-rules` | 🔒🏢💳 | Create rule (age surcharge, license discount, etc.) |
| PATCH | `/pricing-rules/:id` | 🔒🏢💳 | Update rule |
| DELETE | `/pricing-rules/:id` | 🔒🏢💳 | Deactivate rule |
| POST | `/pricing-rules/preview` | 🔒🏢💳 | Preview rules applied to a hypothetical booking. Body: `{ customerId, additionalDrivers, dailyRate, totalDays }` |
### Financial Reporting (Accountant Export)
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/reports/financial` | 🔒🏢💳 | Financial report. `?period=WEEK|MONTH|YEAR&startDate=&endDate=&format=JSON|CSV` |
| GET | `/reports/financial/summary` | 🔒🏢💳 | Aggregate totals only (for dashboard KPIs) |
### Admin Panel Routes
Base path: `/api/v1/admin/` — requires `requireAdminAuth` middleware (separate JWT from company auth)
| Method | Path | Role | Description |
|--------|------|------|-------------|
| POST | `/admin/auth/login` | — | Admin login |
| GET | `/admin/auth/me` | Any | Admin profile |
| GET | `/admin/companies` | Any | All companies `?q=&status=&plan=` |
| POST | `/admin/companies` | ADMIN+ | Create company |
| GET | `/admin/companies/:id` | Any | Full company detail |
| PATCH | `/admin/companies/:id` | ADMIN+ | Edit company profile |
| PATCH | `/admin/companies/:id/status` | ADMIN+ | Change status |
| PATCH | `/admin/companies/:id/plan` | ADMIN+ | Change plan (bypasses payment) |
| DELETE | `/admin/companies/:id` | SUPER_ADMIN | Hard delete |
| POST | `/admin/companies/:id/impersonate` | ADMIN+ | Get 30-min impersonation token |
| GET | `/admin/companies/:id/employees` | Any | List employees |
| POST | `/admin/companies/:id/employees` | ADMIN+ | Add employee |
| PATCH | `/admin/companies/:id/employees/:eid` | ADMIN+ | Edit employee |
| PATCH | `/admin/companies/:id/employees/:eid/role` | ADMIN+ | Change role |
| DELETE | `/admin/companies/:id/employees/:eid` | ADMIN+ | Deactivate |
| POST | `/admin/companies/:id/employees/:eid/reset-password` | ADMIN+ | Trigger reset email |
| GET | `/admin/metrics` | Any | Platform MRR, churn, signups |
| GET | `/admin/admins` | SUPER_ADMIN | List admin users |
| POST | `/admin/admins` | SUPER_ADMIN | Create admin |
| PATCH | `/admin/admins/:id/role` | SUPER_ADMIN | Change admin role |
| DELETE | `/admin/admins/:id` | SUPER_ADMIN | Deactivate admin |
| GET | `/admin/audit-log` | SUPER_ADMIN | Audit log `?adminId=&targetType=&startDate=&endDate=` |
The OpenAPI file is useful for request/response contracts, but it is not yet a perfect mirror of every newer route. This document should be updated together with `app.ts`, route files, and the OpenAPI generator whenever route groups change.